Skip to content

fix(trading): take quote network costs into account in postSellNativeCurrencyOrder - #967

Open
Kropiunig wants to merge 1 commit into
cowprotocol:mainfrom
Kropiunig:fix/eth-flow-network-costs-in-post-sell-native
Open

fix(trading): take quote network costs into account in postSellNativeCurrencyOrder#967
Kropiunig wants to merge 1 commit into
cowprotocol:mainfrom
Kropiunig:fix/eth-flow-network-costs-in-post-sell-native

Conversation

@Kropiunig

@Kropiunig Kropiunig commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Selling 0.1 ETH through TradingSdk.postSellNativeCurrencyOrder() creates an EthFlow order that sells 0.098646335338956442 ETH, and sends that same reduced amount as the transaction value. The user under-sells, and the order is signed as if network costs were zero, so the solver is left no room for gas while still owing the quoted buyAmount — it typically never fills and has to be refunded after validTo.

The reproduction is the README example for this method, unchanged:

const parameters: TradeParameters = {
  kind: OrderKind.SELL,
  sellToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
  sellTokenDecimals: 18,
  buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59',
  buyTokenDecimals: 18,
  amount: '100000000000000000', // 0.1 ETH
}

await sdk.postSellNativeCurrencyOrder(parameters)

Cause

TradingSdk.postSellNativeCurrencyOrder() fetches a quote and then forwards only advancedSettings?.additionalParams, dropping the costs of the quote it just fetched:

const { tradeParameters, quoteResponse } = quoteResults.result

return postSellNativeCurrencyOrder(
  quoteResults.orderBookApi,
  quoteResults.result.appDataInfo,
  swapParamsToLimitOrderParams(/* ... */),
  advancedSettings?.additionalParams, // networkCostsAmount / protocolFeeBps never forwarded
  quoteResults.result.signer,
)

For SELL orders the /quote API returns sellAmount after network costs, so the amount the user asked to sell is quote.sellAmount + quote.feeAmount. With networkCostsAmount missing it falls back to '0' in getEthFlowTransaction(), getQuoteAmountsAndCosts() then computes beforeAllFees.sellAmount === quote.sellAmount, getOrderToSign() signs that, and getEthFlowTransaction() sends it as transaction.value.

postSwapOrder() routes native sell tokens through the same EthFlow path (via postCoWProtocolTrade()) and does forward networkCostsAmount from the quote, so the two documented entry points produced different orders for identical parameters.

Fix

Forward networkCostsAmount and protocolFeeBps from the quote response, mirroring what postSwapOrderFromQuote() already does. ...advancedSettings?.additionalParams is spread last, so anyone already passing either value by hand keeps their behaviour — same precedence as postSwapOrderFromQuote().

Tests

New: packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts — 3 tests, run against all three adapters (ethers v5 / ethers v6 / viem). The quote mock is the one already used by postSwapOrder.test.ts: sellAmount = 98646335338956442, feeAmount = 1353664661043558.

  1. The signed EthFlow order and the transaction value sell quote.sellAmount + quote.feeAmount. On main this fails with Expected: "100000000000000000" / Received: "98646335338956442".
  2. postSellNativeCurrencyOrder() and postSwapOrder() build the same order for a native sell token. Also fails on main.
  3. An explicit advancedSettings.additionalParams.networkCostsAmount still overrides the quote value, so a future refactor can't silently reverse the spread order.
pnpm --filter @cowprotocol/sdk-trading test

@cowprotocol/sdk-trading goes from 22 to 23 suites and 254 to 257 passing (2 skipped); no pre-existing test changed status. Full turbo run test is 36/36 green, and tsc --noEmit and eslint are clean on packages/trading.

Summary by CodeRabbit

  • Bug Fixes

    • Native currency sell orders now correctly include quoted network costs and protocol fees when submitted.
    • Caller-provided network cost overrides continue to be honored.
    • Native sell orders now send the full requested amount on-chain and remain consistent with standard swap order behavior.
  • Tests

    • Added coverage for native sell orders across all configured adapters and relevant cost scenarios.

…CurrencyOrder

`TradingSdk.postSellNativeCurrencyOrder()` fetched a quote and then forwarded only
`advancedSettings.additionalParams` to `postSellNativeCurrencyOrder()`, dropping the
costs of the quote it had just fetched.

For SELL orders the `/quote` API returns `sellAmount` AFTER network costs, so the amount
the user asked to sell is `quote.sellAmount + quote.feeAmount`. With `networkCostsAmount`
missing it defaults to `'0'`, so `getOrderToSign()` builds the EthFlow order with
`beforeAllFees.sellAmount === quote.sellAmount`. The on-chain `createOrder` call is then
sent with that reduced amount as `msg.value`: selling 0.1 ETH creates an order that sells
0.098646335338956442 ETH, priced as if there were no network costs at all.

`postSwapOrder()` also routes native sell tokens through the EthFlow flow (via
`postCoWProtocolTrade()`), and it does forward `networkCostsAmount`, so the two documented
entry points produced different orders for the same parameters.

Forward `networkCostsAmount` and `protocolFeeBps` from the quote response, mirroring
`postSwapOrderFromQuote()`. `advancedSettings.additionalParams` is still spread last, so
explicit overrides keep working.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

postSellNativeCurrencyOrder now forwards quote network costs and protocol fee basis points to EthFlow. New tests verify native SELL amounts, transaction values, swap parity, and network cost overrides.

Changes

Native SELL order cost forwarding

Layer / File(s) Summary
Forward costs and validate native SELL orders
packages/trading/src/tradingSdk.ts, packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts
EthFlow order creation now receives quote network costs, protocol fee basis points, and existing additional parameters. Tests cover full sell amounts, on-chain native values, postSwapOrder() parity, adapter behavior, and network cost overrides.

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

Suggested reviewers: shoom3301

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the trading fix and the need to account for quote network costs in postSellNativeCurrencyOrder.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts

Parsing error: Unable to parse the specified 'tsconfig' file. Ensure it's correct and has valid syntax.

packages/contracts-ts/tsconfig.json(2,14): error TS6053: File '@cow-sdk/typescript-config/base.json' not found.

packages/trading/src/tradingSdk.ts

Parsing error: Unable to parse the specified 'tsconfig' file. Ensure it's correct and has valid syntax.

packages/contracts-ts/tsconfig.json(2,14): error TS6053: File '@cow-sdk/typescript-config/base.json' not found.


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.

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

🧹 Nitpick comments (1)
packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts (1)

39-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for nonzero protocolFeeBps.

The fixture omits protocolFeeBps. Therefore, the conversion and forwarding branch in packages/trading/src/tradingSdk.ts Line 277 does not run. Add a quote with a nonzero protocol fee and assert the derived EthFlow order applies that fee.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts` around
lines 39 - 61, Extend the SELL_QUOTE_MOCK test coverage with a nonzero
protocolFeeBps value, then assert the EthFlow order produced by the trading SDK
conversion and forwarding path applies the corresponding protocol fee. Ensure
the assertion targets the derived order’s fee-related field and preserves
existing zero-fee behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts`:
- Around line 39-61: Extend the SELL_QUOTE_MOCK test coverage with a nonzero
protocolFeeBps value, then assert the EthFlow order produced by the trading SDK
conversion and forwarding path applies the corresponding protocol fee. Ensure
the assertion targets the derived order’s fee-related field and preserves
existing zero-fee behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bb9391ba-b883-401d-af68-4c7a600a2ce2

📥 Commits

Reviewing files that changed from the base of the PR and between 8a756e8 and d4d3723.

📒 Files selected for processing (2)
  • packages/trading/src/tradingSdk.postSellNativeCurrencyOrder.test.ts
  • packages/trading/src/tradingSdk.ts

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