Skip to content

test: cover EthTransferFailed when pre-funded ETH skips WETH withdrawal - #21

Merged
aniket866 merged 1 commit into
StabilityNexus:mainfrom
IIITManjeet:test/native-eth-prefunded-transfer-failed
Sep 4, 2026
Merged

test: cover EthTransferFailed when pre-funded ETH skips WETH withdrawal#21
aniket866 merged 1 commit into
StabilityNexus:mainfrom
IIITManjeet:test/native-eth-prefunded-transfer-failed

Conversation

@IIITManjeet

@IIITManjeet IIITManjeet commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Addressed Issues:

Fixes #19

Summary:

_safeTransferTokenOrETH can reach EthTransferFailed two ways: after WETH.withdraw(), or straight from an existing native ETH balance with the unwrap skipped. #17 covered the first (exchange starts at zero ETH). #18 reached the skip-unwrap branch but with an ETH-accepting seller, so settlement succeeded. Skipping the unwrap and failing the send was untested.

test_nativeETH_revertOnFailedEthTransferWhenPrefunded pre-funds the exchange with exactly the 99.9 ETH seller payout and matches against a RejectETHReceiver maker. The payout goes straight from the pre-funded balance, the send fails, and the whole match rolls back.

Contract Scope:

No src/ or script/ changes. RejectETHReceiver is reused unchanged.

Security Considerations:

None — test only, no production code touched. It pins the rollback guarantee on this route, which matters because the route deactivates both orders and completes a token transfer before the ETH send fails.

Storage/Layout Impact:

  • No storage layout changes
  • Storage layout changed (explain below)

Gas and Performance:

No production gas change — WindmillExchange is byte-identical to main at 10,692 B runtime / 10,974 B initcode.

.gas-snapshot gains 1 entry. 27 existing entries move by −67 to +162 gas: adding a public function shifts solc's dispatch table for the test contract. 22 entries are untouched.

Test Evidence:

  • forge fmt --check — clean, no diffs

  • forge build --sizes — 10,692 / 10,974, unchanged from main

  • forge test — 50 passed, 0 failed, 0 skipped; new test at gas 787,995

  • forge snapshot --check — passes, 50 entries

  • forge coverage — aggregate unchanged, since EthTransferFailed was already reached by test: cover EthTransferFailed path in native ETH settlement #17. The LCOV counts show the new path:

    line before after
    102 guard evaluated 6 7
    103 WETH.withdraw() executed 5 5
    107 revert branch taken 1 2

    One more guard evaluation, zero more withdrawals, revert firing a second time — the failure now happens on the branch that never unwraps.

The -vvvv trace contains no MockWETH::withdraw and no MockWETH::balanceOf at all; the && on line 102 short-circuits, so the WETH balance is never read. #17's trace on the same revert contains both.

Deployment and Verification:

  • Requires deployment script updates
  • Requires env/config updates (PRIVATE_KEY, RPC URL, explorer key)
  • Requires explorer verification updates
  • No deployment impact

Screenshots/Recordings:

N/A — test-only change.

Additional Notes:

Rebased onto main after #20 merged.

The buy order is funded with plain WETH rather than native {value:} on purpose: MockWETH.mint creates unbacked supply, so the mock holds no ETH and any stray withdraw() on the failing leg would revert with the mock's own string instead of EthTransferFailed — making the skipped unwrap provable without relying on cheatcode behaviour.

Verified non-vacuous with three negative controls, each reverted after checking: removing the pre-fund fails with ETH transfer failed != EthTransferFailed(); setting the expectCall count to 1 fails with called 0 times; removing vm.expectRevert fails with EthTransferFailed().

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions.
  • If applicable, I have made corresponding changes or additions to the documentation.
  • If applicable, I have made corresponding changes or additions to tests.
  • My changes generate no new warnings or errors.
  • I have joined the Stability Nexus's Discord server and I will share a link to this PR with the project maintainers there.
  • I have read the Contribution Guidelines.
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I reviewed contract access control and authorization paths.
  • I reviewed reentrancy/external-call ordering where relevant.
  • I confirmed storage compatibility (or documented intentional breakage).
  • I reviewed gas impact for hot paths.

AI Usage Disclosure

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: Claude Code (Claude Opus 5, Claude Fable 5).

AI Notice - Important!

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact.

Summary by CodeRabbit

  • Bug Fixes

    • Improved native ETH settlement when prefunded ETH is available, ensuring seller payouts and keeper fees use the correct funds.
    • Added validation that failed native ETH transfers revert safely without altering escrow balances, order states, or prefunded funds.
  • Tests

    • Added coverage for prefunded ETH payouts, skipped WETH unwrapping, and failed transfer handling.
    • Updated recorded gas usage for exchange test cases.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: da00e55a-fca9-40a7-a835-b265de02d94b

📥 Commits

Reviewing files that changed from the base of the PR and between 00581ca and 8d967b4.

📒 Files selected for processing (2)
  • .gas-snapshot
  • test/WindmillExchange.t.sol

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


Walkthrough

The test suite adds coverage for failed native ETH settlement when prefunded exchange ETH skips WETH withdrawal. It verifies transaction rollback and updates recorded gas values, including the new test.

Changes

Prefunded ETH settlement coverage

Layer / File(s) Summary
Prefunded ETH failure and rollback test
test/WindmillExchange.t.sol
Adds a rejecting-recipient test that confirms WETH withdrawal is skipped, EthTransferFailed is raised, and orders, escrow balances, and prefunded ETH remain unchanged.
Gas snapshot recalculation
.gas-snapshot
Updates gas values for affected tests and records gas usage for the new test.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 8d967

This change adds regression coverage for rejected native-ETH payouts from a prefunded exchange and records updated gas snapshots. The failure path and rollback behavior are covered, with no remaining merge-blocking risk identified.

Suggested labels: Solidity Lang

Suggested reviewers: aniket866

Poem

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the new test for EthTransferFailed when pre-funded ETH skips WETH withdrawal.
Description check ✅ Passed The description follows the template and documents the issue, implementation, scope, security impact, storage impact, gas impact, test evidence, deployment impact, checklist, and AI usage.
Linked Issues check ✅ Passed The changes satisfy issue #19 by testing an ETH-rejecting maker with pre-funded exchange ETH, expecting EthTransferFailed, confirming WETH withdrawal is skipped, and verifying rollback of orders, escr…
Out of Scope Changes check ✅ Passed The changes are limited to settlement-path tests, related NatSpec documentation, and the regenerated gas snapshot. No unrelated production, deployment, or configuration changes are present.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@IIITManjeet

Copy link
Copy Markdown
Contributor Author

@aniket866 please do review and merge the PR

@IIITManjeet
IIITManjeet force-pushed the test/native-eth-prefunded-transfer-failed branch from 3169a54 to 77f2394 Compare September 2, 2026 18:37
_safeTransferTokenOrETH can reach the failing ETH send by two routes:
after unwrapping WETH, or directly from an existing native ETH balance
with the unwrap skipped. StabilityNexus#17 covered the first, starting the exchange at
zero ETH. StabilityNexus#18 reached the skip-unwrap branch but with an ETH-accepting
seller, so settlement succeeded. Skipping the unwrap and failing the
send was untested.

Pre-fund the exchange with native ETH equal to the seller payout and
match against an ETH-rejecting maker, so the failing send lands on the
branch that never called withdraw. Assert EthTransferFailed, that both
orders stay active and unfilled, that the WETH and token escrows are
intact, that the pre-funded ETH is untouched, and that the rejecting
maker received nothing.

Fund the buy order with plain WETH rather than native value, so the mock
holds no ETH and any stray withdraw would revert with the mock's own
string instead of EthTransferFailed. That makes the skipped unwrap
provable without relying on cheatcode behaviour.

Regenerate .gas-snapshot for the 55-test suite, rebased onto main after
StabilityNexus#22 merged. The 13 shifted entries are solc dispatch-table movement from
adding a public test function; production bytecode is unchanged.

Closes StabilityNexus#19

Signed-off-by: IIITManjeet <manjeetpathak2003@gmail.com>
@IIITManjeet
IIITManjeet force-pushed the test/native-eth-prefunded-transfer-failed branch from 77f2394 to 8d967b4 Compare September 2, 2026 18:39
@aniket866

Copy link
Copy Markdown
Contributor

@coderabbitai review and flag issues with this PR if any

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Changed files =='
git diff --name-status HEAD^ HEAD

echo '== PR diff =='
git diff --unified=80 HEAD^ HEAD -- test/WindmillExchange.t.sol .gas-snapshot

echo '== Relevant Solidity outlines =='
fd -e sol . | sort | while IFS= read -r file; do
  if rg -q 'safeTransferTokenOrETH|skipUnwrapWhenPrefunded|revertOnFailedEthTransferWhenPrefunded|RejectETHReceiver' "$file"; then
    echo "--- $file"
    ast-grep outline "$file" --items all --type function,contract,struct,error 2>/dev/null || true
  fi
done

echo '== Relevant source occurrences =='
rg -n -C 12 '(_safeTransferTokenOrETH|skipUnwrapWhenPrefunded|revertOnFailedEthTransferWhenPrefunded|RejectETHReceiver|EthTransferFailed)' -g '*.sol' .

Length of output: 36321


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Test fixtures, mock WETH, and rejecting receiver =='
sed -n '1,165p' test/WindmillExchange.t.sol

echo '== Test contract setup and helper functions =='
rg -n -A150 -B10 'function setUp\s*\(' test/WindmillExchange.t.sol

echo '== Exchange order creation and matching =='
sed -n '145,270p' src/core/WindmillExchange.sol

echo '== Imports and token-transfer library =='
rg -n -C 8 'library TokenTransfer|function safeTransfer|function safeTransferFrom' -g '*.sol' src test

Length of output: 18697


@aniket866 I found no actionable issues in this PR.

The new test correctly covers the pre-funded native-ETH path. It verifies that MockWETH.withdraw is not called, the rejected seller payout reverts with EthTransferFailed, and the transaction rolls back order state and balances.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@aniket866 aniket866 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.

Nice!

@aniket866
aniket866 merged commit a68eee6 into StabilityNexus:main Sep 4, 2026
5 checks passed
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.

test: cover EthTransferFailed when pre-funded exchange ETH skips WETH withdrawal

2 participants