Skip to content

Your first read

By the end of this page you will have read a real fleet out of the live game. No wallet, no keys, no setup beyond an install.

Terminal window
pnpm add @aephia/sage @solana/kit

@solana/kit is a peer dependency: it provides the RPC client and the Address type. Installing it yourself means you control its version.

import { createSolanaRpc, address } from '@solana/kit';
import { createSageClient } from '@aephia/sage';
const rpc = createSolanaRpc('https://testnet-rpc.z.ink');
const sage = createSageClient({ cluster: 'zink-ptr', rpc });
const profileAddress = address('J4r2s9QA2SHWf8zLPmvPhVphiK92h3rVmoXxcSM8M2vv');
const character = await sage.characters.forProfile(profileAddress);
const fleets = await character.fleets.all();
const fleet = fleets[0];
if (!fleet) throw new Error('That profile has no fleets.');
console.log(fleet.name); // "Ravager"
console.log(fleet.state); // { kind: 'docked', system: … }

That address is a real profile on the test realm. It should work as written.

Four things, worth naming because the rest of the SDK works the same way.

createSageClient did no network work. It is synchronous and allocates a context: a cache, an RPC binding, and the cluster’s known addresses. Nothing is fetched until you ask for something.

cluster: 'zink-ptr' filled in the game. The preset knows the program address and the Game account, so you did not have to.

forProfile returned a loaded object. Not a handle you have to load() first — the data is already there. Every read in this SDK works that way.

.fleets.all() followed a relationship. Going from a character to its fleets is a separate read, so it is a separate await. Anything that costs a network round trip is a method you call, not a property you touch.

Cargo is a relationship too:

const inventory = await fleet.inventory.get();
console.log(inventory.cargoHold.items[0]?.quantityRaw);

Quantities are bigint, not number. Game amounts routinely exceed what a JavaScript number can hold exactly, and silently rounding someone’s ore count is worse than making you type n.

A network or CORS error usually means the RPC endpoint rejected the request. See setting up your RPC.

MissingGameContextError means a cluster other than zink-ptr was used without supplying a Game address. Use the preset unless you know you need otherwise.

An empty fleets array is a valid answer: that profile currently has no fleets. Try another address.

How the SDK thinks explains caching, entry points, and why reads are shaped the way they are.