Skip to main content

Constants

Protocol constants and configuration values for the Seesaw protocol.

Overview

Time Constants

Market Duration

Market durations are configurable per-market, from 60 seconds to 7 days:

const MIN_DURATION_SECONDS = 60; // 1 minute
const MAX_DURATION_SECONDS = 604_800; // 7 days
const DEFAULT_DURATION_SECONDS = 900; // 15 minutes

Market Timing

ConstantValueDescription
MIN_DURATION60 secondsMinimum market duration
MAX_DURATION604,800 secondsMaximum market duration (7 days)
DEFAULT_DURATION900 secondsDefault market duration (15 min)
PRE_CREATE_WINDOW300 secondsMarkets can be created 5 min early
EXPIRY_WINDOW604800 seconds7 days until force close available
MAX_SNAPSHOT_DELAY3600 seconds1 hour grace period for snapshots

Calculating Market Timing

// Market ID from timestamp and duration
function getMarketId(timestamp: number, durationSeconds: number): bigint {
  return BigInt(Math.floor(timestamp / durationSeconds));
}

// Market boundaries
function getMarketTimes(
  marketId: bigint,
  durationSeconds: number
): { tStart: number; tEnd: number } {
  const tStart = Number(marketId) * durationSeconds;
  const tEnd = tStart + durationSeconds;
  return { tStart, tEnd };
}

// Current market ID (for a 15-minute market)
const now = Math.floor(Date.now() / 1000);
const currentMarketId = getMarketId(now, 900); // e.g., 1892160n

Price Constants

Basis Points

const PRICE_SCALE = 10_000; // 100% = 10,000 bps
const MIN_PRICE_BPS = 1; // 0.01%
const MAX_PRICE_BPS = 9_999; // 99.99%
const DEFAULT_TICK_SIZE_BPS = 100; // 1%

Price Calculations

ConstantValueDescription
PRICE_SCALE10,000100% in basis points
MIN_PRICE_BPS1Minimum valid price (0.01%)
MAX_PRICE_BPS9,999Maximum valid price (99.99%)
DEFAULT_TICK_SIZE_BPS100Default tick (1%)
MAX_TICK_SIZE_BPS1,000Maximum tick size (10%)

Converting Prices

// Basis points to percentage
function bpsToPercent(bps: number): number {
  return bps / 100; // 6000 bps = 60%
}

// Percentage to basis points
function percentToBps(percent: number): number {
  return percent * 100; // 60% = 6000 bps
}

// Basis points to decimal
function bpsToDecimal(bps: number): number {
  return bps / PRICE_SCALE; // 6000 bps = 0.60
}

// NO price from YES price
function noFromYes(yesPriceBps: number): number {
  return PRICE_SCALE - yesPriceBps; // 6000 -> 4000
}

Tick Rounding

const TICK_SIZE = 100; // 1%

// Round bid down
function roundBid(priceBps: number): number {
  return Math.floor(priceBps / TICK_SIZE) * TICK_SIZE;
}

// Round ask up
function roundAsk(priceBps: number): number {
  const remainder = priceBps % TICK_SIZE;
  return remainder === 0 ? priceBps : priceBps + (TICK_SIZE - remainder);
}

// Examples
roundBid(6050); // 6000
roundAsk(6050); // 6100

Order Book Limits

Capacity

const MAX_ORDERS_PER_SIDE = 64; // 64 bids + 64 asks
const MAX_FILLS_PER_TX = 10; // Compute budget limit
const ORDER_SIZE_BYTES = 48; // Per order storage
ConstantValueDescription
MAX_ORDERS_PER_SIDE64Maximum orders per side
MAX_FILLS_PER_TX10Maximum fills per transaction
MIN_ORDER_QUANTITY1Minimum order quantity
MAX_ORDER_QUANTITY2^64-1Maximum order quantity

Order ID

// Order IDs are monotonically increasing per orderbook
const ORDER_ID_BITS = 64;
const MAX_ORDER_ID = BigInt(2) ** BigInt(64) - BigInt(1);

Fee Constants

Trading Fees

const TAKER_FEE_BPS = 30; // 0.3%
const MAKER_REBATE_BPS = 10; // 0.1%
const NET_PROTOCOL_FEE_BPS = 20; // 0.2%
const MAX_TAKER_FEE_BPS = 500; // Maximum: 5%
ConstantValueDescription
TAKER_FEE_BPS300.3% taker fee
MAKER_REBATE_BPS100.1% maker rebate
NET_PROTOCOL_FEE20Net fee to protocol
MAX_TAKER_FEE_BPS500Maximum allowed taker fee

Fee Calculations

// Calculate taker fee (rounds UP)
function calculateTakerFee(amount: bigint, feeBps: number): bigint {
  const fee = amount * BigInt(feeBps);
  return (fee + BigInt(9999)) / BigInt(10000); // Ceiling division
}

// Calculate maker rebate (rounds DOWN)
function calculateMakerRebate(amount: bigint, rebateBps: number): bigint {
  return (amount * BigInt(rebateBps)) / BigInt(10000); // Floor division
}

// Example: 1000 USDC trade
const tradeAmount = 1000_000_000n; // 1000 USDC (6 decimals)
const takerFee = calculateTakerFee(tradeAmount, TAKER_FEE_BPS); // 3_000_000
const makerRebate = calculateMakerRebate(tradeAmount, MAKER_REBATE_BPS); // 1_000_000

Crank Rewards

const CRANK_REWARD_LAMPORTS = 1_000_000; // 0.001 SOL
const CRANK_REWARD_SOL = 0.001;
OperationReward
create_market0.001 SOL
snapshot_start0.001 SOL
snapshot_end0.001 SOL
resolve_market0.001 SOL
settle_position0.001 SOL
close_market0.001 SOL

Account Sizes

Storage Requirements

const MARKET_ACCOUNT_SIZE = 256;
const ORDERBOOK_ACCOUNT_SIZE = 10240;
const POSITION_ACCOUNT_SIZE = 192;
const CONFIG_ACCOUNT_SIZE = 128;
AccountSize (bytes)Description
MarketAccount256Market state
OrderbookAccount8,192Order storage (64+64 orders)
PositionAccount192User position
ConfigAccount128Protocol config

Rent Costs

// Approximate rent-exempt minimums (varies by Solana runtime)
const MARKET_RENT_LAMPORTS = 2_600_000; // ~0.0026 SOL
const ORDERBOOK_RENT_LAMPORTS = 57_000_000; // ~0.057 SOL
const POSITION_RENT_LAMPORTS = 1_900_000; // ~0.0019 SOL

PDA Seeds

Seed Constants

const SEED_PREFIX = 'seesaw';
const SEED_CONFIG = 'config';
const SEED_MARKET = 'market';
const SEED_ORDERBOOK = 'orderbook';
const SEED_VAULT = 'vault';
const SEED_POSITION = 'position';

PDA Derivation

import { PublicKey } from '@solana/web3.js';

// Config PDA
function findConfigPda(programId: PublicKey): [PublicKey, number] {
  return PublicKey.findProgramAddressSync(
    [Buffer.from(SEED_PREFIX), Buffer.from(SEED_CONFIG)],
    programId
  );
}

// Market PDA (includes feed, duration, market ID, and creator)
function findMarketPda(
  pythFeedId: Uint8Array, // 32-byte feed ID
  durationSeconds: bigint,
  marketId: bigint,
  creatorPubkey: PublicKey,
  programId: PublicKey
): [PublicKey, number] {
  const durationBuffer = Buffer.alloc(8);
  durationBuffer.writeBigUInt64LE(durationSeconds);
  const marketIdBuffer = Buffer.alloc(8);
  marketIdBuffer.writeBigUInt64LE(marketId);

  return PublicKey.findProgramAddressSync(
    [
      Buffer.from(SEED_PREFIX),
      Buffer.from(SEED_MARKET),
      pythFeedId,
      durationBuffer,
      marketIdBuffer,
      creatorPubkey.toBuffer(),
    ],
    programId
  );
}

// Orderbook PDA
function findOrderbookPda(marketPda: PublicKey, programId: PublicKey): [PublicKey, number] {
  return PublicKey.findProgramAddressSync(
    [Buffer.from(SEED_PREFIX), Buffer.from(SEED_ORDERBOOK), marketPda.toBuffer()],
    programId
  );
}

// Vault PDA
function findVaultPda(marketPda: PublicKey, programId: PublicKey): [PublicKey, number] {
  return PublicKey.findProgramAddressSync(
    [Buffer.from(SEED_PREFIX), Buffer.from(SEED_VAULT), marketPda.toBuffer()],
    programId
  );
}

// Position PDA
function findPositionPda(
  marketPda: PublicKey,
  userPubkey: PublicKey,
  programId: PublicKey
): [PublicKey, number] {
  return PublicKey.findProgramAddressSync(
    [
      Buffer.from(SEED_PREFIX),
      Buffer.from(SEED_POSITION),
      marketPda.toBuffer(),
      userPubkey.toBuffer(),
    ],
    programId
  );
}

Instruction Discriminators

Discriminator Values

const DISCRIMINATORS = {
  INITIALIZE_CONFIG: 0x00,
  CREATE_MARKET: 0x01,
  PLACE_ORDER: 0x04,
  RESOLVE_MARKET: 0x06,
  SETTLE_POSITION: 0x07,
  CLOSE_MARKET: 0x08,
  FORCE_CLOSE: 0x09,
  EXPIRE_MARKET: 0x0c,
} as const;
InstructionDiscriminator
initialize_config0x00
create_market0x01
place_order0x04
resolve_market0x06
settle_position0x07
close_market0x08
force_close0x09
expire_market0x0C

Oracle Constants

Pyth Configuration

// Pyth program ID (mainnet)
const PYTH_PROGRAM_ID = new PublicKey('FsJ3A3u2vn5cTVofAjvy6y5kwABJAqYWpe4975bi2epH');

// Common feed IDs
const PYTH_FEEDS = {
  BTC_USD: 'e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
  ETH_USD: 'ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
  SOL_USD: 'ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d',
};

// Confidence ratio (optional gating)
const MAX_CONFIDENCE_RATIO = 100; // 1% = conf/price <= 0.01

Compute Budget

CU Estimates

const COMPUTE_ESTIMATES = {
  PLACE_ORDER_NO_MATCH: 15_000,
  PLACE_ORDER_ONE_FILL: 25_000,
  PLACE_ORDER_MAX_FILLS: 60_000,
  CANCEL_ORDER: 10_000,
  SETTLE_POSITION: 20_000,
  RESOLVE_MARKET: 30_000,
};

// Recommended CU limits
const RECOMMENDED_CU = {
  SIMPLE_OPERATION: 100_000,
  COMPLEX_OPERATION: 200_000,
  MAX_FILLS_OPERATION: 400_000,
};

Network Constants

RPC Endpoints

// Public endpoints
const RPC_ENDPOINTS = {
  MAINNET: 'https://api.mainnet-beta.solana.com',
  DEVNET: 'https://api.devnet.solana.com',
};

// WebSocket endpoints
const WS_ENDPOINTS = {
  MAINNET: 'wss://api.mainnet-beta.solana.com',
  DEVNET: 'wss://api.devnet.solana.com',
};

Quick Reference

TIME                          PRICES
────────────────────────      ────────────────────────
DEFAULT_DURATION:   900s      PRICE_SCALE:      10,000
PRE_CREATE:         300s      MIN_PRICE:        1 bps
EXPIRY:             604,800s  MAX_PRICE:        9,999 bps
                              DEFAULT_TICK:     100 bps

LIMITS                        FEES
────────────────────────      ────────────────────────
MAX_ORDERS/SIDE:    64        TAKER_FEE:        30 bps
MAX_FILLS/TX:       10        MAKER_REBATE:     10 bps
                              CRANK_REWARD:     0.001 SOL

ACCOUNTS                      PDA SEEDS
────────────────────────      ────────────────────────
Market:             256B      Config:   [seesaw, config]
Orderbook:          8,192B    Market:   [seesaw, market, feed, dur, id, creator]
Position:           192B      Orderbook:[seesaw, orderbook, mkt]
Config:             128B      Position: [seesaw, position, mkt, user]

Next Steps