Skip to main content

Documentation Index

Fetch the complete documentation index at: https://cowswap-mintlify-seo-audit-1777280932.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Partner Fee

CoW Protocol allows partners and integrators to collect fees on orders placed through their applications. Partner fees can be configured as a percentage of volume, surplus, or price improvement.

Overview

Partner fees enable:
  • Revenue sharing: Collect fees from orders placed through your app
  • Flexible fee structures: Choose from volume-based, surplus-based, or price improvement fees
  • Transparent fee collection: Fees are automatically calculated and deducted
  • Multiple fee tiers: Support different fee levels for different user segments

Fee Types

Volume-Based Fee (volumeBps)

A fixed percentage of the order’s sell or buy amount:
const partnerFee = {
  volumeBps: 50, // 0.5% of order volume
  recipient: '0xPartnerAddress',
}
Volume-based fees are capped at 100 BPS (1%) at the protocol level.

Surplus-Based Fee (surplusBps)

A percentage of the order’s surplus (the difference between limit price and execution price):
const partnerFee = {
  surplusBps: 5000, // 50% of surplus
  maxVolumeBps: 100, // Maximum 1% of volume
  recipient: '0xPartnerAddress',
}

Price Improvement Fee (priceImprovementBps)

A percentage of the price improvement over the best available market price:
const partnerFee = {
  priceImprovementBps: 5000, // 50% of price improvement
  maxVolumeBps: 100, // Maximum 1% of volume
  recipient: '0xPartnerAddress',
}

Configuration

Single Fee Structure

import { CowSdk, OrderKind } from '@cowprotocol/cow-sdk'

const sdk = new CowSdk({ chainId, adapter })

// Create order with partner fee
const { quote } = await sdk.trading.getQuote({
  kind: OrderKind.SELL,
  sellToken: '0xTokenAddress',
  buyToken: '0xAnotherTokenAddress',
  sellAmount: '1000000000000000000',
  partnerFee: {
    volumeBps: 50, // 0.5%
    recipient: '0xPartnerAddress',
  },
})

Multiple Fee Tiers

You can specify multiple fee structures, and the protocol will choose the most beneficial one:
const partnerFee = [
  // Primary: Take 50% of surplus, capped at 1% volume
  {
    surplusBps: 5000,
    maxVolumeBps: 100,
    recipient: '0xPartnerAddress',
  },
  // Fallback: If no surplus, take 0.5% of volume
  {
    volumeBps: 50,
    recipient: '0xPartnerAddress',
  },
]

const { quote } = await sdk.trading.getQuote({
  kind: OrderKind.SELL,
  sellToken: WETH_ADDRESS,
  buyToken: USDC_ADDRESS,
  sellAmount: parseEther('1'),
  partnerFee,
})

Usage with TradingSdk

Get Quote with Partner Fee

import { CowSdk, OrderKind } from '@cowprotocol/cow-sdk'
import { parseEther } from 'viem'

const sdk = new CowSdk({ chainId: 1, adapter })

// Request quote with partner fee
const { quote } = await sdk.trading.getQuote({
  kind: OrderKind.SELL,
  sellToken: WETH_ADDRESS,
  buyToken: USDC_ADDRESS,
  sellAmount: parseEther('10'),
  partnerFee: {
    volumeBps: 50, // 0.5% fee
    recipient: '0xYourPartnerAddress',
  },
})

// The quote already includes the partner fee deduction
console.log('Buy amount after fees:', quote.buyAmount)
console.log('Partner fee amount:', quote.feeAmount)

Post Order with Partner Fee

// Partner fee is included in the quote
const orderId = await sdk.trading.postSwapOrder({
  quote: quote.quote,
  // Partner fee is automatically included from the quote
})

console.log('Order placed with partner fee:', orderId)

Limit Orders with Partner Fee

const orderId = await sdk.trading.postLimitOrder({
  chainId: 1,
  sellToken: WETH_ADDRESS,
  buyToken: USDC_ADDRESS,
  sellAmount: parseEther('5'),
  buyAmount: parseUnits('10000', 6),
  partnerFee: {
    volumeBps: 50,
    recipient: '0xPartnerAddress',
  },
})

Partner Fee in App Data

Partner fees are stored in the order’s app data:
import { generateAppDataDoc } from '@cowprotocol/sdk-app-data'

const appDataDoc = await generateAppDataDoc({
  appCode: 'my-app',
  metadata: {
    partnerFee: {
      volumeBps: 50,
      recipient: '0xPartnerAddress',
    },
  },
})

// Use in order
const { quote } = await sdk.trading.getQuote({
  kind: OrderKind.SELL,
  sellToken: WETH_ADDRESS,
  buyToken: USDC_ADDRESS,
  sellAmount: parseEther('1'),
  appData: appDataDoc.fullAppData,
})

Fee Calculation

For Sell Orders

Partner fee is deducted from the buy amount:
// Before partner fee
const buyAmountBeforeFee = quote.buyAmount

// Partner fee calculation (0.5% of volume)
const volumeBps = 50
const partnerFeeAmount = (buyAmountBeforeFee * BigInt(volumeBps)) / BigInt(10000)

// After partner fee
const buyAmountAfterFee = buyAmountBeforeFee - partnerFeeAmount

console.log('Buy amount (before fee):', buyAmountBeforeFee)
console.log('Partner fee:', partnerFeeAmount)
console.log('Buy amount (after fee):', buyAmountAfterFee)

For Buy Orders

Partner fee is added to the sell amount:
// Before partner fee
const sellAmountBeforeFee = quote.sellAmount

// Partner fee calculation (0.5% of volume)
const volumeBps = 50
const partnerFeeAmount = (sellAmountBeforeFee * BigInt(volumeBps)) / BigInt(10000)

// After partner fee
const sellAmountAfterFee = sellAmountBeforeFee + partnerFeeAmount

console.log('Sell amount (before fee):', sellAmountBeforeFee)
console.log('Partner fee:', partnerFeeAmount)
console.log('Sell amount (after fee):', sellAmountAfterFee)

Extracting Partner Fee from Quote

Use the utility function to get partner fee BPS:
import { getPartnerFeeBps } from '@cowprotocol/sdk-trading'

const partnerFee = {
  volumeBps: 50,
  recipient: '0xPartnerAddress',
}

const feeBps = getPartnerFeeBps(partnerFee)
console.log('Partner fee in BPS:', feeBps) // 50

// For multiple fee structures, it returns the first volumeBps found
const multiPartnerFee = [
  { surplusBps: 5000, maxVolumeBps: 100, recipient: '0x...' },
  { volumeBps: 50, recipient: '0x...' },
]

const feeBps2 = getPartnerFeeBps(multiPartnerFee)
console.log('Partner fee in BPS:', feeBps2) // 50

Complete Example

import { CowSdk, OrderKind } from '@cowprotocol/cow-sdk'
import { EthersV6Adapter } from '@cowprotocol/sdk-ethers-v6-adapter'
import { parseEther, parseUnits } from 'viem'
import { JsonRpcProvider, Wallet } from 'ethers'

// Initialize
const provider = new JsonRpcProvider('YOUR_RPC_URL')
const wallet = new Wallet('YOUR_PRIVATE_KEY', provider)
const adapter = new EthersV6Adapter({ provider, signer: wallet })

const sdk = new CowSdk({
  chainId: 1,
  adapter,
})

// Define partner fee structure
const partnerFee = {
  volumeBps: 50, // 0.5% fee
  recipient: '0xYourPartnerFeeRecipient',
}

// Get quote with partner fee
const { quote } = await sdk.trading.getQuote({
  kind: OrderKind.SELL,
  sellToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
  buyToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  sellAmount: parseEther('10'),
  partnerFee,
})

console.log('Quote buy amount (after partner fee):', quote.buyAmount)
console.log('Partner fee amount:', quote.feeAmount)

// Post order (partner fee automatically included)
const orderId = await sdk.trading.postSwapOrder({
  quote: quote.quote,
})

console.log('Order with partner fee placed:', orderId)

// Track fee collection
const order = await sdk.orderBook.getOrder(orderId)
console.log('Order status:', order.status)
console.log('Executed buy amount:', order.executedBuyAmount)

Best Practices

Choose appropriate fee type

  • Use volumeBps for predictable, transparent fees
  • Use surplusBps to share in the value created
  • Use priceImprovementBps to incentivize better execution
  • Combine multiple types for optimal fee capture

Set reasonable fee rates

// Good: Competitive rates
const partnerFee = { volumeBps: 25, recipient } // 0.25%

// Avoid: Excessive fees that hurt user experience
const partnerFee = { volumeBps: 100, recipient } // 1% (maximum)

Display fees to users

Always show users the fee they’re paying:
const feeBps = getPartnerFeeBps(partnerFee)
const feePercent = feeBps / 100
console.log(`Partner fee: ${feePercent}%`)

// Calculate fee amount
const feeAmount = (sellAmount * BigInt(feeBps)) / BigInt(10000)
console.log(`Fee amount: ${formatUnits(feeAmount, decimals)}`)

Use surplus-based fees wisely

Surplus-based fees can be zero if there’s no positive slippage:
// Provide fallback for guaranteed minimum fee
const partnerFee = [
  { surplusBps: 5000, maxVolumeBps: 100, recipient },
  { volumeBps: 25, recipient }, // Fallback
]

Fee Recipient Management

Multiple Recipients

Support different fee recipients for different user tiers:
const getFeeRecipient = (userTier: string) => {
  switch (userTier) {
    case 'premium':
      return '0xPremiumFeeRecipient'
    case 'standard':
      return '0xStandardFeeRecipient'
    default:
      return '0xDefaultFeeRecipient'
  }
}

const partnerFee = {
  volumeBps: 50,
  recipient: getFeeRecipient(user.tier),
}

Fee Splitter Contract

Use a fee splitter to distribute fees:
// Deploy or use existing fee splitter
const FEE_SPLITTER = '0xFeeSplitterContractAddress'

const partnerFee = {
  volumeBps: 50,
  recipient: FEE_SPLITTER, // Automatically splits among recipients
}

Monitoring Fee Collection

// Track total fees collected
let totalFeesCollected = 0n

const orders = await sdk.orderBook.getOrders({
  owner: userAddress,
})

for (const order of orders) {
  if (order.status === 'fulfilled') {
    const appData = await fetchDocFromAppData(order.appData)
    if (appData.metadata.partnerFee) {
      const feeBps = getPartnerFeeBps(appData.metadata.partnerFee)
      const feeAmount = (BigInt(order.executedBuyAmount) * BigInt(feeBps)) / BigInt(10000)
      totalFeesCollected += feeAmount
    }
  }
}

console.log('Total fees collected:', formatUnits(totalFeesCollected, 6), 'USDC')

Next Steps

  • Hooks - Execute custom logic before and after orders
  • TWAP Orders - Split large orders over time
Last modified on April 27, 2026