SDK Examples#
Complete copy-pasteable TypeScript programs for common Seesaw operations. Each
example assumes you have already worked through Building Transactions,
which covers the sendInstructions helper, keypair loading, and the Kit v6
signing flow used by every example below.
All examples use Solana Kit v6 (@solana/kit family). Import shapes match
the actual exports of @seesaw/core and @seesaw/trustless (verified from
packages/core/src/index.ts and packages/trustless/src/index.ts). There are
no @solana/web3.js references.
Additional runnable examples ship inside the packages themselves:
packages/core/examples/— numbered01–09: API client, WebSocket streams, instruction builders, referral/fees, order managementpackages/trustless/— seeREADME.mdfor theresolveAndTradescript
Shared setup#
Every example below imports sendInstructions, loadKeypair, RPC_URL, and
API_URL from a shared module. The full implementation of sendInstructions
(compile → simulate → sign → send) is documented in
Building Transactions.
Save it as shared-setup.ts in your project before running the examples.
The per-example import delta — what each example adds on top of the shared block — is shown at the top of each example's code snippet.
// shared-setup.ts — imports only (see Building Transactions for the full file)
import { loadKeypair, sendInstructions, RPC_URL, API_URL } from './shared-setup.js';
Trust-based examples#
Example T1 — View current markets and orderbook#
Reads market list and live orderbook through the Seesaw hosted API, then estimates slippage for a hypothetical 50 USDT BuyYes order.
import {
createSeesawClient,
aggregateOrderbook,
estimateFillFromLevels,
OrderSide,
OrderType,
} from '@seesaw/core';
import { TrustlessRpc, TrustlessResolver } from '@seesaw/trustless';
import { address } from '@solana/addresses';
import { API_URL, RPC_URL } from './shared-setup.js';
const client = createSeesawClient({ apiUrl: API_URL });
// List active markets via the hosted API
const markets = await client.api.markets.list({ limit: 10 });
console.log(`Found ${markets.length} markets`);
for (const m of markets.slice(0, 3)) {
console.log({
id: m.marketId,
start: new Date(Number(m.tStart) * 1000).toISOString(),
end: new Date(Number(m.tEnd) * 1000).toISOString(),
outcome: m.outcome,
});
}
// Read the fee curve from the hosted API
const feeConfig = await client.api.fees.getConfig();
// Subscribe to live orderbook updates (WebSocket)
const stop = client.streams.orderbook(markets[0].address, (update) => {
console.log('Orderbook update', update.data);
});
// Unsubscribe after 5 seconds in this demo
setTimeout(() => {
stop();
console.log('Unsubscribed');
}, 5_000);
// Slippage preview: buy 50 USDT of YES on the first market
const rpc = new TrustlessRpc({ url: RPC_URL });
const resolver = new TrustlessResolver(rpc);
const { value: orderbook } = await resolver.getOrderbook(address(markets[0].address));
const levels = aggregateOrderbook(orderbook);
const estimate = estimateFillFromLevels({
side: OrderSide.BuyYes,
quantity: 50_000_000n, // 50 USDT in 6-decimal base units
orderType: OrderType.Limit,
limitPriceBps: 7_000, // willing to pay up to 70%
book: levels,
fee: { capBps: feeConfig.feeCapBps, decayBps: feeConfig.decayRateBps },
});
console.log({
filled: estimate.filled.toString(),
remaining: estimate.remaining.toString(),
restsRemainder: estimate.restsRemainder,
avgPriceBps: estimate.avgPriceBps,
slippageBps: estimate.slippageBps,
totalCost: estimate.totalCost.toString(),
});
Example T2 — Buy YES shares (trust-based)#
Places a limit BuyYes order. PDA derivation is done manually here to show what accounts are required; use the trustless resolver to do this automatically (Example TL1).
import { address } from '@solana/addresses';
import {
createSeesawClient,
buildPlaceOrderIx,
deriveConfigPda,
deriveMarketPda,
deriveOrderbookPda,
deriveVaultPda,
derivePositionPda,
deriveYesMintPda,
deriveNoMintPda,
deriveYesEscrowPda,
deriveNoEscrowPda,
deriveTraderLedgerPda,
decodeMarketAccount,
OrderSide,
OrderType,
parseSeesawError,
hexToBytes,
getCurrentMarketId,
} from '@seesaw/core';
import { TOKEN_PROGRAM_ADDRESS, SYSTEM_PROGRAM_ADDRESS, deriveAta } from '@seesaw/trustless';
import { API_URL, RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
import { createSolanaRpc } from '@solana/rpc';
// ── Configuration ──────────────────────────────────────────────────────────
const PYTH_FEED_ID_HEX = process.env.PYTH_FEED_ID_HEX!; // 64 hex chars
const SETTLEMENT_MINT = address(process.env.SETTLEMENT_MINT!);
const CREATOR = address(process.env.MARKET_CREATOR!);
const DURATION = 900n;
const keyPair = await loadKeypair();
const user = address(
await (async () => {
const { getAddressFromPublicKey } = await import('@solana/addresses');
return getAddressFromPublicKey(keyPair.publicKey);
})()
);
const feedId = hexToBytes(PYTH_FEED_ID_HEX);
const marketId = BigInt(getCurrentMarketId(Number(DURATION)));
// ── Derive accounts ────────────────────────────────────────────────────────
const [configPda] = await deriveConfigPda();
const [marketPda] = await deriveMarketPda(feedId, DURATION, marketId, CREATOR);
const [orderbook] = await deriveOrderbookPda(marketPda);
const [vault] = await deriveVaultPda(marketPda);
const [userPosition] = await derivePositionPda(marketPda, user);
const [yesMint] = await deriveYesMintPda(marketPda);
const [noMint] = await deriveNoMintPda(marketPda);
const [yesEscrow] = await deriveYesEscrowPda(marketPda);
const [noEscrow] = await deriveNoEscrowPda(marketPda);
const [traderLedger] = await deriveTraderLedgerPda(marketPda);
const userSettlementAta = await deriveAta(user, SETTLEMENT_MINT);
const userYesAta = await deriveAta(user, yesMint);
const userNoAta = await deriveAta(user, noMint);
// Fetch treasury recipient from the API
const client = createSeesawClient({ apiUrl: API_URL });
const feeConfig = await client.api.fees.getConfig();
const protocolTreasuryIndex = 0;
const treasuryTokenAccount = address(feeConfig.treasuryRecipients[protocolTreasuryIndex]);
// ── Build + send ───────────────────────────────────────────────────────────
const ix = buildPlaceOrderIx(
{
side: OrderSide.BuyYes,
priceBps: 6_000, // 60% YES
quantity: 10_000_000n, // 10 USDT
orderType: OrderType.Limit,
protocolTreasuryIndex,
worstAcceptablePriceBps: 6_500, // IOC slippage tolerance (optional for Limit)
},
{
market: marketPda,
orderbook,
userPosition,
userTokenAccount: userSettlementAta,
vault,
user,
config: configPda,
treasuryTokenAccount,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
systemProgram: SYSTEM_PROGRAM_ADDRESS,
settlementMint: SETTLEMENT_MINT,
yesEscrow,
noEscrow,
userYesAta,
userNoAta,
yesMint,
noMint,
traderLedger,
}
);
try {
const sig = await sendInstructions([ix], keyPair, RPC_URL);
console.log('Order placed:', sig);
} catch (error) {
const parsed = parseSeesawError(error);
if (parsed) console.error(`${parsed.name}: ${parsed.message}`);
else throw error;
}
Example T3 — View and cancel your orders#
Fetches your open orders via the API, then cancels the first one.
import { address } from '@solana/addresses';
import {
createSeesawClient,
buildCancelOrderIx,
deriveOrderbookPda,
deriveVaultPda,
derivePositionPda,
deriveYesMintPda,
deriveNoMintPda,
deriveYesEscrowPda,
deriveNoEscrowPda,
deriveTraderLedgerPda,
} from '@seesaw/core';
import { TOKEN_PROGRAM_ADDRESS, deriveAta } from '@seesaw/trustless';
import { getAddressFromPublicKey } from '@solana/addresses';
import { API_URL, RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
const keyPair = await loadKeypair();
const user = address(await getAddressFromPublicKey(keyPair.publicKey));
const client = createSeesawClient({ apiUrl: API_URL });
const settlementMint = address(process.env.SETTLEMENT_MINT!);
// List open orders via the hosted API
const orders = await client.api.orders.list({ status: 'open' });
console.log(`You have ${orders.length} open orders`);
for (const o of orders) {
console.log(` Order ${o.orderId} @ ${o.priceBps} bps, qty ${o.quantity}, market ${o.market}`);
}
if (orders.length === 0) {
console.log('Nothing to cancel.');
process.exit(0);
}
// Cancel the first open order
const order = orders[0];
const marketAddress = address(order.market);
const [orderbook] = await deriveOrderbookPda(marketAddress);
const [vault] = await deriveVaultPda(marketAddress);
const [userPosition] = await derivePositionPda(marketAddress, user);
const [yesMint] = await deriveYesMintPda(marketAddress);
const [noMint] = await deriveNoMintPda(marketAddress);
const [yesEscrow] = await deriveYesEscrowPda(marketAddress);
const [noEscrow] = await deriveNoEscrowPda(marketAddress);
const [traderLedger] = await deriveTraderLedgerPda(marketAddress);
const userTokenAccount = await deriveAta(user, settlementMint);
const userYesAta = await deriveAta(user, yesMint);
const userNoAta = await deriveAta(user, noMint);
const ix = buildCancelOrderIx(
{ orderId: BigInt(order.orderId) },
{
market: marketAddress,
orderbook,
userPosition,
userTokenAccount,
vault,
user,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
settlementMint,
yesEscrow,
noEscrow,
userYesAta,
userNoAta,
yesMint,
noMint,
traderLedger,
}
);
const sig = await sendInstructions([ix], keyPair, RPC_URL);
console.log('Cancelled:', sig);
Example T4 — Manage positions and claim creator fees#
Reads positions across all markets, shows settled vs. unsettled, and claims any accumulated creator fees.
import { address } from '@solana/addresses';
import {
createSeesawClient,
buildClaimCreatorFeesIx,
deriveVaultPda,
getMarketState,
MarketState,
Outcome,
} from '@seesaw/core';
import { TOKEN_PROGRAM_ADDRESS, deriveAta } from '@seesaw/trustless';
import { getAddressFromPublicKey } from '@solana/addresses';
import { API_URL, RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
const keyPair = await loadKeypair();
const creator = address(await getAddressFromPublicKey(keyPair.publicKey));
const client = createSeesawClient({ apiUrl: API_URL });
const settlementMint = address(process.env.SETTLEMENT_MINT!);
// List positions for this wallet
const positions = await client.api.positions.list({ owner: creator.toString(), settled: false });
console.log(`Positions: ${positions.length}`);
for (const p of positions) {
const outcomeName = ['None', 'UP', 'DOWN', 'Expired'][p.outcome] ?? '?';
console.log({
market: p.market,
yesShares: p.yesShares,
noShares: p.noShares,
settled: p.settled,
payout: p.payout,
outcome: outcomeName,
});
}
// Claim creator fees for markets where this wallet is the creator.
// buildClaimCreatorFeesIx is a permissionless crank — anyone can trigger
// the claim, but payout is always sent to the market creator's token account
// (pinned to market.creator by the program).
const creatorMarkets = await client.api.markets.list({ creator: creator.toString() });
const creatorTokenAccount = await deriveAta(creator, settlementMint);
for (const m of creatorMarkets) {
if (!m.accruedCreatorFees || m.accruedCreatorFees === '0') continue;
const marketAddress = address(m.address);
const [vaultPda] = await deriveVaultPda(marketAddress);
// ClaimCreatorFees accounts: market, vault, creatorTokenAccount,
// settlementMint, caller (signer), tokenProgram
const ix = buildClaimCreatorFeesIx({
market: marketAddress,
vault: vaultPda,
creatorTokenAccount,
settlementMint,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
caller: creator,
});
const sig = await sendInstructions([ix], keyPair, RPC_URL);
console.log(`Creator fees claimed for market ${m.address}:`, sig);
}
Trustless examples#
Example TL1 — Discover markets and place an order (full trustless)#
No Seesaw API in the trust path. Every account is resolved and validated directly from chain state.
import { address } from '@solana/addresses';
import {
TrustlessRpc,
TrustlessResolver,
listMarkets,
TOKEN_PROGRAM_ADDRESS,
SYSTEM_PROGRAM_ADDRESS,
AccountNotFoundError,
AccountValidationError,
PreconditionError,
} from '@seesaw/trustless';
import {
buildPlaceOrderIx,
OrderSide,
OrderType,
parseSeesawError,
getMarketState,
MarketState,
aggregateOrderbook,
estimateFillFromLevels,
bpsToPercent,
} from '@seesaw/core';
import { getAddressFromPublicKey } from '@solana/addresses';
import { RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
// ── 1. Setup ───────────────────────────────────────────────────────────────
const rpc = new TrustlessRpc({ url: RPC_URL });
const resolver = new TrustlessResolver(rpc);
const keyPair = await loadKeypair();
const user = address(await getAddressFromPublicKey(keyPair.publicKey));
// ── 2. Discover markets from chain state (no indexer) ─────────────────────
const markets = await listMarkets(rpc);
console.log(`Discovered ${markets.length} markets on-chain`);
// Filter to markets currently in Trading state
const tradingMarkets = markets.filter(
({ market }) => getMarketState(market) === MarketState.Trading
);
if (tradingMarkets.length === 0) {
console.log('No markets currently in Trading state.');
process.exit(0);
}
const { address: marketAddress, market } = tradingMarkets[0];
console.log(
'Selected market:',
marketAddress,
'ends at',
new Date(Number(market.tEnd) * 1000).toISOString()
);
// ── 3. Slippage preview ────────────────────────────────────────────────────
const { value: orderbook } = await resolver.getOrderbook(marketAddress);
const levels = aggregateOrderbook(orderbook);
const preview = estimateFillFromLevels({
side: OrderSide.BuyYes,
quantity: 20_000_000n, // 20 USDT
orderType: OrderType.Limit,
limitPriceBps: 6_500,
book: levels,
});
console.log('Slippage preview:', {
filled: preview.filled.toString(),
avgPrice: bpsToPercent(preview.avgPriceBps ?? 0).toFixed(2) + '%',
slippageBps: preview.slippageBps,
restsRemainder: preview.restsRemainder,
});
// ── 4. Resolve every account PlaceOrder needs (validated, slot-anchored) ──
let r;
try {
r = await resolver.resolvePlaceOrder({
marketAddress,
user,
includeReferral: true,
});
} catch (error) {
if (error instanceof PreconditionError) {
console.error('Cannot place order —', error.kind, error.message);
process.exit(1);
}
throw error;
}
console.log('Resolved at slot', r.slot.toString());
console.log('Referral state:', r.referral.state);
// ── 5. Build the PlaceOrder instruction ───────────────────────────────────
const ix = buildPlaceOrderIx(
{
side: OrderSide.BuyYes,
priceBps: 6_500, // 65%
quantity: 20_000_000n, // 20 USDT
orderType: OrderType.Limit,
protocolTreasuryIndex: r.protocolTreasuryIndex,
worstAcceptablePriceBps: 7_000,
},
{
...r.accounts,
user,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
systemProgram: SYSTEM_PROGRAM_ADDRESS,
...(r.referral.state === 'active' && r.referral.accounts
? {
takerReferralAccount: r.referral.accounts.referralAccount,
referrerEarningsAccount: r.referral.accounts.referrerEarningsAccount,
referrerTreasury: r.referral.accounts.referrerTreasury,
}
: {}),
}
);
// ── 6. Prepend any missing-ATA creates, then simulate + sign + send ───────
const instructions = [...r.prependInstructions, ix];
try {
const sig = await sendInstructions(instructions, keyPair, RPC_URL);
console.log('Order placed:', sig);
} catch (error) {
const parsed = parseSeesawError(error);
if (parsed) console.error(`Program error — ${parsed.name}: ${parsed.message}`);
else throw error;
}
Example TL2 — Redeem winnings after resolution (full trustless)#
Discovers resolved markets where the user has an unsettled position, validates the market state on-chain, and redeems.
import { address } from '@solana/addresses';
import {
TrustlessRpc,
TrustlessResolver,
listMarkets,
listUserPositions,
TOKEN_PROGRAM_ADDRESS,
} from '@seesaw/trustless';
import {
buildRedeemIx,
TokenType,
getMarketState,
MarketState,
Outcome,
formatPrice,
} from '@seesaw/core';
import { getAddressFromPublicKey } from '@solana/addresses';
import { RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
// ── 1. Setup ───────────────────────────────────────────────────────────────
const rpc = new TrustlessRpc({ url: RPC_URL });
const resolver = new TrustlessResolver(rpc);
const keyPair = await loadKeypair();
const user = address(await getAddressFromPublicKey(keyPair.publicKey));
// ── 2. Discover all user positions from chain state ────────────────────────
const allPositions = await listUserPositions(rpc, user);
console.log(`Found ${allPositions.length} position accounts for ${user}`);
// ── 3. Iterate resolved markets with pending payouts ──────────────────────
for (const { address: positionAddress, position } of allPositions) {
if (position.settled) {
console.log('Already settled:', positionAddress);
continue;
}
// The position address encodes the market address in its seeds;
// use the resolver to fetch the market by re-deriving from the position.
// We need the market address separately — list it from the position's
// decoded market field:
const marketAddress = position.market as unknown as import('@solana/addresses').Address;
let market;
try {
({ value: market } = await resolver.getMarket(marketAddress));
} catch {
console.warn('Could not fetch market for position', positionAddress);
continue;
}
const state = getMarketState(market);
if (state !== MarketState.Resolved && state !== MarketState.Closed) {
console.log('Market not yet resolved, skipping:', marketAddress);
continue;
}
const outcomeNames = ['None', 'UP', 'DOWN', 'Expired'];
console.log(`Market ${marketAddress} resolved: ${outcomeNames[market.outcome]}`);
// Resolve the full trading account set (validate mints, vault, ATAs)
const { value: accts } = await resolver.resolveTradingAccounts({ marketAddress, user });
// Build a Redeem instruction for each token type the user holds
const redemptions: (typeof buildRedeemIx)[] = [];
if (position.yesShares > 0n) {
redemptions.push(
buildRedeemIx(
{ amount: position.yesShares, tokenType: TokenType.Yes },
{
market: marketAddress,
yesMint: accts.yesMint,
noMint: accts.noMint,
userYesAta: accts.userYesAta,
userNoAta: accts.userNoAta,
userStablecoinAta: accts.userTokenAccount,
vault: accts.vault,
user,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
settlementMint: accts.settlementMint,
userPosition: accts.userPosition,
orderbook: accts.orderbook,
traderLedger: accts.traderLedger,
}
)
);
}
if (position.noShares > 0n) {
redemptions.push(
buildRedeemIx(
{ amount: position.noShares, tokenType: TokenType.No },
{
market: marketAddress,
yesMint: accts.yesMint,
noMint: accts.noMint,
userYesAta: accts.userYesAta,
userNoAta: accts.userNoAta,
userStablecoinAta: accts.userTokenAccount,
vault: accts.vault,
user,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
settlementMint: accts.settlementMint,
// Pass position/orderbook/traderLedger to release locked collateral
userPosition: accts.userPosition,
orderbook: accts.orderbook,
traderLedger: accts.traderLedger,
}
)
);
}
if (redemptions.length === 0) {
// Position may still have locked bid collateral from resting orders.
// Passing the optional accounts to Redeem with amount=0 will release it.
console.log('No shares to redeem, but may have locked collateral — skipping.');
continue;
}
const sig = await sendInstructions(
[...accts.prependInstructions, ...redemptions],
keyPair,
RPC_URL
);
console.log('Redeemed position for market', marketAddress, ':', sig);
}
console.log('Done.');
Example TL3 — Cancel all open orders (trustless)#
Discovers open orders directly from the on-chain book, verifies each order id belongs to the user, and cancels them in a single transaction (up to the per-transaction account/size limits).
import { address } from '@solana/addresses';
import {
TrustlessRpc,
TrustlessResolver,
listMarkets,
TOKEN_PROGRAM_ADDRESS,
} from '@seesaw/trustless';
import { buildCancelMultipleOrdersByIdIx, getMarketState, MarketState } from '@seesaw/core';
import { getAddressFromPublicKey } from '@solana/addresses';
import { RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
const rpc = new TrustlessRpc({ url: RPC_URL });
const resolver = new TrustlessResolver(rpc);
const keyPair = await loadKeypair();
const user = address(await getAddressFromPublicKey(keyPair.publicKey));
const markets = await listMarkets(rpc);
const activeMarkets = markets.filter(
({ market }) => getMarketState(market) === MarketState.Trading
);
for (const { address: marketAddress } of activeMarkets) {
// Discover the user's open orders from the on-chain book (no indexer)
const { value: orders } = await resolver.findUserOrders(marketAddress, user);
if (orders.length === 0) continue;
console.log(`Market ${marketAddress}: found ${orders.length} open order(s)`);
const orderIds = orders.map((o) => o.orderId);
const { value: accts } = await resolver.resolveTradingAccounts({ marketAddress, user });
// CancelMultipleOrdersById cancels up to 6 orders in one instruction (on-chain limit)
const MAX_PER_IX = 6;
for (let i = 0; i < orderIds.length; i += MAX_PER_IX) {
const chunk = orderIds.slice(i, i + MAX_PER_IX);
const ix = buildCancelMultipleOrdersByIdIx(
{ orderIds: chunk },
{
market: marketAddress,
orderbook: accts.orderbook,
userPosition: accts.userPosition,
userTokenAccount: accts.userTokenAccount,
vault: accts.vault,
user,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
settlementMint: accts.settlementMint,
yesEscrow: accts.yesEscrow,
noEscrow: accts.noEscrow,
userYesAta: accts.userYesAta,
userNoAta: accts.userNoAta,
yesMint: accts.yesMint,
noMint: accts.noMint,
traderLedger: accts.traderLedger,
}
);
const sig = await sendInstructions([...accts.prependInstructions, ix], keyPair, RPC_URL);
console.log(`Cancelled ${chunk.length} order(s):`, sig);
}
}
console.log('Done.');
Example TL4 — Create a market (trustless, pull-oracle mode)#
Creates a 15-minute market using Pyth's pull-oracle mode (oracle_mode = 1).
The EnsureTraderLedgerSpace prelude is handled by buildCreateMarketBundle.
import { address } from '@solana/addresses';
import {
buildCreateMarketBundle,
deriveMarketPda,
deriveOrderbookPda,
deriveVaultPda,
deriveYesMintPda,
deriveNoMintPda,
deriveYesEscrowPda,
deriveNoEscrowPda,
deriveTraderLedgerPda,
deriveAssetPda,
deriveConfigPda,
CAPACITY_TIERS,
getCurrentMarketId,
hexToBytes,
} from '@seesaw/core';
import { SYSTEM_PROGRAM_ADDRESS, TOKEN_PROGRAM_ADDRESS } from '@seesaw/trustless';
import { getAddressFromPublicKey } from '@solana/addresses';
import { RPC_URL, loadKeypair, sendInstructions } from './shared-setup.js';
// Pull-oracle mode: pass the posted ephemeral PriceUpdateV2 account as the
// pythFeed account meta. After validation, the program stores the zero-address
// sentinel in market.pythFeed to mark the market as pull-mode.
const PYTH_FEED_ID_HEX = process.env.PYTH_FEED_ID_HEX!;
const SETTLEMENT_MINT = address(process.env.SETTLEMENT_MINT!);
const PRICE_UPDATE_V2 = address(process.env.PRICE_UPDATE_V2_ACCOUNT!); // ephemeral
const keyPair = await loadKeypair();
const creator = address(await getAddressFromPublicKey(keyPair.publicKey));
const feedId = hexToBytes(PYTH_FEED_ID_HEX);
const durationSeconds = 900n;
const marketId = BigInt(getCurrentMarketId(Number(durationSeconds)));
// ── Derive PDAs ─────────────────────────────────────────────────────────────
const [marketPda] = await deriveMarketPda(feedId, durationSeconds, marketId, creator);
const [orderbook] = await deriveOrderbookPda(marketPda);
const [vault] = await deriveVaultPda(marketPda);
const [yesMint] = await deriveYesMintPda(marketPda);
const [noMint] = await deriveNoMintPda(marketPda);
const [yesEscrow] = await deriveYesEscrowPda(marketPda);
const [noEscrow] = await deriveNoEscrowPda(marketPda);
const [traderLedger] = await deriveTraderLedgerPda(marketPda);
const [assetState] = await deriveAssetPda(feedId);
const [configPda] = await deriveConfigPda();
const tier = CAPACITY_TIERS.find((t) => t.id === 'standard')!;
// ── Build the bundle ─────────────────────────────────────────────────────────
const instructions = buildCreateMarketBundle(
{
maxConfidenceRatioBps: 500,
durationSeconds,
maxOracleJumpBps: 2_000,
marketSizeParams: {
bidsSize: 512,
asksSize: 512,
numSeats: tier.numSeats,
},
oracleMode: 'pull', // pull-oracle: feed id validated, no address binding
},
{
market: marketPda,
orderbook,
vault,
yesMint,
noMint,
assetState,
config: configPda,
pythFeed: PRICE_UPDATE_V2, // ephemeral PriceUpdateV2 for pull-mode CreateMarket
settlementMint: SETTLEMENT_MINT,
payer: creator,
systemProgram: SYSTEM_PROGRAM_ADDRESS,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
yesEscrow,
noEscrow,
traderLedger,
}
);
console.log(`Bundle: ${instructions.length - 1} × EnsureTraderLedgerSpace + 1 × CreateMarket`);
// For small/standard tiers the bundle fits in one transaction.
// For large/max tiers split the prelude across multiple transactions.
const PRELUDE_PER_TX = 10;
const prelude = instructions.slice(0, -1);
const createMarketIx = instructions[instructions.length - 1]!;
for (let i = 0; i < prelude.length; i += PRELUDE_PER_TX) {
await sendInstructions(prelude.slice(i, i + PRELUDE_PER_TX), keyPair, RPC_URL);
}
const sig = await sendInstructions([createMarketIx], keyPair, RPC_URL);
console.log('Market created:', marketPda, 'tx:', sig);
Reference: Notes and Common Gotchas#
| Issue | Explanation |
|---|---|
Simulation failed: TradingEnded | The market's t_end has passed. Check market.tEnd before placing. |
Simulation failed: InsufficientCollateral | Your settlement ATA balance is too low for the order's collateral requirement. |
Simulation failed: InsufficientShares | You do not have enough YES/NO shares to fill a sell order. |
Simulation failed: OrderPriceOutOfBand | Your limit-buy price is outside the current maker price band. Use IOC if you need immediate execution at any price. |
PreconditionError: trading-window | The market has expired before you built the transaction (stale preview). |
AccountValidationError: market | The market address failed the PDA cross-check — ensure the correct creator, feed id, and duration are used. |
NotFoundOnChainError: order … | The order id is not present on the on-chain book. It may already have been filled or cancelled. |
| Missing ATA creates | Always prepend r.prependInstructions / accts.prependInstructions — they are idempotent and no-ops if the accounts already exist. |
Locked collateral. Resting limit-buy collateral is NOT returned automatically. Cancel open orders before expiry, or call
Redeemafter resolution — it releases locked bid collateral alongside the share payout. The market becomes eligible forCloseMarketteardown 7 days afterresolved_at; remaining unclaimed balances sweep to the market creator. See the settlement timeline for the full teardown sequence.
Next Steps#
- Building Transactions — signing flow, priority fees, crank instructions
- Trustless SDK — trust model, safeguards, Python and Rust equivalents
- Reading Data — market state, orderbook decoding, slippage estimation
- Referral and Creator Fees — referral lock wizard, fee claiming