Skip to content

Start typing to search the documentation.

Smart Contracts

Self Agent ID deploys 9 contracts on Celo. The core registry implements IERC8004 and the IERC8004ProofOfHuman extension.

Contract Overview

ContractRole
SelfAgentRegistryCore registry — ERC-721 soulbound NFTs, IERC8004 + IERC8004ProofOfHuman, registration modes, credentials, proof expiry, sybil detection (UUPS upgradeable)
SelfHumanProofProviderImplements IHumanProofProvider — metadata wrapper connecting to Self Protocol Hub V2 (verification strength: 100)
SelfReputationRegistryERC-8004 Reputation Registry — aggregated feedback with document-type weighted signals (UUPS upgradeable)
SelfValidationRegistryERC-8004 Validation Registry — on-demand validation requests/responses from third parties (UUPS upgradeable)
AgentDemoVerifierEIP-712 meta-transaction verifier for gasless on-chain agent verification
AgentGateAbstract contract with onlyVerifiedAgent modifier for gating access
LocalRegistryHarnessLocal mock for testing without Hub V2 dependency

Deployed Addresses

Celo Mainnet (42220)

ContractAddress
SelfAgentRegistry (proxy)0xaC3DF9ABf80d0F5c020C06B04Cced27763355944
SelfHumanProofProvider0x4b036aFD959B457A208F676cf44Ea3ef73Ea3E3d
SelfReputationRegistry (proxy)0x69Da18CF4Ac27121FD99cEB06e38c3DC78F363f4
SelfValidationRegistry (proxy)0x71a025e0e338EAbcB45154F8b8CA50b41e7A0577
AgentDemoVerifier0xD8ec054FD869A762bC977AC328385142303c7def
AgentGate0x26e05bF632fb5bACB665ab014240EAC1413dAE35
Hub V20xe57F4773bd9c9d8b6Cd70431117d353298B9f5BF

Celo Sepolia (11142220)

ContractAddress
SelfAgentRegistry (proxy)0x043DaCac8b0771DD5b444bCC88f2f8BBDBEdd379
SelfHumanProofProvider0x5E61c3051Bf4115F90AacEAE6212bc419f8aBB6c
SelfReputationRegistry (proxy)0x3Bb0A898C1C0918763afC22ff624131b8F420CC2
SelfValidationRegistry (proxy)0x84cA20B8A1559F136dA03913dbe6A7F68B6B240B
AgentDemoVerifier0xc31BAe8f2d7FCd19f737876892f05d9bDB294241
AgentGate0x86Af07e30Aa42367cbcA7f2B1764Be346598bbc2
Hub V20x16ECBA51e18a4a7e61fdC417f0d47AFEeDfbed74

ERC-8004 Interfaces

The registry implements two standard interfaces:

IERC8004 (Base Identity Registry)

// Registration (3 overloads)
function register() external returns (uint256 agentId);
function register(string calldata agentURI) external returns (uint256 agentId);
function register(string calldata agentURI, string[] calldata keys, bytes[] calldata values) external returns (uint256 agentId);

// Agent URI
function setAgentURI(uint256 agentId, string calldata newURI) external;

// Metadata (key-value store)
function getMetadata(uint256 agentId, string memory key) external view returns (bytes memory);
function setMetadata(uint256 agentId, string calldata key, bytes calldata value) external;

// Agent Wallet (EIP-712 signature required from newWallet)
function setAgentWallet(uint256 agentId, address newWallet, uint256 deadline, bytes calldata signature) external;
function getAgentWallet(uint256 agentId) external view returns (address);
function unsetAgentWallet(uint256 agentId) external;

IERC8004ProofOfHuman (Extension)

// Register with human proof from approved provider
function registerWithHumanProof(string calldata agentURI, address proofProvider, bytes calldata proof, bytes calldata providerData) external returns (uint256 agentId);

// Revoke proof (requires re-proving same human)
function revokeHumanProof(uint256 agentId, address proofProvider, bytes calldata proof, bytes calldata providerData) external;

// Proof queries
function hasHumanProof(uint256 agentId) external view returns (bool);
function proofExpiresAt(uint256 agentId) external view returns (uint256);
function isProofFresh(uint256 agentId) external view returns (bool);
function getHumanNullifier(uint256 agentId) external view returns (uint256);
function getProofProvider(uint256 agentId) external view returns (address);
function isApprovedProvider(address provider) external view returns (bool);

// Sybil detection
function sameHuman(uint256 agentIdA, uint256 agentIdB) external view returns (bool);
function getAgentCountForHuman(uint256 nullifier) external view returns (uint256);

Key Interfaces

Reading Agent State

// Check if an agent is verified
bool verified = registry.isVerifiedAgent(agentKey);

// Get agent ID from key
uint256 agentId = registry.getAgentId(agentKey);

// Get proof provider
address provider = registry.getProofProvider(agentId);

// Get ZK-attested credentials
AgentCredentials memory creds = registry.getAgentCredentials(agentId);

// Check proof freshness and expiry
bool fresh = registry.isProofFresh(agentId);
uint256 expiresAt = registry.proofExpiresAt(agentId);

Sybil Detection

// Check if two agents share the same human
bool same = registry.sameHuman(agentId1, agentId2);

// Count agents for a nullifier
uint256 count = registry.getAgentCountForHuman(nullifier);

Metadata & Agent URI

// Set/get agent URI (ERC-8004)
registry.setAgentURI(agentId, "https://example.com/agent.json");

// Set/get metadata key-value pairs
registry.setMetadata(agentId, "name", abi.encode("My Agent"));
bytes memory value = registry.getMetadata(agentId, "name");

// Set agent wallet (requires EIP-712 signature from newWallet)
registry.setAgentWallet(agentId, newWallet, deadline, signature);

Gating Access

Use AgentGate as a base contract:

import { AgentGate } from "./AgentGate.sol";

contract MyProtocol is AgentGate {
    constructor(address _registry) AgentGate(_registry) {}

    function protectedAction() external onlyVerifiedAgent {
        // Only verified agents can call this
        bytes32 agentKey = bytes32(uint256(uint160(msg.sender)));
        uint256 agentId = registry.getAgentId(agentKey);
        // ... your logic
    }
}

EIP-712 Meta-Transactions

The AgentDemoVerifier contract accepts EIP-712 signed messages for gasless verification:

// Domain: { name: "AgentDemoVerifier", version: "1", chainId, verifyingContract }
// Type: MetaVerify(bytes32 agentKey, uint256 nonce, uint256 deadline)

function metaVerify(
    bytes32 agentKey,
    uint256 nonce,
    uint256 deadline,
    bytes calldata signature
) external;

Proof Expiry

Each agent’s human proof has a validity window. After expiry, isProofFresh() returns false and the agent must re-authenticate.

// Check if proof is still valid
bool fresh = registry.isProofFresh(agentId);

// Get the expiry timestamp
uint256 expiresAt = registry.proofExpiresAt(agentId);

The expiry is calculated as the minimum of:

  • Document expiry date (from passport)
  • Registration timestamp + maxProofAge (default: 1 year)

Reputation Registry

The SelfReputationRegistry is a standalone ERC-8004 Reputation Registry scoped to the SelfAgentRegistry. It stores aggregated feedback from clients with document-type weighted signals.

// Give feedback on an agent
reputationRegistry.giveFeedback(agentId, value, decimals, tag1, tag2, endpoint, uri, hash);

// Agent responds to feedback
reputationRegistry.appendResponse(agentId, clientAddress, feedbackIndex, uri, hash);

// Get aggregated summary
(uint256 count, int256 sum, uint8 decimals) = reputationRegistry.getSummary(agentId, clients, tag1, tag2);

Document-type weights (auto-assigned at registration):

DocumentWeightTag
E-Passport (NFC)100passport-nfc
EU ID Card (NFC)100id-card-nfc
Aadhaar80aadhaar
KYC50kyc

Validation Registry

The SelfValidationRegistry is a standalone ERC-8004 Validation Registry for on-demand validation requests.

// Request validation from a third party
bytes32 requestHash = validationRegistry.requestValidation(agentId, validator, requestURI);

// Validator responds
validationRegistry.respondValidation(requestHash, response, responseURI, tag);

// Get validation summary
(uint256 count, uint256 avgResponse) = validationRegistry.getSummary(agentId, validators, tag);

Deregistration

Three paths to deregister an agent (all burn the soulbound NFT):

  1. Hub V2 callback_deregisterAgent() triggered by a new deregistration proof
  2. Self-deregisterselfDeregister(agentId) called by the NFT owner
  3. Guardian revokeguardianRevoke(agentId) called by the agent’s guardian

All converge on _revokeAgent() which clears all state (credentials, nullifier, provider, guardian, metadata) and burns the NFT.

Governance

The registry uses role-based governance with two multisig wallets:

RoleCapability
SECURITY_ROLEApprove proof providers, set verification configs, set max proof age
OPERATIONS_ROLEGrant roles, link reputation/validation registries

Integration with Self Protocol

The SelfHumanProofProvider connects to Self Protocol’s Identity Verification Hub V2. See the Working with userDefinedData guide for details on how the registration data is encoded.

All core registries (SelfAgentRegistry, SelfReputationRegistry, SelfValidationRegistry) are deployed as UUPS proxies with ERC-7201 namespaced storage, enabling safe upgrades without storage collision.