If you’ve spent time building extrinsics on Polkadot, you’ve probably fought with the same things I have. Is this balance in planck or DOT? Is this hex 32 bytes or 64? Did I get the SCALE encoding right? Most tools read the type name from metadata and give you a text field. You figure out the rest.
I built Relaycode to fix this. It’s a dual-pane extrinsic builder: human-readable form on one side, live SCALE-encoded hex on the other. Edit the form, hex updates. Paste encoded call data, the form populates.

You can connect your wallet (Polkadot.js, Talisman, SubWallet via LunoKit), sign, and submit transactions directly from the builder. Works across Polkadot, Kusama, and Westend right now.
relaycode.org | GitHub | Docs
How the inputs work
There’s a priority-based component registry under the hood. When the builder encounters a type, it walks the registry top to bottom and the first pattern match wins. So Compact<Balance> gets the Balance component (with denomination switching, planck display, and a Max button) before the generic Amount component catches it. Vec<u8> gets a multi-mode bytes editor (hex, text, JSON, Base64, file upload) instead of the Vector component trying to render individual u8 fields.
22 input components and 10 contextual selectors. A few I’m particularly happy with:
AccountId becomes a wallet-connected selector with identicons, display names, and balance previews. Handles MultiAddress wrapping automatically.

Balance reads the chain’s decimals and symbol. Type “1.5 DOT” and it converts to planck for you.

Call is a recursive mini-builder. This is what makes utility.batch actually usable – you build each inner call visually.

Enum, Struct, Tuple, Option, Vector, BTreeMap all compose recursively. Option<Vec<(AccountId, Balance)>> renders as a toggle wrapping a vector of tuples, each with an account selector and balance input. No special-casing for that combination – the components just delegate inner types back to the registry.
Contextual selectors
Type matching covers about 90% of cases, but some fields need on-chain context. A u32 representing a referendum index is more useful as a selector showing active proposals than a plain number field. So there’s a second layer – pallet-level overrides:
convictionVoting.vote.poll_indexshows active referenda with titles and vote tallies (via Polkassembly)staking.nominate.targetsshows validators with commission rates and identity (via Subscan)- Pool, Bounty, Asset, Track, and Era selectors work the same way – they query chain state and show what’s actually available

For power users
One of the earliest requests I got was from people who already work with raw encoded call data. They’d have a hex-encoded extrinsic from somewhere – a governance proposal, a multisig call, a batch they built before – and they just wanted to paste it, see what it actually contains, tweak one parameter, and resubmit. That’s exactly what the bi-directional editing does. Paste hex into the right pane, the form populates with decoded values. Change the one field you need, the hex updates, sign and submit.
This side-by-side view with two-way sync turned out to be the thing that makes the builder fast for people who already know what they’re doing.
Tech Stack
All the Polkadot integration is through Dedot by for type-safe chain interactions. Wallet connection is built from LunoKit. Frontend is Next.js + TypeScript + shadcn/ui. Both milestones of our W3F grant are complete.
What I’d like feedback on
The part I care most about is whether the input components feel right. What Substrate types or extrinsic workflows does the current tooling handle badly? What would you want a builder like this to do better?
I wrote a detailed walkthrough of every component here: Every Substrate Type Deserves a Better Input and an intro article if you just want the tl;dr Building an Extrinsic Builder for Polkadot
Links:
- Live: relaycode.org
- Component docs: relaycode.org/docs/components
- GitHub: github.com/itsyogesh/relaycode
