Skip to main content

Best practices

Signer setup

The SDK supports three signer formats:

Private key

The simplest option — pass a hex-encoded private key:
const ctx = new ContextClient({
  apiKey: "...",
  signer: { privateKey: "0xabc123..." as `0x${string}` },
})

Viem account

Pass a viem Account object directly:
import { privateKeyToAccount } from "viem/accounts"

const ctx = new ContextClient({
  apiKey: "...",
  signer: { account: privateKeyToAccount("0xabc123...") },
})

Viem wallet client

Pass an existing WalletClient for full control over transport and chain:
import { createWalletClient, http } from "viem"
import { privateKeyToAccount } from "viem/accounts"
import { baseSepolia } from "viem/chains"

const walletClient = createWalletClient({
  account: privateKeyToAccount("0xabc123..."),
  chain: baseSepolia,
  transport: http("https://your-rpc.com"),
})

const ctx = new ContextClient({
  apiKey: "...",
  signer: { walletClient },
})
Use private key for scripts and bots. Use viem account when you already have an account object. Use wallet client when you need custom RPC or chain configuration.

Wallet onboarding

The critical flow before first trade: check status, setup, fund, deposit.
// Always check status first
const status = await ctx.account.status()

// Setup if needed (approve contracts)
if (!status.isOperatorApproved) {
  await ctx.account.setup()
}

// Mint test USDC (testnet only)
await ctx.account.mintTestUsdc(1000)

// Deposit to exchange
await ctx.account.deposit(1000)
You must call setup() before your first trade. The SDK will throw if contracts aren’t approved.

Price encoding

Prices are in cents (1-99), not decimals:
// Correct — 45 cents
await ctx.orders.create({ priceCents: 45, ... })

// Wrong — this is less than 1 cent
await ctx.orders.create({ priceCents: 0.45, ... })

Gasless operations

For users without ETH for gas:
// Gasless operator approval (no ETH needed)
await ctx.account.relayOperatorApproval()

// Gasless deposit via permit signature
await ctx.account.relayDeposit(1000)

Pagination

// Auto-paginate all results
const allMarkets = await ctx.markets.listAll({ status: "active" })
const allOrders = await ctx.orders.allMine()

// Manual cursor pagination
let cursor: string | undefined
do {
  const page = await ctx.markets.list({ cursor, limit: 50 })
  // process page.markets
  cursor = page.cursor
} while (cursor)

Common mistakes

  1. Must call setup() before first trade — the SDK will throw ContextApiError if contracts aren’t approved
  2. Prices are in cents (45), not decimals (0.45) — a price of 0.45 means less than 1 cent
  3. Outcomes are "yes" / "no" strings, not booleans
  4. mintTestUsdc() only works on testnet — it will fail on mainnet
  5. SDK doesn’t read env vars — pass apiKey explicitly to the constructor
  6. Specify orderbook depth — default may be insufficient for analysis
  7. Use simulate() before large orders to preview slippage and fill price

Error handling

The SDK throws typed errors:
ErrorWhen
ContextApiErrorHTTP errors (4xx/5xx) — includes status and body
ContextSigningErrorEIP-712 signing failures
ContextConfigErrorMissing signer when calling write operations
import { ContextApiError, ContextConfigError } from "@contextwtf/sdk"

try {
  await ctx.orders.create({ ... })
} catch (err) {
  if (err instanceof ContextApiError) {
    console.error(`API error ${err.status}:`, err.body)
  }
  if (err instanceof ContextConfigError) {
    console.error("Missing signer:", err.message)
  }
}