TypeScript SDK for building Settlement.settle() calldata — converts Solver API /solve request + response into ABI-encoded calldata ready for on-chain submission.
No npm install needed. Copy the standalone example directly into your project:
This single file contains all types, ABI, and the buildSettleCalldata() function — zero internal dependencies, only requires ethers@^5.7.0.
# 1. Copy the standalone file into your project
curl -o settle-calldata.ts https://raw.githubusercontent.com/okxlabs/web3-dex-evm-intent-sdk/main/examples/settle-calldata-standalone.ts
# 2. Import and useimport { buildSettleCalldata } from './settle-calldata';
import { ethers } from 'ethers';
// Step 1: Receive /solve request & compute your solution (response)
const request = await receiveAuctionRequest(); // SolveRequest
const response = await computeSolution(request); // SolveResponse
// Step 2: Receive settleId from /settle callback
const settleId = settleRequest.settleInfos[0].settleId;
// Step 3: Build threshold-schema calldata
// The standalone helper takes interactions directly as its fourth argument.
const { calldata } = buildSettleCalldata(request, response, settleId, [[], [], []]);
// Step 4: Submit settle() transaction
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const solver = new ethers.Wallet(SOLVER_PK, provider);
const tx = await solver.sendTransaction({
to: SETTLEMENT_CONTRACT,
data: calldata,
});
await tx.wait();The standalone file demonstrates only the threshold schema. Solvers that must serve both legacy and threshold contracts should use the address-aware package API described below.
| Directory | Description |
|---|---|
packages/solver |
Solver calldata builder (buildSettleCalldata) |
packages/common |
Core types, constants, and utilities |
packages/contracts |
Settlement contract ABI |
The SDK defines strict types for the /solve endpoint wire format.
interface SolveRequest {
auctionId?: string; // Auction ID from /solve API (not used by buildSettleCalldata)
orders: SolveRequestOrder[];
}
interface SolveRequestOrder {
fromTokenAddress: string; // Token to sell
toTokenAddress: string; // Token to buy
owner: string; // Order signer address
receiver: string; // Proceeds recipient (owner if same)
fromTokenAmount: string; // Sell amount (decimal string)
toTokenAmount: string; // Min buy amount (decimal string)
validTo: number; // Expiry (unix seconds)
appDataHash: string; // Application data (bytes32 hex)
swapMode: string; // "exactIn" | "exactOut"
partiallyFillable: boolean;
signingScheme: string; // "eip712" | "ethSign" | "eip1271" | "preSign"
signature: string; // Order signature (hex)
thresholdPercent?: string; // Output shortfall tolerance, 1e9 precision (default "0")
commissionInfos: ApiCommissionInfo[];
}thresholdPercent is settlement-only metadata supplied by the backend and is not part of the
user's EIP-712 signature. Values must be in [0, 1_000_000_000]; for example, 5_000_000
means 50 bps (0.5%). For a threshold Settlement, omitting it encodes 0, preserving the previous
zero-tolerance behavior. A legacy Trade has no thresholdPercent tuple field at all. The signed
toTokenAmount remains the hard minimum payout floor.
Deployment note: the threshold contract has a new
settleselector (0x462c3ed1) and must be called at the newly deployed Settlement address. That address is also the EIP-712verifyingContract, so orders signed for the previous Settlement address cannot be reused.
interface SolveResponse {
solutions: Solution[]; // Typically one solution
}
interface Solution {
clearingPrices: Record<string, string>; // tokenAddress → price (decimal string)
orders: SolveResponseOrder[]; // Parallel to request.orders
surplusFeeInfo: ApiSurplusFeeInfo; // Protocol surplus fee config
}
interface SolveResponseOrder {
executedFromTokenAmount: string; // Actual sell amount (decimal string)
executedToTokenAmount: string; // Actual buy amount (decimal string)
commissionInfos: ApiCommissionInfo[];
solverFeeInfo: ApiSolverFeeInfo;
}interface ApiCommissionInfo {
feePercent: string; // Rate in 1e9 precision ("3000000" = 0.3%)
referrerWalletAddress: string; // Fee recipient
feeDirection: boolean; // true = fromToken, false = toToken
toB: boolean; // true = Settlement pays, false = user pays
commissionType: string; // "okx" | "parent" | "child"
}
interface ApiSolverFeeInfo {
feePercent: string; // Solver fee rate (1e9 precision)
solverAddress: string;
feeDirection: boolean; // true = fromToken, false = toToken
feeAmount: string; // Informational total amount
}
interface ApiSurplusFeeInfo {
feePercent: string; // Surplus fee rate (1e9 precision)
trimReceiver: string; // Protocol fee recipient
flag: string; // Flag value
}Note: The raw API response may be wrapped in
{ code, msg, data: { solutions } }. You need to unwrap it before passing tobuildSettleCalldata(). See the full example for the unwrapping pattern.
buildSettleCalldata supports two strategies for computing clearing prices:
Uses solution.clearingPrices from the API response directly. The SDK converts decimal string ratios into uint256 integers by scaling all prices to the same decimal precision.
const { calldata } = buildSettleCalldata(request, response, settleId, {
useComputedPrices: false,
});When to use: Your solver API already returns well-formed clearing prices.
Derives clearing prices from execution amounts and fees. For each order:
P_sell = executedToTokenAmount(buy-side price)P_buy = executedFromTokenAmount - totalFromTokenFees(sell-side price minus fees)
const { calldata } = buildSettleCalldata(request, response, settleId, {
useComputedPrices: true,
});When to use: You want prices that exactly match the execution amounts, or when handling complex multi-token batches where API prices may not capture fee adjustments precisely.
function buildSettleCalldata(
request: SolveRequest,
response: SolveResponse,
settleId: string | bigint, // from /settle callback's settleInfos[].settleId
options?: BuildSettleCalldataOptions
): BuildSettleCalldataResult;
interface BuildSettleCalldataOptions {
interactions?: [Interaction[], Interaction[], Interaction[]]; // [pre, swap, post]
solutionIndex?: number; // Which solution to use (default: 0)
useComputedPrices?: boolean; // Derive prices from execution data (default: true)
settlementAddress?: string; // Selects the calldata schema from the SDK's known-address map
}The same SDK supports both Settlement calldata schemas without exposing a version parameter.
Pass the target Settlement address and the SDK selects its mapped schema. The address can come
from the /solve request's settlementContract field or any solver-side configuration:
import { buildSettleCalldata } from '@okx-intent-swap/sdk-solver';
const result = buildSettleCalldata(request, response, settleId, {
settlementAddress: rawSolveRequest.settlementContract,
});For backwards compatibility with existing production solvers, omitting settlementAddress keeps
the historical legacy calldata behavior. A supplied but unknown address throws instead of falling
back to a default. Address resolution is local and does not perform an RPC request. Decoding
automatically detects the calldata schema from the selector.
- Convert
settleId→ bigint (from /settle callback, distinct from auctionId) - Collect all unique token addresses →
tokens[] - Convert clearing prices (decimal string → uint256)
- Build
Trade[]by merging request orders + response execution data; includethresholdPercentonly for the threshold schema - Pack commission flags (direction + ToB + label → single uint256 bitmask)
- Sort trades by
toTokenAddressIndexascending (Settlement contract requirement) - ABI-encode everything as
Settlement.settle()calldata
examples/settle-calldata-standalone.ts — single-file, zero-dependency (except ethers). Copy it directly into your solver project.
The standalone file demonstrates the threshold schema. Solvers that serve both contract versions
should use the address-aware API from packages/solver described above.
examples/nodejs-ethers5/src/build-calldata.ts — demonstrates how to transform raw /solve API JSON into SDK types and produce ABI-encoded calldata.
cd examples/nodejs-ethers5
pnpm build && pnpm build-calldatapnpm install # Install dependencies
pnpm build # Build all packages
pnpm test # Run tests
pnpm typecheck # Type checkMIT