Skip to main content

SDK Examples

Complete code examples for common Seesaw operations.

Setup

import {
  Connection,
  PublicKey,
  Keypair,
  Transaction,
  sendAndConfirmTransaction,
  SystemProgram,
} from '@solana/web3.js';
import { getAssociatedTokenAddress, TOKEN_PROGRAM_ID } from '@solana/spl-token';
import {
  findConfigPda,
  findMarketPda,
  findOrderbookPda,
  findVaultPda,
  findPositionPda,
  parseMarket,
  parseOrderbook,
  parsePosition,
  parseConfig,
  createPlaceOrderInstruction,
  createCancelOrderInstruction,
  createSettlePositionInstruction,
  OrderSide,
  OrderType,
} from '@seesaw/sdk';

// Configuration
const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');
const programId = new PublicKey('SEESAW_PROGRAM_ID');
const wallet = Keypair.fromSecretKey(/* your secret key */);

Example 1: View Current Market

async function viewCurrentMarket() {
  // Calculate current market ID
  const now = Math.floor(Date.now() / 1000);
  const durationSeconds = 900; // default; use market's actual duration
  const marketId = BigInt(Math.floor(now / durationSeconds));

  // Derive PDAs
  const [marketPda] = findMarketPda(marketId, programId);
  const [orderbookPda] = findOrderbookPda(marketPda, programId);

  // Fetch accounts
  const [marketAccount, orderbookAccount] = await connection.getMultipleAccountsInfo([
    marketPda,
    orderbookPda,
  ]);

  if (!marketAccount) {
    console.log('No market exists for current epoch');
    console.log('Market ID:', marketId.toString());
    console.log('Start time:', new Date(Number(marketId) * 900 * 1000));
    return;
  }

  const market = parseMarket(marketAccount.data);
  const orderbook = orderbookAccount ? parseOrderbook(orderbookAccount.data) : null;

  console.log('=== Market Info ===');
  console.log('Market ID:', market.marketId.toString());
  console.log('Start:', new Date(Number(market.tStart) * 1000));
  console.log('End:', new Date(Number(market.tEnd) * 1000));
  console.log('Pyth Feed:', market.pythFeed.toBase58());
  console.log('Start Price:', market.startPrice.toString());
  console.log('End Price:', market.endPrice.toString());
  console.log('Outcome:', market.outcome === 0 ? 'Pending' : market.outcome === 1 ? 'UP' : 'DOWN');
  console.log('Total YES:', market.totalYesShares.toString());
  console.log('Total NO:', market.totalNoShares.toString());
  console.log('Volume:', market.totalVolume.toString());

  if (orderbook) {
    console.log('\n=== Order Book ===');
    console.log('Best Bid:', orderbook.bestBidPrice, 'bps');
    console.log('Best Ask:', orderbook.bestAskPrice, 'bps');
    console.log('Spread:', orderbook.bestAskPrice - orderbook.bestBidPrice, 'bps');
    console.log('Bid Count:', orderbook.bidCount);
    console.log('Ask Count:', orderbook.askCount);
  }
}

await viewCurrentMarket();

Example 2: Buy YES Shares

async function buyYesShares(marketId: bigint, priceBps: number, quantity: bigint) {
  console.log(`Buying ${quantity} YES shares at ${priceBps} bps...`);

  // 1. Derive PDAs
  const [configPda] = findConfigPda(programId);
  const [marketPda] = findMarketPda(marketId, programId);
  const [orderbookPda] = findOrderbookPda(marketPda, programId);
  const [vaultPda] = findVaultPda(marketPda, programId);
  const [positionPda] = findPositionPda(marketPda, wallet.publicKey, programId);

  // 2. Get market info
  const marketAccount = await connection.getAccountInfo(marketPda);
  if (!marketAccount) throw new Error('Market not found');
  const market = parseMarket(marketAccount.data);

  // 3. Get config for treasury
  const configAccount = await connection.getAccountInfo(configPda);
  const config = parseConfig(configAccount!.data);

  // 4. Get token accounts
  const userTokenAccount = await getAssociatedTokenAddress(market.settlementMint, wallet.publicKey);
  const treasuryTokenAccount = await getAssociatedTokenAddress(
    market.settlementMint,
    config.treasury
  );

  // 5. Calculate required collateral
  const collateralNeeded = (BigInt(priceBps) * quantity) / 10000n;
  console.log(`Collateral needed: ${collateralNeeded} tokens`);

  // 6. Build instruction
  const placeOrderIx = createPlaceOrderInstruction(
    {
      market: marketPda,
      orderbook: orderbookPda,
      position: positionPda,
      userTokenAccount,
      vault: vaultPda,
      user: wallet.publicKey,
      config: configPda,
      treasuryTokenAccount,
      tokenProgram: TOKEN_PROGRAM_ID,
      systemProgram: SystemProgram.programId,
    },
    {
      side: OrderSide.BuyYes,
      priceBps,
      quantity,
      orderType: OrderType.Limit,
    },
    programId
  );

  // 7. Send transaction
  const tx = new Transaction().add(placeOrderIx);
  const signature = await sendAndConfirmTransaction(connection, tx, [wallet]);

  console.log('Order placed! Signature:', signature);
  return signature;
}

// Usage: Buy 100 YES shares at 60%
const marketId = BigInt(Math.floor(Date.now() / 1000 / 900)); // 900 = default 15-min duration
await buyYesShares(marketId, 6000, BigInt(100));

Example 3: Sell NO Shares

async function sellNoShares(marketId: bigint, priceBps: number, quantity: bigint) {
  console.log(`Selling ${quantity} NO shares at ${priceBps} bps...`);

  // 1. Derive PDAs
  const [configPda] = findConfigPda(programId);
  const [marketPda] = findMarketPda(marketId, programId);
  const [orderbookPda] = findOrderbookPda(marketPda, programId);
  const [vaultPda] = findVaultPda(marketPda, programId);
  const [positionPda] = findPositionPda(marketPda, wallet.publicKey, programId);

  // 2. Check position
  const positionAccount = await connection.getAccountInfo(positionPda);
  if (!positionAccount) throw new Error('No position found');
  const position = parsePosition(positionAccount.data);

  const availableNo = position.noShares - position.lockedNoShares;
  if (availableNo < quantity) {
    throw new Error(`Insufficient NO shares. Available: ${availableNo}`);
  }

  // 3. Get market and config
  const marketAccount = await connection.getAccountInfo(marketPda);
  const market = parseMarket(marketAccount!.data);
  const configAccount = await connection.getAccountInfo(configPda);
  const config = parseConfig(configAccount!.data);

  // 4. Get token accounts
  const userTokenAccount = await getAssociatedTokenAddress(market.settlementMint, wallet.publicKey);
  const treasuryTokenAccount = await getAssociatedTokenAddress(
    market.settlementMint,
    config.treasury
  );

  // 5. Build instruction
  const placeOrderIx = createPlaceOrderInstruction(
    {
      market: marketPda,
      orderbook: orderbookPda,
      position: positionPda,
      userTokenAccount,
      vault: vaultPda,
      user: wallet.publicKey,
      config: configPda,
      treasuryTokenAccount,
      tokenProgram: TOKEN_PROGRAM_ID,
      systemProgram: SystemProgram.programId,
    },
    {
      side: OrderSide.SellNo,
      priceBps,
      quantity,
      orderType: OrderType.Limit,
    },
    programId
  );

  // 6. Send transaction
  const tx = new Transaction().add(placeOrderIx);
  const signature = await sendAndConfirmTransaction(connection, tx, [wallet]);

  console.log('Order placed! Signature:', signature);
  return signature;
}

Example 4: View and Cancel Orders

async function viewAndCancelOrders(marketId: bigint) {
  // 1. Derive PDAs
  const [marketPda] = findMarketPda(marketId, programId);
  const [orderbookPda] = findOrderbookPda(marketPda, programId);
  const [vaultPda] = findVaultPda(marketPda, programId);
  const [positionPda] = findPositionPda(marketPda, wallet.publicKey, programId);

  // 2. Get orderbook
  const orderbookAccount = await connection.getAccountInfo(orderbookPda);
  if (!orderbookAccount) throw new Error('Orderbook not found');
  const orderbook = parseOrderbook(orderbookAccount.data);

  // 3. Find user's orders
  const userOrders = [
    ...orderbook.bids.filter((o) => o.isActive && o.owner.equals(wallet.publicKey)),
    ...orderbook.asks.filter((o) => o.isActive && o.owner.equals(wallet.publicKey)),
  ];

  console.log('=== Your Orders ===');
  for (const order of userOrders) {
    const sideNames = ['BuyYes', 'SellYes', 'BuyNo', 'SellNo'];
    console.log(
      `Order ${order.orderId}: ${sideNames[order.originalSide]} @ ${order.priceBps} bps, qty: ${order.quantity}`
    );
  }

  if (userOrders.length === 0) {
    console.log('No open orders');
    return;
  }

  // 4. Cancel first order as example
  const orderToCancel = userOrders[0];
  console.log(`\nCancelling order ${orderToCancel.orderId}...`);

  // Get market for mint
  const marketAccount = await connection.getAccountInfo(marketPda);
  const market = parseMarket(marketAccount!.data);

  const userTokenAccount = await getAssociatedTokenAddress(market.settlementMint, wallet.publicKey);

  const cancelIx = createCancelOrderInstruction(
    {
      market: marketPda,
      orderbook: orderbookPda,
      position: positionPda,
      userTokenAccount,
      vault: vaultPda,
      user: wallet.publicKey,
      tokenProgram: TOKEN_PROGRAM_ID,
    },
    {
      orderId: orderToCancel.orderId,
    },
    programId
  );

  const tx = new Transaction().add(cancelIx);
  const signature = await sendAndConfirmTransaction(connection, tx, [wallet]);

  console.log('Order cancelled! Signature:', signature);
}

Example 5: View Position

async function viewPosition(marketId: bigint) {
  const [marketPda] = findMarketPda(marketId, programId);
  const [positionPda] = findPositionPda(marketPda, wallet.publicKey, programId);

  const [marketAccount, positionAccount] = await connection.getMultipleAccountsInfo([
    marketPda,
    positionPda,
  ]);

  if (!marketAccount) {
    console.log('Market not found');
    return;
  }

  if (!positionAccount) {
    console.log('No position in this market');
    return;
  }

  const market = parseMarket(marketAccount.data);
  const position = parsePosition(positionAccount.data);

  console.log('=== Your Position ===');
  console.log('YES Shares:', position.yesShares.toString());
  console.log('NO Shares:', position.noShares.toString());
  console.log('Locked YES:', position.lockedYesShares.toString());
  console.log('Locked NO:', position.lockedNoShares.toString());
  console.log('Collateral Locked:', position.collateralLocked.toString());
  console.log('Settled:', position.settled);
  console.log('Payout:', position.payout.toString());
  console.log('Active Orders:', position.orderCount);

  // Calculate value
  const [orderbookPda] = findOrderbookPda(marketPda, programId);
  const orderbookAccount = await connection.getAccountInfo(orderbookPda);

  if (orderbookAccount) {
    const orderbook = parseOrderbook(orderbookAccount.data);
    const midPrice = (orderbook.bestBidPrice + orderbook.bestAskPrice) / 2;

    const yesValue = (Number(position.yesShares) * midPrice) / 10000;
    const noValue = (Number(position.noShares) * (10000 - midPrice)) / 10000;

    console.log('\n=== Position Value ===');
    console.log('Mid Price:', midPrice, 'bps');
    console.log('YES Value:', yesValue.toFixed(2), 'USDC');
    console.log('NO Value:', noValue.toFixed(2), 'USDC');
    console.log('Total Value:', (yesValue + noValue).toFixed(2), 'USDC');
  }
}

Example 6: Settle Position After Resolution

async function settleMyPosition(marketId: bigint) {
  const [configPda] = findConfigPda(programId);
  const [marketPda] = findMarketPda(marketId, programId);
  const [positionPda] = findPositionPda(marketPda, wallet.publicKey, programId);
  const [vaultPda] = findVaultPda(marketPda, programId);

  // Check market is resolved
  const marketAccount = await connection.getAccountInfo(marketPda);
  if (!marketAccount) throw new Error('Market not found');
  const market = parseMarket(marketAccount.data);

  if (market.outcome === 0) {
    throw new Error('Market not yet resolved');
  }

  console.log('Outcome:', market.outcome === 1 ? 'UP' : 'DOWN');

  // Check position exists and not settled
  const positionAccount = await connection.getAccountInfo(positionPda);
  if (!positionAccount) throw new Error('No position found');
  const position = parsePosition(positionAccount.data);

  if (position.settled) {
    console.log('Position already settled');
    console.log('Payout was:', position.payout.toString());
    return;
  }

  // Calculate expected payout
  const expectedPayout = market.outcome === 1 ? position.yesShares : position.noShares;
  console.log('Expected payout:', expectedPayout.toString(), 'USDC');

  // Get accounts
  const configAccount = await connection.getAccountInfo(configPda);
  const config = parseConfig(configAccount!.data);

  const userTokenAccount = await getAssociatedTokenAddress(market.settlementMint, wallet.publicKey);

  // Build settle instruction
  const settleIx = createSettlePositionInstruction(
    {
      market: marketPda,
      position: positionPda,
      userTokenAccount,
      vault: vaultPda,
      user: wallet.publicKey,
      cranker: wallet.publicKey, // Self-settle
      config: configPda,
      treasury: config.treasury,
      tokenProgram: TOKEN_PROGRAM_ID,
    },
    programId
  );

  const tx = new Transaction().add(settleIx);
  const signature = await sendAndConfirmTransaction(connection, tx, [wallet]);

  console.log('Position settled! Signature:', signature);
}

Example 7: Monitor Market in Real-Time

async function monitorMarket(marketId: bigint) {
  const [marketPda] = findMarketPda(marketId, programId);
  const [orderbookPda] = findOrderbookPda(marketPda, programId);

  console.log('Monitoring market', marketId.toString());
  console.log('Press Ctrl+C to stop\n');

  // Subscribe to market changes
  const marketSubId = connection.onAccountChange(
    marketPda,
    (accountInfo) => {
      const market = parseMarket(accountInfo.data);
      console.log('[Market Update]');
      console.log('  Start Price:', market.startPrice.toString());
      console.log('  End Price:', market.endPrice.toString());
      console.log(
        '  Outcome:',
        market.outcome === 0 ? 'Pending' : market.outcome === 1 ? 'UP' : 'DOWN'
      );
    },
    'confirmed'
  );

  // Subscribe to orderbook changes
  const orderbookSubId = connection.onAccountChange(
    orderbookPda,
    (accountInfo) => {
      const orderbook = parseOrderbook(accountInfo.data);
      console.log('[Orderbook Update]');
      console.log('  Best Bid:', orderbook.bestBidPrice, 'bps');
      console.log('  Best Ask:', orderbook.bestAskPrice, 'bps');
      console.log('  Spread:', orderbook.bestAskPrice - orderbook.bestBidPrice, 'bps');
    },
    'confirmed'
  );

  // Cleanup on exit
  process.on('SIGINT', async () => {
    console.log('\nStopping monitor...');
    await connection.removeAccountChangeListener(marketSubId);
    await connection.removeAccountChangeListener(orderbookSubId);
    process.exit(0);
  });

  // Keep running
  await new Promise(() => {});
}

Example 8: Simple Trading Bot

async function simpleBot(marketId: bigint) {
  const [marketPda] = findMarketPda(marketId, programId);
  const [orderbookPda] = findOrderbookPda(marketPda, programId);

  // Configuration
  const MAX_POSITION = BigInt(500);
  const MIN_TIME_LEFT = 120; // 2 minutes

  console.log('Starting simple bot...');

  async function tick() {
    try {
      // Get market state
      const [marketAccount, orderbookAccount] = await connection.getMultipleAccountsInfo([
        marketPda,
        orderbookPda,
      ]);

      if (!marketAccount || !orderbookAccount) {
        console.log('Waiting for market...');
        return;
      }

      const market = parseMarket(marketAccount.data);
      const orderbook = parseOrderbook(orderbookAccount.data);

      // Check if trading
      if (market.startPrice === 0n) {
        console.log('Trading not started');
        return;
      }

      if (market.endPrice !== 0n) {
        console.log('Trading ended');
        return;
      }

      // Check time remaining
      const now = Math.floor(Date.now() / 1000);
      const timeLeft = Number(market.tEnd) - now;

      if (timeLeft < MIN_TIME_LEFT) {
        console.log(`Only ${timeLeft}s left, not trading`);
        return;
      }

      // Simple strategy: buy if spread is wide
      const spread = orderbook.bestAskPrice - orderbook.bestBidPrice;

      if (spread > 400) {
        console.log(`Spread ${spread} bps - opportunity!`);

        // Check position
        const [positionPda] = findPositionPda(marketPda, wallet.publicKey, programId);
        const positionAccount = await connection.getAccountInfo(positionPda);

        let currentYes = BigInt(0);
        if (positionAccount) {
          const position = parsePosition(positionAccount.data);
          currentYes = position.yesShares;
        }

        if (currentYes < MAX_POSITION) {
          console.log('Placing buy order...');
          // Would call buyYesShares here
        }
      }
    } catch (error) {
      console.error('Tick error:', error);
    }
  }

  // Run every 10 seconds
  setInterval(tick, 10000);
  tick();

  // Keep running
  await new Promise(() => {});
}

Next Steps