Operate20

Mainnet checklist

Everything already points at Arc mainnet 5042; what is missing is keys, deploys and one onboarding. Each row below is one switch, in the order they depend on each other. Keep at most 100 USDC on-chain at any time and keep COMPLIR_PAUSED=1 as the kill switch.

Status board

CapabilityTodayFlip
Screening, verdicts, EIP-712 signaturesliveATTESTER_PRIVATE_KEY (else an ephemeral key per process)
Watches, webhooks, blocklist reactor, Trust Map, ledgerliveCRON_SECRET + crons or workers running
Analyst and operator model stepslive / fallbackLLM_PROVIDER=0g + ZEROG_API_KEY (Claude via 0G Compute Router; ANTHROPIC_API_KEY also works); deterministic fallback on timeout
AttestationRegistry on Arclive · 0xA82EDb5e…deployed + verified (Sourcify)
Merkle anchoringliveanchorRoot() on every watcher tick when ≥ ANCHOR_EVERY_N decisions are pending
GuardedTreasury / Guarded Payoutslive · 0x5465f31a…TREASURY_MODE=live; caps 10 USDC / payout, 20 / day
Circle Developer-Controlled Wallets (revenue 0x9b0d…977e, ops 0xa494…7c69, SCA on ARC)liveprovisioned; registry writes relay through the ops wallet with Gas Station sponsored gas (ERC-4337 EntryPoint); local attester key is the fallback
x402 charging (0.01 USDC / screen, Circle Facilitator, eip155:5042)livefirst real settlement landed in the revenue wallet
Auto-refunds, tip bountieslive via Circle revenue walletsent from the Circle revenue wallet; Gas Station policy active
Revenue yield sweep (Circle Earn · Steakhouse Prime USDC 0xbeef0016…7298)live · moves only above the floatYIELD_MODE=live; Circle revenue wallet approve → deposit / redeem; today 2.02 USDC < 5 float → NONE (step 5)
ERC-8004 identitypendingstep 6
Hosted URL, yente in productionpendinga host + libSQL URL; a yente container the host can reach

1 · Registry

sh
# contracts/README.md - Arc mainnet, chain id 5042, gas is USDC; use a keystore account, not a raw key
export ARC_RPC_URL=https://rpc.mainnet.arc.io
export OWNER_ADDRESS=0x…        # cold admin: rotates the attester, pauses, revokes
export ATTESTER_ADDRESS=0x…     # address of ATTESTER_PRIVATE_KEY
forge script script/Deploy.s.sol:Deploy --rpc-url $ARC_RPC_URL --account <keystore> --sender <addr>              # simulate, check block.chainid
forge script script/Deploy.s.sol:Deploy --rpc-url $ARC_RPC_URL --account <keystore> --sender <addr> --broadcast
# then in the app:
REGISTRY_ADDRESS=0x…
ATTESTER_PRIVATE_KEY=0x…

Verdicts are mirrored on-chain from the next screen; registryTx stops being null, isAllowed starts answering, and the watcher anchors Merkle roots every 50 entries. Verify the contract on the explorer with the commands in contracts/README.md.

2 · GuardedTreasury

sh
export REGISTRY_ADDRESS=<AttestationRegistry>
export TOKEN_ADDRESS=0x3600000000000000000000000000000000000000
export OWNER_ADDRESS=<address of TREASURY_PRIVATE_KEY>
forge script script/Deploy.s.sol:DeployGuardedTreasury --rpc-url $ARC_RPC_URL --private-key $PRIVATE_KEY --broadcast
cast send 0x3600…0000 "transfer(address,uint256)" <GuardedTreasury> 50000000 --rpc-url $ARC_RPC_URL --private-key $PRIVATE_KEY   # ≤ 50 USDC
# app: GUARDED_TREASURY_ADDRESS, TREASURY_PRIVATE_KEY, TREASURY_MODE=live (+ REGISTRY_ADDRESS, ATTESTER_PRIVATE_KEY)

Live mode requires all four env values; GET /api/v1/payouts → missingForLive tells you which are absent. Details on Guarded payouts.

3 · Circle wallets and Gas Station

sh
CIRCLE_API_KEY=…                                   # console.circle.com → API Keys (Mainnet toggle)
node scripts/circle/setup.ts --generate-secret      # → CIRCLE_ENTITY_SECRET
node --env-file=.env.local scripts/circle/setup.ts --register     # once; keep the recovery file, it cannot be downloaded again
node --env-file=.env.local scripts/circle/setup.ts --balances     # provisions "revenue" + "ops" SCA wallets; prints X402_PAY_TO, OWN_WALLETS, CIRCLE_WALLET_SET_ID
# console → Gas Station → Mainnet → policy on ARC (max/day, per-tx) → Activate · fund ops with a few USDC
SIGNER_MODE=circle
curl -s https://…/api/internal/circle -H "Authorization: Bearer $CRON_SECRET"   # effectiveSigner: "circle", balances, hints
  • Why SCA: Gas Station sponsorship on EVM is ERC-4337, so the relaying ops wallet must be an SCA. The attester key stays local because the registry verifies with ECDSA.recover, which an SCA signature cannot satisfy.
  • Policies: spending caps are enforced app-side (CIRCLE_*_POLICY_*) because developer-controlled wallets have no policy API; Gas Station adds a Circle-side cap on sponsored gas. Transactions above the policy pay from the SCA's own USDC, so keep a small float in ops.
  • Unverified: Gas Station on Arc mainnet is documented but was not exercised without an account; the first live relay should be one cheap write.

4 · x402

env
X402_DISABLED=0
X402_PAY_TO=<Circle revenue wallet>
X402_FACILITATOR_URL=https://api.circle.com/v1/facilitator/x402
X402_FACILITATOR_API_KEY=<Circle Console key>       # or X402_SELLER_PRIVATE_KEY for the keyless trial
X402_NETWORK=eip155:5042                            # optional, defaults from ARC_CHAIN_ID

Set OWN_WALLETS to the Circle wallets and any test payers so they are excluded from /numbers and listed in every report. Then run one paid call through the SDK and confirm PAYMENT-RESPONSE carries a transaction on Arc.

5 · Revenue yield (Circle Earn)

Idle revenue goes into a Circle Earn vault on Arc: Morpho USDC vaults (ERC-4626, Morpho Vault V2) with no allowlist and no minimum. The planner reads Circle's Earn API (GET api.circle.com/v1/earnKit/vaults/explore?chain=ARC, keyless), refuses any vault Circle flags low_liquidity, any RED warning or unlisted vault, requires Circle Sentinel coverage and TVL ≥ 100k, and only uses a vault whose available liquidity is at least 2× what we would need to redeem. The best APY among the rest wins; today that is Steakhouse Prime USDC 0xbeef0016cb2fd5c352ea7ca08a9f54739dfa7298 (≈3.1%).

  1. YIELD_MODE=live (default YIELD_BACKEND=earn); REVENUE_WALLET/X402_PAY_TO must be the Circle revenue wallet.
  2. Pin the revenue wallet's contract calls to USDC + the vault: npx tsx --env-file=.env.local scripts/circle/allow-contracts.ts revenue 0x3600…0000 0xbeef0016…7298 (tips and refunds are plain transfers and stay unaffected).
  3. Keep YIELD_FLOAT_USDC (5) + obligations due 48 h liquid; deposits are capped at YIELD_MAX_SWEEP_USDC (25 = the per-tx policy).

Each executed move is logged as OPERATOR · YIELD_SWEEP / YIELD_REDEEM with tx hashes on /ledger; plans that move nothing are NOOP · YIELD_PLAN. USYC (permissioned, ~100k minimum) stays available as YIELD_BACKEND=usyc: see docs/YIELD.md.

6 · ERC-8004 identity

sh
PUBLIC_BASE_URL=https://<prod host>
npx tsx --env-file=.env.local scripts/erc8004/register.ts --print            # dry run: metadata + calldata
# host scripts/erc8004/agent.built.json at $PUBLIC_BASE_URL/.well-known/agent-registration.json, or use --data-uri
ERC8004_OWNER_PRIVATE_KEY=0x… npx tsx --env-file=.env.local scripts/erc8004/register.ts [--data-uri]   # prints agentId
ERC8004_AGENT_ID=<id>   # then re-run to setAgentURI with the registrations[] back-reference

Registering mints an ERC-721 identity in the Arc IdentityRegistry (0x8004A169FB4a3325136EB29fA0ceB6D2e539a432, the same CREATE2 singleton as on Ethereum and Base) whose URI lists CompliR's services, including the x402 screen endpoint with its price and network. Without a key the script prints the raw transaction so a multisig can send it. Coinbase's x402 Bazaar indexes only CDP-settled payments on its own networks, so the ERC-8004 entry and APEX's Arc x402 explorer are the discoverability paths that work on Arc today.

After the flips

  • Health: /api/v1/health should show the registry address, x402.disabled: false and signer.mode: circle.
  • Integrations: replace https://trycomplir.xyz in docs/integrations/*/PR.md with the hosted URL, add the registry address to the CeedeBooks PR, then open the three prepared PRs (STEWARD, Vestiarion, CeedeBooks).
  • Report: the first signed daily report publishes at 00:05 UTC; add notes and mistakes to .data/report-notes.json.