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.jsonfromsolana-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:
-
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.
-
Sign in to the web app following the steps you'll receive.
-
Create an API key under Profile → API keys.
-
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:
- The RFQ lifecycle — the concepts.
- Build an RFQ quoter — the full quoting loop.
Amounts, IDs, and other conventions
A few conventions apply across the whole SDK:
- Amounts are
bigintin 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 bygetId(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 fromprivateKeyunless you override them.