# Guide: Wallet Connection & Signing

ChainRender apps read from the chain for free, but *actions* (claiming, swapping, minting) require a wallet and a
signed transaction. This guide shows the wallet + signing flow using `@chainrender/client/runtime`.

## The model

An action is **declared** on a component (target, signature, arg shape). The runtime turns that declaration into a
transaction request — the calldata can *only* be what was declared (construct-for-sign). You then hand that request
to the user's wallet.

## Build the transaction from a declared action

```ts
import { buildActionRequest, describeAction, splitArgs } from "@chainrender/client/runtime";

// the action the component declared:
const action = {
  to: "0xYourContract",
  sig: "claim(uint256)",
  args: splitArgs("$amount"),   // parse the declared arg template
};

// show the user exactly what they're signing, in plain language:
console.log(describeAction(action, { amount: "1000" }));
// → "Call claim(1000) on 0xYourContract"

// construct the exact calldata — nothing else is expressible:
const tx = buildActionRequest(action, { amount: "1000" });
// → { to: "0xYourContract", data: "0x…", value: 0n }
```

## Send it with the user's wallet

`buildActionRequest` returns a standard `{ to, data, value }` — use it with any wallet library (viem, ethers,
EIP-1193 provider):

```ts
import { createWalletClient, custom } from "viem";

const wallet = createWalletClient({ transport: custom(window.ethereum) });
const [account] = await wallet.getAddresses();
const hash = await wallet.sendTransaction({ account, ...tx });
```

## Reading actions off a rendered component

If you're driving the flow from a rendered app, `readAction` / `readActions` extract the declared actions from the
component so you can wire buttons to `buildActionRequest`:

```ts
import { readActions } from "@chainrender/client/runtime";
const actions = readActions(componentHtmlOrAst);   // the declared, gated actions
```

## Guarantees

- The transaction can **only** be the declared `{to, selector, args}` — a button can't smuggle a different call
  (enforced by rails R3–R5). See **[Actions & Signing](../concepts/actions.md)**.
- Moving ETH requires the app to have declared `VALUE`; typed-data signing requires `SIGN_MESSAGE`.
- All runtime hooks **fail closed**: no wallet present → safe no-ops, never a broken or misleading state.

## Related
- **[Declare a Safe Action](./declare-action.md)** · **[Actions & Signing](../concepts/actions.md)**
