ERC7984ObserverAccessExample

Category: Identity | Difficulty: Intermediate | Chapters: Erc7984 | Concept: ERC7984ObserverAccess for opt-in audit / compliance observers

Travel-Rule style observer access for confidential token balances and transfer amounts

Why this example

This example focuses on ERC7984ObserverAccess for opt-in audit / compliance observers. It is designed to be self-contained and easy to run locally.

Quick start

npm install
npm run test:mocked -- test/identity/ERC7984ObserverAccessExample.test.ts

Dependencies

None

Deployment plan

Step
Contract
Args
Saves As

1

ERC7984ObserverAccessExample

$deployer

token

Contract and test

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {FHE, externalEuint64} from "@fhevm/solidity/lib/FHE.sol";
import {ZamaEthereumConfig} from "@fhevm/solidity/config/ZamaConfig.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC7984} from "@openzeppelin/confidential-contracts/token/ERC7984/ERC7984.sol";
// solhint-disable-next-line max-line-length
import {ERC7984ObserverAccess} from "@openzeppelin/confidential-contracts/token/ERC7984/extensions/ERC7984ObserverAccess.sol";

/**
 * @title ERC7984ObserverAccessExample
 * @author Gustavo Valverde
 * @notice Travel-Rule style observer access for confidential token balances and transfer amounts
 * @dev Example for fhEVM Examples - OpenZeppelin Confidential Contracts
 *
 * @custom:category identity
 * @custom:chapter erc7984
 * @custom:concept ERC7984ObserverAccess for opt-in audit / compliance observers
 * @custom:difficulty intermediate
 * @custom:deploy-plan [{"contract":"ERC7984ObserverAccessExample","saveAs":"token","args":["$deployer"]}]
 *
 * Production alignment:
 * - Users may need to grant a VASP/compliance officer limited visibility for audits / Travel Rule.
 * - `ERC7984ObserverAccess` lets a user opt-in an "observer" that gets ACL access to:
 *   - the user's confidential balance handle
 *   - transfer amount handles involving the user
 *
 * Important pitfall:
 * - ACL grants are permanent for a given ciphertext handle: removing an observer stops *future* grants,
 *   but does not revoke access to ciphertext handles already shared earlier.
 */
contract ERC7984ObserverAccessExample is ERC7984ObserverAccess, Ownable, ZamaEthereumConfig {
    /**
     * @notice Initializes the Observer Access ERC7984 token
     * @param initialOwner The address that will own the contract
     */
    constructor(address initialOwner)
        ERC7984("Observer Access Token", "OAT", "ipfs://observer-access")
        Ownable(initialOwner)
    {}

    /**
     * @notice Mint confidential tokens (owner-only)
     * @param to Recipient
     * @param amount Encrypted amount
     * @param inputProof Proof for the encrypted input
     */
    function mint(address to, externalEuint64 amount, bytes calldata inputProof) external onlyOwner {
        _mint(to, FHE.fromExternal(amount, inputProof));
    }
}

Pitfalls to avoid

  • should not grant future access after observer removal, but old ciphertext stays decryptable

API Reference

Overview

Travel-Rule style observer access for confidential token balances and transfer amounts

Developer Notes

Example for fhEVM Examples - OpenZeppelin Confidential Contracts

constructor

Initializes the Observer Access ERC7984 token

Parameters

Name
Type
Description

initialOwner

address

The address that will own the contract

mint

Mint confidential tokens (owner-only)

Parameters

Name
Type
Description

to

address

Recipient

amount

externalEuint64

Encrypted amount

inputProof

bytes

Proof for the encrypted input

Last updated