Identity Registry

ERC-3643 component acting as source of truth, mapping wallet addresses to verified on-chain identities and enabling compliance checks before transfers.

Identity Registry is the core ERC-3643 component that serves as the authoritative source of truth for investor verification, mapping wallet addresses to verified on-chain identities (ONCHAINID contracts). Before any security token transfer can execute, the token contract queries the Identity Registry to confirm that both sender and recipient are verified investors with valid credentials, enabling compliance enforcement at the protocol level.

The article describes this as part of ERC-3643's "tripartite system": "The Identity Registry: Acts as the source of truth, mapping wallet addresses to verified On-Chain Identities." This separation of concerns—token balances in one contract, identity verification in another—enables: modular upgrades (update identity logic without touching tokens), gas optimization (identity checks are external calls), and regulatory flexibility (different registries for different jurisdictions).

Architecture and Data Model

The Identity Registry maintains mappings between:

1// Core Identity Registry storage
2mapping(address => address) public identity; // wallet → ONCHAINID contract
3mapping(address => uint16) public investorCountry; // wallet → country code
4mapping(address => bool) public frozen; // wallet → freeze status
5
6// Trusted claim issuers for verification
7mapping(address => bool) public trustedIssuers;
8mapping(uint256 => address[]) public claimTopics; // topic → approved issuers

Wallet → Identity Mapping: Each verified wallet address points to an ONCHAINID contract. This decouples identity from the wallet—if a user loses their private key, they can link a new wallet to their existing identity without re-verification.

Country Codes: Stored for geographic compliance (sanctions screening, jurisdiction-specific rules). The Modular Compliance contract checks these codes against allowlists/blocklists.

Freeze Status: Per-wallet freeze capability for regulatory holds, investigations, or court orders. Frozen wallets cannot send or receive tokens.

Verification Flow

When a security token transfer is attempted, the Identity Registry verification follows this sequence:

1// Token contract calls Identity Registry
2function transfer(address to, uint256 amount) public override returns (bool) {
3 // Step 1: Check recipient is registered
4 require(identityRegistry.isVerified(to), "Recipient not verified");
5
6 // Step 2: Check recipient has required claims
7 require(identityRegistry.hasValidClaim(to, KYC_TOPIC), "Missing KYC");
8
9 // Step 3: Check neither party is frozen
10 require(!identityRegistry.isFrozen(msg.sender), "Sender frozen");
11 require(!identityRegistry.isFrozen(to), "Recipient frozen");
12
13 // Step 4: Proceed with compliance module check
14 require(compliance.canTransfer(msg.sender, to, amount), "Not compliant");
15
16 _transfer(msg.sender, to, amount);
17 return true;
18}

The Identity Registry answers: "Is this wallet linked to a verified identity?" and "Does that identity have the required credentials (claims)?" The Compliance Module then answers: "Does this specific transfer violate any active rules?"

Claim-Based Verification

Identity Registry verification relies on cryptographic claims stored in ONCHAINID contracts:

Claim Structure:

  • Topic: What the claim asserts (KYC verified, accredited investor, country of residence)
  • Issuer: Who signed the claim (trusted third party like a KYC provider)
  • Signature: Cryptographic proof the issuer made this assertion
  • Data Hash: Reference to off-chain data (actual documents stored privately)

The Identity Registry checks: Is the claim from a trusted issuer for this topic? Is the claim signature valid? Has the claim expired or been revoked?

1function hasValidClaim(address wallet, uint256 topic) public view returns (bool) {
2 address identityContract = identity[wallet];
3 require(identityContract != address(0), "No identity");
4
5 // Get claim from ONCHAINID
6 (uint256 claimTopic, , address issuer, bytes memory sig, bytes memory data, ) =
7 IIdentity(identityContract).getClaim(claimId);
8
9 // Verify issuer is trusted for this topic
10 require(isTrustedIssuer(issuer, topic), "Untrusted issuer");
11
12 // Verify signature (claim authenticity)
13 require(verifySignature(issuer, claimTopic, data, sig), "Invalid signature");
14
15 return true;
16}

Identity Registry Operations

Registration: Authorized agents register new identities:

1function registerIdentity(
2 address wallet,
3 address identityContract,
4 uint16 country
5) external onlyAgent {
6 require(identity[wallet] == address(0), "Already registered");
7 identity[wallet] = identityContract;
8 investorCountry[wallet] = country;
9 emit IdentityRegistered(wallet, identityContract, country);
10}

Identity Updates: Users can update their linked wallet (key rotation) without re-verification:

1function updateIdentity(
2 address oldWallet,
3 address newWallet
4) external {
5 // Caller must be the identity contract itself (via management key)
6 require(msg.sender == identity[oldWallet], "Not identity owner");
7
8 identity[newWallet] = identity[oldWallet];
9 investorCountry[newWallet] = investorCountry[oldWallet];
10 delete identity[oldWallet];
11
12 emit IdentityUpdated(oldWallet, newWallet);
13}

Freezing: Agents can freeze wallets for compliance holds:

1function setFrozen(address wallet, bool status) external onlyAgent {
2 frozen[wallet] = status;
3 emit FreezeStatusSet(wallet, status);
4}

Security Considerations

Agent Privilege Abuse: Identity Registry agents can: register arbitrary addresses as verified, freeze legitimate investors, update identities to attacker-controlled contracts, and manipulate country codes. Mitigation: multi-sig agent roles, timelock on sensitive operations, event monitoring for anomalies.

Trusted Issuer Compromise: If a trusted claim issuer is compromised, attackers can forge claims for any identity. The article notes: "Do not store PII on the blockchain. Instead, a trusted 3rd party signs a hash (a Claim) validating the user." If that third party is compromised, the entire verification system fails. Mitigation: multiple required issuers, claim expiration, revocation checking.

Identity Squatting: Malicious actors could register identities for addresses they don't control, potentially blocking legitimate users from registering. Mitigation: require proof of wallet ownership during registration.

Country Code Manipulation: If country codes can be arbitrarily set, attackers could bypass geographic restrictions. Mitigation: country codes should only be settable based on verified claims from trusted issuers, not arbitrary agent input.

Frozen State Bypass: Ensure frozen checks occur on both sender AND recipient, and that frozen status cannot be bypassed through: batch transfers, approvals set before freezing, or other transfer paths.

Audit Checklist for Identity Registries

  1. Registration Security: Who can register identities? What verification is required?
  2. Agent Powers: What can agents do? Are powers appropriately limited?
  3. Freeze Implementation: Are all transfer paths checked for freeze status?
  4. Claim Verification: How are claims validated? Can forged claims pass?
  5. Trusted Issuer Management: How are issuers added/removed? What's the attack surface?
  6. Identity Updates: Can identity updates be exploited to bypass verification?
  7. Country Code Integrity: How are country codes set and validated?
  8. Event Emissions: Are all state changes properly logged for compliance auditing?

Integration with Token Contracts

The Identity Registry must be called by the token contract before every transfer:

1// In ERC-3643 Token Contract
2IIdentityRegistry public identityRegistry;
3
4modifier onlyVerified(address wallet) {
5 require(identityRegistry.isVerified(wallet), "Not verified");
6 _;
7}
8
9function transfer(address to, uint256 amount)
10 public
11 onlyVerified(msg.sender)
12 onlyVerified(to)
13 returns (bool)
14{
15 // Additional compliance checks...
16 _transfer(msg.sender, to, amount);
17 return true;
18}

Gas Considerations: Identity checks are external calls, adding gas costs to every transfer. Implementations should: minimize storage reads, batch verification where possible, and consider caching for frequently-verified addresses (with invalidation logic).

Multi-Token Registries

A single Identity Registry can serve multiple security tokens:

1// Shared registry for multiple tokens
2mapping(address => mapping(address => bool)) public tokenVerified;
3// token address → wallet → verified for this specific token
4
5function isVerifiedForToken(address token, address wallet) public view returns (bool) {
6 // Check global verification
7 if (!isVerified(wallet)) return false;
8
9 // Check token-specific requirements
10 return tokenVerified[token][wallet];
11}

This enables: single KYC verification serving multiple tokens, token-specific additional requirements, and efficient identity management across token portfolios.

Compliance Reporting

Identity Registry data supports regulatory reporting:

Holder Reports: Query all registered identities with their country codes for investor demographics.

Transfer Logs: Combined with token transfer events, provides complete audit trail of who transferred to whom.

Freeze History: Track all freeze/unfreeze events for regulatory inquiries.

Claim Status: Query current claim validity for compliance verification.

Understanding the Identity Registry is essential for auditing ERC-3643 security tokens. It's the foundation of the compliance architecture—if the Identity Registry can be bypassed or manipulated, the entire permissioned model fails. Auditors must verify that: all transfer paths query the registry, agent powers are appropriately restricted, claim verification is robust, and state changes are properly logged.

Need expert guidance on Identity Registry?

Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.

Get a Quote

oog
zealynx

Subscribe to Our Newsletter

Stay updated with our latest security insights and blog posts

© 2024 Zealynx