C0LLBACK
c0llback/handshake

A decentralized, single-deal escrow with a timelock and arbitration. Deploy one per exchange — funds settle by the code, with no admin keys and nobody able to touch them off-script.

solidityescrowp2ptimelockarbitrationdefiethereum
Solidity 0.8.17License MITNetwork Ethereum7 files
Solidity 68.3%Markdown 26.1%Text 5.6%
c0llbackc2f5a98escrow: timelock auto-release + arbiter-resolved disputes (ETH & ERC20)Nov 21, 2022
README.md

HANDSHAKE

Solidity License Build Tests

A decentralized, single-deal escrow with a timelock and arbitration. To open an exchange you deploy the contract yourself with the two counterparties and an arbiter — then the trade settles by the rules in the code, with nobody (not even the deployer) able to touch the funds off-script.

One contract is one deal. No shared pool, no admin keys, no upstream owner. After the FTX implosion of late 2022, "deploy your own escrow and let the code hold the money" felt like the only honest way to swap value with a stranger.


The flow

┌──────────────────┐
buyer.deposit() ─────→│ AwaitingDelivery │
└───────┬──────────┘
seller delivers off-chain │
┌─────────────────────────┼─────────────────────────┐
↓ ↓ ↓
buyer.confirmReceipt() buyer/seller.raiseDispute() seller.claimTimeout()
│ │ │ (after releaseTimeout)
↓ ↓ ↓
→ seller (Complete) → Disputed → seller (Complete)
arbiter.resolveDispute(toSeller)
split seller / buyer, minus arbiter fee (Resolved)

Plus two escape hatches: seller.refundBuyer() (voluntary return while funded) and cancel() (abandon before any funds move).

The two safety rails

  • TimelockclaimTimeout() lets the seller withdraw once releaseTimeout

has elapsed since funding and the buyer has neither confirmed nor disputed. A buyer who simply vanishes can't lock the seller's payment forever.

  • ArbitrationraiseDispute() (by either party) freezes the timeout and

hands the call to the arbiter, who assigns the post-fee pot between the two sides with resolveDispute(toSeller). The arbiter earns arbiterFeeBps (capped at 10%) only on a resolved dispute.


States

StateMeaningExits
AwaitingPaymentdeployed, unfundeddeposit → AwaitingDelivery · cancel → Cancelled
AwaitingDeliveryfunded, seller deliveringconfirmReceipt/claimTimeout → Complete · refundBuyer → Refunded · raiseDispute → Disputed
Disputedarbiter rulingresolveDispute → Resolved
Complete / Refunded / Resolved / Cancelledterminal

Deploy & use

// 1. Open a deal: 1 ETH escrow, 3-day timelock, 2% arbiter fee.
// token = address(0) for ETH; pass an ERC20 address for tokens.
Escrow escrow = new Escrow(
buyer, // pays
seller, // delivers
arbiter, // neutral third party
address(0), // ETH
1 ether, // amount
3 days, // releaseTimeout
200 // arbiterFeeBps = 2.00%
);
 
// 2. Buyer funds
escrow.deposit{value: 1 ether}(); // ERC20: approve first, then deposit()
 
// 3a. Happy path — buyer got the goods
escrow.confirmReceipt(); // → seller
 
// 3b. Buyer ghosted — seller waits out the timelock
escrow.claimTimeout(); // → seller, after 3 days
 
// 3c. Something went wrong — go to arbitration
escrow.raiseDispute(); // by buyer or seller
escrow.resolveDispute(0.6 ether); // arbiter: 0.6 → seller, rest → buyer (minus fee)

Prefer one call? EscrowFactory.createEscrow(...) deploys the same Escrow and indexes it by buyer/seller/arbiter so a UI can list "my deals" with no backend.

Build & test

forge build
forge test

Security

  • Not audited. Independent work, published for review. Don't escrow real

value before an audit.

  • Roles are immutable and must be three distinct addresses; the deployer

has no special power. Choose the arbiter as carefully as the counterparty — on a dispute they decide the split (a multisig / known mediator is wise).

  • Every payout is nonReentrant and follows checks-effects-interactions; state

flips before funds move.

  • Fee-on-transfer / rebasing tokens are unsupported — the accounting assumes

the contract receives exactly amount.

  • ETH payouts use call; an arbiter/party that is a contract reverting on

receive can only block its own leg. Use EOAs or receivers that accept ETH.

License

MIT © 2022 c0llback

← Back to index© 2020 c0llbackMIT