ERC7984 Confidential Token
Overview
Quick Start
# Create new project from this template
npx labz create erc7984-token my-project
# Navigate and install
cd my-project
npm install
# Run tests
npx hardhat testContract
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.27;
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {FHE, externalEuint64, euint64} from "@fhevm/solidity/lib/FHE.sol";
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
import {ERC7984} from "@openzeppelin/confidential-contracts/token/ERC7984/ERC7984.sol";
/// @title ERC7984Example - Confidential Fungible Token
/// @notice A confidential token where balances and transfers are encrypted using FHE
/// @dev Extends OpenZeppelin's ERC7984 base implementation with minting/burning capabilities
contract ERC7984Example is ZamaEthereumConfig, ERC7984, Ownable2Step {
// ============ Events ============
/// @notice Emitted when tokens are minted
event TokensMinted(address indexed to, uint64 amount);
/// @notice Emitted when tokens are burned (confidential amount)
event TokensBurned(address indexed from);
// ============ Constructor ============
/// @param owner The initial owner of the contract
/// @param initialSupply Initial token supply to mint to owner
/// @param name_ Token name
/// @param symbol_ Token symbol
/// @param tokenURI_ Token metadata URI
constructor(
address owner,
uint64 initialSupply,
string memory name_,
string memory symbol_,
string memory tokenURI_
) ERC7984(name_, symbol_, tokenURI_) Ownable(owner) {
if (initialSupply > 0) {
euint64 encryptedAmount = FHE.asEuint64(initialSupply);
_mint(owner, encryptedAmount);
}
}
// ============ Minting Functions ============
/// @notice Mint tokens with a visible amount (for public mints)
/// @param to Recipient address
/// @param amount Amount to mint (visible in calldata)
function mint(address to, uint64 amount) external onlyOwner {
_mint(to, FHE.asEuint64(amount));
emit TokensMinted(to, amount);
}
/// @notice Mint tokens with an encrypted amount (for private mints)
/// @param to Recipient address
/// @param encryptedAmount Encrypted amount from client
/// @param inputProof Proof validating the encrypted input
function confidentialMint(
address to,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) external onlyOwner returns (euint64 transferred) {
transferred = _mint(to, FHE.fromExternal(encryptedAmount, inputProof));
emit TokensBurned(to);
}
// ============ Burning Functions ============
/// @notice Burn tokens with a visible amount
/// @param from Address to burn from
/// @param amount Amount to burn (visible in calldata)
function burn(address from, uint64 amount) external onlyOwner {
_burn(from, FHE.asEuint64(amount));
emit TokensBurned(from);
}
/// @notice Burn tokens with an encrypted amount
/// @param from Address to burn from
/// @param encryptedAmount Encrypted amount from client
/// @param inputProof Proof validating the encrypted input
function confidentialBurn(
address from,
externalEuint64 encryptedAmount,
bytes calldata inputProof
) external onlyOwner returns (euint64 transferred) {
transferred = _burn(from, FHE.fromExternal(encryptedAmount, inputProof));
emit TokensBurned(from);
}
// ============ View Functions ============
/// @notice Get token decimals (ERC7984 uses 6 decimals by default)
function decimals() public view virtual override returns (uint8) {
return 6;
}
/// @notice Check if an address has any balance
/// @param account Address to check
/// @return True if balance is initialized (may still be zero)
function hasBalance(address account) external view returns (bool) {
euint64 balance = confidentialBalanceOf(account);
return FHE.isInitialized(balance);
}
}
Code Explanation
Inheritance
Constructor
Mint
Confidential Transfer
Operators
FHE Operations Used
FHE Types Used
Tags
Related Examples
Prerequisites
Next Steps
Last updated
