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.
import { ContextClient } from "context-markets";
import { privateKeyToAccount } from "viem/accounts";
const ctx = new ContextClient({
apiKey: process.env.CONTEXT_API_KEY!,
signer: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: "mainnet", // or "testnet"
});
// Always check status first
const status = await ctx.account.status()
// Setup if needed — chain-aware:
// testnet: uses gasless relay (free, no ETH needed)
// mainnet: uses on-chain transactions (requires ETH for gas)
if (!status.isReady) {
await ctx.account.setup()
}
// Mint test USDC (testnet only)
await ctx.account.mintTestUsdc(1000)
// Deposit to exchange — same chain-aware behavior as setup()
await ctx.account.deposit(1000)
You must call setup() before your first trade. The SDK will throw if contracts aren’t approved.
The deprecated gaslessSetup() and gaslessDeposit() methods still work as aliases, but new code should use the chain-aware setup() and deposit() which automatically select the right strategy based on your configured chain.
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, ... })
Chain-aware operations
The setup() and deposit() methods automatically pick the right strategy based on your configured chain:
| Chain | setup() | deposit() |
|---|
| Testnet | Gasless relay (no ETH needed) | Gasless permit signature |
| Mainnet | On-chain transactions (requires ETH) | On-chain USDC transfer |
For granular control on mainnet, you can call the explicit methods directly:
// Explicit on-chain approvals
await ctx.account.approveUsdc()
await ctx.account.approveOperator()
// Explicit on-chain deposit
await ctx.account.onchainDeposit(1000)
// 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
- Must call
setup() before first trade — the SDK will throw ContextApiError if contracts aren’t approved
- Prices are in cents (45), not decimals (0.45) — a price of
0.45 means less than 1 cent
- Outcomes are
"yes" / "no" strings, not booleans
mintTestUsdc() only works on testnet — it will fail on mainnet
- SDK doesn’t read env vars — pass
apiKey explicitly to the constructor
- Specify orderbook
depth — default may be insufficient for analysis
- Use
simulate() before large orders to preview slippage and fill price
Error handling
The SDK throws typed errors:
| Error | When |
|---|
ContextApiError | HTTP errors (4xx/5xx) — includes status and body |
ContextSigningError | EIP-712 signing failures |
ContextConfigError | Missing signer when calling write operations |
import { ContextApiError, ContextConfigError } from "context-markets"
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)
}
}