Overview
The SDK provides high-level methods for trading on Kalshi markets:
executeBuyFlow / executeSellFlow - Place buy/sell orders with automatic signature generation
executeCancelOrderFlow - Cancel existing orders
executeMintFlow / executeBurnFlow - Create/destroy position tokens
getUserOrders / getUserPositions - Query user’s orders and positions
Placing Orders
Place limit orders on Kalshi markets. The SDK handles EIP-712 signature generation automatically.
executeBuyFlow
async executeBuyFlow(params: {
walletClient: WalletClient;
publicClient: PublicClient;
userAddress: `0x${string}`;
chain: string;
marketId: string;
side: 'yes' | 'no';
number: number;
priceUusdc: number;
onEvent?: (event: BisonEvent) => void;
onError?: (error: Error) => void;
}): Promise<{ disconnect: () => void; txHash: `0x${string}` | null }>
Parameters
Viem wallet client for signing messages
Viem public client for reading contract state
Chain identifier (currently only "base" is supported). Vault address is automatically resolved from the /info endpoint.
Kalshi market ticker (e.g., "KXFEDDECISION-26JAN-T425")
Position side: "yes" or "no"
Number of contracts to buy
Optional callback for order events (placed, filled, etc.)
Optional callback for WebSocket errors
Returns
Object containing:
disconnect - Function to stop listening for events
txHash - Transaction hash (currently always null for off-chain orders)
Example
import { createBisonClient } from 'bison-sdk-ts';
import { createWalletClient, createPublicClient, http, custom } from 'viem';
import { base } from 'viem/chains';
const client = createBisonClient({
baseUrl: 'https://api.bison.markets'
});
const walletClient = createWalletClient({
chain: base,
transport: custom(window.ethereum!),
});
const publicClient = createPublicClient({
chain: base,
transport: http(),
});
// Buy 10 YES contracts at 65 cents each
const result = await client.executeBuyFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'yes',
number: 10,
priceUusdc: 650000, // $0.65 * 10^6
onEvent: (event) => {
if (event.type === 'order_placed') {
console.log('Order placed successfully:', event.orderId);
} else if (event.type === 'order_filled') {
console.log('Order filled:', event.orderId);
}
},
onError: (error) => {
console.error('WebSocket error:', error);
},
});
// Stop listening for events when done
// result.disconnect();
executeSellFlow
Works identically to executeBuyFlow but places a sell order.
async executeSellFlow(params: {
walletClient: WalletClient;
publicClient: PublicClient;
userAddress: `0x${string}`;
chain: string;
marketId: string;
side: 'yes' | 'no';
number: number;
priceUusdc: number;
onEvent?: (event: BisonEvent) => void;
onError?: (error: Error) => void;
}): Promise<{ disconnect: () => void; txHash: `0x${string}` | null }>
Example
// Sell 5 NO contracts at 40 cents each
const result = await client.executeSellFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'no',
number: 5,
priceUusdc: 400000, // $0.40 * 10^6
});
Cancelling Orders
Cancel an existing open order.
executeCancelOrderFlow
async executeCancelOrderFlow(params: {
walletClient: WalletClient;
userAddress: `0x${string}`;
chain: string;
orderId: string;
}): Promise<void>
Parameters
Viem wallet client for signing messages
Chain identifier (currently only "base" is supported). Vault address is automatically resolved from the /info endpoint.
ID of the order to cancel (from order event or getUserOrders)
Example
await client.executeCancelOrderFlow({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
orderId: 'abc123-order-id',
});
console.log('Order cancellation requested');
Minting & Burning Positions
Create or destroy position tokens directly on-chain.
executeMintFlow
Mint new position tokens by locking USDC in the vault.
async executeMintFlow(params: {
walletClient: WalletClient;
publicClient: PublicClient;
userAddress: `0x${string}`;
chain: string;
marketId: string;
side: 'yes' | 'no';
number: number;
}): Promise<{ txHash: `0x${string}` }>
Example
// Mint 20 YES tokens for a market
const result = await client.executeMintFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'yes',
number: 20,
});
console.log('Tokens minted! Transaction:', result.txHash);
executeBurnFlow
Burn position tokens to unlock USDC from the vault.
async executeBurnFlow(params: {
walletClient: WalletClient;
publicClient: PublicClient;
userAddress: `0x${string}`;
chain: string;
marketId: string;
side: 'yes' | 'no';
number: number;
}): Promise<{ txHash: `0x${string}` }>
Example
// Burn 15 NO tokens
const result = await client.executeBurnFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'no',
number: 15,
});
console.log('Tokens burned! Transaction:', result.txHash);
Querying Orders & Positions
getUserOrders
Get all orders for a user.
async getUserOrders(userId: string): Promise<GetUserOrdersResponse>
Example
const { orders } = await client.getUserOrders(
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
);
console.log('Open orders:');
orders.forEach(order => {
console.log(`${order.action} ${order.requestedQuantity} ${order.side} @ $${order.priceUusdc / 1_000_000}`);
console.log(` Market: ${order.marketId}`);
console.log(` Filled: ${order.filledQuantity}/${order.requestedQuantity}`);
console.log(` Order ID: ${order.kalshiOrderId}`);
});
getUserPositions
Get all positions for a user.
async getUserPositions(userId: string): Promise<GetUserPositionsResponse>
Example
const { positions } = await client.getUserPositions(
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
);
console.log('Current positions:');
positions.forEach(position => {
console.log(`Market: ${position.marketId}`);
console.log(` ${position.side.toUpperCase()}: ${position.quantity} contracts`);
});
Position Token Utilities
Get token addresses and balances for position tokens.
getPositionTokenAddress
async getPositionTokenAddress(params: {
publicClient: PublicClient;
chain: 'base';
marketId: string;
side: 'yes' | 'no';
}): Promise<`0x${string}` | null>
Returns the ERC20 token address for a position, or null if not yet created. The vault address is automatically resolved from the /info endpoint.
getTokenBalance
async getTokenBalance(params: {
publicClient: PublicClient;
tokenAddress: `0x${string}`;
userAddress: `0x${string}`;
}): Promise<bigint>
Returns the token balance for a user.
addTokenToWallet
async addTokenToWallet(params: {
tokenAddress: `0x${string}`;
marketId: string;
side: 'yes' | 'no';
}): Promise<boolean>
Prompts the user to add a position token to their wallet (MetaMask, etc.).
Example
// Get token address
const tokenAddress = await client.getPositionTokenAddress({
publicClient,
chain: 'base',
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'yes',
});
if (tokenAddress) {
// Get user's balance
const balance = await client.getTokenBalance({
publicClient,
tokenAddress,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
});
console.log('Token balance:', balance.toString());
// Add to wallet
const added = await client.addTokenToWallet({
tokenAddress,
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'yes',
});
if (added) {
console.log('Token added to wallet');
}
}
Error Handling
try {
await client.executeBuyFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
marketId: 'KXFEDDECISION-26JAN-T425',
side: 'yes',
number: 10,
priceUusdc: 650000,
});
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('insufficient balance')) {
console.error('Not enough USDC deposited');
} else if (error.message.includes('User rejected')) {
console.error('User cancelled signature request');
} else {
console.error('Failed to place order:', error.message);
}
}
}
Important Notes
- All order prices are in µUSDC (1 USD = 1,000,000 µUSDC, so $0.65 = 650000 µUSDC)
- Order signatures expire after 10 minutes
- Minting requires sufficient deposited USDC in the vault
- Burning requires holding the position tokens
- The SDK automatically handles EIP-712 signature generation
- WebSocket connection is automatically established for buy/sell flows
- Always call
disconnect() to clean up WebSocket connections