Think of it as a
passport system for permissioned tokens
ONCHAINID stores identity keys and claims, while ERC-3643 asks, before minting or transferring, whether a wallet belongs to an identity that has the right attestations.
Why this exists
Plain ERC-20 tokens only know balances and allowances. Permissioned real-world-asset tokens usually need more: who the holder is, which claims they have, which issuers are trusted, and whether token rules should allow minting or transfer. ERC-3643 solves that by pairing a token with identity and compliance contracts. ONCHAINID is the identity layer inside that stack.
// token only sees addresses
function mint(address to, uint256 amount) external onlyOwner
_mint(to, amount);
- No built-in concept of verified investor identity
- No standard place for KYC or accreditation attestations
- No issuer trust list, claim topic list, or regulated transfer gate
// ERC-3643 token checks identity registry first
function mint(address to, uint256 amount) public onlyAgent
require(identityRegistry.isVerified(to), "Identity is not verified.");
_mint(to, amount);
- Every wallet can map to an ONCHAINID contract
- Claims can prove facts like KYC passed or investor accredited
- Only claims from trusted issuers on required topics count
The stack at a glance
Core components
1. Identity
A contract that implements ERC-734 key management and ERC-735 claims.
2. ClaimIssuer
A specialized identity that can validate and revoke claims signed by its claim keys.
3. IdFactory
Deploys user or token ONCHAINIDs via CREATE2 and tracks wallet bindings.
4. IdentityRegistry
Maps wallets to ONCHAINID contracts and answers whether a wallet is verified.
5. ClaimTopicsRegistry
Stores the claim topics a token requires, up to 15 topics.
6. TrustedIssuersRegistry
Stores which claim issuers are trusted for which topics.
How ONCHAINID itself works
Keys define who can control or use the identity
In the ONCHAINID Identity contract, keys are stored by purpose. The important built-in purposes are 1 = management, 2 = action, 3 = claim signer, 4 = encryption.
// example: Alice's hardware wallet controls her ONCHAINID
bytes32 managementKey = keccak256(abi.encode(aliceSafe));
identity.addKey(managementKey, 1, 1);
// example: a service wallet can perform actions
bytes32 actionKey = keccak256(abi.encode(aliceOpsWallet));
identity.addKey(actionKey, 2, 1); Claims attach attestations to the identity
A claim has a topic, issuer, signature, data, and URI. The claim ID is keccak256(abi.encode(issuer, topic)), so there is effectively one stored claim per issuer-topic pair on a given identity.
// example: KYC provider attests topic 7 for Alice
uint256 topic = 7;
address issuer = address(kycClaimIssuer);
bytes memory data = abi.encode(
"KYC passed on 2026-04-23",
"country=AE"
);
bytes memory signature = sign(keccak256(abi.encode(aliceIdentity, topic, data)));
identity.addClaim(topic, 1, issuer, signature, data, "ipfs://..."); - The issuer is usually a ClaimIssuer contract, not just a bare EOA
- The signature must match the hash of identity address, topic, and data
- For external issuers, addClaim checks validity by calling issuer.isClaimValid(...)
ClaimIssuer verifies or revokes those attestations
A ClaimIssuer contract extends Identity. It checks that the recovered signer address has purpose 3, meaning it is a valid claim signer key of the issuer identity. It also tracks revoked signatures.
// concrete example
// RegCorp has a ClaimIssuer contract with signer key signer1
bytes32 signerKey = keccak256(abi.encode(signer1));
claimIssuer.addKey(signerKey, 3, 1);
// if signer1 signed Alice's KYC claim, this returns true
bool ok = claimIssuer.isClaimValid(aliceIdentity, 7, signature, data);
// later, RegCorp can revoke the same signature
claimIssuer.revokeClaimBySignature(signature); Does ONCHAINID mint claims?
Not really. It is more accurate to say ONCHAINID stores claims, while an issuer creates and signs the claim off-chain, and then someone with a claim key adds that signed claim on-chain to the identity.
How IdFactory works
The IdFactory deploys IdentityProxy contracts using CREATE2. It keeps track of used salts, wallet-to-identity links, token-to-identity links, and linked wallets for each identity.
User identity example
// factory owner creates Alice's ONCHAINID
address aliceId = idFactory.createIdentity(
aliceWallet,
"alice-2026-04"
);
// internally, the factory prefixes the salt with "OID"
// and deploys IdentityProxy via CREATE2 - The initial management key is Alice wallet by default
- The factory records wallet → identity
- Later, linked wallets can be added with linkWallet()
Token identity example
// TREXFactory can create a token ONCHAINID too
address tokenId = idFactory.createTokenIdentity(
address(token),
tokenOwner,
"us-tbill-series-a"
);
token.setOnchainID(tokenId); - The token identity is separate from investor identities
- ERC-3643 token metadata can point to this ONCHAINID
- The token owner becomes the initial management key
How ERC-3643 uses ONCHAINID
Register the wallet and ONCHAINID
The Identity Registry stores the investor wallet, its ONCHAINID contract, and a country code. In the ERC-3643 implementation, registerIdentity delegates storage to IdentityRegistryStorage.
identityRegistry.registerIdentity(
aliceWallet,
IIdentity(aliceId),
784 // UAE for example, using ISO numeric-style metadata in project conventions
); Define which claim topics matter
The Claim Topics Registry tells the token which topics must be present. The registry itself does not know semantics, only topic numbers.
// example semantics chosen by your deployment
uint256 TOPIC_KYC = 7;
uint256 TOPIC_ACCREDITED = 42;
claimTopicsRegistry.addClaimTopic(TOPIC_KYC);
claimTopicsRegistry.addClaimTopic(TOPIC_ACCREDITED); Define which issuers are trusted for which topics
The Trusted Issuers Registry binds claim issuers to allowed topics. For example, one issuer may be trusted for KYC, another for accredited-investor status.
trustedIssuersRegistry.addTrustedIssuer(
IClaimIssuer(address(regCorpIssuer)),
[TOPIC_KYC]
);
trustedIssuersRegistry.addTrustedIssuer(
IClaimIssuer(address(brokerDealerIssuer)),
[TOPIC_ACCREDITED]
); Token checks verification during mint and transfer
The ERC-3643 token calls identityRegistry.isVerified(to) in mint, transfer, and transferFrom. If verification fails, the action reverts.
function mint(address to, uint256 amount) public onlyAgent
require(identityRegistry.isVerified(to), "Identity is not verified.");
require(compliance.canTransfer(address(0), to, amount), "Compliance not followed");
_mint(to, amount);
What exactly does isVerified() do?
This is the key ERC-3643 check. For each required topic, the Identity Registry gets all trusted issuers allowed for that topic, computes the expected claim IDs, loads the claims from the user’s ONCHAINID, and asks the issuer whether the signature is still valid.
- If the wallet has no identity registered, verification fails immediately
- If no claim topics are configured, verification passes trivially
- For each required topic, there must be at least one trusted issuer for that topic
- For each required topic, at least one matching stored claim must exist and validate
- If a claim is missing, signed by the wrong issuer key, or revoked, verification fails
Concrete example for each component
Identity
Alice has ONCHAINID 0xAliceId. Her Safe is a management key, and her backend signer is an action key.
ClaimIssuer
RegCorp has issuer contract 0xRegIssuer. It trusts signer 0xSigner1 as a claim key for topic 7, KYC passed.
IdFactory
Issuer admin deploys Alice ONCHAINID with salt alice-2026-04. Factory remembers that wallet 0xAliceWallet maps to 0xAliceId.
ClaimTopicsRegistry
The token requires topic 7 for KYC and topic 42 for accreditation.
TrustedIssuersRegistry
RegCorp is trusted for topic 7. BrokerDealerCo is trusted for topic 42.
IdentityRegistry
Wallet 0xAliceWallet is registered to 0xAliceId with country metadata. The token queries this registry whenever Alice receives tokens.
End-to-end example
- If Alice later loses her wallet, a new wallet can be linked to the same ONCHAINID, preserving identity continuity
- If RegCorp revokes the KYC claim signature, future transfers or mints to Alice can fail verification
- If the token changes its required topics, the same ONCHAINID may become insufficient until new claims are added
Trade-offs and gotchas
Pros
- Reusable identity across multiple permissioned tokens
- Clear issuer trust model instead of hard-coding one verifier
- Revocable claims, not permanent one-time approvals
- Wallet continuity via linked wallets and identity mapping
Cons
- Topic numbers are deployment conventions, not self-describing semantics
- Claim creation still depends on an off-chain attestation workflow
- More contracts and governance complexity than a plain token
- Verification can fail if issuer keys rotate or claims are revoked without careful ops
Key takeaways
ONCHAINID is an on-chain identity contract, not just a whitelist row
It combines ERC-734 keys and ERC-735 claims so identities can be controlled, updated, and attested over time.
Claims are signed by issuers, then stored on the identity
The identity does not invent facts. It stores attestations signed by claim issuer keys and can later prove or revoke them.
ERC-3643 uses ONCHAINID through registries
IdentityRegistry, ClaimTopicsRegistry, and TrustedIssuersRegistry together decide whether a wallet is verified for minting or transfers.
The real power is reusable identity + revocable attestations
That is what makes permissioned tokens feel more like regulated systems and less like static whitelists.