A production-pattern regulated security token stack built on the ERC-3643 T-REX protocol v4.1.6 with ONCHAINID v2.2.1 identity contracts, plus a Next.js admin console for managing it end-to-end.
Status: localhost-verified end-to-end (10 drill tests + 17 Hardhat tests). Sepolia deployment wiring in progress.
Before Sepolia or mainnet cut-over: see the vendor-credentials + signer-posture checklist in
docs/CHAINLINK_ACE_INTEGRATION.md§8.2–§8.3. Every box in §8.3 must be ticked before the mainnet deploy script runs. Chainlink ACE is a permissioned product — start the intake atace@chain.link2–6 weeks before you need it (process documented in §8.2.0).
ERC-3643 is the institutional standard for permissioned security tokens. Every transfer is gated by:
- ONCHAINID — each investor has an on-chain identity contract carrying signed KYC claims.
- Trusted Issuers Registry — only claims from approved KYC providers are recognized.
- Identity Registry — binds wallets to ONCHAINIDs and a country code.
- Modular Compliance — stackable rules (country allow-list, max-balance cap, supply cap, time-window limits).
- Token agents — operational roles for mint / burn / freeze / recovery.
- ACE / AML risk gate — continuous Chainlink ACE attestations (risk score + freshness) enforced at transfer time via
AMLRiskModulereading claim topic 10105.
This repo gives you the full deployable suite plus an admin UI to drive every lever without writing a line of hardhat console code.
trex-testenv/ Hardhat + T-REX contracts, deploy & seed scripts, tests
trex-dashboard/ Next.js 16 admin console (wagmi v2 + viem)
contracts/imports/— pulls in T-REX, ONCHAINID, and SolidState interfacesscripts/deploy-suite.ts— deploys TREXFactory, TREXGateway, IdFactory, and all implementationsscripts/deploy-demo-token.ts— spins up aRETdemo token with IR / MC / TIR / CTR wired upscripts/seed-investors.ts— deploys ClaimIssuer, three investor ONCHAINIDs, KYC claims, CountryAllowModule (FR/DE), mints 1000 RET to Alice + 500 to Bobscripts/prep-recovery.ts— adds a new wallet as a management key on an existing ONCHAINID (for testing recovery)scripts/deploy-ace.ts— deploysAMLRiskModule+MockACEOracle(the localhost stand-in forACEAttestationPublisher), binds the module toModularCompliancewithmaxRiskScore=50/maxAge=86400s, provisions the oracle as a purpose-3 CLAIM key on each investor ONCHAINID, and publishes baseline risk-score=10 attestations on claim topic 10105test/trex.test.ts— full regression suite (deploy + mint + transfer + freeze + pause + recovery + ACE AML gate)
Next.js 16 App Router. Connects via wagmi v2 + viem. All pages are client components that read live state from chain and write via the connected wallet — except the Onboarding Wizard, which runs a server-side orchestration route.
Three terminals:
# Terminal A — hardhat node
cd trex-testenv
npm install
npx hardhat node
# Terminal C — deploy + seed
cd trex-testenv
npx hardhat run scripts/deploy-suite.ts --network localhost
npx hardhat run scripts/deploy-demo-token.ts --network localhost
npx hardhat run scripts/seed-investors.ts --network localhost
npx hardhat run scripts/deploy-ace.ts --network localhost
node ../trex-dashboard/scripts/sync-abis.mjs
# Terminal B — dashboard
cd trex-dashboard
npm install --legacy-peer-deps
npm run devDashboard: http://localhost:3000
Connect MetaMask to Localhost 8545 (chain id 31337), import Hardhat account #0:
Private key: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
Address: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
This wallet is the deployer, the TIR/TCR/IR/MC owner, and both a Token Agent and IR Agent.
cd trex-testenv
npx hardhat test| Page | What it controls |
|---|---|
| Overview | High-level stats (supply, paused, agent count, module count) |
| Token Mgmt | Mint, burn, pause/unpause, full-wallet freeze, partial token freeze |
| Investors | Onboarding Wizard (deploys ONCHAINID + signs KYC claim + registers in IR, all in one click) and manual existing-ID registration |
| Compliance | Live KYC + compliance matrix for seeded investors; Country Allow-List editor routed through ModularCompliance.callModuleFunction |
| ACE / AML | Module config (maxRiskScore + maxAge + trusted issuers), per-investor risk-score table decoded from topic 10105 claims, and a DON re-screening simulator that signs and publishes new attestations through MockACEOracle.publishAMLClaim |
| Agents | Grant/revoke Token Agent and IR Agent roles with a watchlist probe pattern (T-REX has no agent enumeration) |
| Issuers | Enumerate TrustedIssuersRegistry, add/remove issuers, manage claim topics |
| Recovery | Token.recoveryAddress(lost, new, identity) — moves balance, freezes old wallet, swaps wallet in IR |
| Simulator | 7-check pre-transfer probe: verified from/to, compliance, paused, frozen, unfrozen balance |
| Audit Log | Live event stream: Token + IR + MC events unified with timestamps |
| Deploy Token | Deploy a fresh T-REX suite + token from the browser (admin bootstrap) |
Runs server-side via POST /api/onboard. Four orchestrated steps:
- Deploy a new
Identitycontract with the investor wallet as MANAGEMENT key - Sign a KYC claim (topic 10101,
keccak256(abi.encode(identity, topic, data))+ EIP-191) - Investor's signer calls
addClaim - Deployer (IR agent) calls
registerIdentity(wallet, identity, country)
Localhost only. Uses the first 20 Hardhat-mnemonic-derived accounts so the API route can sign on the investor's behalf. For Sepolia/mainnet the wizard needs to split into (a) server signs the claim, (b) investor connects their wallet to addClaim.
TREXFactory ─── deploys ───▶ Token ──────────┐
│ │
┌────────┴─────────┐ │
│ │ │
IdentityRegistry ModularCompliance
│ │
▼ ▼
IdentityRegistryStorage CountryAllowModule (+ more)
│
▼
TrustedIssuersRegistry ──▶ ClaimIssuer ──▶ signs claims on
investor ONCHAINIDs
Every investor ONCHAINID is an independent Identity contract. The investor wallet holds a MANAGEMENT key (purpose 1). Recovery works by adding the new wallet as a MANAGEMENT key on the existing ONCHAINID, then the token contract swaps the bound wallet in the IR.
Sumsub (KYC) ──signs topic 10101──▶ Identity.claims ──▶ IR.isVerified
Elliptic (AML risk) ──▶ Chainlink ACE DON ──signs topic 10105──▶ Identity.claims
│
AMLRiskModule.moduleCheck
│
MC.canTransfer
On localhost the ACEAttestationPublisher contract is replaced by MockACEOracle, which holds a purpose-3 CLAIM key on each investor ONCHAINID and calls Identity.addClaim directly. Payload is abi.encode(uint8 riskScore, uint64 expiresAt, bytes32 providerRef) signed EIP-191 by the ClaimSigner EOA. AMLRiskModule rejects transfers whose sender OR receiver has no 10105 claim, an expired one, or a score above the configured threshold.
- The ClaimSigner private key used by the onboarding wizard is the public Hardhat test mnemonic. Do NOT use this wiring on any network where real value lives.
MC.canTransferandIR.isVerifiedare independent trust gates. Both must pass for an actual transfer — a "Compliant: yes" in the dashboard only means the active modules approve; it does NOT imply KYC is valid.- Agent roles are not enumerable on-chain in T-REX. The Agents page uses a watchlist probe pattern against known addresses.
MockACEOracleis a localhost-only stand-in. It signs with the Hardhat test mnemonic and auto-publishes on admin request. In prod the real Chainlink ACE DON signs off-chain via OCR2 aggregation andACEAttestationPublisherenforces DON-onlyaddClaim. Do NOT deployMockACEOracleto any network with real value.- The
AMLRiskModulegate is additive toIR.isVerified+ country allow-list — all three must pass. SettingmaxRiskScore=99effectively disables the score check but staleness enforcement still runs. - This repo implements the on-chain compliance surface only. Two adjacent controls are vendor-operated off-chain and are NOT code in this repo: Elliptic KYT (post-transaction monitoring — replays each
Transferevent, generates alerts by tx hash) and Sumsub Travel Rule / TRP (FATF rec. 16 VASP-to-VASP message exchange on transfers ≥ US$1,000). Seedocs/USER_JOURNEY_AND_CNAD_ARCHITECTURE.md§4.4 for the full vendor map.
Private / internal.