For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending.mdto the URL or sendingAccept: text/markdown.
TypeScript SDK
For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /sdks/typescript.md.
The @usearete/sdk package is a framework-agnostic client for consuming streaming data and preparing Solana operations with Arete. It uses AsyncIterable-based streaming and works in any JavaScript environment — Node.js, browsers, Deno, or Bun.
Installation
Section titled “Installation”npm install @usearete/sdkNo peer dependencies. Works anywhere JavaScript runs.
Quick Start
Section titled “Quick Start”import { createSession } from "@usearete/sdk";import { ORE_STREAM_STACK } from "./generated/ore-stack";
const session = await createSession({ stacks: { ore: ORE_STREAM_STACK },});
// Stream entities with full type safetyfor await (const round of session.stacks.ore.views.OreRound.latest.use()) { console.log("Round:", round.id.roundId);}Cartridges and the Console
Section titled “Cartridges and the Console”Think of a generated stack as a cartridge: it packages a domain’s typed data and operations. Think of the Arete session as the console that connects those cartridges to shared chain reads and execution. These are mental models only—the API keeps the existing generated stack objects and types.
A connected stack at session.stacks.<name> has these stable namespaces:
| Namespace | Purpose |
|---|---|
views | Typed live and point-in-time entity views |
queries | Stack-level HTTP queries |
programs | Programs packaged with the stack |
addresses | Domain address derivations, when defined |
constants | Domain constants and enums, when defined |
defaults | Reusable domain defaults, when defined |
math | Pure domain calculations, when defined |
read | Connected domain reads, when defined |
flows | Stack-level multi-transaction operations, when defined |
Programs at session.stacks.<name>.programs.<program> and session.programs.<name> have these stable namespaces: programId, schemas, pdas, addresses, accounts, queries, raw, instructions, transactions, flows, constants, defaults, and math.
Use session.chain for generic Solana reads and session.execute(prepared) for any prepared semantic instruction, transaction, or flow.
For transaction inspection and execution, connected sessions automatically use the authenticated Arete transaction transport. See Transactions for auto, direct, and custom transport modes, scopes, and safe failure handling.
Semantic Prepare vs. Raw IDL
Section titled “Semantic Prepare vs. Raw IDL”Prefer semantic operations for application code. They accept normal camelCase objects, derive routine addresses, normalize values such as UI token amounts, and describe their cardinality through the namespace:
const prepared = await session.stacks.presale.programs.presale.flows.permissionlessDeposit.prepare({ presale, owner, quoteMint, payerQuoteToken, tokenProgram, maxAmount: { ui: "10.5" },});
await session.execute(prepared);Use program.raw as an escape hatch when you need the exact generated IDL shape. Raw names and nested fields are not normalized; snake_case remains snake_case. Raw .build(...) returns one instruction for manual composition:
const instruction = session.stacks.presale.programs.presale.raw.deposit.build({ max_amount: 10_500_000n, remaining_account_info: { slices: [] }, presale, quote_mint: quoteMint, escrow, payer_quote_token: payerQuoteToken, token_program: tokenProgram, program: session.stacks.presale.programs.presale.programId,});
await session.transaction([instruction]);Connection
Section titled “Connection”Create a Session
Section titled “Create a Session”Each generated stack definition includes its endpoints, so inserting it into a session is simple:
import { createSession } from "@usearete/sdk";import { ORE_STREAM_STACK } from "./generated/ore-stack";
const session = await createSession({ stacks: { ore: ORE_STREAM_STACK },});
// Now you have fully typed viewsconst rounds = await session.stacks.ore.views.OreRound.latest.get();
// Generic chain reads belong to the session consoleconst balance = await session.chain.balance({ owner, mint });Multiple Stacks and Programs
Section titled “Multiple Stacks and Programs”Name each inserted stack or standalone program. Stack names become properties under session.stacks; programs packaged by those stacks and explicitly attached programs are available under session.programs:
const session = await createSession({ stacks: { ore: ORE_STREAM_STACK, presale: METEORA_PRESALE_STREAM_STACK, }, programs: { splToken: SPL_TOKEN_PROGRAM, },});
session.programs.presale;session.programs.splToken;Promotion reuses the stack’s connected program object and endpoint. If multiple stacks package the same key, the first stack owns the promoted alias and a warning is emitted; use session.stacks.<name>.programs.<program> to disambiguate. An explicit standalone program wins its top-level key.
Direct Single-Stack Connection
Section titled “Direct Single-Stack Connection”Arete.connect(...) remains available for direct single-stack clients and advanced per-connection configuration:
import { Arete } from "@usearete/sdk";
const ore = await Arete.connect(ORE_STREAM_STACK, { url: "wss://custom.endpoint.com",});Connection Options
Section titled “Connection Options”const ore = await Arete.connect(ORE_STREAM_STACK, { maxEntriesPerView: 5000, // Limit entries per view (default: 10000) autoConnect: true, // Start the initial connection autoReconnect: true, // Recover after an established socket is lost});| Option | Type | Default | Description |
|---|---|---|---|
url | string | stack.url | Override the stack’s default URL |
maxEntriesPerView | number | null | 10000 | Max entries per view before LRU eviction |
autoConnect | boolean | true | Start the initial WebSocket connection |
autoReconnect | boolean | true | Recover after an established WebSocket connection is lost |
validateFrames | boolean | true | Log generated-schema frame rejections |
onFrameValidationError | function | — | Observe structured generated-schema frame rejections |
autoConnect and autoReconnect control separate lifecycle phases. Set autoConnect: false to create a disconnected client that the application connects later. Set autoReconnect: false when the application will handle a later connection loss itself.
Connection State
Section titled “Connection State”// Check current stateconsole.log(session.stacks.ore.connectionState);// 'connected' | 'connecting' | 'disconnected' | 'reconnecting' | 'error'
// Listen for changesconst unsubscribe = session.stacks.ore.onConnectionStateChange((state) => { console.log("Connection state:", state);});
// Later: stop listeningunsubscribe();Disconnect
Section titled “Disconnect”session.close();View Modes
Section titled “View Modes”Every view operates in one of two modes, which determines how you access data:
| Mode | Description | Key Required | Returns |
|---|---|---|---|
| State | Lookup individual entities by key | Yes | Single entity (T | null) |
| List | Access a collection of entities | No | Array of entities (T[]) |
Default Views
Section titled “Default Views”By default, each entity in a stack exposes two views:
| View Name | Mode | Description |
|---|---|---|
state | State | Lookup any entity by its key (e.g., address) |
list | List | Collection of all entities |
// State view — get a specific round with its generated keyconst round = await session.stacks.ore.views.OreRound.state.get({ roundId });
// List view — get all roundsconst rounds = await session.stacks.ore.views.OreRound.list.get();Custom Views
Section titled “Custom Views”Stacks can define additional views beyond the defaults. For example, the ORE stack includes a custom latest view that streams only recent rounds. Custom views are defined using the Rust DSL when building your stack — see Stack Definitions for details.
Custom views are accessed the same way as default views:
// Custom list view defined in the ORE stackfor await (const round of session.stacks.ore.views.OreRound.latest.use()) { console.log("Recent round:", round.id.roundId);}Each view type supports both streaming (real-time updates) and one-shot (point-in-time snapshot) access patterns.
Streaming Methods
Section titled “Streaming Methods”Streaming methods return AsyncIterable and continuously emit data as entities change. The connection stays open until you break out of the loop or abort.
| Method | Emits | Use Case |
|---|---|---|
.use() | T | Simplest — just the current entity state after each change |
.watch() | Update<T> | When you need to know the operation type and membership change |
.watchRich() | RichUpdate<T> | When you need before/after comparison |
.use() — Stream Merged Entities
Section titled “.use() — Stream Merged Entities”The simplest streaming method. Emits the full merged entity after each change:
// List view — no key requiredfor await (const round of session.stacks.ore.views.OreRound.latest.use()) { console.log("Round:", round.id.roundId); console.log("Motherlode:", round.state.motherlode);}
// State view — key requiredfor await (const round of session.stacks.ore.views.OreRound.state.use({ roundId })) { console.log("Round updated:", round.state.motherlode);}Signatures:
// State view; key type is generated from the entity primary keyuse(key: TKey, options?: WatchOptions): AsyncIterable<T>
// List viewuse(options?: WatchOptions): AsyncIterable<T>.watch() — Stream Canonical Updates
Section titled “.watch() — Stream Canonical Updates”Use when you need to know what operation occurred. These updates use the same canonical SDK-facing shape as .use() and .get(), including camelCase field names and bigint values for large integer families:
for await (const update of session.stacks.ore.views.OreRound.latest.watch()) { switch (update.type) { case "upsert": console.log("Created or replaced:", update.data); break; case "patch": console.log("Partial update:", update.data); break; case "remove": console.log("Left this query:", update.key); break; case "delete": console.log("Deleted:", update.key); break; }}Signatures:
// State viewwatch(key: TKey, options?: WatchOptions): AsyncIterable<Update<T>>
// List viewwatch(options?: WatchOptions): AsyncIterable<Update<T>>Update types:
type Update<T> = | { type: "upsert"; key: string; data: T } // Full entity create/replace | { type: "patch"; key: string; data: Partial<T> } // Partial update | { type: "remove"; key: string } // Left this query only | { type: "delete"; key: string }; // Deleted from the source view.watchRich() — Stream with Before/After Diffs
Section titled “.watchRich() — Stream with Before/After Diffs”Use when you need to compare the previous and new canonical state:
for await (const update of session.stacks.ore.views.OreRound.latest.watchRich()) { switch (update.type) { case "created": console.log("New entity:", update.data); break; case "updated": console.log(`Changed: ${update.before.state.motherlode} → ${update.after.state.motherlode}`); break; case "removed": console.log("Left this query:", update.lastKnown); break; case "deleted": console.log("Deleted:", update.lastKnown); break; }}Signatures:
// State viewwatchRich(key: TKey, options?: WatchOptions): AsyncIterable<RichUpdate<T>>
// List viewwatchRich(options?: WatchOptions): AsyncIterable<RichUpdate<T>>RichUpdate types:
type RichUpdate<T> = | { type: "created"; key: string; data: T } | { type: "updated"; key: string; before: T; after: T; patch?: unknown } | { type: "removed"; key: string; lastKnown?: T } | { type: "deleted"; key: string; lastKnown?: T };One-Shot Methods
Section titled “One-Shot Methods”One-shot methods return a point-in-time snapshot without subscribing to updates. Use these when you need the current state but don’t need real-time streaming.
| Method | Returns | Behavior |
|---|---|---|
.get() | Promise<T> | Async — waits for data if not yet loaded |
.getSync() | T | undefined | Sync — returns immediately, undefined if not loaded |
.get() — Async Snapshot
Section titled “.get() — Async Snapshot”Fetches the current state. Returns a promise that resolves when data is available:
// List view — returns all entitiesconst rounds = await session.stacks.ore.views.OreRound.latest.get();console.log(`Found ${rounds.length} rounds`);
// State view — returns single entity or nullconst round = await session.stacks.ore.views.OreRound.state.get({ roundId });if (round) { console.log("Round:", round.id.roundId);}Signatures:
// State view — returns single entity or null if not foundget(key: TKey): Promise<T | null>
// List view — returns array of all entitiesget(): Promise<T[]>.getSync() — Synchronous Snapshot
Section titled “.getSync() — Synchronous Snapshot”Returns immediately with cached data. Returns undefined if data hasn’t been loaded yet:
// List viewconst rounds = session.stacks.ore.views.OreRound.latest.getSync();if (rounds) { console.log(`Cached: ${rounds.length} rounds`);} else { console.log("Data not yet loaded");}
// State viewconst round = session.stacks.ore.views.OreRound.state.getSync({ roundId });Signatures:
// State view — returns entity, null (not found), or undefined (not loaded)getSync(key: TKey): T | null | undefined
// List view — returns array or undefined (not loaded)getSync(): T[] | undefinedStore Size Limits
Section titled “Store Size Limits”Each view maintains an in-memory store of entities. By default, stores are limited to 10,000 entries to prevent memory issues on long-running clients. When the limit is reached, oldest entries are evicted (LRU).
const ore = await Arete.connect(ORE_STREAM_STACK, { maxEntriesPerView: 5000, // Custom limit});To disable limiting (not recommended for production):
const ore = await Arete.connect(ORE_STREAM_STACK, { maxEntriesPerView: null, // Unlimited});Subscription Options
Section titled “Subscription Options”The streaming methods (.use(), .watch(), .watchRich()) accept options for pagination and validation:
interface WatchOptions { partition?: string; // Exact _partition match filters?: Record<string, unknown>; // Server-side dot-path equality filters take?: number; // Live query window size skip?: number; // Live query window offset withSnapshot?: boolean; // Include initial snapshot (default true) after?: string; // Incremental _seq cursor snapshotLimit?: number; // Initial snapshot row cap schema?: Schema<T>; // Client-side schema validation}Every options object defines exact protocol v2 query membership. Different windows and filters on one view can coexist without leaking entities, while equivalent normalized queries share one reference-counted wire subscription:
const rounds = session.stacks.ore.views.OreRound.latest;
const firstPage = rounds.watch({ take: 10 });const secondPage = rounds.watch({ take: 10, skip: 10 });After reconnect, a completed authoritative snapshot atomically replaces membership for its exact query. A query with after receives an incremental snapshot and merges without pruning.
Limit Results
Section titled “Limit Results”// Only receive the first 10 entitiesfor await (const round of session.stacks.ore.views.OreRound.latest.use({ take: 10 })) { console.log("Round:", round.id.roundId);}Pagination
Section titled “Pagination”// Skip first 20, take next 10for await (const round of session.stacks.ore.views.OreRound.latest.use({ skip: 20, take: 10 })) { console.log("Round:", round.id.roundId);}Server-Side Filtering
Section titled “Server-Side Filtering”Use exact-match dot-path filters for dynamic equality predicates:
for await (const order of session.stacks.market.views.Order.list.use({ filters: { "state.status": "open", "market.symbol": "SOL" }, take: 10,})) { console.log(order);}Filtering is case-sensitive and type-sensitive. Filters run before skip and take. Use a custom view when filtering, sorting, or aggregation needs to be declared as part of the stack rather than supplied by each client.
Filter keys are WebSocket wire-schema dot paths, so snake_case remains correct there. Generated TypeScript entity properties and generated state keys use camelCase.
See Filtering Feeds for details on all filtering options, or Stack Definitions for how to define custom views using the Rust DSL.
Stream Control
Section titled “Stream Control”Use standard AsyncIterable patterns to control streams client-side.
Stop on Condition
Section titled “Stop on Condition”for await (const update of session.stacks.ore.views.OreRound.latest.watch()) { if (update.type === "upsert") { const round = update.data; if (round.state.motherlode && round.state.motherlode > 1_000_000_000) { console.log("Found high-value round:", round.id.roundId); break; } }}Cancellable Streams
Section titled “Cancellable Streams”Use an AbortController to cancel from outside the loop:
const controller = new AbortController();setTimeout(() => controller.abort(), 30_000); // Cancel after 30s
try { for await (const update of session.stacks.ore.views.OreRound.latest.watch()) { if (controller.signal.aborted) break; console.log("Update:", update.data); }} catch (e) { if (!controller.signal.aborted) throw e;}Client-Side Filtering
Section titled “Client-Side Filtering”for await (const update of session.stacks.ore.views.OreRound.latest.watch()) { if (update.type !== "upsert") continue; if ((update.data.metrics.deployCount ?? 0) < 100) continue;
console.log("Active round:", update.data.id.roundId);}Schema Validation
Section titled “Schema Validation”Every stack ships with Zod schemas alongside its TypeScript interfaces. Use them to validate data at two levels:
Frame Validation
Section titled “Frame Validation”Generated schemas always normalize and validate frames before storage. Invalid data is dropped and logged by default. Observe structured failures without scraping console output:
const ore = await Arete.connect(ORE_STREAM_STACK, { onFrameValidationError(diagnostic) { console.error('Rejected Arete frame', diagnostic); },});Set validateFrames: false to suppress the default warning while keeping normalization, rejection, and onFrameValidationError active.
Query-Level Validation
Section titled “Query-Level Validation”Pass a schema to .use() to filter out entities that don’t match:
import { OreRoundCompletedSchema } from "./generated/ore-stack";
// Only emit rounds where every field is present. Rejected entities are skipped.for await (const round of session.stacks.ore.views.OreRound.latest.use({ schema: OreRoundCompletedSchema,})) { console.log(round.id.roundId, round.state.motherlode);}React view hooks additionally support onSchemaValidationError when an application needs diagnostics for caller-supplied schema rejections.
See Schema Validation for the full guide on generated schemas, custom schemas, and the “Completed” schema pattern.
Resolved Data
Section titled “Resolved Data”Arete can automatically enrich your entities with data that doesn’t live on-chain. For example, the ORE stack includes token metadata (name, symbol, decimals, logo) resolved server-side from the DAS API:
for await (const round of session.stacks.ore.views.OreRound.latest.use()) { // Token metadata is resolved automatically — no extra API calls console.log(round.oreMetadata?.name); // "Ore" console.log(round.oreMetadata?.symbol); // "ORE" console.log(round.oreMetadata?.decimals); // 11}Resolved data arrives as part of the entity alongside on-chain fields. Your client code reads it the same way — no distinction between on-chain and resolved data.
See Resolvers for how to add resolved data when building your own stack.
Amount Conversion
Section titled “Amount Conversion”toRawAmount(input, decimals) is convenient when invalid input should throw. Use safeToRawAmount(input, decimals) at user-input boundaries:
import { safeToRawAmount } from "@usearete/sdk";
const parsed = safeToRawAmount({ ui: amountText }, 9);if (!parsed.success) { console.error(parsed.error); return;}
console.log(parsed.data); // bigint in raw base unitssafeToRawAmount returns { success: true, data } | { success: false, error } instead of throwing for invalid amount input. It is also re-exported by @usearete/react.
Error Handling
Section titled “Error Handling”import { AreteError, createSession } from "@usearete/sdk";import { ORE_STREAM_STACK } from "./generated/ore-stack";
try { const session = await createSession({ stacks: { ore: ORE_STREAM_STACK } });} catch (error) { if (error instanceof AreteError) { console.error("Arete error:", error.message); console.error("Code:", error.code); } else { throw error; }}API Reference
Section titled “API Reference”createSession(definition, options?)
Section titled “createSession(definition, options?)”Creates the console that connects generated stacks and standalone programs. Programs packaged by stacks are automatically promoted to session.programs by reference.
Parameters:
definition.stacks— Named generated stack definitions (optional)definition.programs— Named standalone program definitions (optional)options.wallet— Shared wallet for execution (optional)options.signerRegistry— Shared additional signers (optional)options.endpoints— Shared HTTP and WebSocket fallback endpoints (optional)options.stacks— Per-stack connection overrides (optional)options.stacks.<name>.autoConnect— Per-stack initial connection control; defaults totrue(optional)options.stacks.<name>.autoReconnect— Per-stack post-connection recovery control; defaults totrue(optional)
At least one stack or program is required.
Returns: Promise<Session>
session.stacks.<name>
Section titled “session.stacks.<name>”Returns the connected generated stack inserted under that name. Use its views, queries, programs, addresses, constants, defaults, math, read, and flows namespaces when present.
session.programs.<name>
Section titled “session.programs.<name>”Returns a connected program packaged by a stack, attached to a stack, or declared standalone. Program namespaces are programId, schemas, pdas, addresses, accounts, queries, raw, instructions, transactions, flows, constants, defaults, and math.
session.chain
Section titled “session.chain”The session-wide generic Solana read surface.
session.execute(prepared, options?)
Section titled “session.execute(prepared, options?)”Executes a value returned by a semantic instruction, transaction, or flow’s .prepare(...) method.
Arete.connect(stack, options?)
Section titled “Arete.connect(stack, options?)”Creates a direct single-stack client. This existing API remains useful for advanced per-connection options such as url, maxEntriesPerView, and validateFrames.
session.stacks.<name>.views
Section titled “session.stacks.<name>.views”Typed views interface based on your stack definition. Access pattern:
session.stacks.<name>.views.<entity>.<viewName>.<method>()
// Examples with default views:session.stacks.ore.views.OreRound.state.get(key) // State mode — requires keysession.stacks.ore.views.OreRound.list.get() // List mode — no keyState Mode Methods
Section titled “State Mode Methods”For views in state mode (keyed lookup). All methods require a key parameter:
| Method | Signature | Returns |
|---|---|---|
use | use(key, options?) | AsyncIterable<T> |
watch | watch(key, options?) | AsyncIterable<Update<T>> |
watchRich | watchRich(key, options?) | AsyncIterable<RichUpdate<T>> |
get | get(key) | Promise<T | null> |
getSync | getSync(key) | T | null | undefined |
List Mode Methods
Section titled “List Mode Methods”For views in list mode (collections). No key parameter:
| Method | Signature | Returns |
|---|---|---|
use | use(options?) | AsyncIterable<T> |
watch | watch(options?) | AsyncIterable<Update<T>> |
watchRich | watchRich(options?) | AsyncIterable<RichUpdate<T>> |
get | get() | Promise<T[]> |
getSync | getSync() | T[] | undefined |
WatchOptions
Section titled “WatchOptions”Options for streaming methods:
interface WatchOptions { partition?: string; filters?: Record<string, unknown>; take?: number; skip?: number; withSnapshot?: boolean; after?: string; snapshotLimit?: number; schema?: Schema<T>;}See Schema Validation for details on using schemas to filter and validate streamed data.
session.stacks.<name>.connectionState
Section titled “session.stacks.<name>.connectionState”Current connection state:
disconnected— Not connectedconnecting— Establishing connectionconnected— Active connectionreconnecting— Auto-reconnecting after failureerror— Connection failed
session.stacks.<name>.onConnectionStateChange(callback)
Section titled “session.stacks.<name>.onConnectionStateChange(callback)”Subscribe to connection state changes. Returns unsubscribe function.
session.close()
Section titled “session.close()”Closes every connection owned by the session.
Next Steps
Section titled “Next Steps”- Schema Validation — Zod schemas for runtime validation and filtering
- React SDK — Hooks and providers for React apps
- Rust SDK — Native Rust client
- Resolvers — Enrich entities with token metadata and computed fields
- Build Your Own Stack — Create custom data streams