Skip to content

MVP for Windmill-EVM-Contracts Single file architecture#3

Closed
aniket866 wants to merge 3 commits into
StabilityNexus:mainfrom
aniket866:MVP-single-file
Closed

MVP for Windmill-EVM-Contracts Single file architecture#3
aniket866 wants to merge 3 commits into
StabilityNexus:mainfrom
aniket866:MVP-single-file

Conversation

@aniket866

@aniket866 aniket866 commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Addressed Issues:

Closes #1

Summary:

Introduces a clean 3-file MVP implementation of WindmillExchange on top of the base template.

Files Introduced

File Description
src/WindmillExchange.sol Core contract : types, math, price curve, token transfer, and exchange logic all in one file
test/WindmillExchange_t.sol Full test suite: unit tests, edge case reverts, and fuzz
script/DeployWindmill_s.sol Forge deploy script

Contract Scope:

N/A

Security Considerations:

Storage/Layout Impact:

  • No storage layout changes
  • Storage layout changed (explain below)
    No storage layout changes this is a fresh deployment with no prior on-chain version, so there is nothing to be compatible with.

Gas and Performance:

this is a fresh deployment with no prior on-chain version, so there is nothing to be compatible with.

Test Evidence:

  • forge fmt --check:
  • forge build --sizes:
  • forge test -vvv:
  • forge coverage:
  • forge snapshot:
image

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:

image

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: TODO

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

  • New Features
    • Launched an on-chain order-book exchange supporting limit orders with time-varying prices and keeper-settlement; added a deployment script for the exchange.
  • Tests
    • Added comprehensive tests covering order creation, cancellation, matching (full/partial), edge-case reverts, pruning of expired orders, and fuzz checks.
  • Removed
    • Removed the simple counter contract, its tests, and its deployment script.

@coderabbitai

coderabbitai Bot commented Mar 16, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Removes the Counter contract, its deployment script, and tests; adds a new WindmillExchange Solidity contract with order lifecycle, dynamic time-based price curves, matching/pruning logic, safe token transfers, a Foundry deploy script, and comprehensive Forge tests.

Changes

Cohort / File(s) Summary
Counter Removal
script/Counter.s.sol, src/Counter.sol, test/Counter.t.sol
Deleted the Counter contract, its Foundry deployment script, and associated Forge test.
WindmillExchange Core
src/WindmillExchange.sol
Added new WindmillExchange contract: errors, Order struct, RAY/math utils, time-varying price curve, create/cancel/match/prune APIs, safe token transfers, pair indexing, events, and non-reentrancy.
WindmillExchange Tests
test/WindmillExchange.t.sol
Added extensive Forge tests and MockERC20: order creation/cancel/match (full & partial), expiry/prune behavior, revert cases, fuzzing, and helper utilities.
Deployment Script
script/DeployWindmill.s.sol
Added Foundry script to deploy WindmillExchange using PRIVATE_KEY env var and log deployed address.

Sequence Diagram

sequenceDiagram
    participant MakerA as Maker (Buy)
    participant MakerB as Maker (Sell)
    participant WE as WindmillExchange
    participant TokenA as TokenA
    participant TokenB as TokenB
    participant Keeper as Keeper

    MakerA->>WE: createOrder(tokenA→tokenB, buy, amountIn, startPrice,...)
    WE->>TokenA: transferFrom(MakerA, WE, amountIn)
    TokenA-->>WE: transfer success
    WE-->>MakerA: orderId

    MakerB->>WE: createOrder(tokenB→tokenA, sell, amountIn, startPrice,...)
    WE->>TokenB: transferFrom(MakerB, WE, amountIn)
    TokenB-->>WE: transfer success
    WE-->>MakerB: orderId

    Keeper->>WE: matchOrders(buyId, sellId, deadline)
    WE->>WE: validate, compute settlement price, compute fills
    WE->>TokenA: safeTransfer(MakerA, outAmountA)
    TokenA-->>MakerA: transfer success
    WE->>TokenB: safeTransfer(MakerB, outAmountB)
    TokenB-->>MakerB: transfer success
    WE-->>Keeper: emit OrderMatched (settlementPrice, filledAmount)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • StabilityNexus/Windmill-EVM-Contracts#12: edits the Counter deployment script which this PR removes (direct overlap).
  • [FEATURE]: WEEK 2 #5: touches Counter deployment/tests also removed here (direct overlap).

Suggested labels

Solidity Lang

Poem

🐰 I hopped from counters to a marketplace bright,
Orders twirl like windmills in flight.
Makers place bids with rhythmic cheer,
Keepers match trades and nibble a fee near.
Thump-thump — on-chain carrots bloom tonight.

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR implements several key requirements from issue #1: reward matchers with keeper fees [#1], pagination/chunked reads via getOrdersByPair() [#1], expiry deadline on matchOrders() [#1], and improved error handling with SameToken [#1]. However, it does not implement price feed integration [#1.6] or admin controls [#1.7]. Clarify whether #1.6 (price feed integration) and #1.7 (admin/owner controls) are intended as follow-up PRs or should be included in this MVP. The current implementation satisfies partial requirements but not all seven feature requests.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: introducing an MVP implementation of WindmillExchange as a single-file contract architecture.
Description check ✅ Passed The description provides the required sections including addressed issues, summary, contract scope, security considerations, storage impact, test evidence, and deployment impact. All major template sections are addressed with appropriate detail.
Out of Scope Changes check ✅ Passed All changes directly support the MVP implementation: Counter-related files were removed as they are replaced by WindmillExchange, and the three new files (contract, tests, deploy script) align with the stated objective of delivering a 3-file MVP.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@aniket866 aniket866 changed the title MVP for Windmill-EVM-Contracts MVP for Windmill-EVM-Contracts Singular file architecture Mar 16, 2026
@aniket866 aniket866 changed the title MVP for Windmill-EVM-Contracts Singular file architecture MVP for Windmill-EVM-Contracts Single file architecture Mar 16, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/WindmillExchange.sol`:
- Around line 291-303: Partial fills currently only update remainingIn on the
buy/sell orders (in the branch where buyFilled/sellFilled is false) but leave
the original createdAt so the residual keeps the original price anchor; modify
the non-filled branches for buyOrderId and sellOrderId to re-anchor the residual
by resetting _orders[buyOrderId].createdAt = block.timestamp (and similarly for
_orders[sellOrderId].createdAt) whenever you set remainingIn to
newBuyRemaining/newSellRemaining so the leftover order reprices from the current
time; keep the existing _pairRemove and active=false logic for fully filled
orders.
- Around line 141-156: safeTransfer and safeTransferFrom currently only assert
the ERC-20 call returned true and therefore allow fee-on-transfer/deflationary
tokens to under- or over-transfer; update both helpers to enforce exact balance
deltas or reject such tokens: for safeTransferFrom(address token, address from,
address to, uint256 amount) read preBalance =
IERC20(token).balanceOf(address(this)) (or balanceOf(to) if pulling directly to
recipient), perform the call, then read postBalance and require(postBalance -
preBalance == amount) else revert TransferFailed(); for safeTransfer(address
token, address to, uint256 amount) read preBalanceTo =
IERC20(token).balanceOf(to), perform the call, then read postBalanceTo and
require(postBalanceTo - preBalanceTo == amount) else revert TransferFailed();
keep the existing checks for token.code.length and call success but add these
balance checks so createOrder/cancelOrder/settlement cannot be cheated by
transfer-tax tokens or explicitly reject tokens that fail the exact-delta
requirement.
- Around line 268-270: The current matching path rejects expired orders but
never deactivates them or removes their ids from _pairOrders, causing stale
entries; update the execution logic (where buy/sell are validated and
OrderExpired is thrown) to mark the expired order(s) inactive and remove their
id(s) from _pairOrders before reverting or continue, and emit an event (e.g.,
OrderPruned) for state transparency; additionally add a permissionless prune
function (e.g., pruneExpiredOrders(pairId, limit)) that scans _pairOrders,
checks buy.expiry/sell.expiry against block.timestamp, sets active=false,
removes ids from _pairOrders and updates any indices/counts to prevent
accumulation of abandoned orders so paginated reads no longer encounter dead
entries.

In `@test/WindmillExchange.t.sol`:
- Around line 203-267: The tests assert only that balances changed, so expand
test_matchOrders_fullFill and test_matchOrders_partialFill to assert exact
post-trade balances and order fields: check exchange.getOrder(...).remainingIn
equals 0 for full fill and equals expected remaining for partial fill, verify
both orders' active flags, assert exact tokenA/tokenB balances for maker/taker
and the exact fee split recipient/amount (keeper/feeRecipient) and paymentOwed
updates after calling exchange.matchOrders; add a new test that exercises the
public deadline expiry path by creating orders, advancing time past the order
deadline with vm.warp and calling exchange.matchOrders expecting
OrderExpired.selector to be reverted. Ensure you reference the same identifiers
used in the diff (test_matchOrders_fullFill, test_matchOrders_partialFill,
exchange.matchOrders, exchange.getOrder, remainingIn, OrderExpired) when adding
the assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f8df01de-68d3-4f66-847e-16f48d6607cb

📥 Commits

Reviewing files that changed from the base of the PR and between 15f4ba1 and 228b104.

📒 Files selected for processing (6)
  • script/Counter.s.sol
  • script/DeployWindmill.s.sol
  • src/Counter.sol
  • src/WindmillExchange.sol
  • test/Counter.t.sol
  • test/WindmillExchange.t.sol
💤 Files with no reviewable changes (3)
  • src/Counter.sol
  • test/Counter.t.sol
  • script/Counter.s.sol

Comment thread src/WindmillExchange.sol
Comment thread src/WindmillExchange.sol
Comment thread src/WindmillExchange.sol Outdated
Comment thread test/WindmillExchange.t.sol

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test/WindmillExchange.t.sol (1)

201-327: ⚠️ Potential issue | 🟠 Major

Add explicit deadline-expiry coverage for matchOrders.

The suite does not directly test the deadline expired guard, so one public revert path is still uncovered.

🧪 Suggested test case
+function test_matchOrders_revert_deadlineExpired() public {
+    uint256 buyId = _buy(alice, 100 ether, RAY, 0, 0);
+    uint256 sellId = _sell(bob, 100 ether, RAY, 0, 0);
+    vm.warp(block.timestamp + 2);
+    vm.expectRevert(bytes("deadline expired"));
+    exchange.matchOrders(buyId, sellId, block.timestamp - 1);
+}

As per coding guidelines, "Ensure failure paths and revert scenarios are explicitly handled and validated."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/WindmillExchange.t.sol` around lines 201 - 327, Add a unit test that
explicitly covers the function-level deadline check in matchOrders: create a
valid buy and sell (use helper _buy/_sell or createOrder), then call
exchange.matchOrders(buyId, sellId, block.timestamp - 1) and
vm.expectRevert(DeadlineExpired.selector) before the call to assert the deadline
guard reverts; name it e.g., test_matchOrders_revert_deadlineExpired and use
matchOrders, _buy/_sell, and DeadlineExpired.selector to locate the relevant
checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/WindmillExchange.sol`:
- Around line 363-384: pruneExpiredOrders currently deactivates and removes
orders but never returns o.remainingIn to o.maker, locking escrow; update
pruneExpiredOrders to follow checks-effects-interactions: record local values
(id, o.maker, o.remainingIn, token address field), set o.active = false and
o.remainingIn = 0 and update pair index/list state before any external call,
then perform the external token transfer/refund of the saved remaining amount to
o.maker (use the same safe transfer routine used by cancelOrder), emit
OrderPruned, and add a reentrancy guard (or apply the contract's existing
nonReentrant modifier) to pruneExpiredOrders to prevent reentrancy during the
refund flow.

In `@test/WindmillExchange.t.sol`:
- Around line 340-356: The test_pruneExpiredOrders currently validates order
deactivation and index cleanup but omits asserting that the maker's escrowed
funds are returned; after calling exchange.pruneExpiredOrders(...) and
before/after checking active flags, capture the maker's tokenA (or relevant
ERC20) balance delta (using tokenA.balanceOf(alice) or the appropriate
escrow/balance-view helper) and assert it increased by the original escrowed
amount from the _buy call for id1 (100 ether in this case); reference the
existing symbols _buy, exchange.pruneExpiredOrders, exchange.getOrder, tokenA,
and alice to locate where to add the balance-before/balance-after assertions so
the test ensures escrow refunds on prune.

---

Duplicate comments:
In `@test/WindmillExchange.t.sol`:
- Around line 201-327: Add a unit test that explicitly covers the function-level
deadline check in matchOrders: create a valid buy and sell (use helper
_buy/_sell or createOrder), then call exchange.matchOrders(buyId, sellId,
block.timestamp - 1) and vm.expectRevert(DeadlineExpired.selector) before the
call to assert the deadline guard reverts; name it e.g.,
test_matchOrders_revert_deadlineExpired and use matchOrders, _buy/_sell, and
DeadlineExpired.selector to locate the relevant checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bd419579-64a8-41af-98af-a54d865793e5

📥 Commits

Reviewing files that changed from the base of the PR and between 228b104 and e9ae6bd.

📒 Files selected for processing (2)
  • src/WindmillExchange.sol
  • test/WindmillExchange.t.sol

Comment thread src/WindmillExchange.sol Outdated
Comment thread test/WindmillExchange.t.sol

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/WindmillExchange.sol`:
- Around line 281-285: When marking an order fully filled in the settlement
logic, also clear its remainingIn to zero to enable the storage refund: after
setting _orders[buyOrderId].active = false (and before/after calling
_pairRemove) assign _orders[buyOrderId].remainingIn = 0; do the same for
_orders[sellOrderId].remainingIn = 0 when sellFilled is true; reference the
_orders mapping, buyOrderId/sellOrderId, remainingIn, active, and the
_pairRemove(...) call to locate and update the two places in the code.

In `@test/WindmillExchange.t.sol`:
- Around line 249-295: Add a test that covers the contract-level deadline revert
by creating a buy and sell order (use _buy and _sell as in other tests), then
call exchange.matchOrders with a deadline strictly less than block.timestamp and
assert the revert message "deadline expired" via vm.expectRevert("deadline
expired"); name the test test_matchOrders_revert_deadlineExpired and mirror the
setup style used in existing tests (e.g., create buyId/sellId then expectRevert
before calling exchange.matchOrders).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0c0bcb8b-e553-4a01-b66d-581d3fd5ede3

📥 Commits

Reviewing files that changed from the base of the PR and between e9ae6bd and 1cca986.

📒 Files selected for processing (2)
  • src/WindmillExchange.sol
  • test/WindmillExchange.t.sol

Comment thread src/WindmillExchange.sol
Comment thread test/WindmillExchange.t.sol
@aniket866

Copy link
Copy Markdown
Contributor Author

Closing
PR #2
does the same thing

@aniket866 aniket866 closed this May 24, 2026
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.

[FEATURE]: MVP to the WindMill-Exchange

1 participant