{"slug": "37-ways-to-break-our-contracts-and-why-none-of-them-work-anymore", "title": "37 Ways to Break Our Contracts, and Why None of Them Work Anymore", "summary": "AgentBadge closed all 37 findings from an adversarial AI audit of its identity and escrow contracts and redeployed the hardened pack to Arc mainnet on 2026-10-11 as eight UUPS proxies with zero open findings. The audit, run through Circle's Arc Studio with a max-preset adversarial prompt requiring a runnable forge PoC per finding, surfaced 21 findings in the identity pack (7 High, 7 Medium, 6 Low, 1 informational) and 16 in the escrow pack, including three Critical-class issues: operator rotation, poster self-release, and a global platform fee. Three flagged issues were rejected with disproof PoCs, and total gas across testnet and mainnet came to roughly 0.9 USDC.", "body_md": "# 37 Ways to Break Our Contracts, and Why None of Them Work Anymore\n\nAn adversarial AI audit via Circle's Arc Studio found 37 findings across the AgentBadge contract pack. A week later all eight contracts shipped to Arc mainnet as hardened UUPS proxies with zero open findings — the full audit → fix → re-audit → deploy story.\n\nAgentBadge ran an adversarial AI audit (Arc Studio, max preset, forge PoC per finding) over its identity and escrow contracts — 37 findings, all closed or disproved. The hardened pack redeployed to Arc mainnet on 2026-10-11 as eight UUPS proxies with role-based access, a platformBps timelock, and a documented EOA→multisig+timelock upgrade-governance path. Total gas: ~0.9 USDC across testnet and mainnet.\n\nOn October 10, 2026 we pointed an AI auditor at the AgentBadge contract pack and told it to be mean. It found **37 findings** across identity and escrow — seven of them High, including a fully transferable \"soulbound\" passport and an escrow operator who could rotate themselves into the payout seat. On October 11, all eight contracts went live on Arc mainnet as hardened UUPS proxies with **zero open findings**.\n\nThis is C28 of the Arc Campaign series. [C1](https://agentbadge.xyz/blog/arc-c1-mainnet-deployment) was the first deployment story; this one is about what happened when we stopped trusting it.\n\n## How do you audit contracts without a security firm?\n\nWe used [Arc Studio](https://studio.arc.io) — Circle's server-side AI builder — as an adversarial auditor. Not a linter, not a checklist: a max-preset adversarial prompt that required **a runnable forge PoC for every finding** before it counted as a finding at all.\n\nThe prompt discipline mattered more than the model. Three rules did the heavy lifting: hunt specific bug classes (access-control bypasses, role confusion between cross-referencing contracts, upgrade/rotation gaps), produce an executable proof-of-concept test for each claim, and *don't touch the source* — audit only, recommended fixes as diff sketches. Two packs, two reports: identity/passport contracts (`VULN-01..21`) and escrow/marketplace (` VULN-1..16`).\n\nThe auditor-earned-its-fee numbers:\n\n- **Identity pack** : 21 findings — 7 High, 7 Medium, 6 Low, 1 informational\n- **Escrow pack** : 16 findings — including 3 Critical-class (operator rotation, poster self-release, global platform fee)\n- Every finding shipped with a forge PoC that compiled and demonstrated the behavior — no \"might be exploitable\" hand-waving\n\n## What were the worst things it found?\n\nThe kind of bugs that don't show up in unit tests because the tests were written by the same happy-path brain that wrote the code.\n\nThe top of the report, paraphrased:\n\n- **Transferable identity (High)** —`AgentPassportNFT` had no`_update` override, so passports — our ERC-8004 identity documents — were fully transferable. An issued identity could be sold or stolen; holder ≠ verified identity. Fixed with the soulbound`_update` guard.\n- **Operator rotation trap (Critical)** — the escrow`operator` could be swapped in one call. Two-step`proposeOperator` /`acceptOperator` plus a zero-address revert closed it.\n- **Poster self-release (Critical)** — the task poster could call`release` directly. Now release is 2-of-2: poster approves, operator executes.\n- **Zero-address log DoS (High)** — constructing with`log_ == address(0)` silently bricked every mint, revoke and attest call forever.`initialize` now reverts on zero.\n- **Revoked-slot lockout (High)** — revoking a pass poisoned its`passOf` slot; a subsequent mint took the renewal branch on a dead token. Irrecoverable — until we made the renewal branch check`!revoked[id]` .\n\nThree findings were *rejected with disproof PoCs* — the auditor flagged \"NFT owner race\" and \"pay() shrinks release window,\" and the same PoC discipline showed the invariants hold. A good audit closes bugs; a great one also kills false alarms.\n\n## What does \"hardened\" actually mean on-chain?\n\nSame eight contracts, rewritten underneath. Every contract is now a UUPS upgradeable proxy (`ERC1967Proxy` subclass `AgentBadgeProxy`), with `_authorizeUpgrade` gated behind `UPGRADER_ROLE`, `_disableInitializers()` in every constructor, and 50-slot storage gaps for future layout.\n\nThe behavioral fixes on top:\n\n- `REVOKER_ROLE` — server operator can revoke a compromised pass without touching the admin key\n- `redirectClaimer` — a claimer key rotates itself without an operator roundtrip\n- `platformBps` timelock — fee changes are propose → 48h → commit; no instant fee rug\n- `xHolds` — escrow holds keyed by a separate uint256 space; an NFT tokenId can never collide with a taskId\n- `MIN_DEADLINE_OFFSET = 1h` — deposits must leave operators time to work\n- `CLASS_FULL_COVERED` whitelist — a FULL pass covers MEDIUM+HEAVY, not \"every future class bit\" (the audit caught CLASS_EAAS leaking through)\n- `_emitLog` in try/catch — a dead event log can't DoS escrow settlement\n\nThe full list is 33 fixes across Critical/High/Medium/Low — every one with a regression test. Final suite: **197 tests green**.\n\n## What's it like auditing with Arc Studio?\n\nPractically useful, with sharp edges worth documenting.\n\n**What worked.** One well-specified prompt per run beat iterative chatting — the agent wrote PoCs, ran forge tests, and assembled the report itself inside the sandbox. The PoC requirement is the killer feature: it converts audit output from opinions into reproducible evidence. Cost was ~3% of the daily budget per pack audit; the whole engagement (two packs + re-audit) fit comfortably inside a week of quota.\n\n**What to watch.** Arc Studio is testnet-only — all mainnet deploys stayed on our hardhat/`chain-cli` path. Its outputs are agent-generated, not attested: we re-verified every finding locally before fixing, and three findings turned out to be false positives. And the audit prompts are worth keeping under version control — ours live in `scripts/arc-studio/prompts/` (`audit-identity.txt`, `audit-escrow.txt`), so the next run is reproducible.\n\nNet: an AI auditor doesn't replace a security firm for mainnet money. But for \"find the bugs your own tests were blind to,\" it earned its tokens.\n\n## How did the deployment go?\n\nTwo chains, one idempotent script, ~0.9 USDC total gas.\n\n`deploy-arc-v2.ts` deploys an implementation, wraps it in `AgentBadgeProxy`, calls `initialize` with the right params (USDC, treasury, operator, `platformBps`), and records every address in `deployments/{network}-v2.json`. Run it twice and it skips what exists — which we proved unintentionally when the first mainnet run was interrupted halfway: the resume deployed only the missing three.\n\n- **Arc testnet (5042002)** : 8/8 proxies, full write-path smoke PASS (registry → badge → passport → passes → escrow deposit/approve/release)\n- **Arc mainnet (5042)** : 8/8 proxies, read-only sanity PASS — writer → passport, escrow usdc/operator/log, splitter bps=1000/treasury\n\n| Contract | Mainnet v2 proxy | \n|---|---|\n| **AgentEventLog** | [`0x2038…0a90`](https://explorer.arc.io/address/0x20387070057178e9629dd6c9037c52487f090a90) | \n| **TrustRegistry** | [`0x0b56…a222`](https://explorer.arc.io/address/0x0b56d876fa0b11742e8139b0291fa5080912a222) | \n| **TrustBadge** | [`0xebcb…2821`](https://explorer.arc.io/address/0xebcb3adb5f7def429e9c3254bcbe6cd46aef2821) | \n| **AgentPassportNFT** | [`0x1f24…0a76`](https://explorer.arc.io/address/0x1f2426b102f57ecfeac2ab75c3fd853493f00a76) | \n| **AccessPassNFT** | [`0x8e25…982f`](https://explorer.arc.io/address/0x8e254abfd8e4691b11f8553fb9a9f180a248982f) | \n| **MarketplacePassNFT** | [`0x6e40…87e5`](https://explorer.arc.io/address/0x6e408672e56001dc24a5db68107f9abf900f87e5) | \n| **TaskEscrow** | [`0xc58d…1d53`](https://explorer.arc.io/address/0xc58dd0890a8a186b651cd02f34af629c8cc91d53) | \n| **MarketplaceSplitter** | [`0x6904…aba9`](https://explorer.arc.io/address/0x69043c847e9ee79b7128ec6d280f5f25fc76aba9) | \n\nWe checked the v1 contracts first (decision D-196-5): two marketplace passports and nothing else, all owned by our deployer — no external holders — so v2 is a clean restart, not a state migration. The v1 addresses stay frozen as read-only history.\n\n## Who can upgrade these contracts?\n\nToday, the deployer EOA — with a documented exit. `UPGRADE-GOVERNANCE.md` lays out the migration: when the first real USDC lands in `TaskEscrow`, `UPGRADER_ROLE` moves to an OZ `TimelockController` (≥24h delay) whose proposer/executor is a multisig. A queued malicious upgrade becomes cancellable inside the timelock window.\n\nThe reasoning is simple: an upgrader key is god — one compromised key swaps in a hostile implementation and repoints every reference. Day-to-day roles (`MINTER`, `RECORDER`, `REVOKER`, `OPERATOR`) deliberately carry no upgrade power.\n\n## For AI Agents\n\nProgrammatic endpoints for everything in this article:\n\n- **Contracts** : v2 proxies above, on Arc mainnet`eip155:5042` — ABIs in the[agentbadge repo](https://github.com/spreadzp/agentbadge)\n- **Audit artifacts** :`VULN-REPORT.md` (identity, escrow) and`VULN-REPORT-v2.md` (re-audit) ship with the contracts package — findings, severities, PoC test names\n- **Verify a deployment** : query`owner()` /`writer()` /`usdc()` on any proxy with`eth_call` ; the event log's`writer()` is the passport proxy — that's the wiring check\n- **Machine-readable identity** :`curl -s https://agentbadge.xyz/.well-known/agent-card.json` and`/did.json` for the platform's DID and ERC-8004 refs\n\n*Don't certify. Measure.*\n\n## Try it yourself\n\n- Deploy ledger: `contracts/deployments/arc-mainnet-v2.json`\n- Governance plan: `contracts/UPGRADE-GOVERNANCE.md`\n- Audit prompts: `scripts/arc-studio/prompts/audit-{identity,escrow}.txt`\n\n*This is C28 in the Arc Campaign series. Earlier: C1 told the first deployment story; C11 covered verdict transparency on-chain. The series index lives at [agentbadge.xyz/blog](https://agentbadge.xyz/blog).*\n\n**Links:** [AgentBadge](https://agentbadge.xyz) · [Arc Studio](https://studio.arc.io) · [Arc Explorer](https://explorer.arc.io)\n\n*Don't certify. Measure.*", "url": "https://wpnews.pro/news/37-ways-to-break-our-contracts-and-why-none-of-them-work-anymore", "canonical_source": "https://agentbadge.xyz/blog/arc-c28-contract-hardening", "published_at": "2026-10-12 00:00:00+00:00", "updated_at": "2026-10-12 07:37:55.333061+00:00", "lang": "en", "topics": ["ai-safety", "ai-agents", "ai-tools"], "entities": ["AgentBadge", "Arc Studio", "Circle", "Arc mainnet", "AgentPassportNFT", "ERC-8004"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/37-ways-to-break-our-contracts-and-why-none-of-them-work-anymore", "markdown": "https://wpnews.pro/news/37-ways-to-break-our-contracts-and-why-none-of-them-work-anymore.md", "text": "https://wpnews.pro/news/37-ways-to-break-our-contracts-and-why-none-of-them-work-anymore.txt", "jsonld": "https://wpnews.pro/news/37-ways-to-break-our-contracts-and-why-none-of-them-work-anymore.jsonld"}}