Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hypertekorg/hyperstack/llms.txt

Use this file to discover all available pages before exploring further.

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

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:
use hyperstack::resolvers::TokenMetadata;

#[entity(name = "OreRound")]
pub struct OreRound {
    pub id: RoundId,
    pub state: RoundState,

    #[resolve(address = "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp")]
    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

FieldTypeDescription
mintstringThe mint address (always present)
name`stringnull`Token name from on-chain metadata
symbol`stringnull`Token ticker symbol
decimals`numbernull`Number of decimal places
logo_uri`stringnull`URL to the token’s logo image

Generated TypeScript

The CLI generates both a TypeScript interface and a Zod schema for TokenMetadata in your stack SDK:
// Auto-generated in your stack SDK
export 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(),
});

Using Resolver Data in Transforms

Resolvers provide data that you can reference in other field mappings. The TokenMetadata resolver’s decimals field is commonly used to convert raw token amounts to human-readable UI amounts:

Option A: Use ui_amount transform on #[map]

#[map(ore_sdk::accounts::Round::motherlode, strategy = LastWrite,
      transform = ui_amount(ore_metadata.decimals))]
pub motherlode: Option<f64>,
This automatically converts the raw motherlode value (e.g., 150000000000) to a UI amount (e.g., 1.5) using the token’s decimal places. Real example from ORE stack:
#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundState {
    #[resolve(address = "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp")]
    pub ore_metadata: Option<TokenMetadata>,

    #[map(ore_sdk::accounts::Round::motherlode, strategy = LastWrite,
          transform = ui_amount(ore_metadata.decimals))]
    pub motherlode: Option<f64>,

    #[map(ore_sdk::accounts::Round::total_deployed, strategy = LastWrite,
          transform = ui_amount(9))]  // SOL always has 9 decimals
    pub total_deployed: Option<f64>,
}

Option B: Use computed fields

Store the raw value and derive the UI amount in a #[computed] field:
#[map(ore_sdk::accounts::Round::motherlode, strategy = LastWrite)]
pub motherlode_raw: u64,

#[computed(state.motherlode_raw.map(|v| v as f64 / 10f64.powi(ore_metadata.decimals.unwrap_or(0) as i32)))]
pub motherlode_ui: Option<f64>,

Resolver Computed Methods

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:
MethodDescriptionExample
ui_amountConverts raw token amount to human-readable UI amount1_000_000_000 with 9 decimals → 1.0
raw_amountConverts human-readable UI amount to raw token amount1.0 with 9 decimals → 1_000_000_000
Example using computed method in transform:
#[map(ore_sdk::accounts::Round::motherlode, strategy = LastWrite,
      transform = ui_amount(ore_metadata.decimals))]
pub motherlode: Option<f64>,
The ui_amount transform uses the resolver’s decimals field to perform the conversion.

Real Example: Converting Raw Amounts

From the ORE stack, converting multiple fields using resolver decimals:
#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct TreasuryState {
    #[map(ore_sdk::accounts::Treasury::motherlode, strategy = LastWrite,
          transform = ui_amount(11))]  // ORE has 11 decimals
    pub motherlode: Option<f64>,

    #[map(ore_sdk::accounts::Treasury::total_refined, strategy = LastWrite,
          transform = ui_amount(11))]
    pub total_refined: Option<f64>,

    #[map(ore_sdk::accounts::Treasury::total_staked, strategy = LastWrite,
          transform = ui_amount(11))]
    pub total_staked: Option<f64>,
}

Using Resolver Fields in Computed Fields

You can reference resolver fields in #[computed] expressions:
#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundState {
    #[resolve(address = "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp")]
    pub ore_metadata: Option<TokenMetadata>,

    #[map(ore_sdk::accounts::Round::deployed, strategy = LastWrite)]
    pub deployed_per_square: Option<Vec<u64>>,

    // Convert each square's raw amount to UI amount
    #[computed(state.deployed_per_square.map(|squares| {
        squares.iter().map(|&raw| {
            raw as f64 / 10f64.powi(ore_metadata.decimals.unwrap_or(0) as i32)
        }).collect::<Vec<f64>>()
    }))]
    pub deployed_per_square_ui: Option<Vec<f64>>,
}

How It Works

  1. You define a TokenMetadata field on your entity in Rust
  2. Hyperstack resolves the metadata server-side when the entity is first created
  3. Computed fields referencing the resolver are evaluated server-side on every update
  4. 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. :::note[Caching] Resolver data is cached server-side. Token metadata is fetched once per mint and reused across all entities that reference it. :::

Real Example: Complete Entity with Resolver

From the ORE stack, showing resolver usage across multiple fields:
#[entity(name = "OreRound")]
#[view(name = "latest", sort_by = "id.round_id", order = "desc")]
pub struct OreRound {
    pub id: RoundId,
    pub state: RoundState,
    pub results: RoundResults,

    #[resolve(address = "oreoU2P8bN6jkk3jbaiVxYnG1dCXcYxwhwyK9jSybcp")]
    pub ore_metadata: Option<TokenMetadata>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundState {
    // Use resolver decimals in transform
    #[map(ore_sdk::accounts::Round::motherlode, strategy = LastWrite,
          transform = ui_amount(ore_metadata.decimals))]
    pub motherlode: Option<f64>,

    // Use fixed decimals for SOL
    #[map(ore_sdk::accounts::Round::total_deployed, strategy = LastWrite,
          transform = ui_amount(9))]
    pub total_deployed: Option<f64>,

    // Store raw values
    #[map(ore_sdk::accounts::Round::deployed, strategy = LastWrite)]
    pub deployed_per_square: Option<Vec<u64>>,

    // Convert to UI amounts in computed field
    #[computed(state.deployed_per_square.map(|x| x.ui_amount(9)))]
    pub deployed_per_square_ui: Option<Vec<f64>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, Stream)]
pub struct RoundResults {
    #[map(ore_sdk::accounts::Round::top_miner_reward, strategy = LastWrite,
          transform = ui_amount(ore_metadata.decimals))]
    pub top_miner_reward: Option<f64>,
}
In this example:
  • ore_metadata is resolved once per round
  • motherlode and top_miner_reward use the resolver’s decimals
  • total_deployed and deployed_per_square_ui use fixed decimals (SOL = 9)
  • All conversions happen server-side

Next Steps

Build docs developers (and LLMs) love