From a1568eb07525750229a247b2f427be13888d20ec Mon Sep 17 00:00:00 2001 From: data-cowwboy Date: Fri, 23 Jan 2026 03:28:30 +0530 Subject: [PATCH 01/11] docs: add quote-to-order tutorial with slippage guidance --- docs/cow-protocol/integrate/api.mdx | 44 +- .../cow-protocol/tutorials/quote-to-order.mdx | 389 ++++++++++++++++++ 2 files changed, 424 insertions(+), 9 deletions(-) create mode 100644 docs/cow-protocol/tutorials/quote-to-order.mdx diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 4ba6c817d..04858eb28 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -31,29 +31,53 @@ The primary API for creating and managing orders on CoW Protocol. curl -X POST "https://api.cow.fi/mainnet/api/v1/quote" \ -H "Content-Type: application/json" \ -d '{ - "sellToken": "0xA0b86a33E6411Ec5d0b9dd2E7dC15A9CAA6C1F8e", + "sellToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "buyToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - "sellAmountBeforeFee": "1000000", + "sellAmountBeforeFee": "1000000000", "kind": "sell", "from": "0xYourWalletAddress" }' ``` -### 2. Sign and Submit Order +### 2. Apply Slippage and Sign Order + +:::caution Important +Before signing, you must apply slippage tolerance to protect against price movements. See the [Quote to Order Tutorial](../tutorials/quote-to-order.mdx) for detailed examples. +::: ```javascript -// After getting a quote, sign the order +import { domain, signOrder, OrderKind, OrderBalance, SigningScheme } from '@cowprotocol/contracts' + +// Apply slippage to the quote before signing +// For sell orders: reduce buyAmount by slippage (e.g., 0.5%) +const buyAmountWithSlippage = BigInt(quoteResponse.quote.buyAmount) * 995n / 1000n + +// Build order object for signing (uses enums, not strings) const order = { - ...quoteResponse, - signature: await signOrder(quoteResponse, signer), - signingScheme: "eip712" + ...quoteResponse.quote, + buyAmount: buyAmountWithSlippage.toString(), + receiver: walletAddress, + kind: OrderKind.SELL, + sellTokenBalance: OrderBalance.ERC20, + buyTokenBalance: OrderBalance.ERC20, } -// Submit the signed order +// Sign using @cowprotocol/contracts +const orderDomain = domain(1, '0x9008D19f58AAbD9eD0D60971565AA8510560ab41') +const signature = await signOrder(orderDomain, order, signer, SigningScheme.EIP712) + +// Submit - API expects strings for kind/balance fields const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(order) + body: JSON.stringify({ + ...quoteResponse.quote, + buyAmount: buyAmountWithSlippage.toString(), + receiver: walletAddress, + signature: signature.data, + signingScheme: 'eip712', + from: walletAddress + }) }) const orderId = await response.text() @@ -155,11 +179,13 @@ try { For complete API documentation including all endpoints, parameters, and response schemas, see: +- **[Quote to Order Tutorial](../tutorials/quote-to-order.mdx)** - Step-by-step guide with slippage handling - **[Order Book API Reference](/cow-protocol/reference/apis/orderbook)** - Complete endpoint documentation - **[API Documentation](https://api.cow.fi/docs/)** - Interactive API explorer ## Resources +- **[Quote to Order Tutorial](../tutorials/quote-to-order.mdx)** - Complete guide with slippage handling - **[Order Book API Reference](/cow-protocol/reference/apis/orderbook)** - Detailed API documentation - **[API Explorer](https://api.cow.fi/docs/)** - Interactive documentation - **[GitHub Examples](https://github.com/cowprotocol/cow-sdk/tree/main/examples)** - Code examples diff --git a/docs/cow-protocol/tutorials/quote-to-order.mdx b/docs/cow-protocol/tutorials/quote-to-order.mdx new file mode 100644 index 000000000..1ad7bdac9 --- /dev/null +++ b/docs/cow-protocol/tutorials/quote-to-order.mdx @@ -0,0 +1,389 @@ +--- +sidebar_position: 1 +--- + +# From Quote to Order: Applying Slippage + +This tutorial explains how to use the CoW Protocol API to get a quote, apply slippage tolerance, and place an order. This is the most common integration pattern for partners building on CoW Protocol. + +:::caution Important +The quote response provides an **estimated** price. You should **not** sign and submit it directly. You must apply your desired slippage tolerance before signing the order. +::: + +## Overview + +The flow consists of three steps: + +1. **Get a quote** - Call `/api/v1/quote` with your trade parameters +2. **Apply slippage** - Adjust the quote amounts based on your slippage tolerance +3. **Sign and submit** - Sign the adjusted order and submit to `/api/v1/orders` + +## Step 1: Get a Quote + +### Sell Order Example + +When you want to sell a specific amount of tokens: + +```bash +curl -X POST "https://api.cow.fi/mainnet/api/v1/quote" \ + -H "Content-Type: application/json" \ + -d '{ + "sellToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "buyToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "sellAmountBeforeFee": "1000000000", + "kind": "sell", + "from": "0xYourWalletAddress" + }' +``` + +Response: + +```json +{ + "quote": { + "sellToken": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "buyToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "sellAmount": "999830727", + "buyAmount": "339197126040197395", + "feeAmount": "169273", + "kind": "sell", + "validTo": 1769119766, + "appData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "partiallyFillable": false, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "signingScheme": "eip712" + }, + "from": "0xYourWalletAddress", + "expiration": "2026-01-22T21:41:26.245665167Z", + "id": 1053640793, + "verified": true +} +``` + +### Buy Order Example + +When you want to buy a specific amount of tokens: + +```bash +curl -X POST "https://api.cow.fi/mainnet/api/v1/quote" \ + -H "Content-Type: application/json" \ + -d '{ + "sellToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "buyToken": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "buyAmountAfterFee": "100000000000000000", + "kind": "buy", + "from": "0xYourWalletAddress" + }' +``` + +Response: + +```json +{ + "quote": { + "sellToken": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "buyToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "sellAmount": "294554318", + "buyAmount": "100000000000000000", + "feeAmount": "147148", + "kind": "buy", + "validTo": 1769119810, + "appData": "0x0000000000000000000000000000000000000000000000000000000000000000", + "partiallyFillable": false, + "sellTokenBalance": "erc20", + "buyTokenBalance": "erc20", + "signingScheme": "eip712" + }, + "from": "0xYourWalletAddress", + "expiration": "2026-01-22T21:42:10.715280266Z", + "id": 1053641256, + "verified": true +} +``` + +## Step 2: Apply Slippage Tolerance + +This is the critical step that many integrators miss. The quote returns an estimated price, but market conditions can change. You must apply slippage tolerance to protect against price movements. + +### For Sell Orders + +You are selling a fixed amount and receiving tokens. Apply slippage to the **buy amount** (the amount you receive): + +``` +actualBuyAmount = quoteBuyAmount × (10000 - slippageBps) / 10000 +``` + +Where `slippageBps` is slippage in basis points (50 = 0.5%, 100 = 1%). + +**Example with 0.5% slippage (50 basis points):** + +- Quote `buyAmount`: `339197126040197395` (≈0.339 WETH) +- Slippage: 50 bps (0.5%) +- Calculation: `339197126040197395 × (10000 - 50) / 10000 = 337501140409996408` +- Actual `buyAmount`: `337501140409996408` + +This means you're willing to accept at minimum ~0.3375 WETH instead of the quoted ~0.339 WETH. + +### For Buy Orders + +You are buying a fixed amount and paying with tokens. Apply slippage to the **sell amount** (the amount you pay): + +``` +actualSellAmount = quoteSellAmount × (10000 + slippageBps) / 10000 +``` + +Where `slippageBps` is slippage in basis points (50 = 0.5%, 100 = 1%). + +**Example with 0.5% slippage (50 basis points):** + +- Quote `sellAmount`: `294554318` (≈294.55 USDC) +- Slippage: 50 bps (0.5%) +- Calculation: `294554318 × (10000 + 50) / 10000 = 296027089` +- Actual `sellAmount`: `296027089` + +This means you're willing to pay at most ~296.03 USDC instead of the quoted ~294.55 USDC. + +## Step 3: Sign and Submit the Order + +After applying slippage, create the order object with the adjusted amounts and sign it. + +### TypeScript Example + +:::tip Recommended +Use the [`@cowprotocol/contracts`](https://www.npmjs.com/package/@cowprotocol/contracts) package for order signing. It handles the EIP-712 type hashing correctly and is the official implementation. +::: + +```bash +npm install @cowprotocol/contracts ethers +``` + +```typescript +import { ethers } from 'ethers'; +import { + Order, + OrderBalance, + OrderKind, + SigningScheme, + domain, + signOrder +} from '@cowprotocol/contracts'; + +// Slippage tolerance in basis points (50 = 0.5%) +// Using integers avoids floating-point precision issues +const SLIPPAGE_BPS = 50n; +const BPS_DENOMINATOR = 10000n; + +// Apply slippage based on order kind +function applySlippage(quote: any, slippageBps: bigint): { sellAmount: string; buyAmount: string } { + if (quote.kind === 'sell') { + // For sell orders: reduce buyAmount (minimum you'll receive) + const buyAmount = BigInt(quote.buyAmount); + const adjustedBuyAmount = (buyAmount * (BPS_DENOMINATOR - slippageBps)) / BPS_DENOMINATOR; + return { + sellAmount: quote.sellAmount, + buyAmount: adjustedBuyAmount.toString() + }; + } else { + // For buy orders: increase sellAmount (maximum you'll pay) + const sellAmount = BigInt(quote.sellAmount); + const adjustedSellAmount = (sellAmount * (BPS_DENOMINATOR + slippageBps)) / BPS_DENOMINATOR; + return { + sellAmount: adjustedSellAmount.toString(), + buyAmount: quote.buyAmount + }; + } +} + +async function placeOrder(quoteResponse: any, signer: ethers.Signer) { + const chainId = 1; // Mainnet + const settlementContract = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'; + + // 1. Apply slippage to the quote + const adjustedAmounts = applySlippage(quoteResponse.quote, SLIPPAGE_BPS); + + // 2. Build the order object + const order: Order = { + sellToken: quoteResponse.quote.sellToken, + buyToken: quoteResponse.quote.buyToken, + receiver: quoteResponse.from, + sellAmount: adjustedAmounts.sellAmount, + buyAmount: adjustedAmounts.buyAmount, + validTo: quoteResponse.quote.validTo, + appData: quoteResponse.quote.appData, + feeAmount: quoteResponse.quote.feeAmount, + kind: quoteResponse.quote.kind === 'sell' ? OrderKind.SELL : OrderKind.BUY, + partiallyFillable: quoteResponse.quote.partiallyFillable, + sellTokenBalance: OrderBalance.ERC20, + buyTokenBalance: OrderBalance.ERC20, + }; + + // 3. Sign the order using the contracts package + const orderDomain = domain(chainId, settlementContract); + const signature = await signOrder(orderDomain, order, signer, SigningScheme.EIP712); + + // 4. Submit the signed order to the API + const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + sellToken: order.sellToken, + buyToken: order.buyToken, + receiver: order.receiver, + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + validTo: order.validTo, + appData: order.appData, + feeAmount: order.feeAmount, + kind: quoteResponse.quote.kind, // API expects string: "sell" or "buy" + partiallyFillable: order.partiallyFillable, + sellTokenBalance: 'erc20', // API expects string + buyTokenBalance: 'erc20', // API expects string + signature: signature.data, + signingScheme: 'eip712', + from: quoteResponse.from + }) + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(`Order submission failed: ${error.description}`); + } + + const orderId = await response.text(); + return orderId; +} +``` + +### Complete Flow Example + +```typescript +import { ethers } from 'ethers'; +import { + Order, + OrderBalance, + OrderKind, + SigningScheme, + domain, + signOrder +} from '@cowprotocol/contracts'; + +// Slippage in basis points (50 = 0.5%, 100 = 1%) +const SLIPPAGE_BPS = 50n; +const BPS_DENOMINATOR = 10000n; + +async function swapTokens( + sellToken: string, + buyToken: string, + amount: string, + kind: 'sell' | 'buy', + signer: ethers.Signer +) { + const walletAddress = await signer.getAddress(); + const chainId = 1; + const settlementContract = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'; + + // 1. Get quote from API + const quoteRequest = kind === 'sell' + ? { sellToken, buyToken, sellAmountBeforeFee: amount, kind, from: walletAddress } + : { sellToken, buyToken, buyAmountAfterFee: amount, kind, from: walletAddress }; + + const quoteResponse = await fetch('https://api.cow.fi/mainnet/api/v1/quote', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(quoteRequest) + }); + + if (!quoteResponse.ok) { + const error = await quoteResponse.json(); + throw new Error(`Quote failed: ${error.description}`); + } + + const quoteData = await quoteResponse.json(); + const quote = quoteData.quote; + + // 2. Apply slippage tolerance using integer math (avoids floating-point issues) + let sellAmount = quote.sellAmount; + let buyAmount = quote.buyAmount; + + if (kind === 'sell') { + // Reduce buyAmount by slippage + const adjusted = (BigInt(buyAmount) * (BPS_DENOMINATOR - SLIPPAGE_BPS)) / BPS_DENOMINATOR; + buyAmount = adjusted.toString(); + } else { + // Increase sellAmount by slippage + const adjusted = (BigInt(sellAmount) * (BPS_DENOMINATOR + SLIPPAGE_BPS)) / BPS_DENOMINATOR; + sellAmount = adjusted.toString(); + } + + // 3. Build order for signing + const order: Order = { + sellToken: quote.sellToken, + buyToken: quote.buyToken, + receiver: walletAddress, + sellAmount, + buyAmount, + validTo: quote.validTo, + appData: quote.appData, + feeAmount: quote.feeAmount, + kind: kind === 'sell' ? OrderKind.SELL : OrderKind.BUY, + partiallyFillable: quote.partiallyFillable, + sellTokenBalance: OrderBalance.ERC20, + buyTokenBalance: OrderBalance.ERC20, + }; + + // 4. Sign the order + const orderDomain = domain(chainId, settlementContract); + const signature = await signOrder(orderDomain, order, signer, SigningScheme.EIP712); + + // 5. Submit to API + const orderResponse = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + sellToken: order.sellToken, + buyToken: order.buyToken, + receiver: order.receiver, + sellAmount: order.sellAmount, + buyAmount: order.buyAmount, + validTo: order.validTo, + appData: order.appData, + feeAmount: order.feeAmount, + kind: kind, + partiallyFillable: order.partiallyFillable, + sellTokenBalance: 'erc20', + buyTokenBalance: 'erc20', + signature: signature.data, + signingScheme: 'eip712', + from: walletAddress + }) + }); + + if (!orderResponse.ok) { + const error = await orderResponse.json(); + throw new Error(`Order submission failed: ${error.description}`); + } + + const orderId = await orderResponse.text(); + console.log('Order placed successfully:', orderId); + return orderId; +} +``` + +## Summary + +| Order Type | What You Specify | What Gets Adjusted | Formula (using basis points) | +|------------|------------------|-------------------|------------------------------| +| Sell | `sellAmountBeforeFee` | `buyAmount` (reduce) | `buyAmount × (10000 - bps) / 10000` | +| Buy | `buyAmountAfterFee` | `sellAmount` (increase) | `sellAmount × (10000 + bps) / 10000` | + +:::tip +Common slippage tolerances: 50 bps (0.5%) for stable pairs, 100-300 bps (1-3%) for volatile pairs. Higher slippage increases execution probability but may result in worse prices. +::: + +## Next Steps + +- **[Order Signing Guide](../reference/core/signing_schemes.mdx)** - Detailed signing documentation +- **[API Reference](/cow-protocol/reference/apis/orderbook)** - Complete endpoint documentation +- **[SDK Integration](../integrate/sdk.mdx)** - Use the SDK for a higher-level abstraction + From 3552895fc9889c89cc55ccf336a9915ff370dd5f Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:25:21 +0200 Subject: [PATCH 02/11] fixes --- docs/cow-protocol/integrate/api.mdx | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 04858eb28..e71b4362e 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -21,7 +21,7 @@ The primary API for creating and managing orders on CoW Protocol. - `POST /api/v1/orders` - Submit signed orders - `GET /api/v1/orders/{uid}` - Get order details - `DELETE /api/v1/orders/{uid}` - Cancel orders -- `GET /api/v1/trades` - Get trade history +- `GET /api/v2/trades` - Get trade history ## Quick Start Example @@ -42,19 +42,19 @@ curl -X POST "https://api.cow.fi/mainnet/api/v1/quote" \ ### 2. Apply Slippage and Sign Order :::caution Important -Before signing, you must apply slippage tolerance to protect against price movements. See the [Quote to Order Tutorial](../tutorials/quote-to-order.mdx) for detailed examples. +Before signing, you should apply slippage tolerance to protect against price movements. See the [Quote to Order Tutorial](../tutorials/quote-to-order.mdx) for detailed examples. ::: ```javascript -import { domain, signOrder, OrderKind, OrderBalance, SigningScheme } from '@cowprotocol/contracts' - // Apply slippage to the quote before signing // For sell orders: reduce buyAmount by slippage (e.g., 0.5%) const buyAmountWithSlippage = BigInt(quoteResponse.quote.buyAmount) * 995n / 1000n +const sellAmount = BigInt(quoteResponse.quote.sellAmount) + BigInt(quoteResponse.quote.feeAmount) -// Build order object for signing (uses enums, not strings) +// Build order for signing const order = { ...quoteResponse.quote, + sellAmount: sellAmount.toString(), buyAmount: buyAmountWithSlippage.toString(), receiver: walletAddress, kind: OrderKind.SELL, @@ -62,22 +62,11 @@ const order = { buyTokenBalance: OrderBalance.ERC20, } -// Sign using @cowprotocol/contracts -const orderDomain = domain(1, '0x9008D19f58AAbD9eD0D60971565AA8510560ab41') -const signature = await signOrder(orderDomain, order, signer, SigningScheme.EIP712) - -// Submit - API expects strings for kind/balance fields +// Submit the signed order const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - ...quoteResponse.quote, - buyAmount: buyAmountWithSlippage.toString(), - receiver: walletAddress, - signature: signature.data, - signingScheme: 'eip712', - from: walletAddress - }) + body: JSON.stringify(order) }) const orderId = await response.text() From 62eb315454c9f0e7dafc1c4f8a53c115959a0055 Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:25:56 +0200 Subject: [PATCH 03/11] Remove space --- docs/cow-protocol/integrate/api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index e71b4362e..1e05e3c72 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -62,7 +62,7 @@ const order = { buyTokenBalance: OrderBalance.ERC20, } -// Submit the signed order +// Submit the signed order const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, From 28408c5c6a6338be997a1c78e778f2199947e382 Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:26:53 +0200 Subject: [PATCH 04/11] more fixes --- docs/cow-protocol/integrate/api.mdx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 1e05e3c72..7f68f7940 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -56,10 +56,6 @@ const order = { ...quoteResponse.quote, sellAmount: sellAmount.toString(), buyAmount: buyAmountWithSlippage.toString(), - receiver: walletAddress, - kind: OrderKind.SELL, - sellTokenBalance: OrderBalance.ERC20, - buyTokenBalance: OrderBalance.ERC20, } // Submit the signed order From 2a0a984ec98f181a88db9edc6da25be662cc9b24 Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:29:37 +0200 Subject: [PATCH 05/11] more fixes --- docs/cow-protocol/integrate/api.mdx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 7f68f7940..9b6f648d7 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -58,11 +58,19 @@ const order = { buyAmount: buyAmountWithSlippage.toString(), } +const signature = signature: await signOrder(quoteResponse, signer) + +const signedOrder = { + ...oder, + signature: signature, + signingScheme: "eip712" +} + // Submit the signed order const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(order) + body: JSON.stringify(signedOrder) }) const orderId = await response.text() From 40e1ff4fefb92ed8ddebaafafb6129c2d96b6e4e Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:31:00 +0200 Subject: [PATCH 06/11] small fixes --- docs/cow-protocol/integrate/api.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 9b6f648d7..ca6e7ca62 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -55,13 +55,13 @@ const sellAmount = BigInt(quoteResponse.quote.sellAmount) + BigInt(quoteResponse const order = { ...quoteResponse.quote, sellAmount: sellAmount.toString(), - buyAmount: buyAmountWithSlippage.toString(), + buyAmount: buyAmountWithSlippage.toString() } const signature = signature: await signOrder(quoteResponse, signer) const signedOrder = { - ...oder, + ...order, signature: signature, signingScheme: "eip712" } From a5f5551e111cb09cbaf556a965397a80de97fdd3 Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:32:22 +0200 Subject: [PATCH 07/11] more fixes --- docs/cow-protocol/integrate/api.mdx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index ca6e7ca62..187a82623 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -172,14 +172,13 @@ try { For complete API documentation including all endpoints, parameters, and response schemas, see: -- **[Quote to Order Tutorial](../tutorials/quote-to-order.mdx)** - Step-by-step guide with slippage handling - **[Order Book API Reference](/cow-protocol/reference/apis/orderbook)** - Complete endpoint documentation - **[API Documentation](https://api.cow.fi/docs/)** - Interactive API explorer ## Resources -- **[Quote to Order Tutorial](../tutorials/quote-to-order.mdx)** - Complete guide with slippage handling - **[Order Book API Reference](/cow-protocol/reference/apis/orderbook)** - Detailed API documentation - **[API Explorer](https://api.cow.fi/docs/)** - Interactive documentation - **[GitHub Examples](https://github.com/cowprotocol/cow-sdk/tree/main/examples)** - Code examples -- **[Order Signing Guide](../reference/core/signing_schemes.mdx)** - Cryptographic signing details \ No newline at end of file +- **[Order Signing Guide](../reference/core/signing_schemes.mdx)** - Cryptographic signing details +- **[Quote to Order Tutorial](../tutorials/quote-to-order.mdx)** - Complete guide with slippage handling From 437cf4c726b5668da0f8671a316a923d780718a7 Mon Sep 17 00:00:00 2001 From: Haris Angelidakis <64154020+harisang@users.noreply.github.com> Date: Fri, 30 Jan 2026 00:33:39 +0200 Subject: [PATCH 08/11] rephrasings --- docs/cow-protocol/tutorials/quote-to-order.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cow-protocol/tutorials/quote-to-order.mdx b/docs/cow-protocol/tutorials/quote-to-order.mdx index 1ad7bdac9..0aa824bf3 100644 --- a/docs/cow-protocol/tutorials/quote-to-order.mdx +++ b/docs/cow-protocol/tutorials/quote-to-order.mdx @@ -4,10 +4,10 @@ sidebar_position: 1 # From Quote to Order: Applying Slippage -This tutorial explains how to use the CoW Protocol API to get a quote, apply slippage tolerance, and place an order. This is the most common integration pattern for partners building on CoW Protocol. +This tutorial explains how to use the CoW Protocol API to get a quote, apply slippage tolerance, and place an order. :::caution Important -The quote response provides an **estimated** price. You should **not** sign and submit it directly. You must apply your desired slippage tolerance before signing the order. +The quote response provides an **estimated** price and does not incorporate any slippage tolerance. Because of this, you should **not** sign and submit it directly, unless you want to submit an order with zero slippage tolerance. ::: ## Overview From 1d339d28a3c0b973c6738d500542ec7aa1be0e88 Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:46:26 +0200 Subject: [PATCH 09/11] fix typo --- docs/cow-protocol/integrate/api.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 187a82623..483dae40a 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -58,7 +58,7 @@ const order = { buyAmount: buyAmountWithSlippage.toString() } -const signature = signature: await signOrder(quoteResponse, signer) +const signature = await signOrder(quoteResponse, signer) const signedOrder = { ...order, From 6fa8ff35c0397ef23b6e4a5a11c2580a2ede0073 Mon Sep 17 00:00:00 2001 From: harisang Date: Fri, 30 Jan 2026 00:51:23 +0200 Subject: [PATCH 10/11] add zero fee --- docs/cow-protocol/integrate/api.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 483dae40a..503a21f70 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -50,12 +50,14 @@ Before signing, you should apply slippage tolerance to protect against price mov // For sell orders: reduce buyAmount by slippage (e.g., 0.5%) const buyAmountWithSlippage = BigInt(quoteResponse.quote.buyAmount) * 995n / 1000n const sellAmount = BigInt(quoteResponse.quote.sellAmount) + BigInt(quoteResponse.quote.feeAmount) +const feeAmount = BigInt(0) // Build order for signing const order = { ...quoteResponse.quote, sellAmount: sellAmount.toString(), - buyAmount: buyAmountWithSlippage.toString() + buyAmount: buyAmountWithSlippage.toString(), + feeAmount: feeAmount.toString() } const signature = await signOrder(quoteResponse, signer) From 58fd8c0f9da6ff636fcbb86564342e77c9970e52 Mon Sep 17 00:00:00 2001 From: data-cowwboy Date: Tue, 3 Feb 2026 00:05:01 +0530 Subject: [PATCH 11/11] docs: address PR review feedback for quote-to-order tutorial - Add fee decomposition explanation (sellAmountBeforeFee = sellAmount + feeAmount) - Clarify fee=0 signing requirement for all order types - Fix buy order slippage example (add feeAmount first, then apply slippage) - Add partner fee section with formulas for sell/buy orders - Add summary table showing what API handles vs what integrators must apply - Remove TypeScript code examples per review feedback - Update api.mdx with consistent fee=0 handling --- docs/cow-protocol/integrate/api.mdx | 22 +- .../cow-protocol/tutorials/quote-to-order.mdx | 338 ++++++------------ 2 files changed, 123 insertions(+), 237 deletions(-) diff --git a/docs/cow-protocol/integrate/api.mdx b/docs/cow-protocol/integrate/api.mdx index 503a21f70..756001d81 100644 --- a/docs/cow-protocol/integrate/api.mdx +++ b/docs/cow-protocol/integrate/api.mdx @@ -49,30 +49,32 @@ Before signing, you should apply slippage tolerance to protect against price mov // Apply slippage to the quote before signing // For sell orders: reduce buyAmount by slippage (e.g., 0.5%) const buyAmountWithSlippage = BigInt(quoteResponse.quote.buyAmount) * 995n / 1000n + +// IMPORTANT: Orders are always signed with feeAmount=0 +// Therefore, we must add the feeAmount back to the sellAmount const sellAmount = BigInt(quoteResponse.quote.sellAmount) + BigInt(quoteResponse.quote.feeAmount) -const feeAmount = BigInt(0) // Build order for signing const order = { ...quoteResponse.quote, sellAmount: sellAmount.toString(), buyAmount: buyAmountWithSlippage.toString(), - feeAmount: feeAmount.toString() + feeAmount: "0", // Always sign with zero fee + receiver: walletAddress, } -const signature = await signOrder(quoteResponse, signer) - -const signedOrder = { - ...order, - signature: signature, - signingScheme: "eip712" -} +const signature = await signOrder(order, signer) // Submit the signed order const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(signedOrder) + body: JSON.stringify({ + ...order, + signature: signature.data, + signingScheme: 'eip712', + from: walletAddress + }) }) const orderId = await response.text() diff --git a/docs/cow-protocol/tutorials/quote-to-order.mdx b/docs/cow-protocol/tutorials/quote-to-order.mdx index 0aa824bf3..8a18a20b2 100644 --- a/docs/cow-protocol/tutorials/quote-to-order.mdx +++ b/docs/cow-protocol/tutorials/quote-to-order.mdx @@ -2,12 +2,12 @@ sidebar_position: 1 --- -# From Quote to Order: Applying Slippage +# From Quote to Order -This tutorial explains how to use the CoW Protocol API to get a quote, apply slippage tolerance, and place an order. +This tutorial explains how to use the CoW Protocol API to get a quote, apply the necessary adjustments, and place an order. This is the most common integration pattern for partners building on CoW Protocol. :::caution Important -The quote response provides an **estimated** price and does not incorporate any slippage tolerance. Because of this, you should **not** sign and submit it directly, unless you want to submit an order with zero slippage tolerance. +The quote response provides an **estimated** price. You should **not** sign and submit it directly. You must apply slippage tolerance (and partner fee, if applicable) before signing the order. ::: ## Overview @@ -61,6 +61,27 @@ Response: } ``` +### Understanding the Fee Decomposition + +Notice that the `sellAmountBeforeFee` in your request (1,000,000,000) is decomposed in the response as: + +``` +sellAmountBeforeFee = sellAmount + feeAmount +1,000,000,000 = 999,830,727 + 169,273 +``` + +The `feeAmount` is an **estimated protocol fee** that solvers may use to cover gas costs. However, this fee is just an estimation and is **not used for signing**. + +:::caution Critical: Fee=0 Signing +Users **always sign orders with `feeAmount: "0"`**. This means when constructing the order for signing, you must add the `feeAmount` back to the `sellAmount`: + +``` +signingSellAmount = sellAmount + feeAmount +``` + +This ensures the full `sellAmountBeforeFee` is available for the trade. The actual fee deduction happens at execution time, handled by the protocol. +::: + ### Buy Order Example When you want to buy a specific amount of tokens: @@ -106,11 +127,14 @@ Response: This is the critical step that many integrators miss. The quote returns an estimated price, but market conditions can change. You must apply slippage tolerance to protect against price movements. +**For both order types**: First add `feeAmount` back to `sellAmount` (see [Fee=0 Signing](#understanding-the-fee-decomposition) above), then apply slippage to the appropriate amount. + ### For Sell Orders You are selling a fixed amount and receiving tokens. Apply slippage to the **buy amount** (the amount you receive): ``` +actualSellAmount = quoteSellAmount + quoteFeeAmount actualBuyAmount = quoteBuyAmount × (10000 - slippageBps) / 10000 ``` @@ -118,6 +142,9 @@ Where `slippageBps` is slippage in basis points (50 = 0.5%, 100 = 1%). **Example with 0.5% slippage (50 basis points):** +- Quote `sellAmount`: `999830727` +- Quote `feeAmount`: `169273` +- Actual `sellAmount`: `999830727 + 169273 = 1000000000` (original `sellAmountBeforeFee`) - Quote `buyAmount`: `339197126040197395` (≈0.339 WETH) - Slippage: 50 bps (0.5%) - Calculation: `339197126040197395 × (10000 - 50) / 10000 = 337501140409996408` @@ -130,7 +157,8 @@ This means you're willing to accept at minimum ~0.3375 WETH instead of the quote You are buying a fixed amount and paying with tokens. Apply slippage to the **sell amount** (the amount you pay): ``` -actualSellAmount = quoteSellAmount × (10000 + slippageBps) / 10000 +baseSellAmount = quoteSellAmount + quoteFeeAmount +actualSellAmount = baseSellAmount × (10000 + slippageBps) / 10000 ``` Where `slippageBps` is slippage in basis points (50 = 0.5%, 100 = 1%). @@ -138,244 +166,100 @@ Where `slippageBps` is slippage in basis points (50 = 0.5%, 100 = 1%). **Example with 0.5% slippage (50 basis points):** - Quote `sellAmount`: `294554318` (≈294.55 USDC) +- Quote `feeAmount`: `147148` +- Base `sellAmount`: `294554318 + 147148 = 294701466` - Slippage: 50 bps (0.5%) -- Calculation: `294554318 × (10000 + 50) / 10000 = 296027089` -- Actual `sellAmount`: `296027089` +- Calculation: `294701466 × (10000 + 50) / 10000 = 296174973` +- Actual `sellAmount`: `296174973` -This means you're willing to pay at most ~296.03 USDC instead of the quoted ~294.55 USDC. - -## Step 3: Sign and Submit the Order +This means you're willing to pay at most ~296.17 USDC instead of the quoted ~294.70 USDC (including fee). -After applying slippage, create the order object with the adjusted amounts and sign it. +## Partner Fee (For Integrators) -### TypeScript Example +If you're a partner integration charging a fee, you must apply it manually. The quote API does **not** deduct partner fees from the quoted amounts—they are taken from surplus at settlement time. -:::tip Recommended -Use the [`@cowprotocol/contracts`](https://www.npmjs.com/package/@cowprotocol/contracts) package for order signing. It handles the EIP-712 type hashing correctly and is the official implementation. +:::note +Partner fees are specified in your [`appData`](/cow-protocol/reference/core/intents/app-data) document. See the [Partner Fee documentation](/governance/fees/partner-fee) for setup details. ::: -```bash -npm install @cowprotocol/contracts ethers -``` +### For Sell Orders -```typescript -import { ethers } from 'ethers'; -import { - Order, - OrderBalance, - OrderKind, - SigningScheme, - domain, - signOrder -} from '@cowprotocol/contracts'; - -// Slippage tolerance in basis points (50 = 0.5%) -// Using integers avoids floating-point precision issues -const SLIPPAGE_BPS = 50n; -const BPS_DENOMINATOR = 10000n; - -// Apply slippage based on order kind -function applySlippage(quote: any, slippageBps: bigint): { sellAmount: string; buyAmount: string } { - if (quote.kind === 'sell') { - // For sell orders: reduce buyAmount (minimum you'll receive) - const buyAmount = BigInt(quote.buyAmount); - const adjustedBuyAmount = (buyAmount * (BPS_DENOMINATOR - slippageBps)) / BPS_DENOMINATOR; - return { - sellAmount: quote.sellAmount, - buyAmount: adjustedBuyAmount.toString() - }; - } else { - // For buy orders: increase sellAmount (maximum you'll pay) - const sellAmount = BigInt(quote.sellAmount); - const adjustedSellAmount = (sellAmount * (BPS_DENOMINATOR + slippageBps)) / BPS_DENOMINATOR; - return { - sellAmount: adjustedSellAmount.toString(), - buyAmount: quote.buyAmount - }; - } -} +Reduce the `buyAmount` by your partner fee percentage: -async function placeOrder(quoteResponse: any, signer: ethers.Signer) { - const chainId = 1; // Mainnet - const settlementContract = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'; - - // 1. Apply slippage to the quote - const adjustedAmounts = applySlippage(quoteResponse.quote, SLIPPAGE_BPS); - - // 2. Build the order object - const order: Order = { - sellToken: quoteResponse.quote.sellToken, - buyToken: quoteResponse.quote.buyToken, - receiver: quoteResponse.from, - sellAmount: adjustedAmounts.sellAmount, - buyAmount: adjustedAmounts.buyAmount, - validTo: quoteResponse.quote.validTo, - appData: quoteResponse.quote.appData, - feeAmount: quoteResponse.quote.feeAmount, - kind: quoteResponse.quote.kind === 'sell' ? OrderKind.SELL : OrderKind.BUY, - partiallyFillable: quoteResponse.quote.partiallyFillable, - sellTokenBalance: OrderBalance.ERC20, - buyTokenBalance: OrderBalance.ERC20, - }; - - // 3. Sign the order using the contracts package - const orderDomain = domain(chainId, settlementContract); - const signature = await signOrder(orderDomain, order, signer, SigningScheme.EIP712); - - // 4. Submit the signed order to the API - const response = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - sellToken: order.sellToken, - buyToken: order.buyToken, - receiver: order.receiver, - sellAmount: order.sellAmount, - buyAmount: order.buyAmount, - validTo: order.validTo, - appData: order.appData, - feeAmount: order.feeAmount, - kind: quoteResponse.quote.kind, // API expects string: "sell" or "buy" - partiallyFillable: order.partiallyFillable, - sellTokenBalance: 'erc20', // API expects string - buyTokenBalance: 'erc20', // API expects string - signature: signature.data, - signingScheme: 'eip712', - from: quoteResponse.from - }) - }); - - if (!response.ok) { - const error = await response.json(); - throw new Error(`Order submission failed: ${error.description}`); - } - - const orderId = await response.text(); - return orderId; -} +``` +buyAmountWithPartnerFee = buyAmount × (10000 - partnerFeeBps) / 10000 ``` -### Complete Flow Example - -```typescript -import { ethers } from 'ethers'; -import { - Order, - OrderBalance, - OrderKind, - SigningScheme, - domain, - signOrder -} from '@cowprotocol/contracts'; - -// Slippage in basis points (50 = 0.5%, 100 = 1%) -const SLIPPAGE_BPS = 50n; -const BPS_DENOMINATOR = 10000n; - -async function swapTokens( - sellToken: string, - buyToken: string, - amount: string, - kind: 'sell' | 'buy', - signer: ethers.Signer -) { - const walletAddress = await signer.getAddress(); - const chainId = 1; - const settlementContract = '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'; - - // 1. Get quote from API - const quoteRequest = kind === 'sell' - ? { sellToken, buyToken, sellAmountBeforeFee: amount, kind, from: walletAddress } - : { sellToken, buyToken, buyAmountAfterFee: amount, kind, from: walletAddress }; - - const quoteResponse = await fetch('https://api.cow.fi/mainnet/api/v1/quote', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(quoteRequest) - }); - - if (!quoteResponse.ok) { - const error = await quoteResponse.json(); - throw new Error(`Quote failed: ${error.description}`); - } - - const quoteData = await quoteResponse.json(); - const quote = quoteData.quote; - - // 2. Apply slippage tolerance using integer math (avoids floating-point issues) - let sellAmount = quote.sellAmount; - let buyAmount = quote.buyAmount; - - if (kind === 'sell') { - // Reduce buyAmount by slippage - const adjusted = (BigInt(buyAmount) * (BPS_DENOMINATOR - SLIPPAGE_BPS)) / BPS_DENOMINATOR; - buyAmount = adjusted.toString(); - } else { - // Increase sellAmount by slippage - const adjusted = (BigInt(sellAmount) * (BPS_DENOMINATOR + SLIPPAGE_BPS)) / BPS_DENOMINATOR; - sellAmount = adjusted.toString(); - } - - // 3. Build order for signing - const order: Order = { - sellToken: quote.sellToken, - buyToken: quote.buyToken, - receiver: walletAddress, - sellAmount, - buyAmount, - validTo: quote.validTo, - appData: quote.appData, - feeAmount: quote.feeAmount, - kind: kind === 'sell' ? OrderKind.SELL : OrderKind.BUY, - partiallyFillable: quote.partiallyFillable, - sellTokenBalance: OrderBalance.ERC20, - buyTokenBalance: OrderBalance.ERC20, - }; - - // 4. Sign the order - const orderDomain = domain(chainId, settlementContract); - const signature = await signOrder(orderDomain, order, signer, SigningScheme.EIP712); - - // 5. Submit to API - const orderResponse = await fetch('https://api.cow.fi/mainnet/api/v1/orders', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - sellToken: order.sellToken, - buyToken: order.buyToken, - receiver: order.receiver, - sellAmount: order.sellAmount, - buyAmount: order.buyAmount, - validTo: order.validTo, - appData: order.appData, - feeAmount: order.feeAmount, - kind: kind, - partiallyFillable: order.partiallyFillable, - sellTokenBalance: 'erc20', - buyTokenBalance: 'erc20', - signature: signature.data, - signingScheme: 'eip712', - from: walletAddress - }) - }); - - if (!orderResponse.ok) { - const error = await orderResponse.json(); - throw new Error(`Order submission failed: ${error.description}`); - } - - const orderId = await orderResponse.text(); - console.log('Order placed successfully:', orderId); - return orderId; -} +**Example with 50 bps (0.5%) partner fee:** + +- Slippage-adjusted `buyAmount`: `337501140409996408` +- Partner fee: 50 bps +- Calculation: `337501140409996408 × (10000 - 50) / 10000 = 335813634607796425` +- Final `buyAmount`: `335813634607796425` + +### For Buy Orders + +Increase the `sellAmount` by your partner fee percentage: + +``` +sellAmountWithPartnerFee = sellAmount × (10000 + partnerFeeBps) / 10000 ``` +**Example with 50 bps (0.5%) partner fee:** + +- Slippage-adjusted `sellAmount`: `296174973` +- Partner fee: 50 bps +- Calculation: `296174973 × (10000 + 50) / 10000 = 297656347` +- Final `sellAmount`: `297656347` + +### Complete Adjustment Sequence + +Apply adjustments in this order: + +1. **Add `feeAmount`** back to `sellAmount` (for fee=0 signing) +2. **Apply slippage** tolerance +3. **Apply partner fee** (if applicable) + +## Step 3: Sign and Submit the Order + +After applying slippage, construct the order object and sign it. Key points: + +1. **Set `feeAmount` to `"0"`** - Orders are always signed with zero fee +2. **Use adjusted amounts** - Apply the slippage-adjusted `sellAmount` and `buyAmount` +3. **Sign with EIP-712** - Use the CoW Protocol domain separator +4. **Submit to API** - POST the signed order to `/api/v1/orders` + +For implementation details and code examples, see: +- **[API Integration Guide](../integrate/api.mdx)** - Quick start with code examples +- **[Order Signing Guide](../reference/core/signing_schemes.mdx)** - Detailed signing documentation +- **[SDK Integration](../integrate/sdk.mdx)** - Higher-level abstraction with the SDK + ## Summary -| Order Type | What You Specify | What Gets Adjusted | Formula (using basis points) | -|------------|------------------|-------------------|------------------------------| -| Sell | `sellAmountBeforeFee` | `buyAmount` (reduce) | `buyAmount × (10000 - bps) / 10000` | -| Buy | `buyAmountAfterFee` | `sellAmount` (increase) | `sellAmount × (10000 + bps) / 10000` | +### Adjustment Formulas + +| Order Type | Signing `sellAmount` | Signing `buyAmount` | +|------------|---------------------|---------------------| +| **Sell** | `sellAmount + feeAmount` | `buyAmount × (10000 - slippageBps) / 10000 × (10000 - partnerFeeBps) / 10000` | +| **Buy** | `(sellAmount + feeAmount) × (10000 + slippageBps) / 10000 × (10000 + partnerFeeBps) / 10000` | `buyAmount` (unchanged) | + +### What the API Handles vs. What You Handle + +| Adjustment | API Handles? | You Must Apply | +|------------|--------------|----------------| +| Protocol fee (`feeAmount`) | ✅ Estimated in quote | Add back to `sellAmount`, sign with `fee=0` | +| Hook gas costs | ✅ Included in `feeAmount` | Already covered by fee handling | +| Slippage tolerance | ❌ | Apply to `buyAmount` (sell) or `sellAmount` (buy) | +| Partner fee | ❌ | Apply after slippage | + +:::info Fee Handling Summary +1. **Request**: You send `sellAmountBeforeFee` (for sell orders) or `buyAmountAfterFee` (for buy orders) +2. **Response**: API returns `sellAmount + feeAmount` (where `sellAmountBeforeFee = sellAmount + feeAmount`) +3. **Signing**: Always use `feeAmount: "0"` and add the `feeAmount` back to `sellAmount` +4. **For buy orders**: Add `feeAmount` first, then apply slippage (gives more room on limit price) +5. **Partner fee**: Apply after slippage, reduces `buyAmount` (sell) or increases `sellAmount` (buy) +6. **Result**: The protocol handles fee deduction at execution time +::: :::tip Common slippage tolerances: 50 bps (0.5%) for stable pairs, 100-300 bps (1-3%) for volatile pairs. Higher slippage increases execution probability but may result in worse prices.