Skip to content

fix: revert when msg.value does not cover a native fill - #132

Open
dev-aly3n wants to merge 1 commit into
catalystsystem:mainfrom
dev-aly3n:fix/native-value-coverage
Open

fix: revert when msg.value does not cover a native fill#132
dev-aly3n wants to merge 1 commit into
catalystsystem:mainfrom
dev-aly3n:fix/native-value-coverage

Conversation

@dev-aly3n

Copy link
Copy Markdown

Description

_fill's native branch sends outputAmount to the recipient, but nothing requires msg.value to cover it. _refundNativeExcess handles only the excess case, so a shortfall is paid out of address(this).balance.

Not exploitable today. The settler holds no ether and has no receive()/fallback(), so Address.sendValue reverts. The concern is that this is safe by circumstance rather than by construction: any wei that reaches the contract (SELFDESTRUCT force-feed, a block-reward/withdrawal recipient, a future residue) would become permissionlessly sweepable via a self-order with a native output and msg.value = 0.

This is distinct from OpenZeppelin May-2026 L-01, which fixed the refund leg; that fix is present and correct. This adds the missing positive coverage check.

The check is placed in _refundNativeExcess rather than in _fill deliberately: fillOrderOutputs accumulates totalNativeSent across outputs and refunds once, so a per-output check would pass on each individual output while the total overdrew. Both entry points already funnel through this helper.

Includes three tests: a single output underfunded, a batch where the sum overdraws but no individual output does, and the funded path still refunding its excess correctly.

Related Issues

Third-Party Integration Checklist

N/A. This PR does not integrate with any external protocol: no new dependencies, no interfaces copied.

Additional Notes

Note on the batch test: with a zero contract balance, a batch overdraw already reverts inside _fill via Address.sendValue (InsufficientBalance), which masks the missing check. The new batch test therefore seeds the settler with a residual balance so the overdraw actually reaches _refundNativeExcess, which is the condition the check exists for.

Gas snapshots are not touched here: this repo's committed snapshots are already stale relative to a clean forge test run, so regenerating them would mix unrelated drift into the diff.

_fill's native branch sends outputAmount to the recipient, but nothing
requires msg.value to cover it. _refundNativeExcess handles only the
excess case, so a shortfall is paid out of address(this).balance.

The check is placed in _refundNativeExcess rather than in _fill because
fillOrderOutputs accumulates totalNativeSent across outputs and settles
once; a per-output check would pass on each individual output while the
total overdrew. Both entry points already funnel through this helper.

Adds a batch test for the cumulative overdraw case and one asserting a
correctly funded fill still refunds its excess and leaves a residual
balance untouched.
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.

1 participant