IBExWalletAPI
Chains

CoW Swap Integration

IBEX FI API β€” CoW Swap integration (quote β†’ presign swap)

This guide describes the end-to-end flow to execute a swap via CoW Protocol with IBEX FI API: get a quote, prepare a Safe operation, WebAuthn (passkey) signing, and on-chain execution (approve + presign via setPreSignature).

Overview

  • Fetch and persist a quote: GET /v1.2/safes/quote β†’ returns quoteId
  • Prepare the Safe operation: POST /v1.2/safes/operations with \{ type: "SWAP_FROM_QUOTE", quoteId \} β†’ returns WebAuthn credentialRequestOptions
  • Sign client-side using simplewebauthn
  • Submit the operation: PUT /v1.2/safes/operations with credential β†’ performs approve + setPreSignature, returns userOpHash

Notes:

  • The API uses an on-chain presign strategy: the order is sent to CoW, then the Safe signs on-chain via setPreSignature(orderUid, true).
  • Idempotency: the API keeps an in-process orderUid cache keyed by (safeAddress, quoteId) to avoid duplicate order creation (cache resets on redeploy).
  • Execution locks: a per-Safe lock prevents concurrent executions sharing the same nonce.

Prerequisites

  • Authentication: JWT (see auth docs)
  • Authorized domain rpId (table Domain)
  • Existing Safe for the current user
  • CoW environment variables configured:
    • COW_BASE_<chainId>``: CoW base URL for the target chain
    • COW_APPCODE, COW_ENV: AppData metadata
    • COW_SLIPPAGE_BPS_<DEFAULT_CHAIN_ID>``: default slippage in bips
    • COW_PARTNER_ADDRESS_<DEFAULT_CHAIN_ID>``: main partner wallet
    • COW_PARTNER_BIPS_<DEFAULT_CHAIN_ID>``: total partner fee bips
    • Optional per-domain (Domain): wallet (partner), share (fraction 0..1 of the partner bips allocated to the domain)
    • On-chain execution contracts (used by the API):
      • COW_VAULT_RELAYER_<DEFAULT_CHAIN_ID>``
      • COW_SETTLEMENT_<DEFAULT_CHAIN_ID>``

1) Get a CoW quote

Endpoint:

GET /v1.2/safes/quote
Authorization: Bearer <JWT>

Query params:

  • sellTokenAddress (required): EVM address of the sell token
  • buyTokenAddress (required): EVM address of the buy token
  • amount (required): human string amount (per sell token decimals)
  • chainId (optional): defaults to Env.DEFAULT_CHAIN_ID
  • safeAddress (optional): defaults to the first Safe of the current user

Response (example):

{
  "quoteId": "f5ca1f1d-...",
  "sellToken": "0x...",
  "sellTokenDecimals": 18,
  "buyToken": "0x...",
  "buyTokenDecimals": 18,
  "sellAmount": "100.0",
  "feeAmount": "123456789",
  "buyAmount": "987654321",
  "validTo": 1730000000
}

Curl example:

curl -sS -H "Authorization: Bearer $JWT" \
  "https://<CNAME-API>/v1.2/safes/quote?sellTokenAddress=0x...&buyTokenAddress=0x...&amount=100.0&chainId=421614"

2) Prepare the Safe operation (SWAP_FROM_QUOTE)

Endpoint:

POST /v1.2/safes/operations
Authorization: Bearer <JWT>
Content-Type: application/json

Body (example):

{
  "safeAddress": "0x...",
  "chainId": 421614,
  "operations": [
    { "type": "SWAP_FROM_QUOTE", "quoteId": "f5ca1f1d-..." }
  ]
}

Response: WebAuthn options for passkey authentication

{ "credentialRequestOptions": { /* ... */ } }

The client then signs using simplewebauthn:

const credential = await startAuthentication({ optionsJSON: credentialRequestOptions });

3) Submit the operation (execution)

Endpoint:

PUT /v1.2/safes/operations
Authorization: Bearer <JWT>
Content-Type: application/json

Body:

{ "credential": { /* passkey WebAuthn credential */ } }

Response:

{ "userOpHash": "0x..." }

Execution details (API side):

  • Approve the sell token for the CoW vault relayer (COW_VAULT_RELAYER_<DEFAULT_CHAIN_ID>``)
  • Call setPreSignature(orderUid, true) on the settlement contract (COW_SETTLEMENT_<DEFAULT_CHAIN_ID>``) via MultiSend
  • Idempotency: if an orderUid already exists for (safe, quoteId), it is reused
  • Per-safe lock avoids β€œsame sender and nonce” errors

Common errors

  • 400: unknown tokens / missing metadata (BCReader), invalid parameters, unknown quoteId
  • 401: missing/invalid JWT or unknown rpId
  • 409: concurrent execution or duplicate nonce
  • 422/500: blockchain constraints, on-chain failures

Best practices

  • Always call GET /v1.2/safes/quote right before preparing to ensure validTo freshness
  • Use X-Blockchain-Id (or ?blockchainId) explicitly when multiple chains are supported
  • Ensure COW_BASE_<chainId>`` is set for the target chain
  • Configure partner variables properly (appCode, env, bips, wallet) when fee sharing is needed

On this page