Skip to main content

Creating Limit Orders

Limit orders allow you to specify both the sell and buy amounts, creating an order that will only execute at your desired price or better.

Overview

Unlike swap orders that execute at market price, limit orders give you precise control over the exchange rate. The order will only be filled when the market price reaches your specified rate.
Limit orders are “good-til-cancelled” by default and remain in the order book until filled or cancelled.

Using postLimitOrder

The postLimitOrder method creates a limit order with your specified amounts.

Required Parameters

  • kind - The order kind (OrderKind.SELL or OrderKind.BUY)
  • sellToken - The sell token address
  • sellTokenDecimals - The sell token decimals
  • buyToken - The buy token address
  • buyTokenDecimals - The buy token decimals
  • sellAmount - The amount to sell in atoms
  • buyAmount - The amount to buy in atoms

Basic Example

import {
  SupportedChainId,
  OrderKind,
  LimitTradeParameters,
  TradingSdk
} from '@cowprotocol/sdk-trading'
import { ViemAdapter } from '@cowprotocol/sdk-viem-adapter'
import { createPublicClient, http, privateKeyToAccount } from 'viem'
import { sepolia } from 'viem/chains'

const adapter = new ViemAdapter({
  provider: createPublicClient({
    chain: sepolia,
    transport: http('YOUR_RPC_URL')
  }),
  signer: privateKeyToAccount('YOUR_PRIVATE_KEY' as `0x${string}`)
})

const sdk = new TradingSdk({
  chainId: SupportedChainId.SEPOLIA,
  appCode: 'YOUR_APP_CODE',
}, {}, adapter)

const limitOrderParameters: LimitTradeParameters = {
  kind: OrderKind.BUY,
  sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14',
  sellTokenDecimals: 18,
  buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59',
  buyTokenDecimals: 18,
  sellAmount: '120000000000000000',
  buyAmount: '66600000000000000000',
}

const { orderId } = await sdk.postLimitOrder(limitOrderParameters)

console.log('Order created, id: ', orderId)

Optional Parameters

ParameterTypeDefaultDescription
quoteIdnumber-ID of a quote from the Quote API to use as reference
validTonumber-Order expiration timestamp in seconds since epoch
envEnvprodThe environment to use (prod or staging)
partiallyFillablebooleanfalseWhether the order can be partially filled
receiverstringorder creatorThe address that will receive the tokens
partnerFeePartnerFee-Partner fee configuration

Example with Optional Parameters

const limitOrderParameters: LimitTradeParameters = {
  kind: OrderKind.SELL,
  sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14',
  sellTokenDecimals: 18,
  buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59',
  buyTokenDecimals: 18,
  sellAmount: '1000000000000000000',
  buyAmount: '2000000000000000000',
  validTo: Math.floor(Date.now() / 1000) + 86400, // Valid for 24 hours
  partiallyFillable: true,
}

const { orderId } = await sdk.postLimitOrder(limitOrderParameters)

Calculating Limit Price

The limit price is determined by the ratio of sellAmount to buyAmount.

Example: Setting a Limit Price

// You want to sell 1 WETH for at least 3000 USDC
const wethDecimals = 18
const usdcDecimals = 6

const limitOrderParameters: LimitTradeParameters = {
  kind: OrderKind.SELL,
  sellToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // WETH
  sellTokenDecimals: wethDecimals,
  buyToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
  buyTokenDecimals: usdcDecimals,
  sellAmount: '1000000000000000000', // 1 WETH (18 decimals)
  buyAmount: '3000000000', // 3000 USDC (6 decimals)
}

const { orderId } = await sdk.postLimitOrder(limitOrderParameters)
Price calculation:
  • Limit price = buyAmount / sellAmount (in token units)
  • In this example: 3000 USDC / 1 WETH = 3000 USDC per WETH

Using Quote ID

You can reference a previous quote when creating a limit order:
// First, get a quote
const { quoteResults } = await sdk.getQuote({
  kind: OrderKind.SELL,
  sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14',
  sellTokenDecimals: 18,
  buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59',
  buyTokenDecimals: 18,
  amount: '1000000000000000000',
})

const quoteId = quoteResults.quoteResponse.id
const sellAmount = quoteResults.orderToSign.sellAmount
const buyAmount = quoteResults.orderToSign.buyAmount

// Create limit order with quote ID
const limitOrderParameters: LimitTradeParameters = {
  kind: OrderKind.SELL,
  sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14',
  sellTokenDecimals: 18,
  buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59',
  buyTokenDecimals: 18,
  sellAmount,
  buyAmount,
  quoteId, // Reference the quote
}

const { orderId } = await sdk.postLimitOrder(limitOrderParameters)

Advanced Settings

Customize limit order behavior with LimitOrderAdvancedSettings:
import { LimitOrderAdvancedSettings, SigningScheme } from '@cowprotocol/sdk-trading'

const advancedSettings: LimitOrderAdvancedSettings = {
  additionalParams: {
    signingScheme: SigningScheme.EIP712,
  },
  appData: {
    metadata: {
      quote: {
        slippageBips: '0', // No slippage for limit orders
      },
    },
  },
}

const { orderId } = await sdk.postLimitOrder(
  limitOrderParameters,
  advancedSettings
)

Order Kinds for Limit Orders

Sell orders guarantee you receive at least the specified buyAmount for your sellAmount:
const limitOrderParameters: LimitTradeParameters = {
  kind: OrderKind.SELL,
  sellAmount: '1000000000000000000', // Selling exactly 1 token
  buyAmount: '2000000000000000000',  // Must receive at least 2 tokens
  // ... other parameters
}
Use case: “I want to sell 1 WETH and receive at least 3000 USDC”

Partially Fillable Orders

By default, limit orders are “fill-or-kill” (must be completely filled). Enable partial fills to allow incremental execution:
const limitOrderParameters: LimitTradeParameters = {
  kind: OrderKind.SELL,
  sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14',
  sellTokenDecimals: 18,
  buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59',
  buyTokenDecimals: 18,
  sellAmount: '10000000000000000000', // 10 tokens
  buyAmount: '20000000000000000000', // 20 tokens
  partiallyFillable: true, // Allow partial fills
}

const { orderId } = await sdk.postLimitOrder(limitOrderParameters)
Partially fillable orders may be executed in multiple transactions over time. Monitor the order status to track fills.

Comparing Swap vs Limit Orders

FeatureSwap OrdersLimit Orders
PriceCurrent market priceYour specified price
AmountOne amount (sell OR buy)Both amounts (sell AND buy)
ExecutionImmediate (if liquidity exists)When price target is reached
SlippageApplies automaticallyControlled by your price
Use CaseQuick trades at market pricePrice-specific trades

Best Practices

  1. Set realistic prices: Orders too far from market price may never fill
  2. Use validTo wisely: Set appropriate expiration times for your strategy
  3. Consider partial fills: For large orders, partial fills can improve execution
  4. Monitor order status: Check if your order has been filled or partially filled
  5. Handle slippage: Limit orders typically use 0 slippage since price is explicit

Next Steps

Last modified on March 4, 2026