Account Layouts
Detailed specification of all on-chain accounts in the Seesaw protocol.
Account Overview
ConfigAccount
Protocol-wide configuration. Singleton per program.
Layout
| Offset | Field | Type | Size | Description |
|---|
| 0 | discriminator | [u8; 8] | 8 | Account type identifier |
| 8 | authority | Pubkey | 32 | Protocol admin |
| 40 | treasury | Pubkey | 32 | Fee recipient |
| 72 | default_settlement_mint | Pubkey | 32 | Default USDC mint |
| 104 | taker_fee_bps | u16 | 2 | Taker fee (default: 30) |
| 106 | maker_rebate_bps | u16 | 2 | Maker rebate (default: 10) |
| 108 | crank_reward_lamports | u64 | 8 | SOL per crank op |
| 116 | tick_size_bps | u16 | 2 | Price tick (default: 100) |
| 118 | max_orders_per_user | u16 | 2 | Order limit per user |
| 120 | max_order_size | u64 | 8 | Max shares per order |
| 128 | markets_created | u64 | 8 | Total markets created |
| 136 | total_volume | u128 | 16 | Total trading volume |
| 152 | total_fees_collected | u128 | 16 | Total fees collected |
| 168 | version | u8 | 1 | Config version |
| 169 | bump | u8 | 1 | PDA bump seed |
| 170 | _reserved | [u8; 86] | 86 | Future use |
| Total | | | 256 | |
Derivation
let (config_pda, bump) = Pubkey::find_program_address(
&[b"seesaw", b"config"],
&program_id,
);
MarketAccount
Individual market state for a single epoch (configurable duration: 60s–7 days).
Layout
| Offset | Field | Type | Size | Description |
|---|
| 0 | discriminator | [u8; 8] | 8 | Account type identifier |
| 8 | market_id | u64 | 8 | Epoch ID = floor(timestamp/900) |
| 16 | pyth_feed | Pubkey | 32 | Oracle price feed |
| 48 | settlement_mint | Pubkey | 32 | USDC mint |
| 80 | t_start | i64 | 8 | Epoch start time |
| 88 | t_end | i64 | 8 | Epoch end time |
| 96 | created_at | i64 | 8 | Creation timestamp |
| 104 | resolved_at | i64 | 8 | Resolution timestamp (0 if pending) |
| 112 | start_price | i64 | 8 | Opening price (0 if not captured) |
| 120 | start_price_conf | u64 | 8 | Start price confidence |
| 128 | start_price_expo | i32 | 4 | Start price exponent |
| 132 | start_price_timestamp | i64 | 8 | Start snapshot time |
| 140 | end_price | i64 | 8 | Closing price (0 if not captured) |
| 148 | end_price_conf | u64 | 8 | End price confidence |
| 156 | end_price_expo | i32 | 4 | End price exponent |
| 160 | end_price_timestamp | i64 | 8 | End snapshot time |
| 168 | outcome | u8 | 1 | 0=None, 1=Up, 2=Down |
| 169 | total_yes_shares | u64 | 8 | Total YES in circulation |
| 177 | total_no_shares | u64 | 8 | Total NO in circulation |
| 185 | total_collateral | u64 | 8 | Vault balance |
| 193 | total_positions | u32 | 4 | Position count |
| 197 | settled_positions | u32 | 4 | Settled count |
| 201 | total_volume | u64 | 8 | Trading volume |
| 209 | total_trades | u32 | 4 | Trade count |
| 213 | max_confidence_ratio_bps | u16 | 2 | Confidence gating (0=disabled) |
| 215 | bump | u8 | 1 | Market PDA bump |
| 216 | orderbook_bump | u8 | 1 | Orderbook PDA bump |
| 217 | vault_bump | u8 | 1 | Vault PDA bump |
| 218 | _reserved | [u8; 294] | 294 | Future use |
| Total | | | 512 | |
Derivation
let (market_pda, bump) = Pubkey::find_program_address(
&[b"seesaw", b"market", &market_id.to_le_bytes()],
&program_id,
);
State Encoding
| outcome | State |
|---|
| 0 | Not resolved |
| 1 | UP (end >= start) |
| 2 | DOWN (end < start) |
OrderbookAccount
Order book for a market with bids and asks.
Layout
| Offset | Field | Type | Size | Description |
|---|
| 0 | discriminator | [u8; 8] | 8 | Account type identifier |
| 8 | market | Pubkey | 32 | Parent market |
| 40 | bid_count | u16 | 2 | Active bids |
| 42 | ask_count | u16 | 2 | Active asks |
| 44 | next_order_id | u64 | 8 | Next order ID |
| 52 | best_bid_price | u16 | 2 | Best bid (0 if empty) |
| 54 | best_ask_price | u16 | 2 | Best ask (10000 if empty) |
| 56 | bump | u8 | 1 | PDA bump |
| 57 | _header_reserved | [u8; 7] | 7 | Reserved |
| 64 | bids | [Order; 63] | 5040 | Bid orders |
| 5104 | asks | [Order; 63] | 5040 | Ask orders |
| 10144 | _padding | [u8; 96] | 96 | Alignment padding |
| Total | | | 10,240 | |
Order Structure (80 bytes)
| Offset | Field | Type | Size | Description |
|---|
| 0 | order_id | u64 | 8 | Unique ID |
| 8 | owner | Pubkey | 32 | Order owner |
| 40 | price_bps | u16 | 2 | Canonical price |
| 42 | quantity | u64 | 8 | Remaining quantity |
| 50 | original_quantity | u64 | 8 | Original size |
| 58 | timestamp | i64 | 8 | Placement time |
| 66 | original_side | u8 | 1 | User's original side |
| 67 | is_active | bool | 1 | Active flag |
| 68 | _reserved | [u8; 12] | 12 | Reserved |
Side Encoding
| Value | Original Side | Canonical Side |
|---|
| 0 | BuyYes | Bid |
| 1 | SellYes | Ask |
| 2 | BuyNo | Ask (converted) |
| 3 | SellNo | Bid (converted) |
Derivation
let (orderbook_pda, bump) = Pubkey::find_program_address(
&[b"seesaw", b"orderbook", market_pda.as_ref()],
&program_id,
);
VaultAccount
SPL Token account holding market collateral.
Layout
Standard SPL Token account (165 bytes):
| Offset | Field | Type | Size | Description |
|---|
| 0 | mint | Pubkey | 32 | Token mint |
| 32 | owner | Pubkey | 32 | Owner (market PDA) |
| 64 | amount | u64 | 8 | Token balance |
| 72 | delegate | Option<Pubkey> | 36 | Delegate |
| 108 | state | AccountState | 1 | Account state |
| 109 | is_native | Option<u64> | 12 | Native flag |
| 121 | delegated_amount | u64 | 8 | Delegated |
| 129 | close_authority | Option<Pubkey> | 36 | Close auth |
| Total | | | 165 | |
Derivation
let (vault_pda, bump) = Pubkey::find_program_address(
&[b"seesaw", b"vault", market_pda.as_ref()],
&program_id,
);
UserPositionAccount
User's position in a specific market.
Layout
| Offset | Field | Type | Size | Description |
|---|
| 0 | discriminator | [u8; 8] | 8 | Account type identifier |
| 8 | market | Pubkey | 32 | Parent market |
| 40 | owner | Pubkey | 32 | Position owner |
| 72 | yes_shares | u64 | 8 | YES shares owned |
| 80 | no_shares | u64 | 8 | NO shares owned |
| 88 | locked_yes_shares | u64 | 8 | Locked in sell orders |
| 96 | locked_no_shares | u64 | 8 | Locked in sell orders |
| 104 | collateral_deposited | u64 | 8 | Total deposited |
| 112 | collateral_locked | u64 | 8 | Locked in buy orders |
| 120 | settled | bool | 1 | Settlement flag |
| 121 | payout | u64 | 8 | Settlement payout |
| 129 | total_bought | u64 | 8 | Shares bought |
| 137 | total_sold | u64 | 8 | Shares sold |
| 145 | total_fees_paid | u64 | 8 | Taker fees |
| 153 | total_rebates_earned | u64 | 8 | Maker rebates |
| 161 | order_count | u16 | 2 | Active orders |
| 163 | first_trade_at | i64 | 8 | First trade time |
| 171 | last_trade_at | i64 | 8 | Last trade time |
| 179 | bump | u8 | 1 | PDA bump |
| 180 | _reserved | [u8; 76] | 76 | Future use |
| Total | | | 256 | |
Derivation
let (position_pda, bump) = Pubkey::find_program_address(
&[
b"seesaw",
b"position",
market_pda.as_ref(),
user_pubkey.as_ref(),
],
&program_id,
);
Discriminators
Each account type has a unique 8-byte discriminator:
fn discriminator(name: &str) -> [u8; 8] {
let hash = sha256(format!("account:{}", name));
hash[..8]
}
| Account | Discriminator Input |
|---|
| ConfigAccount | account:ConfigAccount |
| MarketAccount | account:MarketAccount |
| OrderbookAccount | account:OrderbookAccount |
| UserPositionAccount | account:UserPositionAccount |
Rent Requirements
| Account | Size | Rent-Exempt (~) |
|---|
| ConfigAccount | 256 bytes | 0.00189 SOL |
| MarketAccount | 512 bytes | 0.00356 SOL |
| OrderbookAccount | 10,304 bytes | 0.07164 SOL |
| VaultAccount | 165 bytes | 0.00203 SOL |
| UserPositionAccount | 256 bytes | 0.00189 SOL |
Account Relationships
Next Steps
- See Instructions for instruction details
- Review PDAs for derivation patterns