⚠️ 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.
Reference: Capabilities (what ChainRender can do, and how)
Every capability an app can use, with the mechanism, the arguments, the trust grade it earns and when to reach for it. This is the same capability pack the visual builder gives its AI, from the same file (ai/capability-pack.json), so an AI reading this page and the builder's AI see identical guidance.
Two companion pages are exact by construction: All Resolver Kinds (every kind's arguments and encoding, and where its contract is deployed) and Deployed Addresses (every address, per chain). Take arguments and addresses from those pages, never from an example below.
Workflow
TARGET: the builder tells you to produce a component | miniapp | standalone (see outputTargets). Structure your output to match. If not told, infer from complexity; default to component.
Compose a Node tree using ONLY the nodeTypes below.
For live data, use a slot/image with a resolver 'bind' from resolverKinds.
For user actions (write/mint/claim), use a button with a declared action {to, sig, value?, args}.
Keep it Grade A: on-chain reads + on-chain art, no off-chain fetch. Rich media (p5/three) is ⚙, honestly labeled.
The output is graded + validated + compiled by ChainRender — you don't need to be trusted, your output is checked.
Node types
Type
What it is
box
container; direction row|column; holds children; the layout primitive. Layout/spacing/surface style tokens are client 0.20 Enhanced mode (VERSION_5): its interpreter is deployed, but the viewer does not style them yet (docs/202 phase 3), so do not use them.
text
text/heading/status/label/note (role attr); the content primitive
button
a clickable that carries a declared action (or is inert); NEVER arbitrary code
input
a form field (text/number); its value feeds action args via $name
icon
a named on-chain icon glyph
link
an anchor (href)
slot
binds live on-chain DATA via a resolver (renders a value/region)
image
binds on-chain MEDIA/art via a resolver (renders SVG/data-URI)
ref
references another registered component by hash (composition)
Capability bits (declare what the app does)
Bit
When to declare it
READ_CHAIN
the universal baseline; any on-chain read. Grade A.
WRITE_CHAIN
declare when the app has a state-changing action (button that writes).
TRANSFER_VALUE
declare when an action carries ETH value (e.g. payable mint). Value-smuggle is refused if undeclared.
SIGN_MESSAGE
declare for EIP-712 typed signing.
BATCH_EXECUTE
declare for batched actions (one signature, N gated calls).
CLIENT_FETCH
declare for an off-chain fetch (drops grade to C unless it's a fail-closed enhancement over a floor).
GPU_EXECUTE
declare for GPU/rich-media enhancement (SDF ray-march, p5/three) — must have an on-chain poster floor.
Output targets
The builder tells you WHICH target to produce (a user choice). Structure your output to match it. If unspecified, infer from complexity: a single piece → component; a self-contained embeddable widget → mini-app; a multi-screen app with navigation → standalone. Default to 'component' when ambiguous.
Target
What
Produce
Compiled by
Use when
component
A single reusable node tree — one registered component, one content hash.
Return ONE Node tree (a box with children). No views, no composition.
compileComponent(tree) → { hash, hex }
a card, a chart, a mint widget, a profile badge — a piece meant to stand alone or be embedded/referenced elsewhere.
miniapp
A composition: a scene/shell + N placed components in a declared arrangement. Forkable, embeddable, capability envelope = UNION of placed components.
Return an array of component trees + an arrangement (which component goes where). Each component is compiled, then combined via buildComposition.
a self-contained widget others can fork or place — a Farcaster frame, an embeddable dashboard, a composed studio. Smaller than a full app, more than one component.
standalone
A full app: multiple VIEWS with in-app navigation + its own capability envelope + (optional) a persistent shell.
Return an array of view trees (each a Node tree) + the nav type (tabs/wizard/master-detail/modal) + an optional shell. Each view compiles to a hash; combine with buildTabs/buildWizard/buildMasterDetail/buildModal.
⚙ above the floor: the on-chain render shows the fallback text to everyone; a ChainRender viewer fills it with this chain's own resolver only after the person chooses to show their data in this app
Use when
show the connected person their own balance, token balance, primary ENS name or identicon. Not for anything that decides what the app shows or does: "$viewer" never goes into an action, a signature or a condition. No other kind can read it (no portfolio total).
⚙ above the floor: the viewer reads the chain's logs (paged eth_getLogs) and states the block window it read; zero-value and dust transfers are hidden and counted, look-alike addresses are flagged, tokens the app did not name are marked unverified
Use when
recent transfers for the viewer (needs their choice to show their data) or for a fixed address or token. Say next to it that it covers a recent block window, not all time.
universal-resolver path; offchain records surface as 'off-chain (CCIP)'
ENS profile (identicon/avatar)
How
image bind 'gidenticon' (on-chain identicon). An ENS avatar is an off-chain pointer: read the avatar text record with 'enstext', and any image fetched from it is CLIENT_FETCH
Grade
A (identicon) / C (fetched avatar)
Use when
a profile picture — on-chain identicon (A) or ENS avatar (needs CLIENT_FETCH → C)
ENS app manifest
How
slot bind 'ensApp' (ENS text key chainrender.app)
Arguments
node:bytes32 (ENS namehash)
Grade
A
Use when
read a name app-manifest text key (chainrender.app) and render/launch the on-chain app it names — ENS as the naming layer for ChainRender apps (dogfood chainrender.eth); works wherever ENS text resolves
the primary ENS name an address resolves to ON A GIVEN CHAIN (chain-aware identity), forward-verified addr(node,coinType)==who. Renders a live name only where an ENSIP-19 reverse registrar for that coinType is populated
prove membership of a set (allowlist/DAO) WITHOUT revealing which member; a valid proof renders the checkmarked statement, a bad one renders 'Unverified'
image bind chart + ChartSlotAdapter + IChartData source (chart.encodeChartSlotArgs); a Chainlink price history is authored from argValues (see "Chainlink history chart")
Grade
A
Use when
a data chart, static or bound to a live on-chain source; fails closed to axes-only
any state-changing call (mint/claim/swap/transfer) — the runtime constructs EXACTLY the declared calldata; the button can't call anything else or smuggle value
A (custody and release enforced by your contract); anything decided off-chain must name who attests it
Use when
hold funds until a rule releases them. The settlement framework (SettlementChannel, LoanChannel, AgreementRegistry) is built but NOT deployed — check Deployed Addresses
Example
GoalEscrow: all-or-nothing fundraising with refunds
gather an EIP-712 signature (permits, votes, orders); chainId-checked. The message is signed exactly as written: no $name inputs, literal values that fit their types (review rule sign-message)
claim myapp.chainrender.eth anonymously — permissionless, gated only by a REFUNDABLE anti-spam stake; label must pass the on-chain charset gate (lowercase a-z0-9-, no confusables)
ALWAYS runs on the composed app: A/B/C letter + itemized deviations; the enforcer REFUSES unsafe apps (arbitrary-code escape hatch, dishonest capability envelope, unknown vocabulary). You don't invoke it — it's the safety net over your output.
token:address, holder:address (can-transfer: token, from, to, amount)
Grade
≤ C
Use when
DISPLAY a holder's ERC-3643 compliance (verified/frozen) or a canTransfer preview — never enforcement; a real transfer re-checks on chain. Identity registry is agent-mutable → ≤ C (DEP_MUTABLE), even though the read is on-chain
does a caller-named EFP list currently follow / block / mute / tag a target (FORWARD read, bounded scan). Follower counts / reverse lookups are OFF-CHAIN (indexer) → grade ≤ C, never render them as an on-chain fact
one attester's vouch (vouched/expired/revoked/unknown) or a k-of-n over a CALLER-supplied attester set. Delegated to a mutable attester → ≤ C. Never a global score; change the set, change the answer
transparent k-of-n across caller-supplied sources (EFP, vouches, EAS, ERC-725, future), rendered 'k/n (grade X)' where X is capped at the weakest source. An A EFP follow can't launder a C vouch into trustlessness
slot bind 'staticread' (CallResolver) at the account's ERC-725Y data key — STATICCALL against a co-located contract on the resolver's chain (typed Erc725ClaimResolver planned)
Arguments
target:address (the ERC-725Y/735 account, on the resolver's chain), callData (getData(key)), returnType, format
Grade
≤ C
Use when
read an ERC-725Y data key or ERC-735 claim from an account co-located on the resolver's chain (Sepolia/Robinhood/Arc) — the standard behind LUKSO Universal Profiles, but LUKSO's chain is NOT integrated (a STATICCALL can't reach a UP that lives only on LUKSO). Owned/mutable source → ≤ C, rendered as 'the current claim of authority Y', never as proof
render the DISCRETE order a CoW Composable-CoW conditional order (TWAP/StopLoss/GoodAfterTime/…) would emit THIS block, with no server; a revert = no valid order now -> 'not active' (fail-closed). The first intent-DeFi resolver: a trustless, serverless order monitor
render a TWAP order's schedule progress — 'part 3/10 - live', next-slice countdown, per-part sell/min-limit, status pending/active/gap/finished. Renders the DEFINITION's schedule (not proof of registration) — pair with 'cowOrder' for live tradeability
slot bind 'cowIntent' (on-chain keccak256 of the app-data document)
Arguments
fullAppData:bytes (the stringified app-data doc shown to the user), expectedAppData:bytes32 (order appData, e.g. from cowOrder), field:uint8 (0=verdict verified/app-data mismatch 1=computed hash hex)
Grade
A
Use when
anti-phishing 'verify what you sign': prove a presented CoW app-data document (carrying the order metadata + pre/post hooks) is byte-identical to the hash the order committed (keccak256(fullAppData)). Tamper one byte -> app-data mismatch. The human-readable hook plan is a CLIENT decode over these hash-verified bytes (pair with cowOrder)
vault:address, assetDec:uint8, shareDec:uint8, field:uint8 (0=NAV per share in asset units 1=totalAssets 2=totalSupply shares 3=shares per 1 asset)
Grade
A
Use when
render a tokenized vault's NAV per share, total assets, share supply — the tokenized-treasury surface; convertToAssets is the vault's OWN accounting (state the trust boundary). Composes with por-coverage + oracle-feed for a full vault panel
render the async request/settle queue (pending/claimable redeem & deposit) for a controller — the redemption queue RWA/T-bill funds hide behind a dashboard, read trustlessly
A for the lobby floor and the settlement; the game screen (signing moves, transport) is a client page, labelled as such
Use when
a turn-based two-player game with an on-chain waiting room and an enforced result (tic-tac-toe, commit-reveal games). NOT for real-time games (racing, physics): the rules cannot check them — no stakes on those. Exactly two players.
Example
floor: staticread slots on GameLobbyV2.offerCount()/offerStake(id) + a button action {to: GameLobbyV2, sig: 'join(uint256)', value: '', args: '$offerId'}; rules: TicTacToeRules or RockPaperScissorsRulesV2