Committed timing profile is development, releasing buyer funds minutes after funding
The committed timing profile is the development one, with minute-scale windows compiled into bytecode. A build from the repository as checked out releases buyer funds to sellers minutes after funding, removing buyer protection on every deal.
Description
contracts/lib/ProtocolTimings.sol is committed carrying the development profile:
string internal constant PROFILE = "development";uint256 internal constant DELIVERY_PERIOD_UNIT = 1 minutes;uint256 internal constant BUYER_OBJECTION_GRACE = 3 minutes;uint256 internal constant REVEAL_WINDOW = 3 minutes;uint256 internal constant PENDING_ABORT_WINDOW = 5 minutes;
These values are read as constant at compile time and baked into deployed bytecode, so anyone who compiles the repository as checked out ships minute-scale windows. The regeneration step runs only from Hardhat's precompile hook, so any Foundry build compiles the committed file unchanged, as does any Hardhat compile that does not regenerate first.
Vulnerable Scenario: The following steps illustrate the issue:
- A deployer checks out the audited commit and builds without regenerating timings.
- Contracts deploy with
DELIVERY_PERIOD_UNIT = 1 minutesandBUYER_OBJECTION_GRACE = 3 minutes. - A buyer funds a deal with
timeToDeliverDays = 5, expecting five days plus a 72-hour objection window. _activateFundingsetsdeliveryDueAt = fundedAt + 5 minutesanddeadline = deliveryDueAt + 3 minutes.- Eight minutes after funding, any caller may invoke
autoReleaseand pay the seller in full. The buyer's entire window to inspect delivery and dispute has elapsed.
Impact
Buyer protection is removed on every deal, with escrowed funds released to sellers automatically minutes after funding. No revert or event signals the misconfiguration. protocolTimingProfile() exposes the active profile, but nothing on-chain reads or enforces it, so a deployment carrying development timings is indistinguishable at runtime from a correct one.
Recommendation
Commit the production profile as the checked-in default so that the safe configuration is what an unmodified build produces and the testnet profile is the one requiring an explicit regeneration step. Add a test asserting the concrete window constants against config/protocol-timings.json — for example BUYER_OBJECTION_GRACE >= 72 hours under the production profile — so that a build carrying development timings fails rather than deploying silently. Assert the constants themselves rather than the PROFILE label, since a label check passes when the label is right and a value is wrong.
test/helpers.js hardcodes the development windows and states that it must match the checked-in profile, so switching the default will break the suite as written. Have the helpers read the constants from the deployed contract rather than duplicate them, otherwise the same drift simply reappears in the fixtures.
Resolution
Fixed. The committed artifact no longer carries the development timing profile.
Affected files
contracts/lib/ProtocolTimings.solconfig/protocol-timings.jsonscripts/generate-protocol-timings.jscontracts/Escrow.sol#L129-L133test/helpers.js#L10-L23