⚠️ 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.
Guided Tours

Tour: Your First Grade-A App — Settlement Desk

You'll build a Settlement Desk: a card that reads a settlement agreement's live on-chain state (status, pooled principal, percent repaid) and shows its self-declared trust facets. Every value is an on-chain read, so the whole app is Grade A. Along the way you'll hit — and fix — the one import/compile pitfall every builder user meets once.

Time: ~10 minutes to a rendering preview; add the deploy/seed steps to see live data.


1. Import the app

Open the Visual Builder (/builder/index.html) → ⋯ More → Import JSON → paste this file. It's a bare node tree — exactly what Import accepts and what Export JSON produces.

{
  "type": "box",
  "direction": "column",
  "children": [
    { "type": "text", "content": "Settlement Desk", "role": "heading" },
    { "type": "text", "content": "A fully on-chain settlement agreement, rendered straight from chain state." },
    { "type": "text", "content": "Live settlement state", "role": "heading" },
    {
      "type": "box",
      "direction": "row",
      "children": [
        { "type": "slot", "bind": "settle-status",    "argValues": { "agreement": "0x1111111111111111111111111111111111111111", "assetDecimals": "6", "fracDigits": "2" } },
        { "type": "slot", "bind": "settle-principal", "argValues": { "agreement": "0x1111111111111111111111111111111111111111", "assetDecimals": "6", "fracDigits": "2" } },
        { "type": "slot", "bind": "settle-repaid",    "argValues": { "agreement": "0x1111111111111111111111111111111111111111", "assetDecimals": "6", "fracDigits": "2" } }
      ]
    },
    { "type": "text", "content": "Trust, declared on-chain", "role": "heading" },
    { "type": "slot", "bind": "trust-facets", "argValues": { "subject": "0x1111111111111111111111111111111111111111" } }
  ]
}

The canvas renders the layout immediately. The 0x1111…1111 addresses are placeholders — you'll replace them with your deployed agreement in step 5. Until then, the slots read nothing, which is exactly what step 4 is about.


2. A heading is a text node with a role — not type: "heading"

This is the gotcha. It is tempting to write a heading as { "type": "heading", … }, and the canvas will even draw it — so it looks fine. But when you preview on-chain or compile, you get:

On-chain preview failed: u8 out of range: undefined.

That error is not about your RPC or interpreter, even though the builder suggests checking them. It is thrown client-side, during compile, before any eth_call. The compiler turns each node's type into a one-byte tag, and the set of real node types is:

text · box · button · input · icon · link · slot · image · ref

heading is not one of them. The canvas renderer tolerates type: "heading" for display, but the compiler doesn't recognize it, so the tag comes back undefined and the byte writer throws u8 out of range: undefined. The renderer and the compiler disagreed, and the compiler is the one that matters for anything that touches the chain.

The fix (already applied in the JSON above): a heading is a text node carrying the ARIA heading role.

{ "type": "heading", "content": "Settlement Desk" }              // ✗ compiles to u8 out of range
{ "type": "text", "content": "Settlement Desk", "role": "heading" }  // ✓ a real, compilable heading

Rule of thumb: if a value isn't one of the nine node types, it belongs in an attribute (role, variant, provenance, direction), not in type. See Gotchas, Do's & Don'ts.


3. Make sure the builder knows the resolver kinds

settle-* and trust-facets must appear in the slot's resolver kind picker. If they don't, your builder's embedded vocabulary is an older snapshot. Regenerate it from the one source of truth and rebuild the site:

cd /h/chainrender_deploy/chainrender-project
npx tsx scripts/sync-builder-vocab.mjs     # VOCAB.resolverKinds <- chainc/resolverkinds.ts (edits only that array)
# then your normal site build regenerates builder/index.html from builder/index-next.html

Confirm the kinds are in the source of truth first:

npx tsx -e "import('./chainc/resolverkinds.js').then(m=>console.log(m.RESOLVER_KINDS.map(k=>k.kind).join(' ')))" \
  | tr ' ' '\n' | grep -E 'settle-|trust-facets'

4. Preview, and read the fail-closed state

Hit Compile / Preview. With the fix from step 2, compile succeeds and the builder calls the interpreter's render(bytes) via eth_call, using the Interpreter address + RPC in the config bar (the gear). Two honest outcomes:

  • Interpreter/RPC not set → now you get a real interpreter/RPC error (this time the builder's hint is right). Set a valid interpreter for your chain and an RPC URL, then retry.
  • Set, but placeholder addresses → the layout, headings, and the grade badge render, and each slot shows "unavailable." That is the design: a Grade-A read fails closed rather than inventing a value. Nothing is wrong — you simply haven't pointed it at a real agreement yet.

Why "unavailable" and not an error? settle-status is a view read. With no real agreement at the address, there's nothing to read, so the honest render is "unavailable." See The Floor.


5. Point it at a real agreement (deploy + seed)

To see live numbers, deploy the stateless resolvers once per chain and a real agreement to read. These contracts are ownerless and immutable — additive, no migration. Never put your deployer key in shell history:

read -rs DEPLOYER_PK ; export DEPLOYER_PK    # hidden input; or: npx hardhat vars set DEPLOYER_PK
echo
# deploy SettlementCardResolver + TrustDisclosureResolver to your target chain (see the deploy guide
# for the exact script/flags for your setup), then deploy/seed an AttestedLoanChannel (or any
# ISettlementView) so there is real state to read.

Then, in the builder, replace 0x1111…1111 in all four slots with your deployed agreement address, set assetDecimals to the escrow asset (6 = USDC, 18 = native), and preview again. The status word, principal, and repaid-percent now render from chain, and trust-facets shows the agreement's declared custody / exit / relayer-binding.

See Deploying an App and Reading On-Chain for the per-chain specifics.


6. Confirm the grade

Check the grade badge: it should read Grade A — every slot is a READ_CHAIN resolver with an on-chain data edge. If you add an off-chain bind later (an event feed, a client fetch), the badge drops to ≤ C by construction and the honest label appears. That guard is the point: the badge can never over-claim.

You've now done the full loop — import → fix → preview → deploy → live → grade. The next two tours reuse this spine.

Next: Proof & Compute →