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).
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: number;
}): 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 (e.g., 100_000_000 for 100 USDC)
Returns
Transaction hash of the deposit transaction.
Example
import { createBisonClient } 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
const txHash = await client.executeDepositFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: 100_000_000, // 100 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: number;
}): Promise<{
withdrawId: string;
amountUusdc: number;
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 (e.g., 50_000_000 for 50 USDC). Must not exceed your deposited balance.
Returns
Withdrawal details including the unique withdrawId and current status.
Example
// Step 1: Schedule a withdrawal (no gas required)
const withdrawal = await client.scheduleWithdraw({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: 50_000_000, // 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: number;
claimedAmountUusdc: number;
remainingUusdc: number;
status: 'pending' | 'fill-locked' | 'unclaimed' | 'claimed';
scheduledAt: string;
lockedAt: string | null;
unclaimedAt: string | null;
}>;
totalPending: number;
totalFillLocked: number;
totalUnclaimed: number;
totalAvailableUnclaimed: number;
}>
Example
const withdrawals = await client.getPendingWithdraws({
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
});
console.log(`Total unclaimed: ${withdrawals.totalAvailableUnclaimed / 1_000_000} USDC`);
withdrawals.withdraws.forEach(w => {
console.log(`${w.id}: ${w.status} - ${w.amountUusdc / 1_000_000} 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
// Schedule a withdrawal
const withdrawal = await client.scheduleWithdraw({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: 50_000_000, // 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: number;
}>
Example
const balance = await client.getDepositedUsdcBalance(
'0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
);
// Convert µUSDC to USDC (1 USDC = 1,000,000 µUSDC)
const usdcAmount = balance.depositedBalanceUusdc / 1_000_000;
console.log(`Balance: ${usdcAmount} USDC`);
// Expected output: "Balance: 100 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: number;
}>
Example
import { VAULT_ABI } from '@bison-markets/sdk-ts';
const userAddress = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
const auth = await client.getWithdrawAuthorization({
chain: 'base',
userAddress,
});
console.log('Max claimable amount:', auth.maxWithdrawAmount / 1_000_000, 'USDC');
console.log('Expires at:', new Date(auth.expiresAt * 1000));
// Expected output:
// "Max claimable amount: 50 USDC"
// "Expires at: 2024-12-31T12:34:56.000Z"
// Use the authorization with the vault contract
const txHash = await walletClient.writeContract({
address: vaultAddress,
abi: VAULT_ABI,
functionName: 'withdrawUSDC',
args: [
auth.uuid,
BigInt(auth.maxWithdrawAmount),
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:
try {
const txHash = await client.executeDepositFlow({
walletClient,
publicClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: 1_000_000_000, // 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
// Scheduling errors
try {
const withdrawal = await client.scheduleWithdraw({
walletClient,
userAddress: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
chain: 'base',
amountUusdc: 50_000_000,
});
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 = balance.depositedBalanceUusdc / 1_000_000;
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 are in µUSDC (6 decimal places: 1 USDC = 1_000_000 µUSDC)
- 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