Overview
The SDK provides deposit and withdrawal methods for managing USDC on Bison:
- Deposits: Use
executeDepositFlow to deposit USDC instantly
- Withdrawals: Use a two-step process with
scheduleWithdraw and claimWithdraw to request and claim withdrawals
- Balance Checking: Use
getDepositedUsdcBalance to check your available balance
These methods handle all the necessary steps including approvals and authorization signatures.
To avoid ambiguity, we denote the smallest possible multiple of USDC (0.000001 USDC) as one uusdc,
which stands for µUSDC (micro-USDC), and denote the smallest possible multiple of a contract (0.01 contract)
as one ccontract (centicontract).User-facing USDC balances are specified as fixed-point strings (e.g. "1.2625" for USDC). Contract quantities
in the API and SDK are specified as integer ccontracts strings (e.g. "1050" for 10.50 contracts at precision 2).
Depositing USDC
Deposit USDC to enable trading on Kalshi markets.
executeDepositFlow
async executeDepositFlow(params: {
walletClient: WalletClient;
publicClient: PublicClient;
userAddress: `0x${string}`;
chain: 'base' | 'bsc';
amountUusdc: string;
}): Promise<`0x${string}`>
Parameters
Viem wallet client for signing transactions
Viem public client for reading contract state
Chain identifier ("base" or "bsc"). Vault and USDC addresses are automatically resolved from the /info endpoint.
Amount in µUSDC as an integer string (e.g., "100000000" for 100 USDC). Use usdcToUusdc() helper for conversion.
Returns
Transaction hash of the deposit transaction.
Example
import { createBisonClient, usdcToUusdc } from '@bison-markets/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(),
});
// Deposit 100 USDC using helper function
const txHash = await client.executeDepositFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: usdcToUusdc(100).toString(), // Helper converts 100 USDC to 100000000 µUSDC
});
console.log('Deposited! Transaction:', txHash);
// Expected output: "Deposited! Transaction: 0xabc123..."
Withdrawing USDC
Withdrawals use a two-step process for security and efficiency:
- Schedule: Request a withdrawal with
scheduleWithdraw (no gas required, just a signature)
- Claim: Execute the withdrawal on-chain with
claimWithdraw (requires gas)
This approach allows the system to batch and optimize withdrawal processing.
scheduleWithdraw
Schedules a withdrawal request. This method requires only a signature and does not consume gas.
async scheduleWithdraw(params: {
walletClient: WalletClient;
userAddress: `0x${string}`;
chain: 'base' | 'bsc';
amountUusdc: string;
}): Promise<{
withdrawId: string;
amountUusdc: string;
status: 'pending' | 'fill-locked' | 'unclaimed' | 'claimed';
scheduledAt: string;
}>
Parameters
Viem wallet client for signing the withdrawal request
Chain identifier ("base" or "bsc")
Amount in µUSDC as an integer string (e.g., "50000000" for 50 USDC). Must not exceed your deposited balance. Use usdcToUusdc() helper for conversion.
Returns
Withdrawal details including the unique withdrawId and current status. Note that amountUusdc is returned as a string.
Example
import { usdcToUusdc } from '@bison-markets/sdk-ts';
// Step 1: Schedule a withdrawal (no gas required)
const withdrawal = await client.scheduleWithdraw({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: usdcToUusdc(50).toString(), // 50 USDC
});
console.log('Withdrawal scheduled:', withdrawal.withdrawId);
console.log('Status:', withdrawal.status); // "pending"
// Expected output:
// "Withdrawal scheduled: 550e8400-e29b-41d4-a716-446655440000"
// "Status: pending"
getPendingWithdraws
Get all pending withdrawals for a user.
async getPendingWithdraws(params: {
userAddress: `0x${string}`;
}): Promise<{
withdraws: Array<{
id: string;
chain: 'base' | 'bsc';
amountUusdc: string;
claimedAmountUusdc: string;
remainingUusdc: string;
status: 'pending' | 'fill-locked' | 'unclaimed' | 'claimed';
scheduledAt: string;
lockedAt: string | null;
unclaimedAt: string | null;
}>;
totalPending: string;
totalFillLocked: string;
totalUnclaimed: string;
totalAvailableUnclaimed: string;
}>
Example
import { uusdcToUsdc } from '@bison-markets/sdk-ts';
const withdrawals = await client.getPendingWithdraws({
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
});
// All amounts are returned as strings - use uusdcToUsdc() to convert for display
console.log(`Total unclaimed: ${uusdcToUsdc(withdrawals.totalAvailableUnclaimed)} USDC`);
withdrawals.withdraws.forEach(w => {
console.log(`${w.id}: ${w.status} - ${uusdcToUsdc(w.amountUusdc)} USDC`);
});
claimWithdraw
Claim all available withdrawals on-chain. This method executes a blockchain transaction and requires gas.
async claimWithdraw(params: {
walletClient: WalletClient;
publicClient: PublicClient;
userAddress: `0x${string}`;
chain: 'base' | 'bsc';
}): Promise<`0x${string}`>
Parameters
Viem wallet client for signing transactions
Viem public client for reading contract state
Chain identifier ("base" or "bsc")
Returns
Transaction hash of the withdrawal claim transaction.
Example
// Step 2: Claim the withdrawal (requires gas)
const txHash = await client.claimWithdraw({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
});
console.log('Withdrawal claimed! Transaction:', txHash);
// Expected output: "Withdrawal claimed! Transaction: 0xdef456..."
Complete Withdrawal Example
import { usdcToUusdc } from '@bison-markets/sdk-ts';
// Schedule a withdrawal
const withdrawal = await client.scheduleWithdraw({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: usdcToUusdc(50).toString(), // 50 USDC
});
console.log('Withdrawal scheduled with ID:', withdrawal.withdrawId);
// Wait for the withdrawal to be processed (status changes to 'unclaimed')
// This typically happens when the system batches and processes withdrawals
// Check pending withdrawals
const pending = await client.getPendingWithdraws({
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
});
if (pending.totalAvailableUnclaimed !== '0') {
// Claim all available withdrawals
const txHash = await client.claimWithdraw({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
});
console.log('Successfully claimed withdrawals:', txHash);
}
Checking Balance
Get the user’s deposited USDC balance:
getDepositedUsdcBalance
async getDepositedUsdcBalance(userAddress: string): Promise<{
depositedBalanceUusdc: string;
}>
Example
import { uusdcToUsdc } from '@bison-markets/sdk-ts';
const balance = await client.getDepositedUsdcBalance(
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
);
// Balance is returned as a string - convert for display
const usdcAmount = uusdcToUsdc(balance.depositedBalanceUusdc);
console.log(`Balance: ${usdcAmount} USDC`);
// Expected output: "Balance: 100.000000 USDC"
Low-Level Methods
For advanced use cases, you can use the low-level authorization methods:
getWithdrawAuthorization
Get a signed authorization for claiming withdrawals. This is used internally by claimWithdraw, but you can call it directly for custom implementations:
async getWithdrawAuthorization(options: {
chain: string;
userAddress: string;
}): Promise<{
uuid: string;
signature: string;
expiresAt: number;
maxWithdrawAmount: string;
}>
Example
import { VAULT_ABI, uusdcToUsdc, parseBigInt } from '@bison-markets/sdk-ts';
const userAddress = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
const auth = await client.getWithdrawAuthorization({
chain: 'base',
userAddress,
});
// maxWithdrawAmount is returned as a string
console.log('Max claimable amount:', uusdcToUsdc(auth.maxWithdrawAmount), 'USDC');
console.log('Expires at:', new Date(auth.expiresAt * 1000));
// Expected output:
// "Max claimable amount: 50.000000 USDC"
// "Expires at: 2024-12-31T12:34:56.000Z"
// Use the authorization with the vault contract - convert string to bigint
const txHash = await walletClient.writeContract({
address: vaultAddress,
abi: VAULT_ABI,
functionName: 'withdrawUSDC',
args: [
auth.uuid,
parseBigInt(auth.maxWithdrawAmount), // Convert string to bigint
userAddress,
BigInt(auth.expiresAt),
auth.signature as `0x${string}`,
],
account: userAddress,
});
console.log('Withdrawal transaction:', txHash);
Error Handling
All deposit and withdrawal methods throw errors when operations fail. Here’s how to handle common errors:
import { usdcToUusdc } from '@bison-markets/sdk-ts';
try {
const txHash = await client.executeDepositFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: usdcToUusdc(1000).toString(), // 1000 USDC
});
console.log('Success:', txHash);
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('insufficient funds')) {
console.error('Not enough USDC in wallet');
// Show error to user: "Insufficient USDC balance"
} else if (error.message.includes('User rejected')) {
console.error('User cancelled transaction');
// User declined wallet approval
} else if (error.message.includes('network')) {
console.error('Network error, please retry');
// Show retry button
} else {
console.error('Deposit failed:', error.message);
// Show generic error message
}
}
}
Withdrawal Error Handling
import { usdcToUusdc, uusdcToUsdc } from '@bison-markets/sdk-ts';
// Scheduling errors
try {
const withdrawal = await client.scheduleWithdraw({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: usdcToUusdc(50).toString(),
});
console.log('Scheduled:', withdrawal.withdrawId);
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('Insufficient balance')) {
// Requested amount is more than available balance
const balance = await client.getDepositedUsdcBalance(userAddress);
const maxAmount = uusdcToUsdc(balance.depositedBalanceUusdc);
console.log(`Maximum withdrawable: ${maxAmount} USDC`);
} else if (error.message.includes('Signature expired')) {
console.error('Signature expired, please retry');
} else if (error.message.includes('Invalid signature')) {
console.error('Signature verification failed');
} else {
console.error('Schedule failed:', error.message);
}
}
}
// Claiming errors
try {
const txHash = await client.claimWithdraw({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
});
console.log('Claimed:', txHash);
} catch (error) {
if (error instanceof Error) {
if (error.message.includes('No unclaimed withdrawals')) {
console.error('No withdrawals ready to claim yet');
// Check status with getPendingWithdraws
} else if (error.message.includes('User rejected')) {
console.error('User cancelled transaction');
} else {
console.error('Claim failed:', error.message);
}
}
}
Important Notes
Withdrawals
- Withdrawals use a two-step process: schedule then claim
scheduleWithdraw only requires a signature (no gas)
claimWithdraw executes the on-chain transaction (requires gas)
- Withdrawal status transitions:
pending → fill-locked → unclaimed → claimed
- You can only schedule withdrawals up to your available balance (deposited balance minus active positions)
- Multiple scheduled withdrawals are batched when claiming
- Withdrawal scheduling signatures expire after 10 minutes
Testnet: Withdrawals are automatically confirmed every 5 minutes. See Testnet Environment for details.
Deposits
- The SDK handles USDC approval automatically for deposits
- After deposits, balances update automatically via blockchain events
General
- All amounts in the API are in µUSDC (6 decimal places: 1 USDC = 1,000,000 µUSDC)
- API responses return amounts as strings for precision
- Use
usdcToUusdc() to convert USDC to µUSDC, then call .toString() for API inputs
- Use
uusdcToUsdc() to convert µUSDC to display format (returns string)
- Vault and USDC addresses are automatically fetched from the
/info endpoint and cached per client instance
- Transaction confirmation can take 2-5 seconds on Base network