Skip to content

Latest commit

 

History

History

README.md

Covenant Relayer

Mirrors Cleanverse query_apass into IdentityRegistry.upsertIdentity. The registry is the on-chain source of truth; nothing in Covenant's lifecycle (confirmObligation, originate, fund, settle) ever calls Cleanverse directly — this is the only piece that does.

Setup

cd relayer
npm install
cp .env.example .env

Fill in .env:

  • IDENTITY_REGISTRY_ADDRESS — from a Deploy.s.sol / Demo.s.sol broadcast.
  • RELAYER_PRIVATE_KEY — must match IdentityRegistry.relayer() (the address passed to its constructor, or set later via setRelayer).
  • CLEANVERSE_MODE=mock for offline deterministic fixtures (src/mockFixtures.ts), or live to hit the real sandbox.

Run

npm run sync

Reads subjects.json (override with SUBJECTS_FILE), queries each address, writes upsertIdentity on-chain, and prints what it wrote. One-shot by design — not a watch/poll daemon (trivial to wrap in a loop later if needed).

Modes

  • mock — fixtures in src/mockFixtures.ts, keyed by lowercased address. The shipped fixtures reproduce exactly the tier/jurisdiction values test/Base.t.sol and script/Demo.s.sol seed via adminSetIdentity for anvil's default mnemonic accounts 1-4, so a mock sync is directly diffable against the existing test suite (verified 2026-07-26: identical isVerified/tier/jurisdiction, and a real confirmObligation + originate against relayer-written identities repriced 97%/88% by tier, matching HappyPath.t.sol).
  • live — POSTs query_apass to Cleanverse. Without CLEANVERSE_API_ID it uses the public no-auth skills surface (CLEANVERSE_SKILLS_URL, default https://uatapi.cleanverse.com/api/skills) — confirmed live and reachable 2026-07-26, same monad chain config (chainId 10143, aUSDC address) as AgentCheckout's earlier sandbox session. With an api-id/api-key (Gateway Member, issued over the hackathon Telegram) it upgrades to the authenticated Cooperate endpoint (CLEANVERSE_BASE_URL) — same interface either way. CLEANVERSE_API_KEY is accepted but currently unused (no encrypted write endpoint is called; query_apass is a plain read).

subjects.json ships with anvil's default dev accounts for local testing — replace it with real supplier/obligor/financier wallets before a live Monad run.

The tier question, and how it resolved

This relayer used to end with an open question: real A-Pass tier is whatever Cleanverse returns numerically, and an early ReceivableNote configured rates only for {1,2,3}, so an obligor whose tier landed outside that set reverted on originate rather than mispricing. The note said not to guess ahead of real data.

Real data settled it. Every A-Pass issued to the demo cast came back with tier = 50 — Cleanverse assigns tier itself, identically, so it cannot carry an application's risk banding. The axis an integrator actually sets at issuance is subTier (0–99), which is what generate_apass takes and what this relayer sends from apass-roster.json.

So Covenant prices on subTier, through descending bands rather than a mapping on discrete values — see src/CovenantDefaults.sol for the curve and ReceivableNote.advanceRateBpsFor. No bucketing function was added here: the relayer still passes Cleanverse's numbers through untouched.