Skip to main content

Getting Started

This guide takes you from an empty project to a running OvercastApp that can read the market, move funds in and out of your escrow account, and talk to the RFQ channel.

Prerequisites​

  • Node.js ≥ 20 and a package manager (npm, pnpm, or yarn).
  • A Solana wallet keypair (e.g. a standard id.json from solana-keygen).
  • An Overcast API key — the backend rejects unauthenticated calls, so every app needs one, including read-only ones. See Getting an API key below.
  • Access to a Solana RPC endpoint.

Getting an API key​

Access is granted per app, so the first key is issued by us:

  1. Request a login. Reach out to the Overcast team (info@overcast.xyz) and ask for access. We create your login for the Overcast web app.

  2. Sign in to the web app following the steps you'll receive.

  3. Create an API key under Profile → API keys.

  4. Store it as an environment variable so it never lands in your source:

    export OVERCAST_API_KEY=<your-api-key>

Install​

The packages are published to npm. Add core and the Solana client to your project:

npm install @overcast-xyz/core @overcast-xyz/solana-client

Or with your package manager of choice:

pnpm add @overcast-xyz/core @overcast-xyz/solana-client
yarn add @overcast-xyz/core @overcast-xyz/solana-client

Create an OvercastApp​

OvercastApp is the single entry point to the SDK. You hand it a chain-specific factory — SolanaFactory — and get back one object that carries every capability:

import { OvercastApp } from "@overcast-xyz/core";
import { solana } from "@overcast-xyz/solana-client";

const app = await OvercastApp.create({
factory: solana.withConfig({
rpcUrl: process.env.SOLANA_RPC_URL, // Solana RPC
privateKey: process.env.SOLANA_PRIVATE_KEY, // JSON byte array string, or number[]
apiKey: process.env.OVERCAST_API_KEY, // authenticates the backend calls
}),
});

console.log("wallet:", app.owner);

apiKey is required: it is sent as a bearer token on every REST read and as the auth token on the RFQ socket handshake. Without it the backend answers 401 and the socket refuses to connect — so app.api and app.rfq are dead even in read-only mode.

Make your first calls​

Read the market​

// Curated, off-chain views from the backend (paginated — except listAssets):
const optionsPage = await app.api.listOptions({});
const options = optionsPage.items;

// Curated asset metadata for this chain, resolved for slot lookups:
const assets = await app.assets();

// Direct on-chain reads (balance takes a curated Asset, not a mint string):
const balance = await app.chain.balance(app.owner, assets.list()[0]);

Fund your escrow account​

All protocol payments (premiums, collateral, settlement) flow through your on-chain escrow account, not your wallet directly. Deposit before you trade:

const usdc = (await app.assets()).bySymbol("USDC"); // Asset | undefined — handle undefined!
const cashSlot = (await app.assets()).slot(usdc!); // canonical Bytes32 slot
const receipts = await app.chain.deposit({
depositor: app.owner,
token: cashSlot, // canonical Bytes32 slot, not a mint string
amount: 1_000_000_000n, // native units, as bigint
});
console.log("deposited, tx", receipts[0].hash); // Array<TxInfo>, TxInfo = { hash, confirmed, description? }

Withdrawing is symmetric:

await app.chain.withdraw({
withdrawer: app.owner,
token: cashSlot,
amount: 500_000_000n,
});

Connect to the RFQ channel​

The RFQ socket is lazy — it only connects the first time you touch app.rfq (and requires a signer):

const rfq = app.rfq;
await rfq.registerRole("MAKER"); // or "TAKER"

rfq.onNewQuote = (quote) => console.log("quote received:", quote.quoteId);

const { rfqId } = await rfq.createRfq({
creator: app.owner,
request: { details }, // partial OptionDetails — everything except the premium
side: "MAKER",
});

What details looks like, who quotes whom, and how a quote becomes an on-chain option is the subject of the next two guides:

Amounts, IDs, and other conventions​

A few conventions apply across the whole SDK:

  • Amounts are bigint in native units (the asset's smallest unit) in all canonical types. On the wire (socket + REST) they travel as decimal strings; the SDK converts for you. Helpers (import from @overcast-xyz/core): parseUnits(value: string, decimals: number), formatUnits(value: bigint, decimals: number), toNativeAmount(display: string, asset: Asset), toDisplayAmount(native: bigint, asset: Asset).
  • IDs are hex strings (Bytes32, 0x-prefixed lowercase hex). Offers and options are content-addressed: their ID is the sha256 of a canonical byte encoding, computed by getId (import { getId } from "@overcast-xyz/core"; const id = await getId(offer); — async) and byte-for-byte identical to the on-chain program's hashing.
  • Two signers exist. The chain signer signs Solana/EVM transactions; the protocol signer (Ed25519ProtocolSigner) signs offer IDs for the RFQ flow. Both are derived from privateKey unless you override them.