feat: implement escrow contract - #1
Conversation
| ## Key conventions | ||
|
|
||
| - **Foundry** is the build/test framework. Use `forge build`, `forge test`, `forge fmt`. | ||
| - **Solidity 0.8.28+**, optimizer enabled with 1M runs, `cancun` EVM target. | ||
| - `deny = "warnings"` is set in `foundry.toml` — compiler and lint warnings are errors. | ||
| - `forge fmt` enforces import sorting (`sort_imports = true`). | ||
| - The `block-timestamp` lint is excluded (cooldown requires `block.timestamp` comparisons). |
There was a problem hiding this comment.
Would add something to try to consume as much as possible ERCs and OpenZeppelin reference contracts. This would help us to decrease the audit surface.
| escrowBadOwner.withdrawDebits(); | ||
| } | ||
|
|
||
| // --- Reentrancy --- |
There was a problem hiding this comment.
Thinking if deposit is also safe with reentrancy. I imagine it is but still I would check it.
There was a problem hiding this comment.
Stale comment
Risk: high. Not approving: this introduces the core Escrow collateral contract (security-sensitive scope) and exceeds the medium-risk approval threshold. Cursor Bugbot and Cursor Security Agent checks were not present; human review is needed for unresolved feedback on Escrow.sol.
Sent by Cursor Approval Agent: Pull Request Router and Approver
Implement the full BYOSEscrow contract with owner/operator role separation, cooldown-based all-or-nothing withdrawals, freeze/unfreeze for Track B investigations, debit authority for Track A/B penalties, and permissionless debit sweeping to owner.
The ADR defines the contract as simply "Escrow". Aligns contract name, test file, test directory, and deploy script with the specification.
The block-timestamp lint warns about validator manipulation of block.timestamp, but this is irrelevant for day-scale cooldowns. Foundry 1.7.1 runs the linter during build and deny=warnings makes this a hard error in CI.
…t tests Extend test_full_withdrawal_lifecycle with a second deposit/withdraw cycle to verify no leftover deposits or totalDebited in contract state. Add test_debit_after_withdrawal_reverts to confirm operator cannot debit a sub-solver after their balance has been fully withdrawn.
Replace direct ownership transfer with a two-step process: owner calls transferOwnership to set a pendingOwner, then pendingOwner calls acceptOwnership to finalize. Prevents irrecoverable loss from typos. A second transferOwnership call overrides any pending transfer.
Aligns test variable names with the domain terminology used in the contract and ADR.
Consolidates the repeated deposits[s] - totalDebited[s] subtraction into a single internal function with an explicit underflow guard. Reverts with InsufficientBalance instead of a raw panic if the invariant is ever violated.
Collapses two per-sub-solver mappings into one. Deposits add to the balance, debits subtract from it, withdrawal zeroes it. Eliminates indefinite accumulation of stale accounting state, removes the _balance() helper (no subtraction needed), and saves one SLOAD in debit() and one SSTORE in executeWithdrawal().
BYOSTrampoline belongs in its own branch/PR. Removes the contract, its tests, and the deploy script reference.
Copied from cow-byos-architecture docs/adr/ as the canonical reference for contract design decisions.
- Single balances mapping instead of deposits/totalDebited - Two-step ownership transfer (transferOwnership + acceptOwnership) - Updated interface, view semantics, and alternatives considered
README covers project overview, contract table, architecture links, and development instructions. AGENTS.md provides guidelines for AI agents: repo structure, conventions, domain language, and testing patterns.
Bring the contract-relevant design context into this repo, which now owns the contract ADRs (the architecture repo was temporary exploration): - CONTEXT.md: domain language and architecture map, scoped to the contracts - ADR-0003: trampoline deployment & settlement integration - ADR-0004: penalty schedule & attribution (contract-scoped extract) - ADR-0005: trampoline execution authority & EIP-712 proposal signature - docs/adr/README.md: index plus known open questions - docs/reference/: CoW slashing framework, auction mechanics, solver CIPs - docs/agents/: issue tracker and triage label conventions Fix the broken references in ADR-0001/0002 (CONTEXT.md, economics note, old ADR-0006 numbering) and convert code anchors to GitHub permalinks pinned at cowprotocol/contracts@c6b61ce. The RFP is cited by its forum URL. AGENTS.md points at CONTEXT.md for vocabulary instead of carrying its own glossary, and README lists the Trampoline as a planned contract. Escrow.deposit() gets a TODO for the deploy-at-deposit-time factory hook settled in ADR-0003; the hook lands with the Trampoline factory.
Prevents a no-op withdrawal (e.g. after full debit during cooldown) from silently succeeding. Now reverts with NothingToWithdraw instead.
dfb2935 to
401e813
Compare
There was a problem hiding this comment.
Risk: high. Not approving: this introduces the core Escrow collateral contract and exceeds the medium-risk approval threshold. Cursor Bugbot and Cursor Security Agent were not present; mendesfabio and yvesfracari are already assigned for unresolved review feedback.
Sent by Cursor Approval Agent: Pull Request Router and Approver
yvesfracari
left a comment
There was a problem hiding this comment.
Approve since we're going to work on the feedbacks on another PRs:
https://linear.app/bleu-builders/issue/COW-1149/change-escrow-contract-to-implement-erc20-interface
https://linear.app/bleu-builders/issue/COW-1150/test-if-escrow-contract-is-safe-agains-reentrancy-on-deposits
https://linear.app/bleu-builders/issue/COW-1148/implement-access-control-on-escrow-contract


Summary
Escrowcontract following the spec in ADR-0002, with native-token sub-solver collateral keyed by addresstransferOwnership+acceptOwnership) to prevent irrecoverable typosbalancesmapping for sub-solver accounting — deposits add, debits subtract, withdrawal zeroesrequestWithdrawal→ wait →executeWithdrawaldebit(subSolver, amount, reason)for Track A (revert penalties) and Track B (EBBO passthrough)deposit(subSolver)andwithdrawDebits()(sweeps to owner)balance,effectiveBalance(0 when withdrawal pending),withdrawableBalanceESCROW_OWNER,ESCROW_OPERATOR,COOLDOWN_PERIOD)docs/adr/, escrow ADR updated to reflect implementationTest plan
forge test -vvv)deny = "warnings"lint policyforge fmt --checkpasses