Quickstart: Your First Seesaw Integration#
This tutorial walks you through installing the SDK, connecting to a Solana RPC node, listing live markets, and reading the order book to estimate a fill — all without placing an order. By the end you will have a working script that talks to your chosen Solana cluster and a clear map to the next steps.
Time: ~15 minutes.
Step 1 — Install the package#
The trustless package works against any standard Solana RPC node; no Seesaw API key is needed for the steps in this tutorial.
npm install @seesaw/trustless @seesaw/core
yarn add / pnpm add work identically. See
Installation for the full dependency table and
TypeScript configuration (module: "node16", target: "ES2020").
Step 2 — Create the RPC client#
// docs-check: semantic
import { TrustlessRpc } from '@seesaw/trustless';
const rpc = new TrustlessRpc({ url: process.env.SOLANA_RPC_URL! });
Set SOLANA_RPC_URL in your environment (e.g., in a .env file):
SOLANA_RPC_URL=https://your-rpc-provider.example
The TrustlessRpc client uses plain fetch — no additional RPC SDK required.
Devnet/local-validator on-ramp#
No public Seesaw devnet deployment is assumed. For a first writable integration, use a local validator or a devnet program you deployed yourself, then set the same variables the examples read:
solana config set --url https://api.devnet.solana.com
solana-keygen new --outfile ~/.config/solana/seesaw-devnet.json
solana airdrop 2 ~/.config/solana/seesaw-devnet.json --url https://api.devnet.solana.com
# Create a disposable 6-decimal settlement mint for test markets.
spl-token create-token --decimals 6 --url https://api.devnet.solana.com \
--fee-payer ~/.config/solana/seesaw-devnet.json
export SOLANA_RPC_URL=https://api.devnet.solana.com
export SEESAW_PROGRAM_ADDRESS=<deployed-seesaw-program-id>
export SEESAW_WALLET_ADDRESS=<your-devnet-wallet-address>
export SEESAW_SETTLEMENT_MINT=<mint-from-spl-token-create-token>
export SEESAW_PYTH_FEED_ID_HEX=ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d
export SEESAW_PYTH_FEED_ADDRESS=<receiver-owned-price-update-v2-or-push-feed-account>
For local validator work, replace SOLANA_RPC_URL with
http://127.0.0.1:8899, deploy the program locally, and use the deployed
program id for SEESAW_PROGRAM_ADDRESS. If you use Pull oracle mode, your app
or test harness must supply a current Receiver-owned PriceUpdateV2 account for
the create snapshot; never embed PYTH_API_KEY in browser or mobile clients.
Step 3 — List live markets#
This step verifies the installation: it calls getProgramAccounts on the
Seesaw program and decodes every MarketAccount directly from chain state.
// docs-check: semantic
import { TrustlessRpc, listMarkets } from '@seesaw/trustless';
import { address } from '@solana/addresses';
const rpc = new TrustlessRpc({ url: process.env.SOLANA_RPC_URL! });
const programAddress = address(process.env.SEESAW_PROGRAM_ADDRESS!);
const { value: markets } = await listMarkets(rpc, programAddress);
console.log(`Found ${markets.length} Seesaw markets`);
for (const { address, market } of markets.slice(0, 5)) {
console.log(address, 'marketId:', market.marketId.toString());
}
console.log('SDK installed successfully!');
If you see Found N Seesaw markets and a list of addresses, your SDK is
working. If you see a rate-limit error, switch to a dedicated RPC provider —
public endpoints throttle getProgramAccounts aggressively.
Step 4 — Read the order book and estimate a fill#
Pick the first market from the previous step and estimate what a buy-NO limit order at 4200 bps would fill for:
// docs-check: semantic
import { TrustlessRpc, TrustlessResolver, listMarkets } from '@seesaw/trustless';
import { aggregateOrderbook, estimateFillFromLevels, OrderSide, OrderType } from '@seesaw/core';
import { address } from '@solana/addresses';
const rpc = new TrustlessRpc({ url: process.env.SOLANA_RPC_URL! });
const programAddress = address(process.env.SEESAW_PROGRAM_ADDRESS!);
const resolver = new TrustlessResolver(rpc, programAddress);
const { value: markets } = await listMarkets(rpc, programAddress);
const marketAddress = markets[0].address;
// Fetch and aggregate the order book.
const { value: orderbook } = await resolver.getOrderbook(marketAddress);
const book = aggregateOrderbook(orderbook);
// Fetch the protocol fee configuration.
const {
value: { config },
} = await resolver.getConfig();
// Estimate the fill.
const estimate = estimateFillFromLevels({
side: OrderSide.BuyNo,
quantity: 50_000_000n,
orderType: OrderType.Limit,
limitPriceBps: 4_200,
book,
fee: { capBps: config.feeCapBps, decayBps: config.decayRateBps },
});
console.log('Fill estimate:', estimate);
estimateFillFromLevels returns the expected fill quantity, average price,
and estimated fee — all computed locally from the decoded chain state, no
server involved.
Next step — build a create-market transaction#
Market creation uses the full @seesaw/core transaction builder. The oracle
mode is explicit:
// docs-check: semantic
import { createMarket, hexToBytes, resolveMarketIdFromClock } from '@seesaw/core';
import { type Address } from '@solana/addresses';
const durationSeconds = 900n;
const marketId = await resolveMarketIdFromClock(fetchAccountData, durationSeconds);
const feedIdBytes = hexToBytes(process.env.SEESAW_PYTH_FEED_ID_HEX!);
const pythAccountAddress = process.env.SEESAW_PYTH_FEED_ADDRESS! as Address;
const settlementMint = process.env.SEESAW_SETTLEMENT_MINT! as Address;
const walletAddress = process.env.SEESAW_WALLET_ADDRESS! as Address;
async function fetchAccountData(account: Address): Promise<Uint8Array | null> {
// Read `account` from your Solana RPC with base64 encoding and return bytes.
// The resolver passes the Clock sysvar here so market IDs never use wall-clock time.
throw new Error('wire this to your RPC client');
}
const { instructions, accounts } = await createMarket({
marketId,
pythFeedId: feedIdBytes, // 32-byte Pyth feed id, not a Solana account
pythFeed: pythAccountAddress,
settlementMint,
payer: walletAddress,
creator: walletAddress,
durationSeconds,
maxConfidenceRatioBps: 500,
maxOracleJumpBps: 0,
oracleMode: 'pull', // launch default; use 'push' only for legacy-compatible feed accounts
marketSizeParams: { bidsSize: 512, asksSize: 512, numSeats: 128 }, // bids/asks select a symmetric deep-orderbook tier
});
For Pull, pythFeed is the Receiver-owned PriceUpdateV2 account supplied for
the create snapshot; later lifecycle calls validate Pull updates by
pythFeedId instead of by a fixed feed-account address. For legacy-compatible
Push creation, pythFeed is the Receiver-owned push-feed account address.
Pyth Core's upgraded Hermes endpoint requires a bearer key. Keep
PYTH_API_KEY on server-side relay/proxy infrastructure only; never embed it in
browser or mobile SDK integrations.
Where next?#
| Goal | Read |
|---|---|
| Understand trust-based vs. trustless and pick your SDK | SDK Guide |
| Install all packages and set up TypeScript | Installation |
| Place, cancel, and redeem orders | Building Transactions |
| Decode PDAs, positions, and streams | Reading Data |
| Copy-pasteable end-to-end programs | Examples |
| Validate every account from any RPC without trusting Seesaw | Trustless SDK |
| Python or Rust | Python SDK · Rust SDK |