RWA compliance2026Shipped
On-chain Identity and KYC
Identity and compliance contracts for real-world-asset (RWA) tokens and AI agents: DID registry, KYC credentials, trusted issuers, scoped agent delegation and Groth16 ZK verification.

At a glance
7 contracts
- One call checks KYC, accreditation and sanctions for both sides of a transfer
- Groth16 ZK verification instead of personal data on-chain
- Scoped, revocable delegation for AI agents
What it is
Give any RWA token a single call to check whether both sides of a transfer are KYC'd, accredited and sanctions-clear.
Why it exists
Tokenized real-world assets have to enforce who can hold them. Doing that inside every token duplicates work and tends to leak personal data on-chain.
How it works
An identity registry stores DIDs, a credential registry issues and revokes KYC and compliance credentials, and an issuer registry keeps a three-tier whitelist of trusted issuers. A delegation manager lets AI agents act for a verified identity within scoped permissions, and a Groth16 verifier checks zero-knowledge proofs. One facade exposes verify() to any token.
Decisions worth explaining
Upgradeable where data lives, immutable where trust lives
The identity, credential and delegation registries sit behind UUPS proxies so they can evolve; the issuer whitelist, ZK verifier and facade are immutable, so the rules a token relies on can't change under it.
One facade for integrators
Tokens call a single verify(address, claim) function and never learn the internal layout, so registries can change without breaking every token that depends on them.
Agents get delegation, not keys
An AI agent acts for a verified identity through scoped, revocable permissions instead of holding a key that carries the identity's full rights.
Results
- Seven contracts behind one facade: Identity, Credential, Issuer (3-tier), Delegation, ZKVerifier and more.
- Targets Polygon mainnet; tested with Hardhat, Chai and gas reporting.
Stack
From the code
modifier onlyCompliant(address to) {require(identity.verify(msg.sender, keccak256("KYC_AML")));require(identity.verify(msg.sender, keccak256("ACCREDITED_INV")));require(identity.verify(msg.sender, keccak256("SANCTIONS_CLEAR")));require(identity.verify(to, keccak256("KYC_AML")));require(identity.verify(to, keccak256("SANCTIONS_CLEAR")));_;}function transfer(address to, uint256 amount)public override onlyCompliant(to) returns (bool){return super.transfer(to, amount);}