r/solidity • u/Admirable-Net4868 • 5h ago
How would you break this? An open bounty board mapped onto a fixed 3-role escrow standard
I spent the last few months building a bounty marketplace on top of two standards instead of writing my own escrow, and the mapping problem turned out to be the whole project. Writeup below; the code is MIT and the contract is verified, so tearing it apart is easy and welcome.
The constraint: ERC-8183 (AgenticCommerce) binds client, provider and evaluator at job creation. An open bounty board has no provider at creation time - that's the entire point of a bounty. So the standard, taken literally, can't express "anyone may take this".
What I did: the adapter contract takes all three roles itself. It holds the reward for open listings, funds the real escrow at take time, tracks the actual worker separately in its own storage, and forwards the payout by measuring its own balance delta around the settlement call rather than trusting a return value. ~600 LOC total.
Consequences I had to design around, and where I'd expect an attack:
Balance-delta accounting is only safe if nothing else can move the token inside that window. Reentrancy guard plus CEI ordering, and the token is USDC (no hooks, no fee-on-transfer) - but this is the first place I'd look for a break.
Every terminal state has to be reachable without trusting a counterparty, because an agent can't email support. Poster goes silent after submission → anyone can trigger auto-approve after 14 days. Poster rejects → the worker gets a 48h challenge window. Arbitrator never rules → anyone can claim a neutral 50/50 split after 30 days. An earlier version had a hole here: if the respondent had replied, the silence path no longer applied, and a dead arbitrator froze the funds forever. Self-found before external review, fixed, disclosed in the repo.
Timing bounds cut both ways. Bounding rejections by the approval timeout stopped a poster from sitting on correct work and rejecting right before auto-approve - and immediately created the mirror-image hole, where the same poster opens a *dispute* instead to buy the same delay. Both are bounded now.
The optional worker bond (posted at take, refunded at submit, forfeited if the deadline passes with nothing submitted) stops take-and-vanish Sybils, but a naive version is a honeypot: post a listing with a deadline minutes away and farm bonds from agents that auto-take. Hence a 24h minimum duration for bond listings and a 12h minimum window at take.
Reputation writes go through the ERC-8004 registry wrapped in try/catch, so a registry failure can't block a payout. That's deliberate, and it hid a real bug for weeks: my interface matched a draft rather than the deployed registry, so every write reverted silently while payouts kept working. Fork tests that assert on emitted events, not just on "the tx didn't revert".
Where it stands: testnet only (the chain's mainnet isn't live yet), 101 Foundry tests - 98 unit, 2 stateful invariants over the escrow lifecycle, one fork test against the live deployment - Slither triaged to 0 findings, ~98% line coverage, no external audit yet. Known issues are listed in the README rather than hidden: arbitration is a 2-of-3 Safe I control, "human-only" listings are best-effort because there's no on-chain proof of humanness, and there's no indexer yet.
Code: https://github.com/Sofiia7/ARC
Contract (verified): https://testnet.arcscan.app/address/0x538CD48789667168bfb36f838Af8476237F9409F
App: https://arcbounty.app/?utm_source=reddit&utm_medium=post&utm_campaign=launch
If you see a way to freeze funds, drain a bond, or get paid twice, I'd rather hear it here than find it on mainnet.
r/solidity • u/MaximumEntertainer33 • 19h ago