Skip to content

Instantly share code, notes, and snippets.

@pasevin
Created May 8, 2026 11:22
Show Gist options
  • Select an option

  • Save pasevin/9e61aa4627959e3500dff71d44cc2fd7 to your computer and use it in GitHub Desktop.

Select an option

Save pasevin/9e61aa4627959e3500dff71d44cc2fd7 to your computer and use it in GitHub Desktop.

ERC-3643 / T-REX Teardown

1. Mental model

In normal ERC-20:

flowchart LR
  A[Wallet A] -->|transfer| T[ERC-20 Token]
  T --> B[Wallet B]
Loading

In ERC-3643:

flowchart LR
  A[Wallet A] -->|transfer request| T[T-REX Token]

  T --> IR[Identity Registry]
  T --> C[Compliance Contract]

  IR --> IRS[Identity Registry Storage]
  IR --> CTR[Claim Topics Registry]
  IR --> TIR[Trusted Issuers Registry]
  IRS --> ID[Investor ONCHAINID]
  ID --> Claims[Signed Claims]

  C --> Rules[Transfer Rules]

  T -->|only if verified + compliant| B[Wallet B]
Loading

The token does not trust wallets as identities. A wallet is just a signing address. ERC-3643 links wallets to ONCHAINID identity contracts, then checks whether those identities carry valid claims such as KYC, AML, investor accreditation, jurisdiction, or token-specific eligibility. (docs.erc3643.org)


2. The banking / regulated-finance concepts, translated

Jargon Plain meaning Blockchain equivalent
Issuer Entity creating the asset/token Token owner / issuer admin
Investor Person/company allowed to hold the token Wallet mapped to ONCHAINID
KYC “We know who this person/company is” Claim on ONCHAINID
AML “Not suspicious / not sanctioned / not laundering” Claim on ONCHAINID
Accredited investor Investor legally allowed to buy certain securities Eligibility claim
Jurisdiction Country/legal region rules Country claim + compliance rule
Transfer restriction Not everyone can receive/sell canTransfer() + identity checks
Registrar / transfer agent Party maintaining ownership records Token + Identity Registry + agents
Custodian Party holding assets for someone Could be wallet/ONCHAINID owner or institution
Primary issuance First sale/distribution Mint to verified investors
Secondary transfer Later investor-to-investor transfer Permissioned transfer()
Redemption Investor exits / burns token for off-chain asset/cash Burn + off-chain settlement

3. Full macro schema

flowchart TB
  subgraph Offchain["Off-chain real world"]
    Asset[Real-world asset\nEquity, bond, fund, real estate, invoice]
    Legal[Legal wrapper\nSPV, fund, issuer entity]
    KYC[KYC/AML provider\nClaim issuer]
    Regulator[Regulator / legal rules]
    Docs[Subscription docs\nInvestor files\nPrivate data]
  end

  subgraph Onchain["On-chain ERC-3643 / T-REX system"]
    Token[T-REX Token\nERC-20 compatible + permissioned]
    Compliance[Compliance Contract\nRules engine]
    IR[Identity Registry\nVerification gateway]
    IRS[Identity Registry Storage\nwallet => ONCHAINID]
    CTR[Claim Topics Registry\nrequired claim types]
    TIR[Trusted Issuers Registry\nallowed claim issuers]
    ONID[ONCHAINID\nidentity contract]
    Claims[Claims\nsigned attestations]
  end

  subgraph Actors["Actors"]
    Issuer[Issuer / Owner]
    Agent[Agent\nmint, burn, freeze, forced transfer]
    Investor[Investor wallet]
    ClaimIssuer[Trusted Claim Issuer]
  end

  Asset --> Legal --> Token
  Regulator --> Compliance
  Regulator --> CTR
  Issuer --> Token
  Issuer --> Compliance
  Issuer --> CTR
  Issuer --> TIR
  Agent --> Token

  Investor --> ONID
  ClaimIssuer --> Claims
  KYC --> ClaimIssuer
  Docs --> KYC
  Claims --> ONID

  Token --> IR
  Token --> Compliance
  IR --> IRS
  IR --> CTR
  IR --> TIR
  IRS --> ONID
  ONID --> Claims
Loading

ERC-3643 separates concerns: the Token handles balances and token lifecycle, the Identity Registry verifies participant eligibility, and the Compliance contract enforces transfer rules. (docs.erc3643.org)


4. Core contracts, very granular

A. Token contract

The token is ERC-20-like, but every meaningful token operation is gated.

flowchart TD
  Token[T-REX Token]

  Token --> ERC20[ERC-20 surface\nbalanceOf, transfer, approve, allowance]
  Token --> IdentityCheck[Identity check\nvia Identity Registry]
  Token --> ComplianceCheck[Compliance check\nvia Compliance]
  Token --> Admin[Admin controls\npause, freeze, recovery]
  Token --> Lifecycle[Lifecycle\nmint, burn, forcedTransfer]
  Token --> Metadata[Token info\nname, symbol, decimals, ONCHAINID]
Loading

Important capabilities: identity verification, compliance checks, pause, freeze, partial freeze, recovery from lost wallets, forced transfers, batch mint/burn/transfer/freeze. (docs.erc3643.org)


B. Identity Registry

This is the main “is this wallet allowed?” contract.

flowchart TD
  IR[Identity Registry]

  IR --> IRS[Identity Registry Storage]
  IR --> CTR[Claim Topics Registry]
  IR --> TIR[Trusted Issuers Registry]

  IRS --> Map[wallet => ONCHAINID]
  CTR --> Topics[Required claimsKYC, AML, accreditation, country]
  TIR --> Issuers[Allowed claim issuers]

  IR --> Verify[isVerified(wallet)]
Loading

isVerified(wallet) roughly means:

sequenceDiagram
  participant Token
  participant IR as Identity Registry
  participant IRS as Identity Registry Storage
  participant CTR as Claim Topics Registry
  participant TIR as Trusted Issuers Registry
  participant ID as ONCHAINID

  Token->>IR: isVerified(wallet)
  IR->>IRS: get ONCHAINID for wallet
  IRS-->>IR: ONCHAINID address
  IR->>CTR: get required claim topics
  CTR-->>IR: required topics
  IR->>TIR: get trusted issuers
  TIR-->>IR: trusted issuers
  IR->>ID: read claims
  ID-->>IR: signed claims
  IR->>IR: verify topics + issuer signatures
  IR-->>Token: true / false
Loading

The docs describe this exact pattern: fetch ONCHAINID, compare claims against required topics and trusted issuers, verify signatures, then return true or false. (docs.erc3643.org)


C. Identity Registry Storage

This is the address book.

erDiagram
  WALLET ||--|| ONCHAINID : maps_to
  IDENTITY_REGISTRY_STORAGE {
    address wallet
    address onchainid
    uint16 country
  }
Loading

Its job is simple but critical: map a wallet address to an ONCHAINID address so the system can treat a wallet as belonging to a verified identity. It can be shared by multiple Identity Registry contracts. (docs.erc3643.org)


D. ONCHAINID

ONCHAINID is an identity smart contract. It stores keys and claims, while private KYC data stays off-chain with the claim issuer; only signatures/hashes are on-chain. (docs.erc3643.org)

flowchart TD
  ONID[ONCHAINID]

  ONID --> Keys[Keys\nmanagement / permissions]
  ONID --> Claims[Claims\nERC-735 style attestations]
  ONID --> Exec[Can execute calls\nERC-734 style]
  Claims --> KYC[KYC claim]
  Claims --> AML[AML claim]
  Claims --> Country[Country / residency claim]
  Claims --> Accredited[Accredited investor claim]
  Claims --> TokenSpecific[Token-specific eligibility claim]
Loading

Think of it as a reusable on-chain passport. It can be used across multiple tokens if the token trusts the same issuers/topics.


E. Claim Topics Registry

This says what claims are required.

flowchart LR
  CTR[Claim Topics Registry] --> KYC[Topic 1: KYC]
  CTR --> AML[Topic 2: AML]
  CTR --> ACC[Topic 3: Accredited]
  CTR --> COUNTRY[Topic 4: Country]
  CTR --> SPECIAL[Topic N: Token-specific eligibility]
Loading

Each token can define its own required claim topics. For example, a private US fund token might require KYC + AML + accredited investor + not-US-sanctioned + specific subscription approval. (docs.erc3643.org)


F. Trusted Issuers Registry

This says who is allowed to issue claims.

flowchart TD
  TIR[Trusted Issuers Registry]
  TIR --> KYCProvider[KYC Provider A]
  TIR --> LawFirm[Law Firm B]
  TIR --> TransferAgent[Transfer Agent C]
  TIR --> Bank[Bank / regulated entity D]
Loading

A claim is not valid just because it exists. It must be issued by an entity trusted for that claim topic. (docs.erc3643.org)


G. Compliance contract

Identity answers: “Who are you, and are you eligible?”

Compliance answers: “Even if both parties are eligible, is this particular transfer allowed?”

flowchart TD
  C[Compliance Contract]

  C --> Can[canTransfer(from, to, amount)]
  C --> Hooks[State hooks]
  Hooks --> Transferred[transferred(from,to,amount)]
  Hooks --> Created[created(to,amount)]
  Hooks --> Destroyed[destroyed(from,amount)]

  C --> Rules[Compliance modules / rules]
  Rules --> MaxInvestors[Max number of investors]
  Rules --> CountryCap[Country ownership limits]
  Rules --> HoldingLimit[Per-investor holding cap]
  Rules --> Lockup[Lockup period]
  Rules --> SupplyLimit[Supply cap]
  Rules --> AgentOnly[Agent-only operations]
Loading

The interface includes canTransfer, transferred, created, destroyed, bindToken, and unbindToken. canTransfer is read-only; state changes happen through hooks like transferred, created, and destroyed. (docs.erc3643.org)


5. Transfer workflow: micro-level

sequenceDiagram
  participant A as Sender Wallet
  participant T as T-REX Token
  participant IR as Identity Registry
  participant C as Compliance
  participant B as Receiver Wallet

  A->>T: transfer(B, amount)

  T->>T: check not paused
  T->>T: check A not frozen
  T->>T: check amount not frozen

  T->>IR: isVerified(A)
  IR-->>T: true / false

  T->>IR: isVerified(B)
  IR-->>T: true / false

  T->>C: canTransfer(A, B, amount)
  C-->>T: true / false

  alt all checks pass
    T->>T: update balances
    T->>C: transferred(A, B, amount)
    T-->>A: Transfer event
  else any check fails
    T-->>A: revert
  end
Loading

A transfer can fail because:

flowchart TD
  Fail[Transfer fails] --> NotRegistered[Wallet not registered]
  Fail --> MissingClaim[ONCHAINID missing required claim]
  Fail --> BadIssuer[Claim not from trusted issuer]
  Fail --> ExpiredClaim[Claim invalid / expired / revoked]
  Fail --> ComplianceRule[Compliance rule blocks transfer]
  Fail --> Frozen[Address or amount frozen]
  Fail --> Paused[Token paused]
  Fail --> Balance[Insufficient transferable balance]
Loading

6. Investor onboarding workflow

sequenceDiagram
  participant Investor
  participant KYC as KYC / Claim Issuer
  participant ID as ONCHAINID
  participant IRS as Identity Registry Storage
  participant IR as Identity Registry
  participant Issuer

  Investor->>ID: create or provide ONCHAINID
  Investor->>KYC: submit private documents off-chain
  KYC->>KYC: verify identity / AML / eligibility
  KYC->>ID: add signed claim
  Issuer->>IRS: register wallet => ONCHAINID + country
  Issuer->>IR: identity now eligible if claims match
Loading

Private documents normally stay off-chain. The chain stores attestations/signatures/hashes, not passport scans. (docs.erc3643.org)


7. Issuance workflow

sequenceDiagram
  participant Issuer
  participant Token
  participant IR as Identity Registry
  participant C as Compliance
  participant Investor

  Issuer->>Token: mint(Investor, amount)
  Token->>IR: isVerified(Investor)
  IR-->>Token: true
  Token->>C: canTransfer(0x0, Investor, amount)
  C-->>Token: true
  Token->>Token: increase supply + investor balance
  Token->>C: created(Investor, amount)
  Token-->>Issuer: Transfer(0x0, Investor, amount)
Loading

Minting is not “free-for-all”; the recipient still needs to be verified and compliant.


8. Recovery workflow: lost wallet

This is one of the biggest differences from pure crypto assets.

sequenceDiagram
  participant Investor
  participant Agent
  participant Old as Old Wallet
  participant New as New Wallet
  participant IRS as Identity Registry Storage
  participant Token

  Investor->>Agent: prove old wallet lost off-chain
  Agent->>IRS: register New Wallet => same/new ONCHAINID
  Agent->>Token: recoveryAddress(Old, New, investorIdentity)
  Token->>Token: move balance Old -> New
  Token->>Token: freeze/clear Old as needed
  Token-->>Agent: RecoverySuccess
Loading

This supports the regulated-finance expectation that legal ownership can survive key loss. The docs explicitly mention token recovery from lost wallets. (docs.erc3643.org)


9. Freeze / forced transfer workflow

flowchart TD
  Start[Regulatory / operational reason] --> Agent[Authorized agent acts]
  Agent --> FullFreeze[Freeze address]
  Agent --> PartialFreeze[Freeze part of balance]
  Agent --> Forced[Forced transfer]
  FullFreeze --> Audit[Event emitted]
  PartialFreeze --> Audit
  Forced --> Checks[Usually to compliant address]
  Checks --> Audit
Loading

This is controversial from a crypto-native perspective, but normal for regulated assets. If a court order, fraud case, sanctions issue, or investor status change happens, the issuer/agent may need control functions.


10. The full object model

classDiagram
  class Token {
    name
    symbol
    decimals
    totalSupply
    balances
    identityRegistry
    compliance
    paused
    frozenAddresses
    frozenTokenAmounts
    transfer()
    mint()
    burn()
    forcedTransfer()
    recoveryAddress()
    freezePartialTokens()
  }

  class IdentityRegistry {
    identityStorage
    trustedIssuersRegistry
    claimTopicsRegistry
    isVerified(address)
    registerIdentity()
    deleteIdentity()
  }

  class IdentityRegistryStorage {
    walletToIdentity
    walletToCountry
    addIdentityToStorage()
    removeIdentityFromStorage()
  }

  class ONCHAINID {
    keys
    claims
    addClaim()
    removeClaim()
    execute()
  }

  class ClaimTopicsRegistry {
    requiredClaimTopics
    addClaimTopic()
    removeClaimTopic()
  }

  class TrustedIssuersRegistry {
    trustedIssuers
    addTrustedIssuer()
    removeTrustedIssuer()
  }

  class Compliance {
    boundToken
    modules/rules
    canTransfer()
    transferred()
    created()
    destroyed()
  }

  Token --> IdentityRegistry
  Token --> Compliance
  IdentityRegistry --> IdentityRegistryStorage
  IdentityRegistry --> ClaimTopicsRegistry
  IdentityRegistry --> TrustedIssuersRegistry
  IdentityRegistryStorage --> ONCHAINID
  ONCHAINID --> TrustedIssuersRegistry : claims signed by
Loading

11. How to think about it as a smart-contract engineer

The key design pattern is:

function transfer(address to, uint256 amount) public override returns (bool) {
    require(!paused, "token paused");
    require(!frozen[msg.sender], "sender frozen");
    require(availableBalance(msg.sender) >= amount, "insufficient unfrozen balance");

    require(identityRegistry.isVerified(msg.sender), "sender not verified");
    require(identityRegistry.isVerified(to), "receiver not verified");

    require(compliance.canTransfer(msg.sender, to, amount), "not compliant");

    _transfer(msg.sender, to, amount);

    compliance.transferred(msg.sender, to, amount);

    return true;
}

That is not exact source code, but it is the mental shape.

ERC-3643 is modular because identity requirements and compliance rules change per jurisdiction, issuer, and asset type. The standard defines interfaces for Token, Identity Registry, Identity Registry Storage, Compliance, Trusted Issuers Registry, and Claim Topics Registry. (Ethereum Improvement Proposals)


12. One-sentence summary

ERC-3643/T-REX is a permissioned ERC-20 architecture where balances are still on-chain, but holding/transferring tokens requires: registered identity + valid claims + trusted claim issuers + token-specific compliance rules + issuer/agent lifecycle controls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment