View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
View RWA-ID's full-sized avatar
💭
Available
💭
Available

Block or report RWA-ID

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
RWA-ID/README.md

RWA ID

Human-Readable Identity Infrastructure for Tokenized Real World Assets

WebsiteWhitepaperTechnical DocsStatus

RWA ID is a non-custodial identity layer that enables RWA platforms to issue human-readable, soulbound or transferable ENS subdomains for clients wallets, tokenized assets, and issuers. Built on proven Web3 standards (ENS + EIP-3668 CCIP-Read), RWA ID provides universal identity resolution across wallets and dApps.

joe.test.rwa-id.eth

🎯 Problem

RWA platforms today face critical identity infrastructure challenges:

  • Opaque wallet addresses create poor user experience
  • Fragmented identities across chains hinder interoperability
  • No shared infrastructure forces every platform to build internally
  • Poor legibility for assets and participants reduces institutional trust

Result: Every platform builds identity systems independently. Nothing interoperates.


✨ Solution

RWA ID provides neutral, shared identity infrastructure that:

  • Issues human-readable names (e.g., joe.test.rwa-id.eth)
  • Resolves in all major wallets (MetaMask, Trust, Rainbow, Uniswap)
  • USDC claim fees with automatic 70/30 platform/protocol split on-chain
  • Soulbound or transferable — configurable per project
  • Requires no custody or personal data collection
  • Uses proven standards (ENS + EIP-3668 CCIP-Read)

🚀 Status

v3 is live on Ethereum mainnet — deployed 1 Aug 2026, block 25661280

  • ✅ Production contracts deployed and verified on Etherscan
  • ✅ Governed by a 2-of-3 Safe multisig (details)
  • ✅ 25 top RWA platform slugs reserved (list)
  • ✅ Identity NFTs render name + artwork fully on-chain (details)
  • ✅ CCIP-Read gateway live at gateway.rwa-id.com, pointed at v3
  • ✅ ENS wildcard resolver active — names resolve in MetaMask & Trust Wallet
  • ✅ Platform identity management dashboard operational

What changed in v3

v2 never implemented tokenURI(), so every identity appeared on marketplaces as an untitled, imageless "RWA ID #n". That could not be patched — v2 has no baseURI setter and is not behind a proxy — so v3 was deployed to replace it.

v2v3
Token metadatanone — tokenURI() returned ""name, description, artwork and traits, rendered on-chain
claim() takesbytes32 nameHashstring label — so the contract can name what it mints
Label casingany bytes acceptedlowercase [a-z0-9-_] enforced
Collection metadatanonecontractURI(), plus ERC-4906 update signalling

Existing allowlists carry over. The Merkle leaf is unchanged — keccak256(claimer, keccak256(bytes(label))) — so roots and proof sets generated for v2 verify against v3 without regenerating anything.


📋 How It Works

For Platforms (3-Step Integration)

  1. Create Project Namespace

    • Platform connects wallet at rwa-id.com
    • Registers namespace (e.g., test.rwa-id.eth) — free to create
    • Optionally sets a USDC claim fee (70% goes to platform treasury 30% to RWA ID)
  2. Upload Allowlist

    • Submit CSV mapping names to wallet addresses (two columns: name,wallet)
    • Names must be lowercase [a-z0-9-_] — see Label rules
    • System computes the Merkle root in the browser, commits it on-chain, and pins the proofs to IPFS via Pinata. Only the root goes on-chain, so client lists never touch a server the platform does not control
    • A shareable claim URL is generated automatically: rwa-id.com/?claim=[projectId]&proofs=[cid]
  3. Clients Claim Identities

    • Platform shares the claim URL with clients (email, dashboard, etc.)
    • Client opens the URL — their wallet is auto-detected from the proof file, no manual input needed
    • Client approves USDC and confirms the claim in two wallet steps
    • Identity NFT minted — resolves immediately across all ENS wallets, and displays as client.yourplatform.rwa-id.eth on marketplaces
    • Format: client.yourplatform.rwa-id.eth

Label rules

Labels are restricted to lowercase [a-z0-9-_], 1–63 characters, and the contract rejects anything else at claim time rather than silently normalising it.

This is not cosmetic. The CCIP-Read gateway lowercases a name before hashing it, so under v2 a client allowlisted as Zac minted at keccak("Zac") while ENS looked up keccak("zac") — the claim succeeded, nothing errored, and the identity was permanently unresolvable. The contract cannot fold the case itself, because the Merkle leaf is built from the raw bytes off-chain and rewriting them would invalidate every proof. So it rejects instead, and the dashboard normalises when building the tree. The same character set is what keeps on-chain rendering safe, since tokenURI() interpolates the label into both JSON and SVG.

Revenue Sharing Model

Every claim pays a USDC fee — always split 70/30 on-chain between the platform treasury and RWA ID. The fee amount depends on whether the platform set one:

ScenarioEffective FeePlatform (70%)RWA ID (30%)
Platform sets no fee$0.50 minimum enforced by contract$0.35$0.15
Platform sets $1.00$1.00$0.70$0.30
Platform sets $5.00$5.00$3.50$1.50

The $0.50 minimum is enforced at the contract level — it cannot be bypassed. If a platform sets claimFee = 0, the contract automatically applies the minimum and distributes it with the same 70/30 split.


📡 Deployed Contracts

Ethereum Mainnet

ContractAddress
RWAIDv30x6413e9E6A0D4e05557463A66C34E18192324A2C7current registry
Wildcard Resolver v20x765FB675AC33a85ccb455d4cb0b5Fb1f2D345eb1unchanged by the v3 cutover
Protocol Safe0xa28743bD38C9c951910d8FA9812c48ab5CDf75Abowner + protocol treasury
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
RWAIDv20xD0B565C7134bDB16Fc3b8A9Cb5fdA003C37930c2superseded — no token metadata

The resolver did not need redeploying for the v3 cutover: it stores no registry address, and resolveWithProof only checks that the gateway's response was signed by trustedSigner. Repointing the protocol at a new registry is a gateway configuration change.

Sepolia Testnet

ContractAddress
RWAIDv20xb0b023c9eD18dCD573B8befC851974f20126ab92
Wildcard Resolver v20xE591Cbe3802e3E4908731E3D4B056cd8b08AE520
MockUSDC0x4CcF36b273dA06D70B235d605639b3f8a6CA6B03

CCIP-Read Gateway

Gateway URL:https://gateway.rwa-id.com/{sender}/{data}.json

The resolver implements EIP-3668 (CCIP-Read) to enable off-chain resolution lookups with on-chain signature verification.

The gateway reads the registry and signs each answer with the trusted signer key registered on the resolver (0xC9fACcf3a77b553375017efFEe441D9770fAA723). Rotating that key requires a Safe transaction calling setTrustedSigner — until that lands, rotating it alone would break resolution for every name.


🔐 Governance

Both the registry and the protocol treasury are controlled by a 2-of-3 Safe multisig (Safe v1.4.1) at 0xa28743bD38C9c951910d8FA9812c48ab5CDf75Ab.

Threshold2 of 3 signers
OwnsRWAIDv3 (owner) and the protocol fee treasury
Cannottouch platform treasuries, seize identities, or move client funds

What requires the multisig

FunctionEffect
reserveNamespace / batchReserveNamespacesReserve a slug for a platform
releaseNamespaceRelease a reservation early
setMinimumClaimFeeChange the protocol-wide fee floor (currently $0.50)
setProtocolFeePercentChange the protocol split (currently 30%, bounded 10–50% by the contract)
setProtocolTreasuryRedirect the protocol's share of fees
setBaseURI / setContractURIRepoint artwork away from the on-chain renderer

Everything a platform does — creating a project, publishing an allowlist, setting its own claim fee and treasury, pausing, revoking — is controlled by that platform's own wallet and needs no protocol involvement.

Proposing a multisig transaction

# Dry run — prints the slugs, calldata and Safe tx hash, signs nothing
npx hardhat run scripts/reserve-slugs-v3.js --network mainnet
# Sign and queue it for the other owners to approve
SUBMIT=1 npx hardhat run scripts/reserve-slugs-v3.js --network mainnet

Owners then approve and execute at app.safe.global.

Note: the Safe UI may show freshly-verified contract calls as raw hex rather than a decoded function name. Before approving, confirm the target is the registry, value is 0, and the operation is CALL — never DELEGATECALL.


🏷️ Reserved Namespaces

25 RWA platform slugs are reserved on v3 until 1 August 2027. Reservations are held at 0x0000000000000000000000000000000000000001 — an address nobody controls — so the namespace is locked against squatters until the real platform claims it.

CategoryReserved slugs
Institutional / securitiessecuritizeondosuperstatebackedarchax
Credit / lendingcentrifugemaplegoldfinchcredixclearpool
Real estaterealtloftypropyhomebasetangible
Infrastructure / compliancepolymeshtokenyrealiodefactorfasset
Commodities / fundsopenedenswarmbitbondfinbloxparcl

If one of these is yours:get in touch and the Safe will reassign the reservation to your wallet with reserveNamespace(slug, yourWallet, duration). You can then create the project yourself — no other party can take the name in the meantime.

Any other slug is open and can be registered permissionlessly at rwa-id.com. Reservations are not permanent: createProject deletes an expired reservation and lets the next caller through, so anything not claimed by August 2027 becomes available.


🖼️ Token Metadata

Every v3 identity renders its own metadata on-chain — no metadata server, no IPFS dependency, nothing to keep alive:

name alice.acme.rwa-id.eth
image on-chain SVG — the RWA·ID mark over the full name
attributes Namespace · Label · Project ID · Transferability · Claimed (date)

contractURI() supplies collection-level metadata to marketplaces the same way. setBaseURI and setContractURI exist as escape hatches if artwork should later point elsewhere; setting a baseURI takes precedence over the on-chain renderer, and emits ERC-4906 so marketplaces re-read.


🏗️ Architecture

┌─────────────┐
│ Wallet │
│ (User) │
└──────┬──────┘
│ Resolve: joe.test.rwa-id.eth
↓
┌──────────────────┐
│ ENS Registry │
│ (Ethereum) │
└──────┬───────────┘
│ Wildcard Resolver → 0x765F...
↓
┌──────────────────┐
│ CCIP-Read │
│ Gateway │
│ gateway.rwa-id.com│
└──────┬───────────┘
│ resolveAddr(node) on RWAIDv3
│ → ownerOf(nodeToTokenId[node]), so a transfer
│ is reflected without re-registering anything
│ Returns signed (node, address, hash, sig)
↓
┌──────────────────┐
│ resolveWithProof │
│ verifies sig │
│ returns address │
└──────────────────┘

🛠️ Developer Setup

git clone https://github.com/RWA-ID/RWA-ID.git
cd RWA-ID
npm install
cp .env.example .env # fill in your keys

Run Tests

npx hardhat test# 112 passing
SuiteCovers
test/RWAIDv2.test.js (68)Registry logic v3 inherits unchanged — projects, reservations, allowlists, fee splitting, soulbound enforcement, revocation, ownership, CCIP-Read resolver
test/RWAIDv3.test.js (44)v3's own surface — label validation, claiming by label, tokenURI, contractURI, the baseURI escape hatch, ERC-4906

The v3 suite is written around the failure modes that motivated v3. It asserts that uppercase labels are rejected rather than folded (folding would invalidate every Merkle proof, and accepting them recreates the v2 bug where a name minted at keccak("Zac") while ENS looked up keccak("zac")); that a label cannot break out of the JSON or SVG that tokenURI() interpolates it into; and that a leaf built the v2 way still verifies, which is what lets existing allowlists carry over.

The suite has been mutation-tested — re-allowing uppercase, making tokenURI return "" as v2 did, and ignoring the baseURI override each produce failures in exactly the tests that should catch them.

Separately, a live-data check runs the same path against the real pinned proof set rather than fixtures:

npx hardhat run scripts/v3-metadata-check.js

It fetches a v2-era proof set from IPFS, publishes its root to a fresh v3 deployment unchanged, and claims with the original untouched proofs.

Deploy

npx hardhat run scripts/deploy-v3.js --network mainnet # dry-runs locally without --network

Prints the deployed address, the block to use as the log-scan start, and the follow-up steps (gateway env var, dashboard constant, Etherscan verification).

Guard the dashboard ABI

npx hardhat compile && node scripts/check-dashboard-abi.mjs

The console declares its own ABI rather than importing artifacts, so a signature change can pass a build and only fail in a user's wallet. This diffs the two.


📦 Repository Structure

contracts/
RWAIDv3.sol — Current registry (adds on-chain token metadata)
RWAIDv2.sol — Superseded registry (no tokenURI)
RwaIdWildcardResolverV2.sol — ENSIP-10 wildcard resolver (CCIP-Read / EIP-3668)
mocks/MockUSDC.sol — Test USDC (Sepolia only)
RwaIdRegistry.sol — v1 contract (legacy)
RwaIdWildcardResolver.sol — v1 resolver (legacy)
scripts/
deploy-v3.js — Deploy RWAIDv3
deploy-mainnet.js — Deploy v2 (historical)
deploy-sepolia.js — Deploy to Sepolia testnet
reserve-slugs-v3.js — Queue v3 slug reservations in the Safe
reserve-slugs.js — v2 equivalent (historical)
verify-reservations.js — Verify reserved slugs on-chain
set-trusted-signer.js — Update gateway signer via Safe multisig
v3-metadata-check.js — Verify v3 metadata + v2 allowlist compatibility
check-dashboard-abi.mjs — Diff the console's ABI against the compiled contract
test/
RWAIDv2.test.js — 68 tests: inherited registry logic
RWAIDv3.test.js — 44 tests: metadata, label rules, escape hatches
apps/
rwa-id-dashboard/ — Platform console (own repo: RWA-ID/RWA-ID-Dashboard)
packages/
rwaid-gateway/ — CCIP-Read gateway (deployed on Replit; not tracked here)

🔒 Regulatory Posture

RWA ID operates as infrastructure only with minimal regulatory surface area:

What RWA ID DoesWhat RWA ID Does NOT Do
✅ Provide identity references❌ Collect personal data
✅ Enable human-readable names❌ Perform KYC/verification
✅ Facilitate on-chain resolution❌ Assert identity claims
✅ Support platform operations❌ Custody funds or assets

📞 Contact

Founder: Hector Morel Email:partner@rwa-id.comWebsite:rwa-id.com


📄 License

MIT


🙏 Built With


RWA ID — Identity Infrastructure for the Tokenized Economy

WebsiteWhitepaperTechnical DocsContact

Pinned Loading

  1. RWA-IDRWA-IDPublic

    Identity Infrastructure For RWA Platforms And Client Wallets Without Touching KYC Or Internal ID Systems | Human-Readable | Verifiable Across Chains | Built With ENS · Ethereum · CCIP

    JavaScript

  2. x402-Identityx402-IdentityPublic

    Mint permanent ENS sub domains for AI agents under 402bot.eth | 402api.eth | 402mcp.eth No renewal, Parent Cannot Control giving minters true ownership.

    TypeScript

  3. BuildSiteBuildSitePublic

    ENS Site Builder

    TypeScript 1

  4. DMpay-IPFSDMpay-IPFSPublic

    Pay To DM On Ethereum

    TypeScript

  5. pet-idpet-idPublic

    Pet ID Personalized Page For Cats And Dogs

    TypeScript

  6. hoodfi-ethhoodfi-ethPublic

    HoodFi Names is the go to naming service on Robinhood chain

    Solidity