From 36e7597a39a6ef7f879b2775675faeafcdda2c87 Mon Sep 17 00:00:00 2001 From: Anxo Date: Fri, 25 Sep 2026 11:17:20 +0100 Subject: [PATCH 1/2] Document that signed feeAmount must be zero Clarify that the API rejects orders with a non-zero feeAmount and add a known issue explaining that the settlement contract still honors a signed fee, so taking it during settlement is expected behavior. --- .../reference/contracts/core/README.mdx | 14 ++++++++++++++ .../reference/contracts/core/settlement.md | 2 +- .../cow-protocol/reference/core/auctions/schema.md | 2 +- .../cow-protocol/reference/core/intents/README.mdx | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/cow-protocol/reference/contracts/core/README.mdx b/docs/cow-protocol/reference/contracts/core/README.mdx index c3eadbfd9..6c65206a4 100644 --- a/docs/cow-protocol/reference/contracts/core/README.mdx +++ b/docs/cow-protocol/reference/contracts/core/README.mdx @@ -75,6 +75,20 @@ If developing smart contracts that create orders, make sure at a contract level ::: +### Signed fee amount + +The order struct still contains a `feeAmount` field, which the settlement contract honors: if an order is signed with a non-zero `feeAmount`, that amount is transferred from the user in addition to the executed sell amount. + +The protocol no longer uses signed fees. Fees are included in the order's limit price, and the API rejects any order with a non-zero `feeAmount` (`NonZeroFee` error). On-chain orders (e.g. ETH flow) with a non-zero fee are recorded with a placement error and excluded from auctions. + +A non-zero `feeAmount` taken during settlement isn't a vulnerability: the user explicitly signed that amount. + +:::tip + +Always sign orders with `feeAmount = 0`. + +::: + ### `ERC-1271` Replayability The security of [`ERC-1271`](../core/signing-schemes#erc-1271) signatures depend on the developers' implementation of the _signing smart contract_. diff --git a/docs/cow-protocol/reference/contracts/core/settlement.md b/docs/cow-protocol/reference/contracts/core/settlement.md index ea26fe818..07755abbf 100644 --- a/docs/cow-protocol/reference/contracts/core/settlement.md +++ b/docs/cow-protocol/reference/contracts/core/settlement.md @@ -97,7 +97,7 @@ struct Data { | `buyAmount` | Amount of `buyToken` that is bought in wei | | `validTo` | UNIX timestamp (in seconds) until which the order is valid | | `appData` | Extra information about the order. Not enforced by the smart contract outside of signature verification (may be used for referrals etc). | -| `feeAmount` | Amount of fees paid in `sellToken` wei | +| `feeAmount` | Amount of fees paid in `sellToken` wei. Must be `0`: the API rejects orders with a non-zero `feeAmount` (see [Signed fee amount](/cow-protocol/reference/contracts/core#signed-fee-amount)) | | `kind` | `buy` or `sell` | | `partiallyFillable` | partially fillable (`true`) or fill-or-kill (`false`) | | `sellTokenBalance` | From where the `sellToken` balance is withdrawn | diff --git a/docs/cow-protocol/reference/core/auctions/schema.md b/docs/cow-protocol/reference/core/auctions/schema.md index 1e9ddafab..26cde9589 100644 --- a/docs/cow-protocol/reference/core/auctions/schema.md +++ b/docs/cow-protocol/reference/core/auctions/schema.md @@ -99,7 +99,7 @@ This key maps to a list containing the set of orders in the batch. Each entry in - `quote`: the winning quote for that order. - `penaltyCapNative`: a stringified integer denoting the cap on the penalty a solver can incur for winning this order but not settling it within the auction deadline, measured in terms of the smallest denomination of the native token of the chain. See the [solver rewards](/cow-protocol/reference/core/auctions/rewards#penalty-caps) page for how this cap is computed and used. - We clarify here that all `market` orders have a potentially non-zero predetermined fee, while all `limit` orders have necessarily a zero signed fee, and the actual fee charged to the order is computed and provided by the solvers when they propose an execution of such an order. More details are provided in the [solutions section](#solutions-output). + We clarify here that all orders have a zero signed fee (`feeAmount`), and the actual fee charged to the order is computed and provided by the solvers when they propose an execution of such an order. More details are provided in the [solutions section](#solutions-output). An example Fill-or-Kill user limit buy order that sells 1000 [COW](https://etherscan.io/token/0xdef1ca1fb7fbcdc777520aa7f396b4e015f497ab) for at least 284.138335 USDC [USDC](https://etherscan.io/token/0xba100000625a3754423978a60c9317c58a424e3d) is given below: diff --git a/docs/cow-protocol/reference/core/intents/README.mdx b/docs/cow-protocol/reference/core/intents/README.mdx index 6030d23d4..9125cc3aa 100644 --- a/docs/cow-protocol/reference/core/intents/README.mdx +++ b/docs/cow-protocol/reference/core/intents/README.mdx @@ -16,7 +16,7 @@ The parameters required for an intent can be broken down into three categories: * amounts to swap (i.e. `sellAmount` and `buyAmount`) * kind of swap (i.e. `sell` or `buy`) * partial fillability (i.e. `true` or `false`) -* fees payable to the protocol (i.e. `feeAmount`) +* signed fee (i.e. `feeAmount`), which must be `0`: fees are included in the limit price instead ### Co-ordination / Settlement From b177fab437526c339e0fb82ca49385c96d36e7f7 Mon Sep 17 00:00:00 2001 From: Anxo Date: Fri, 25 Sep 2026 11:33:16 +0100 Subject: [PATCH 2/2] Scope signed fee known issue to single execution --- docs/cow-protocol/reference/contracts/core/README.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/cow-protocol/reference/contracts/core/README.mdx b/docs/cow-protocol/reference/contracts/core/README.mdx index 6c65206a4..bedd68418 100644 --- a/docs/cow-protocol/reference/contracts/core/README.mdx +++ b/docs/cow-protocol/reference/contracts/core/README.mdx @@ -77,11 +77,13 @@ If developing smart contracts that create orders, make sure at a contract level ### Signed fee amount -The order struct still contains a `feeAmount` field, which the settlement contract honors: if an order is signed with a non-zero `feeAmount`, that amount is transferred from the user in addition to the executed sell amount. +The order struct still contains a `feeAmount` field, which the settlement contract honors: if an order is signed with a non-zero `feeAmount`, that amount is transferred from the user in addition to the executed sell amount (pro rata for partially fillable orders). +The limit price is checked against `sellAmount` and `buyAmount` only, so the effective price of an order with a signed fee is `buyAmount / (sellAmount + feeAmount)`. The protocol no longer uses signed fees. Fees are included in the order's limit price, and the API rejects any order with a non-zero `feeAmount` (`NonZeroFee` error). On-chain orders (e.g. ETH flow) with a non-zero fee are recorded with a placement error and excluded from auctions. -A non-zero `feeAmount` taken during settlement isn't a vulnerability: the user explicitly signed that amount. +Charging the signed `feeAmount` once per order, and the resulting effective price, is expected behavior: the user explicitly signed both. +This doesn't apply to orders with zero amounts, which can be executed more than once and charge the fee repeatedly (see [Orders with zero amounts](#orders-with-zero-amounts)). :::tip