Resolvers
Resolvers enrich your entities with data that doesn’t live on-chain. When you define a resolver on an entity field, Hyperstack automatically fetches the external data server-side and delivers it to your clients as part of the entity — no extra API calls needed.
Token Metadata
Section titled “Token Metadata”The built-in TokenMetadata resolver enriches your entity with SPL token metadata (name, symbol, decimals, logo) for any mint address. Hyperstack resolves this automatically server-side when your entity includes a field typed as TokenMetadata:
#[hyperstack(idl = "idl/ore.json")]pub mod ore_stream { #[entity] pub struct OreRound { #[map(ore_sdk::accounts::Round::reward_mint, primary_key, strategy = SetOnce)] pub mint: String,
// Hyperstack resolves this automatically from the mint pub ore_metadata: Option<TokenMetadata>, }}When a new OreRound entity is created, Hyperstack sees the TokenMetadata field, resolves the metadata server-side, and delivers it as part of the entity. By the time data reaches your TypeScript client, the field is already filled in:
for await (const round of hs.views.OreRound.latest.use()) { console.log(round.ore_metadata?.name); // "Ore" console.log(round.ore_metadata?.symbol); // "ORE" console.log(round.ore_metadata?.decimals); // 11 console.log(round.ore_metadata?.logo_uri); // "https://..."}TokenMetadata Fields
Section titled “TokenMetadata Fields”| Field | Type | Description |
|---|---|---|
mint | string | The mint address (always present) |
name | string | null | Token name from on-chain metadata |
symbol | string | null | Token ticker symbol |
decimals | number | null | Number of decimal places |
logo_uri | string | null | URL to the token’s logo image |
Generated TypeScript
Section titled “Generated TypeScript”The CLI generates both a TypeScript interface and a Zod schema for TokenMetadata in your stack SDK:
// Auto-generated in your stack SDKexport interface TokenMetadata { mint: string; name?: string | null; symbol?: string | null; decimals?: number | null; logo_uri?: string | null;}
export const TokenMetadataSchema = z.object({ mint: z.string(), name: z.string().nullable().optional(), symbol: z.string().nullable().optional(), decimals: z.number().nullable().optional(), logo_uri: z.string().nullable().optional(),});Computed Fields from Resolvers
Section titled “Computed Fields from Resolvers”Resolvers also provide computed methods — functions that derive new values from the resolved data. These are evaluated server-side and delivered to your client as regular entity fields.
The TokenMetadata resolver provides two computed methods:
| Method | Description | Example |
|---|---|---|
ui_amount | Converts raw token amount to human-readable UI amount | 1_000_000_000 with 9 decimals → 1.0 |
raw_amount | Converts human-readable UI amount to raw token amount | 1.0 with 9 decimals → 1_000_000_000 |
Use these in #[computed] expressions:
#[entity]pub struct OreRound { pub ore_metadata: Option<TokenMetadata>,
#[map(ore_sdk::accounts::Round::motherlode, strategy = LastWrite)] pub motherlode_raw: u64,
// Server-side: converts raw amount using the resolved decimals #[computed(TokenMetadata::ui_amount(motherlode_raw, ore_metadata.decimals))] pub motherlode_ui: Option<f64>,}On the client, motherlode_ui arrives as a ready-to-display number:
for await (const round of hs.views.OreRound.latest.use()) { console.log(round.motherlode_ui); // 1.5 (human-readable ORE amount)}How It Works
Section titled “How It Works”- You define a
TokenMetadatafield on your entity in Rust - Hyperstack resolves the metadata server-side when the entity is first created
- Computed fields referencing the resolver are evaluated server-side on every update
- Your client receives the fully enriched entity — metadata and computed values included
The resolution happens transparently. Your TypeScript and React code simply reads the fields like any other entity data.
Next Steps
Section titled “Next Steps”- Schema Validation — Validate resolved data with Zod schemas on the client
- Macro Reference — Complete documentation of
#[computed]and other field macros - Population Strategies — How incoming data merges with existing state