⚠️ Alpha · testnets only. ChainRender is in active alpha and deployed on testnets only — Ethereum Sepolia, the Robinhood Chain Testnet, and the Circle Arc Testnet — not on any mainnet. Contracts, addresses, and APIs may change. Do not use with real funds or production assets.
Guides

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

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

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:

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.
  • 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