Using OvercastApp
OvercastApp is the facade that composes the whole SDK. You construct it once
from a chain-specific ProtocolFactory and use its surfaces everywhere; no
other wiring is required.
import { OvercastApp } from "@overcast/core";
import { SolanaFactory } from "@overcast/solana-client";
const factory = new SolanaFactory({
rpcUrl: "https://api.devnet.solana.com",
backend: "https://backend.example.com",
privateKey: process.env.OVERCAST_KEYPAIR,
});
const app = await OvercastApp.create({ factory });
Configuration
SolanaConfig extends the chain-agnostic OvercastConfig:
| Option | Default | Purpose |
|---|---|---|
rpcUrl | http://127.0.0.1:8899 | Solana RPC endpoint (Solana-specific). |
commitment | confirmed | Solana commitment level (Solana-specific). |
backend | localhost | Overcast backend base URL (REST + RFQ socket). |
socketUrl | backend | Override the socket base separately (e.g. "" for a same-origin proxy). |
privateKey | — | Wallet key: byte array, base58, or id.json contents. Omit for read-only. |
transport | websocket | RFQ transport: websocket or polling. |
ackTimeoutMs | 10000 | Timeout for RFQ request acknowledgments. |
protocolSigner | derived from privateKey | Inject a custom OvercastProtocolSigner (e.g. remote signing). |
overcastApi | HTTP view over backend | Inject a custom OvercastView implementation. |
axios | — | Extra axios defaults for the HTTP view. |
The surfaces
One app, five surfaces:
app.publicKey; // Id — your wallet public key / fee payer
app.chain; // on-chain reads + signed writes
app.api; // curated off-chain REST reads
app.rfq; // realtime RFQ client (lazy)
app.settlement; // settlement-layer registry
app.utils; // chain-specific address helpers
app.chain — on-chain reads and writes
Reads:
await app.chain.balance(user, mint); // escrow vault balance
await app.chain.getOption(collateralOfferId, settlementOfferId);
await app.chain.getCollateralOffer(id);
await app.chain.getSettlementOffer(id);
await app.chain.exerciseClaims(option, holder); // taker's remaining claims
await app.chain.redeemClaims(option, holder); // maker's remaining claims
Note that getOption takes the offer ID pair — on-chain, an option
account is seeded by its two offers.
Writes (each builds, signs, and submits a transaction, returning
TxInfo = { hash, confirmed }):
await app.chain.deposit({ depositor, mint, amount });
await app.chain.withdraw({ withdrawer, mint, amount });
await app.chain.createCollateralOffer(offer);
await app.chain.createSettlementOffer(offer);
await app.chain.acceptOffer(settlementOffer, collateralOffer); // mints the option
await app.chain.exerciseOption(option, amount); // taker
await app.chain.redeemOption(option, amount); // maker
app.api — curated backend views
Read-only, paginated queries against the backend's curated data (assets resolved, amounts formatted for display):
await app.api.listAssets(); // the asset allowlist
await app.api.listOptions({ maker });
await app.api.listCollateralOffers({ asset });
await app.api.listSettlementOffers({});
await app.api.listRfqs({ status: "open" });
await app.api.listQuotes({ rfqId });
app.rfq — the realtime RFQ client
app.rfq is a lazy getter: the socket connects on first access and the same
RfqClient instance is returned afterwards. It throws if the app has no
signer ("RFQ requires a signer") because every quote and accept is
authorized by an ed25519 signature.
const rfq = app.rfq; // connects
await rfq.registerRole("TAKER"); // or "MAKER"
// ... see the RFQ quoter guide ...
rfq.disconnect(); // tears down
There is no app.start() / app.stop(): create() builds everything,
.rfq connects on demand, rfq.disconnect() is the only teardown needed.
app.settlement — settlement-layer registry
Resolves settlement-layer configs to pin into OptionDetails before signing:
app.settlement.types(); // ["physical", "oracle"]
app.settlement.defaultConfig(details); // config Id for the default layer
app.settlement.resolve("oracle").defaultConfig(details);
Read-only mode
Construct the app without a privateKey and you get a fully functional
read-only client — app.api and the app.chain read methods work, while
writes and app.rfq are unavailable. This is the right shape for dashboards
and indexers.
Why a factory?
@overcast/core is chain-agnostic: it defines the interfaces
(OvercastReader, OvercastWriter, OvercastChainSigner,
SettlementLayerRegistry, …) and OvercastApp composes whatever a
ProtocolFactory produces. SolanaFactory is the Solana implementation —
one config in, every per-chain piece out. Supporting another chain means
implementing one factory, not touching application code.