Skip to main content

Overview

The settlement module provides tools for encoding trades, interactions, and prices into the format required by the CoW Protocol settlement contract. The SettlementEncoder class is the primary interface for building settlement calldata.

SettlementEncoder Class

The SettlementEncoder class manages the encoding of orders, interactions, and prices into settlement calldata:
export class SettlementEncoder {
  constructor(public readonly domain: TypedDataDomain);
  
  // Token management
  get tokens(): string[];
  
  // Trade management
  get trades(): Trade[];
  encodeTrade(order: Order, signature: Signature, execution?: Partial<TradeExecution>): void;
  signEncodeTrade(order: Order, owner: Signer, scheme: EcdsaSigningScheme, execution?: Partial<TradeExecution>): Promise<void>;
  
  // Interaction management
  get interactions(): [Interaction[], Interaction[], Interaction[]];
  encodeInteraction(interaction: InteractionLike, stage?: InteractionStage): void;
  
  // Price and settlement encoding
  clearingPrices(prices: Prices): BigNumberish[];
  encodedSettlement(prices: Prices): EncodedSettlement;
  
  // Static helpers
  static encodedSetup(...interactions: InteractionLike[]): EncodedSettlement;
}

Constructor

domain
TypedDataDomain
required
The EIP-712 domain for signing orders. Used to ensure orders are valid for the specific settlement contract and chain.
import { SettlementEncoder, domain } from "@cowprotocol/contracts";

const settlementContract = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";
const encoder = new SettlementEncoder(domain(1, settlementContract));

Encoding Trades

encodeTrade

Adds a signed order to the settlement:
encodeTrade(
  order: Order,
  signature: Signature,
  execution?: Partial<TradeExecution>
): void
order
Order
required
The order to encode.
signature
Signature
required
The order signature (ECDSA, EIP-1271, or PreSign).
execution
Partial<TradeExecution>
Trade execution details. Required for partially fillable orders.
import { signOrder, SigningScheme } from "@cowprotocol/contracts";

// Sign the order
const signature = await signOrder(
  encoder.domain,
  order,
  wallet,
  SigningScheme.EIP712
);

// Encode the trade
encoder.encodeTrade(order, signature);

// For partially fillable orders, specify executed amount
encoder.encodeTrade(partialOrder, signature, {
  executedAmount: ethers.utils.parseEther("0.5"),
});

signEncodeTrade

Signs an order and encodes it in one step:
async signEncodeTrade(
  order: Order,
  owner: Signer,
  scheme: EcdsaSigningScheme,
  execution?: Partial<TradeExecution>
): Promise<void>
// Sign and encode in one call
await encoder.signEncodeTrade(
  order,
  wallet,
  SigningScheme.EIP712
);

// With partial fill
await encoder.signEncodeTrade(
  partialOrder,
  wallet,
  SigningScheme.EIP712,
  { executedAmount: ethers.utils.parseEther("0.5") }
);

Trade Interface

The encoded trade format used by the settlement contract:
export interface Trade {
  sellTokenIndex: BigNumberish;  // Index in the token array
  buyTokenIndex: BigNumberish;   // Index in the token array
  receiver: string;              // Address to receive buy tokens
  sellAmount: BigNumberish;      // Order sell amount
  buyAmount: BigNumberish;       // Order buy amount
  validTo: number;               // Unix timestamp
  appData: string;               // 32-byte hash
  feeAmount: BigNumberish;       // Protocol fee
  flags: BigNumberish;           // Encoded trade flags
  executedAmount: BigNumberish;  // Executed amount for partial fills
  signature: BytesLike;          // Encoded signature
}

TradeExecution

export interface TradeExecution {
  executedAmount: BigNumberish;
}
executedAmount
BigNumberish
required
The amount executed in this trade:
  • Partially fillable sell orders: Amount of sell tokens to trade
  • Partially fillable buy orders: Amount of buy tokens to trade
  • Fill-or-kill orders: Ignored (set to 0)

Flag Encoding

Trade Flags

Trade flags encode order and signature information into a single bitfield:
export interface TradeFlags {
  kind: OrderKind;                    // Sell or buy
  partiallyFillable: boolean;         // Partial fill allowed
  sellTokenBalance: OrderBalance;     // Sell token source
  buyTokenBalance: OrderBalance;      // Buy token destination
  signingScheme: SigningScheme;       // Signature type
}

encodeTradeFlags

export function encodeTradeFlags(flags: TradeFlags): number
import {
  encodeTradeFlags,
  OrderKind,
  OrderBalance,
  SigningScheme,
} from "@cowprotocol/contracts";

const flags = encodeTradeFlags({
  kind: OrderKind.SELL,
  partiallyFillable: false,
  sellTokenBalance: OrderBalance.ERC20,
  buyTokenBalance: OrderBalance.ERC20,
  signingScheme: SigningScheme.EIP712,
});

console.log("Encoded flags:", flags); // Output: 0

decodeTradeFlags

export function decodeTradeFlags(flags: BigNumberish): TradeFlags
import { decodeTradeFlags } from "@cowprotocol/contracts";

const decoded = decodeTradeFlags(trade.flags);
console.log("Order kind:", decoded.kind);
console.log("Signing scheme:", decoded.signingScheme);

Token Registry

Trades reference tokens by index rather than address to save gas. The TokenRegistry class manages this mapping:
export class TokenRegistry {
  get addresses(): string[];
  index(token: string): number;
}
import { TokenRegistry } from "@cowprotocol/contracts";

const registry = new TokenRegistry();

// Get token index (adds if not present)
const wethIndex = registry.index("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
const usdcIndex = registry.index("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48");

console.log("WETH index:", wethIndex); // 0
console.log("USDC index:", usdcIndex); // 1
console.log("All tokens:", registry.addresses);

Prices

Settlements require clearing prices for all tokens:
export type Prices = Record<string, BigNumberish | undefined>;

clearingPrices

Converts a price map to an array aligned with the token registry:
clearingPrices(prices: Prices): BigNumberish[]
const prices: Prices = {
  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": ethers.utils.parseEther("1"),
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": ethers.utils.parseUnits("3000", 6),
};

const priceVector = encoder.clearingPrices(prices);
console.log("Price vector:", priceVector);

Encoded Settlement

EncodedSettlement Type

export type EncodedSettlement = [
  string[],              // Token addresses
  BigNumberish[],        // Clearing prices
  Trade[],               // Encoded trades
  [Interaction[], Interaction[], Interaction[]]  // Pre, intra, post interactions
];

encodedSettlement

Generates the final encoded settlement:
encodedSettlement(prices: Prices): EncodedSettlement
const settlement = encoder.encodedSettlement(prices);

const [tokens, clearingPrices, trades, interactions] = settlement;
console.log("Tokens:", tokens);
console.log("Prices:", clearingPrices);
console.log("Trades:", trades.length);
console.log("Interactions:", interactions);

InteractionStage Enum

Defines when interactions execute during settlement:
export enum InteractionStage {
  PRE = 0,    // Before trading
  INTRA = 1,  // During trading (after sells, before buys)
  POST = 2,   // After trading
}
InteractionStage.PRE
number
Executed before any trading. Use for:
  • EIP-2612 permit calls
  • Setting up allowances
  • Pre-trade state changes
InteractionStage.INTRA
number
Executed during trading (after sell transfers, before buy transfers). Use for:
  • AMM swaps
  • Liquidity interactions
  • Complex trade routing
InteractionStage.POST
number
Executed after all trading completes. Use for:
  • Post-trade cleanup
  • Fee distributions
  • Order refunds

Complete Example

import {
  SettlementEncoder,
  domain,
  Order,
  OrderKind,
  SigningScheme,
  InteractionStage,
  Prices,
} from "@cowprotocol/contracts";
import { ethers } from "ethers";

async function createSettlement() {
  // Setup
  const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
  const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
  const settlementContract = "0x9008D19f58AAbD9eD0D60971565AA8510560ab41";
  
  // Create encoder
  const encoder = new SettlementEncoder(domain(1, settlementContract));

  // Create orders
  const order1: Order = {
    sellToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
    buyToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",  // USDC
    sellAmount: ethers.utils.parseEther("1"),
    buyAmount: ethers.utils.parseUnits("3000", 6),
    validTo: Math.floor(Date.now() / 1000) + 3600,
    appData: 0,
    feeAmount: ethers.utils.parseEther("0.001"),
    kind: OrderKind.SELL,
    partiallyFillable: false,
  };

  const order2: Order = {
    sellToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
    buyToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", // WETH
    sellAmount: ethers.utils.parseUnits("3000", 6),
    buyAmount: ethers.utils.parseEther("0.99"),
    validTo: Math.floor(Date.now() / 1000) + 3600,
    appData: 0,
    feeAmount: ethers.utils.parseUnits("5", 6),
    kind: OrderKind.SELL,
    partiallyFillable: false,
  };

  // Sign and encode trades
  await encoder.signEncodeTrade(order1, wallet, SigningScheme.EIP712);
  await encoder.signEncodeTrade(order2, wallet, SigningScheme.EIP712);

  // Add interactions (optional)
  encoder.encodeInteraction(
    {
      target: "0x1234567890123456789012345678901234567890",
      callData: "0xabcdef",
    },
    InteractionStage.INTRA
  );

  // Define clearing prices
  const prices: Prices = {
    "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": ethers.utils.parseEther("1"),
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48": ethers.utils.parseUnits("0.000333", 18),
  };

  // Generate settlement
  const settlement = encoder.encodedSettlement(prices);

  return settlement;
}

// Execute
const [tokens, prices, trades, interactions] = await createSettlement();
console.log(`Settlement with ${trades.length} trades across ${tokens.length} tokens`);

Decoding

decodeOrder

Decodes a trade back into an order:
export function decodeOrder(trade: Trade, tokens: string[]): Order
import { decodeOrder } from "@cowprotocol/contracts";

const decodedOrder = decodeOrder(trade, tokens);
console.log("Sell token:", decodedOrder.sellToken);
console.log("Buy token:", decodedOrder.buyToken);
console.log("Sell amount:", decodedOrder.sellAmount.toString());

Static Methods

encodedSetup

Creates a settlement with only interactions (no trades):
static encodedSetup(...interactions: InteractionLike[]): EncodedSettlement
Useful for setting up allowances or initializing contract state.
import { SettlementEncoder } from "@cowprotocol/contracts";
import { GPv2Settlement__factory } from "@cowprotocol/contracts";

const settlement = GPv2Settlement__factory.connect(address, provider);
const iface = settlement.interface;

// Create setup-only settlement
const setupSettlement = SettlementEncoder.encodedSetup(
  {
    target: tokenAddress,
    callData: iface.encodeFunctionData("approve", [spender, amount]),
  },
  {
    target: anotherToken,
    callData: iface.encodeFunctionData("approve", [spender, amount]),
  }
);

// Execute setup
await settlement.settle(...setupSettlement);

Best Practices

Token Order

Tokens are indexed in the order they’re first encountered. Be consistent with token ordering for predictable indices.

Price Precision

Use high precision for clearing prices to minimize rounding errors in settlements.

Partial Fills

Always specify executedAmount for partially fillable orders to avoid errors.

Interaction Stages

Choose the correct interaction stage based on when the interaction needs to execute relative to token transfers.

Next Steps

Interactions

Learn how to create and encode interactions

Orders

Review order creation and management

Build docs developers (and LLMs) love