> ## Documentation Index
> Fetch the complete documentation index at: https://cofhe-docs.fhenix.zone/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> How an FHERC20 token stores encrypted balances, what stays public, and how to mint, burn, and read balances

FHERC20 is a token whose balances exist only as encrypted values. It implements the confidential token interface from [ERC-7984](https://eips.ethereum.org/EIPS/eip-7984) and keeps a read-only ERC-20 surface so wallets and explorers still recognize it. Read this page to build an FHERC20 token and to understand what it reveals.

If your token must also keep working as a normal ERC-20, use [ERC20Confidential](/cofhe/fhe-library/confidential-contracts/dual-mode/overview) instead. To make an existing ERC-20 or ETH confidential, use a [wrapper](/cofhe/fhe-library/confidential-contracts/fherc20/fherc20-wrapper).

## What is encrypted and what is public?

Each balance is an `euint64` handle (a reference to an encrypted value). The total supply is an `euint64` too. Transfer amounts are encrypted inputs or handles, never plaintext.

What anyone can still see:

* The sender and recipient of every transfer, from the `ConfidentialTransfer` event.
* That an account has never held the token. A transfer or burn from such an account reverts with `FHERC20ZeroBalance`, because its balance handle was never created.
* Transfer activity per account: the indicator values below move by one step per transfer received or sent.
* Amounts you pass in plaintext, such as the `uint64` argument of a mint function you write.

## What do the ERC-20 functions return?

`balanceOf` and `totalSupply` return an indicator, not a balance. The indicator tells a wallet that something changed without saying how much.

| Function | Returns |
| - | - |
| <code style={{ whiteSpace: "nowrap" }}>balanceOf</code> | `0` for an account that never interacted. Otherwise starts at `7984.0000` plus `0.0001` per received transfer, minus `0.0001` per sent transfer |
| <code style={{ whiteSpace: "nowrap" }}>totalSupply</code> | Starts at `7984.0000`, plus `0.0001` per mint, minus `0.0001` per burn |
| <code style={{ whiteSpace: "nowrap" }}>balanceOfIsIndicator</code> | `true` |
| <code style={{ whiteSpace: "nowrap" }}>indicatorTick</code> | The raw size of one `0.0001` step: `10 ** (decimals - 4)`, or `1` when `decimals` is 4 or less |
| <code style={{ whiteSpace: "nowrap" }}>transfer</code>, <code style={{ whiteSpace: "nowrap" }}>transferFrom</code>, <code style={{ whiteSpace: "nowrap" }}>approve</code>, <code style={{ whiteSpace: "nowrap" }}>allowance</code> | Revert with `FHERC20IncompatibleFunction` |

The values read as `7984.xxxx` when `decimals` is 4 or more. A token with 6 decimals reports a first-time recipient as `7984000100` raw, which a wallet shows as `7984.0001`.

Every transfer, mint, and burn also emits the standard ERC-20 `Transfer` event with a fixed value of `7984.0001`, so explorers index the activity. The real amount is in `ConfidentialTransfer`, as a handle.

An account can call `resetIndicatedBalance()` to set its own indicator back to `0`.

## Who can decrypt a balance?

The token grants access to every new balance handle for two accounts: the token itself and the holder. Nobody else can decrypt a balance unless your contract grants access with `FHE.allow`.

To show a balance in your app, read the handle and decrypt it with the SDK:

```typescript theme={null}
import { FheTypes } from '@cofhe/sdk';

const handle = await token.confidentialBalanceOf(account);
const balance = await client.decryptForView(handle, FheTypes.Uint64).execute();
```

`decryptForView` signs with an [Access Control Permission](/cofhe/client-sdk/guides/acps) (ACP) for the connected account. See [decrypt to view](/cofhe/client-sdk/guides/decrypt-to-view) for setting one up.

`confidentialBalanceOf` returns a handle, not an amount. A handle you read once goes stale after the next transfer, because every balance change creates a new handle.

## How do you mint and burn?

`FHERC20` exposes no public mint or burn. Your contract calls the internal functions and decides who may use them:

| Function | Effect |
| - | - |
| <code style={{ whiteSpace: "nowrap" }}>\_mint</code> | `_mint(address to, euint64 amount)` adds `amount` to `to` and to the total supply |
| <code style={{ whiteSpace: "nowrap" }}>\_burn</code> | `_burn(address from, euint64 amount)` removes `amount` from `from` and from the total supply |

Both return the encrypted amount that actually moved. Neither reverts on an encrypted condition:

* A burn larger than the balance burns zero.
* A mint that would overflow the 64-bit total supply mints zero.

This token mints and burns from encrypted inputs, so the amounts stay private:

```solidity RewardPoints.sol theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import { FHE, externalEuint64 } from "@fhenixprotocol/cofhe-contracts/FHE.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { FHERC20 } from "fhenix-confidential-contracts/contracts/FHERC20/FHERC20.sol";

contract RewardPoints is FHERC20, Ownable {
    constructor() FHERC20("Reward Points", "ePTS", 6, "") Ownable(msg.sender) {}

    function mint(address to, externalEuint64 amount, bytes calldata inputProof) external onlyOwner {
        _mint(to, FHE.asEuint64(amount, inputProof));
    }

    function burn(address from, externalEuint64 amount, bytes calldata inputProof) external onlyOwner {
        _burn(from, FHE.asEuint64(amount, inputProof));
    }
}
```

The constructor takes the name, symbol, decimals, and a contract URI ([ERC-7572](https://eips.ethereum.org/EIPS/eip-7572)). Pass an empty string if you have no URI. `FHERC20` does not need [the shared library](/cofhe/fhe-library/confidential-contracts/overview#link-the-shared-library), so it deploys like any other contract.

<Note>
  A mint function that takes a plaintext `uint64` works too, but the amount is visible in the transaction's calldata.
</Note>

## Which variants exist?

| Contract | Use it for |
| - | - |
| <code style={{ whiteSpace: "nowrap" }}>FHERC20</code> | A token deployed with a constructor |
| <code style={{ whiteSpace: "nowrap" }}>FHERC20Upgradeable</code> | The same token behind a proxy. See [upgradeable tokens](/cofhe/fhe-library/confidential-contracts/upgradeable) |
| <code style={{ whiteSpace: "nowrap" }}>FHERC20ERC20Wrapper</code> | An FHERC20 backed by an existing ERC-20 |
| <code style={{ whiteSpace: "nowrap" }}>FHERC20NativeWrapper</code> | An FHERC20 backed by ETH |

`supportsInterface` reports `IFHERC20`, `IERC7984`, `IERC20`, and `IERC165`.

## Next steps

* [Transfers and disclosure](/cofhe/fhe-library/confidential-contracts/fherc20/core-features): send tokens and make an amount public.
* [Operators](/cofhe/fhe-library/confidential-contracts/fherc20/operators): let another account move tokens for a holder.
* [Transfer callbacks](/cofhe/fhe-library/confidential-contracts/fherc20/transfer-callbacks): notify a receiving contract in the same transaction.
* [Best practices](/cofhe/fhe-library/confidential-contracts/fherc20/best-practices): mistakes that leak amounts or lose funds.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.