# Rendering Uniswap V4 Hooks (and any contract's view state)

**You do not need a special "hook resolver."** A Uniswap V4 hook is just a contract, and ChainRender already ships
a generic, robust, ethos-compliant way to read + render *any* contract's view function: the **`CallResolver`**
(`kind = "staticread"`) and, for several fields at once, **`MultiCallResolver`** (`kind = "multiread"`). This page
shows how to use them to render live hook state.

> Why not a dedicated hook resolver? Because it would duplicate `CallResolver`. ChainRender's design is *modular +
> additive*: one generic view-reader serves hooks, oracles, ERC-20s, governance, and anything else with a `view`
> function. Fewer contracts, one audited path, no per-integration special cases.

## What `CallResolver` does (kind `"staticread"`)

`args = abi.encode(address target, bytes callData, uint8 retType, uint8 fmt, uint8 dec, uint8 frac)`

- **target** — the contract to read (the **hook address**, or a `StateView`, or any contract).
- **callData** — the encoded view call (selector + args), e.g. `abi.encodeWithSelector(hook.swapCount.selector, poolId)`.
- **retType** — how to interpret the returned word: `0=uint 1=int 2=address 3=bool 4=string 5=bytes32` (`ReadFormat`).
- **fmt / dec / frac** — formatting: abbreviate (K/M/B/T), fixed-point decimals, fractional digits.

It `staticcall`s with a gas cap + reserve, then formats via `ReadFormat`. **Fails closed** to a fallback string on
any revert, missing contract, or malformed return — never reverts the render. Immutable, ownerless, view-only.

## Example: rendering a hook's swap counter (DinoHook-style)

Say a V4 hook exposes `function swapCount(bytes32 poolId) external view returns (uint256)`. To render it:

```solidity
bytes memory callData = abi.encodeWithSelector(0xXXXXXXXX /* swapCount */, poolId);
bytes memory args = abi.encode(
    hookAddress,               // target = the hook
    callData,                  // the view call
    uint8(0),                  // retType = RET_UINT
    uint8(1),                  // fmt = abbreviate (12.3K)
    uint8(0), uint8(0)         // dec, frac
);
// resolver kind "staticread"; the component slot references this and renders the number live.
```

In the **visual builder**, this is a data-bound slot: point it at `CallResolver` with kind `staticread` and those
args. The rendered component shows the hook's live swap count, read on-chain during the `eth_call` render \u2014 no
server, no indexer, fail-closed if the hook is unreachable.

## Reading several hook fields at once (kind `"multiread"`)

For a hook dashboard \u2014 swap count, current dynamic fee, last swapper, TVL \u2014 use `MultiCallResolver`
(`"multiread"`), which batches multiple `staticread`-shaped calls into one rendered result. Same target/callData/
retType shape per field; one component, many live hook values.

## What this renders (and what it doesn't)

- **Renders:** any hook `view`/`pure` function \u2014 counters, dynamic fees (`getSlot0().lpFee` reflects hook-set fees),
  order-book sizes, reputation scores, custom curve params, whatever the hook exposes.
- **Does NOT execute:** rendering is a `view`. To *act* on a hook (trigger a swap that runs the hook, place an order),
  that's the **declared-action layer** \u2014 a button whose action is decoded honestly before signing (see the action
  decoder + swap honesty flag). ChainRender *renders* hook state and *declares* hook-triggering actions; it never
  becomes the AMM.

## The three ways ChainRender meets hooks (complete picture)

1. **Render hook-influenced pool state** \u2014 `UniswapV4PriceResolver` reads `getSlot0(poolId)`; the `lpFee` it returns
   is the current (possibly hook-adjusted) fee. (V3 pools: `UniswapV3PriceResolver`, kept alongside.)
2. **Render a hook's own state** \u2014 `CallResolver` / `MultiCallResolver` read the hook's view functions (this page).
3. **A hook that drives ChainRender** \u2014 e.g. DinoHook: swap activity in the hook feeds a ChainRender NFT's render.
   That's hooks *feeding* ChainRender, the reverse direction.

## Expandability (the ethos-correct kind)

New return types or formats are added by *versioning* `ReadFormat` / deploying a new resolver additively \u2014 never by
mutating a deployed contract. `CallResolver` is immutable and ownerless; if you ever need a capability it lacks, you
deploy `CallResolverV2` and reference it, leaving V1 untouched. That is how ChainRender "expands": more immutable
pieces, never a mutable one.

## Related
- **`contracts/CallResolver.sol`** (`staticread`) \u2014 the generic view reader (renders any hook view function).
- **`contracts/MultiCallResolver.sol`** (`multiread`) \u2014 batch several hook fields.
- **`contracts/ReadFormat.sol`** \u2014 the return-type + formatting layer (uint/int/address/bool/bytes32/string).
- **`contracts/UniswapV4PriceResolver.sol`** \u2014 render V4 pool price (reflects hook-set fees).
- **`contracts/UniswapV3PriceResolver.sol`** \u2014 render V3 pool price (kept alongside V4).
