ERC7984 Standard

Confidential ERC20-compatible token using the ERC7984 standard. Supports encrypted balances and transfers with optional private minting, serving as a foundation for privacy-focused DeFi applications.

circle-info

To run this example correctly, make sure the files are placed in the following directories:

  • .sol file โ†’ <your-project-root-dir>/contracts/

  • .ts file โ†’ <your-project-root-dir>/test/

This ensures Hardhat can compile and test your contracts as expected.

chevron-right๐Ÿ” FHE API Reference (5 items)hashtag

Types: euint64 ยท externalEuint64

Functions:

  • FHE.allow() - Grants PERMANENT permission for address to decrypt/use value

  • FHE.asEuint64() - Encrypts a plaintext uint64 value into euint64

  • FHE.fromExternal() - Validates and converts external encrypted input using inputProof

// 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 as ERC7984Base
} from "@openzeppelin/confidential-contracts/token/ERC7984/ERC7984.sol";

/**
 * @notice Confidential ERC20-compatible token using the ERC7984 standard.
 *         Supports encrypted balances and transfers with optional private minting,
 *         serving as a foundation for privacy-focused DeFi applications.
 *
 * @dev Combines standard ERC20 logic with FHE-based confidential operations.
 */
contract ERC7984Example is ZamaEthereumConfig, ERC7984Base, Ownable2Step {
    /// @notice Creates a new confidential ERC7984 token with initial supply
    /// @param owner Address that will own the token and receive initial supply
    /// @param amount Initial supply amount (visible on-chain but stored encrypted)
    /// @param name_ Token name
    /// @param symbol_ Token symbol
    /// @param tokenURI_ Token metadata URI
    constructor(
        address owner,
        uint64 amount,
        string memory name_,
        string memory symbol_,
        string memory tokenURI_
    ) ERC7984Base(name_, symbol_, tokenURI_) Ownable(owner) {
        // ๐Ÿ” Mint initial supply as encrypted amount
        euint64 encryptedAmount = FHE.asEuint64(amount);
        _mint(owner, encryptedAmount);
    }

    // ==================== MINTING ====================

    /// @notice Mint with visible amount (owner knows the amount)
    /// @dev Amount is visible on-chain but stored encrypted
    function mint(address to, uint64 amount) external onlyOwner {
        _mint(to, FHE.asEuint64(amount));
    }

    /// @notice Mint with encrypted amount (full privacy)
    /// @dev Even the contract doesn't know the minted amount
    function confidentialMint(
        address to,
        externalEuint64 encryptedAmount,
        bytes calldata inputProof
    ) external onlyOwner returns (euint64 transferred) {
        return _mint(to, FHE.fromExternal(encryptedAmount, inputProof));
    }

    // ==================== BURNING ====================

    /// @notice Burn with visible amount
    function burn(address from, uint64 amount) external onlyOwner {
        _burn(from, FHE.asEuint64(amount));
    }

    /// @notice Burn with encrypted amount (full privacy)
    function confidentialBurn(
        address from,
        externalEuint64 encryptedAmount,
        bytes calldata inputProof
    ) external onlyOwner returns (euint64 transferred) {
        return _burn(from, FHE.fromExternal(encryptedAmount, inputProof));
    }

    // ==================== INTERNAL ====================

    /// @dev Grant owner access to total supply on every transfer
    function _update(
        address from,
        address to,
        euint64 amount
    ) internal virtual override returns (euint64 transferred) {
        transferred = super._update(from, to, amount);
        // Allow owner to decrypt total supply
        FHE.allow(confidentialTotalSupply(), owner());
    }
}

Last updated