Skip to content

feat: implement escrow contract - #1

Merged
jean-neiverth merged 16 commits into
mainfrom
feat/escrow-contract
Jul 13, 2026
Merged

feat: implement escrow contract#1
jean-neiverth merged 16 commits into
mainfrom
feat/escrow-contract

Conversation

@jean-neiverth

@jean-neiverth jean-neiverth commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Implements the Escrow contract following the spec in ADR-0002, with native-token sub-solver collateral keyed by address
  • Owner/operator role separation: owner (multisig) controls configuration and receives debited funds; operator (EOA in BYOS service) handles automated debit/freeze operations
  • Two-step ownership transfer (transferOwnership + acceptOwnership) to prevent irrecoverable typos
  • Single balances mapping for sub-solver accounting — deposits add, debits subtract, withdrawal zeroes
  • All-or-nothing withdrawal with configurable cooldown period — requestWithdrawal → wait → executeWithdrawal
  • Operator-exclusive debit(subSolver, amount, reason) for Track A (revert penalties) and Track B (EBBO passthrough)
  • Freeze/unfreeze mechanism blocks withdrawal execution during Track B investigations without affecting effective balance
  • Permissionless deposit(subSolver) and withdrawDebits() (sweeps to owner)
  • View functions: balance, effectiveBalance (0 when withdrawal pending), withdrawableBalance
  • 54 tests covering full lifecycle, access control, freeze semantics, reentrancy safety, transfer failures, event emissions, and edge cases
  • Deploy script with constructor params via env vars (ESCROW_OWNER, ESCROW_OPERATOR, COOLDOWN_PERIOD)
  • ADRs (escrow + trampoline) brought into this repo under docs/adr/, escrow ADR updated to reflect implementation
  • README and AGENTS.md for project and agent onboarding

Test plan

  • All 54 tests pass (forge test -vvv)
  • Full build passes with deny = "warnings" lint policy
  • forge fmt --check passes
  • Review against ADR-0002 interface specification
  • Reentrancy safety verified (CEI pattern + explicit test with reentrant attacker contract)
  • Transfer failure paths tested (ETH-rejecting contracts for both sub-solver and owner)

Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol Outdated
Comment thread src/contracts/Escrow.sol Outdated
Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol
Comment thread test/Escrow/Escrow.t.sol Outdated
Comment thread test/Escrow/Escrow.t.sol
Comment thread test/Escrow/Escrow.t.sol
Comment thread src/contracts/Escrow.sol Outdated
@jean-neiverth jean-neiverth changed the title feat: implement escrow contract per ADR-0002 feat: implement escrow contract Jul 9, 2026
@jean-neiverth
jean-neiverth marked this pull request as ready for review July 9, 2026 16:56
Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol
Comment thread src/contracts/Escrow.sol Outdated
Comment thread AGENTS.md
Comment on lines +18 to +24
## 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).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread test/Escrow/Escrow.t.sol
escrowBadOwner.withdrawDebits();
}

// --- Reentrancy ---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking if deposit is also safe with reentrancy. I imagine it is but still I would check it.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@cursor
cursor Bot requested review from mendesfabio and yvesfracari July 13, 2026 18:32
jean-neiverth and others added 16 commits July 13, 2026 15:34
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.
@jean-neiverth
jean-neiverth force-pushed the feat/escrow-contract branch from dfb2935 to 401e813 Compare July 13, 2026 18:36

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

@yvesfracari yvesfracari left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jean-neiverth
jean-neiverth merged commit d9d617d into main Jul 13, 2026
3 checks passed
@yvesfracari
yvesfracari deleted the feat/escrow-contract branch July 17, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants