# Resolvers & Data Binding

A resolver is an on-chain contract that supplies **live data** to your UI at render time. It's how a ChainRender
app shows real, current chain state — a price, a balance, an ENS profile, a chart series — while staying fully
on-chain and grade-A.

## The idea

Your composition has **data slots**. Each slot names a resolver and a "kind" of data. When the app renders, the
interpreter calls `resolver.resolve(kind, args)` — a `view` call — and inlines the (escaped) result. The data is
never baked into the app's bytes; it's read live, so the same app always shows *current* state.

```ts
// a slot bound to a live oracle price
compose.slot("feed", { resolver: "0x…", arg: encodedArgs  }) // slot(bind, { resolver, arg })
```

## Why resolvers instead of baking data in

If data were baked into the app's bytes, the app would show *stale* data forever (and every update would be a new
hash). By reading through a resolver at render time, the app stays immutable *and* current — the bytes say "show
the price from this feed," and the price itself is always fresh. This is the separation of **structure** (immutable,
in the hash) from **state** (live, from resolvers).

## The resolver interface

Every resolver implements a tiny interface: `supportsKind(kind)` (a capability check) and `resolve(kind, args)`
(the read). This uniformity is what lets the interpreter treat all data sources identically, and lets you swap one
resolver for another without changing your UI. Some resolvers also implement richer interfaces (`IChartData`,
`ITableSource`, `IRouteSource`) for structured data.

## Fail-closed, always

A resolver that reverts, runs out of gas, or returns garbage never breaks the render — the slot falls back to an
honest "unavailable." A resolver can never inject markup (its output is escaped) and never move value (reads are
`view`). Binding live data adds a `READ_CHAIN` capability and nothing more.

## The built resolver catalog

ChainRender ships a large library of ready resolvers — oracles (Chainlink, Uniswap V3), DeFi (Aave, protocol
stats), identity (ENS profile, attested credentials), media (NFT images, generative art, SDF scenes, geo maps),
and structured data (charts, tables). See the **[Resolver Catalog](../reference/resolvers.md)** for a tour, and
**[All Resolver Kinds](../reference/resolver-kinds.md)** for every first-party kind with its exact args — the same list
`vocabulary().resolverKinds` returns.

**Encode the args the way the kind says.** A slot's `arg` is the bytes the resolver receives: `abi.encode` of the
kind's args, unless the kind is marked *packed* (raw bytes — `ensName` takes exactly the 20 address bytes and
reverts on anything else, which falls back to the slot's text). A wrongly encoded arg doesn't error at build time; it
renders the fallback.

## Related
- **[Guides → Bind Live On-Chain Data](../guides/bind-data.md)** — a hands-on walkthrough.
- **[The Trust Grade](./trust-grade.md)** — how a live read affects the grade (spoiler: an on-chain read stays A).
