---
title: "Resolvers"
editUrl: true
head: []
template: "doc"
sidebar: {"order":4,"hidden":false,"attrs":{}}
pagefind: true
draft: false
---

Resolvers enrich your entities with data that doesn't live on-chain. When you define a resolver on an entity field, Arete 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. Arete resolves this automatically server-side when your entity includes a field typed as `TokenMetadata`:

```rust
#[arete(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,

        // Arete resolves this automatically from the mint
        pub ore_metadata: Option<TokenMetadata>,
    }
}
```

When a new `OreRound` entity is created, Arete 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:

```typescript
for await (const round of a4.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

| 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

The CLI generates both a TypeScript interface and a Zod schema for `TokenMetadata` in your stack SDK:

```typescript
// 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(),
});
```

---

## 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:

```rust
#[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:

```typescript
for await (const round of a4.views.OreRound.latest.use()) {
  console.log(round.motherlode_ui); // 1.5 (human-readable ORE amount)
}
```

---

## How It Works

1. **You define** a `TokenMetadata` field on your entity in Rust
2. **Arete 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
Resolver data is cached server-side. Token metadata is fetched once per mint and reused across all entities that reference it.
:::

---

## Next Steps

- [Schema Validation](/sdks/validation/) — Validate resolved data with Zod schemas on the client
- [Macro Reference](./macros) — Complete documentation of `#[computed]` and other field macros
- [Population Strategies](./strategies) — How incoming data merges with existing state
