Skip to main content

Overview

The OrderSigningUtils class provides cryptographic utilities for signing CoW Protocol orders and cancellations according to EIP-712 specifications. It supports multiple signing schemes including standard wallets and smart contract wallets.

Installation

npm install @cowprotocol/sdk-order-signing
# or
pnpm add @cowprotocol/sdk-order-signing
# or
yarn add @cowprotocol/sdk-order-signing

Usage

All methods are static and can be called directly on the class.
import { OrderSigningUtils, SupportedChainId } from '@cowprotocol/sdk-order-signing'
import { EthersV6Adapter } from '@cowprotocol/sdk-ethers-v6-adapter'
import { JsonRpcProvider, Wallet } from 'ethers'
import { setGlobalAdapter } from '@cowprotocol/sdk-common'

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

// Set global adapter for utilities to use
setGlobalAdapter(adapter)

Methods

signOrder

Sign an order intent using EIP-712.
static async signOrder(
  order: UnsignedOrder,
  chainId: SupportedChainId,
  signer: Signer
): Promise<SigningResult>
order
UnsignedOrder
required
Unsigned order to sign
chainId
SupportedChainId
required
Chain ID (e.g., 1 for Mainnet, 100 for Gnosis Chain)
signer
Signer
required
Wallet signer instance
result
SigningResult
Signature and signing scheme
Ensure the chainId is correct for your network. An incorrect chainId will result in an invalid signature.

Example

import { OrderSigningUtils } from '@cowprotocol/sdk-order-signing'

const orderToSign = {
  sellToken: '0xA0b86a33E6417b528874E10EB3a95beb4F25A0E3',
  buyToken: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
  sellAmount: '1000000000000000000',
  buyAmount: '1000000000000000000',
  validTo: Math.floor(Date.now() / 1000) + 3600, // 1 hour
  appData: '0x0000000000000000000000000000000000000000000000000000000000000000',
  feeAmount: '0',
  kind: 'sell',
  partiallyFillable: false,
  sellTokenBalance: 'erc20',
  buyTokenBalance: 'erc20',
  receiver: '0x0000000000000000000000000000000000000000',
}

const signingResult = await OrderSigningUtils.signOrder(
  orderToSign,
  1, // Mainnet
  adapter.signer
)

console.log('Signature:', signingResult.signature)
console.log('Scheme:', signingResult.signingScheme)

signOrderCancellation

Sign a cancellation for a single order.
static async signOrderCancellation(
  orderUid: string,
  chainId: SupportedChainId,
  signer: Signer
): Promise<SigningResult>
orderUid
string
required
Unique order identifier to cancel
chainId
SupportedChainId
required
Chain ID
signer
Signer
required
Signer who created the order
result
SigningResult
Signature and signing scheme

Example

const orderId = '0xd64389693b6cf89ad6c140a113b10df08073e5ef...'

const cancellationResult = await OrderSigningUtils.signOrderCancellation(
  orderId,
  1,
  adapter.signer
)

console.log('Cancellation signature:', cancellationResult.signature)

signOrderCancellations

Sign a cancellation for multiple orders in a single signature.
static async signOrderCancellations(
  orderUids: string[],
  chainId: SupportedChainId,
  signer: Signer
): Promise<SigningResult>
orderUids
string[]
required
Array of order UIDs to cancel
chainId
SupportedChainId
required
Chain ID
signer
Signer
required
Signer who created the orders
result
SigningResult
Signature and signing scheme
More gas-efficient than signing cancellations individually when cancelling multiple orders.

Example

const orderIds = [
  '0xd64389693b6cf89ad6c140a113b10df08073e5ef...',
  '0xa12345678b6cf89ad6c140a113b10df08073e5ef...',
]

const cancellationResult = await OrderSigningUtils.signOrderCancellations(
  orderIds,
  1,
  adapter.signer
)

// Use with OrderBookApi
await orderBookApi.sendSignedOrderCancellations({
  ...cancellationResult,
  orderUids: orderIds,
})

getDomain

Get EIP-712 typed domain data for a chain.
static async getDomain(
  chainId: SupportedChainId
): Promise<TypedDataDomain>
chainId
SupportedChainId
required
Chain ID
domain
TypedDataDomain
EIP-712 domain data

Example

const domain = await OrderSigningUtils.getDomain(1)

console.log('Domain:', domain)
// {
//   name: 'Gnosis Protocol',
//   version: 'v2',
//   chainId: 1,
//   verifyingContract: '0x9008D19f58AAbD9eD0D60971565AA8510560ab41'
// }

getDomainSeparator

Get the domain separator hash.
static async getDomainSeparator(
  chainId: SupportedChainId
): Promise<string>
chainId
SupportedChainId
required
Chain ID
domainSeparator
string
Hex-encoded domain separator hash

Example

const separator = await OrderSigningUtils.getDomainSeparator(1)
console.log('Domain separator:', separator)

generateOrderId

Generate deterministic order ID from order data.
static async generateOrderId(
  chainId: SupportedChainId,
  order: Order,
  params: { owner: string }
): Promise<{ orderId: string; orderDigest: string }>
chainId
SupportedChainId
required
Chain ID
order
Order
required
Order data
params
{ owner: string }
required
Order parameters with owner address
result
object
Order ID and digest

Example

const { orderId, orderDigest } = await OrderSigningUtils.generateOrderId(
  1,
  orderData,
  { owner: '0x123...' }
)

console.log('Order ID:', orderId)
console.log('Order digest:', orderDigest)

getEIP712Types

Get the EIP-712 type definitions for CoW Protocol orders.
static getEIP712Types(): typeof COW_EIP712_TYPES
types
object
EIP-712 type definitions

Example

const types = OrderSigningUtils.getEIP712Types()
console.log('Order types:', types)
// {
//   Order: [
//     { name: 'sellToken', type: 'address' },
//     { name: 'buyToken', type: 'address' },
//     ...
//   ]
// }

getEip1271Signature

Encode order and ECDSA signature for EIP-1271 verification.
static getEip1271Signature(
  orderToSign: UnsignedOrder,
  ecdsaSignature: string
): string
orderToSign
UnsignedOrder
required
Order to encode
ecdsaSignature
string
required
ECDSA signature (65 bytes)
signature
string
ABI-encoded signature for EIP-1271
Useful for smart contract wallets that implement EIP-1271 signature verification.

Example

const ecdsaSignature = '0x...' // 65 bytes from signing

const eip1271Signature = OrderSigningUtils.getEip1271Signature(
  orderToSign,
  ecdsaSignature
)

// Use with smart contract wallet

encodeUnsignedOrder

Encode unsigned order for hashing.
static encodeUnsignedOrder(
  orderToSign: UnsignedOrder
): Record<string, string>
orderToSign
UnsignedOrder
required
Order to encode
encoded
Record<string, string>
Encoded order fields

Example

const encoded = OrderSigningUtils.encodeUnsignedOrder(orderToSign)
console.log('Encoded order:', encoded)

Signing Schemes

CoW Protocol supports multiple signing schemes:

EIP-712 (Default)

Standard wallet signature using typed structured data.
const result = await OrderSigningUtils.signOrder(order, chainId, signer)
// result.signingScheme === 'eip712'

EthSign (EIP-191)

Legacy signing method using eth_sign.
// Automatically used as fallback if EIP-712 is not supported

Pre-sign

For smart contract wallets that cannot sign directly.
import { SigningScheme } from '@cowprotocol/sdk-order-book'

// Order is created with PRESIGN scheme
// Then execute pre-sign transaction on-chain
// See TradingSdk.getPreSignTransaction()

EIP-1271

For contracts implementing EIP-1271 signature verification.
const ecdsaSignature = await signer.signMessage(orderHash)
const eip1271Signature = OrderSigningUtils.getEip1271Signature(
  order,
  ecdsaSignature
)

Integration Examples

With OrderBookApi

import { OrderBookApi } from '@cowprotocol/sdk-order-book'
import { OrderSigningUtils } from '@cowprotocol/sdk-order-signing'

const orderBookApi = new OrderBookApi({ chainId: 1 })

// 1. Get quote
const { quote } = await orderBookApi.getQuote(quoteRequest)

// 2. Sign order
const signingResult = await OrderSigningUtils.signOrder(
  quote,
  1,
  signer
)

// 3. Submit order
const orderId = await orderBookApi.sendOrder({
  ...quote,
  ...signingResult,
})

console.log('Order submitted:', orderId)

With TradingSdk

import { TradingSdk } from '@cowprotocol/sdk-trading'

// TradingSdk handles signing internally
const sdk = new TradingSdk(
  { chainId: 1, appCode: 'My App' },
  {},
  adapter
)

const { orderId } = await sdk.postSwapOrder(params)
// Signing happens automatically

Cancelling Orders

// Sign cancellation
const cancellation = await OrderSigningUtils.signOrderCancellations(
  [orderId1, orderId2],
  1,
  signer
)

// Submit to API
await orderBookApi.sendSignedOrderCancellations({
  ...cancellation,
  orderUids: [orderId1, orderId2],
})

Error Handling

try {
  const result = await OrderSigningUtils.signOrder(order, chainId, signer)
  console.log('Signature:', result.signature)
} catch (error) {
  if (error.message.includes('User denied')) {
    console.error('User rejected signature request')
  } else if (error.message.includes('chainId')) {
    console.error('Chain ID mismatch')
  } else {
    console.error('Signing failed:', error)
  }
}

Best Practices

  1. Always verify chain ID - Incorrect chain ID results in invalid signatures
  2. Set global adapter - Required for utilities to function properly
  3. Use typed data (EIP-712) - More secure than eth_sign
  4. Batch cancellations - Use signOrderCancellations for multiple orders
  5. Handle rejections - Users can deny signature requests

See Also

Build docs developers (and LLMs) love