NOTE:
This tutorial builds on top of a really nice post
by Bader Youssef about writing RISC-V Rust contracts on Polkadot.
Be sure to read it first beforehand!
Hey folks, this is Husni, a software engineer with almost five years of experience in blockchain and full-stack development. I've spent most of my professional career diving deep into Polkadot's ecosystem, tinkering with everything from Substrate nodes to dApps.
Recently, I got my hands on this neat little project: a Rust-based smart contract running on PolkaVM, which compiles to RISC-V for that bare-metal efficiency. The example is the classic "Flipper" contract, which toggles a boolean value, but pairing it with a frontend really brings it to life. The codebase is up on GitHub.
After poking around the web and X for frontend tooling, I found that Parity and the Web3 Foundation haven't rolled out anything specific yet for these Rust-style contracts. So I leaned on Ethereum-compatible libs like wagmi and viem, leveraging Polkadot's JSON-RPC adapter. It works surprisingly well, and community repos like this one are stepping in with basic hooks and UI bits.
Let me walk you through why this matters, how to get it running, and some detailed breakdowns of the code. This article is my perspective as someone who's built and broken plenty of these setups.
Why Bother with a Frontend?
Look, most users aren't going to fire up a terminal to poke at a smart contract. They want an app, with something that they can interact with and get real-time feedback. That's where a frontend shines: it turns your Rust logic into something clickable, like flipping that boolean in Flipper. It makes the tech accessible, pulling in users who wouldn't touch a CLI.
Plus, seeing it visualized sparks ideas. I've used simple UIs like this to brainstorm bigger things, like interacting with on-chain governance proposals or token swaps in dApps. It lowers the entry barrier, letting more devs experiment and push the Polkadot ecosystem forward.
A few more perks I've noticed:
- Wider Reach: Mobile and web users can jump in without installing tools, growing your audience beyond hardcore devs.
- Better Debugging: A visual layer helps spot contract quirks faster, which is great for teaching newbies too.
- Showcasing Efficiency: PolkaVM's perks, like cheaper fees and multi-lang support, really pop when demoed in a real app, nudging folks away from plain old EVM.
- Hybrid Plays: It proves Rust contracts can mingle with Solidity ones using the same frontend stack, opening doors for mixed dApps.
Setting It Up: The Nuts and Bolts
This project splits into a Rust backend for the contract and a Next.js frontend. It taps Polkadot's Ethereum compat layer for smooth sailing.
Tech Stack
-
Contract Side: Rust in
no_stdmode, PolkaVM for RISC-V, Foundry'scastfor JSON-RPC deploys. - Frontend: Next.js for React stuff, wagmi for wallets and hooks, viem for contract connection, TypeScript to keep type-safe. Tools: Rust/Cargo, Foundry, Node/npm.
- Network: Connect to Polkadot testnet at this URL, or spin up a local node.
Step-by-Step Setup
- Clone it:
git clone https://github.com/ical10/flipper-pvm.git && cd flipper-pvm
- Backend tools: Grab Rust/Cargo if needed, then
cargo install --force --locked cargo-pvm-contract
Install Foundry via
curl -L https://foundry.paradigm.xyz | bash && foundryup
- Build the contract:
cd contracts && cargo build
then it will generate target/flipper.debug.polkavm
- Deploy: Set envs like
export ETH_RPC_URL="https://services.polkadothub-rpc.com/testnet"
export PRIVATE_KEY=0xYourTestKey
plaintext
Then
cast send --private-key $PRIVATE_KEY --create "$(xxd -p -c 99999 target/flipper.debug.polkavm)" --json
plaintext
Jot down the address.
- Frontend:
cd frontend && npm install
plaintext
Add .env.local with
NEXT_PUBLIC_CONTRACT_ADDRESS=0xYourAddress
NEXT_PUBLIC_RPC_URL=https://services.polkadothub-rpc.com/testnet
Fire it up with npm run dev.
Boom – you're up and running locally, frontend talking to the contract.
How It All Hangs Together
The Rust contract compiles to RISC-V bytecode, deploys through Polkadot's Revive pallet (handles EVM and PVM), and the frontend uses Ethereum-like calls to interact.
Run the backend post-deploy with cast call for reads or cast send for writes. What about the frontend? npm run dev loads it at localhost:3000. Connect a wallet like MetaMask, see the state, hit "Flip".
The flow:
- Wallet connect via wagmi.
- Query
get()with viem to show true/false. - Button triggers
flip()tx via wagmi. - Tx hits JSON-RPC, PolkaVM runs Rust, updates storage, emits event.
- Frontend refreshes the state.
It's a bare-bones UI: state display and flip button. Under the hood, the contract uses get_storage/set_storage for the boolean. Frontend makes it feel like any Ethereum dApp, it is abstraction at its finest.
Code Deep Dive
Codebase splits: contracts/ for backend, frontend/ for UI.
Backend Bits
-
Cargo.toml: Rust deps like
alloy-sol-typesfor ABI,pallet-revive-uapifor host calls; with RISC-V targets. -
src/flipper.rs: Core logic in
no_std–sol!for ABI,get()checks selector, pulls from storage;flip()toggled -> storage updated -> event emitted. - Flipper.sol: Solidity ABI stub for viem encoding.
Frontend Breakdown
- package.json: Deps – next, react, wagmi, viem, react-query; scripts for dev/build.
- next.config.ts: Next.js tweaks, like wagmi transpiles.
- tsconfig.json: TS config for React/Next.
-
app/page.tsx: Center of the dApp – wagmi's
useAccount/useConnectfor wallets;useContractReadfor state;useContractWritefor flips. Simple render: connect, read state, button for updating state. Pulls ABI and address from env. - Extras:
.prettierrcfor clean code, README for tips.
Env handling in .env.local, Next.js app router for structure.
Pros and Cons of the Frontend Setup
The frontend (Next.js with wagmi/viem/React Query/TS) borrows Ethereum maturity for Polkadot. The backend itself gets PolkaVM speed, but frontend's where user magic happens. As a fresh repo (minimal commits), it's got solid stuff but room for growth.
Pros
- Easy Integration: Wagmi/viem plug right into Ethereum tools.
- Snappy UX: Next.js SSR/SSG for fast loads; React Query caches and refetches seamlessly.
- Safe and Modular: TS ensures type-safety; wagmi hooks keep code reusable.
- Chain Flexibility: Wagmi manages multi-chain, cuts boilerplate.
- Extensible: Next.js APIs for future backend; font opts boost perf.
- VM Bridge: Works for Rust or Solidity calls in Polkadot's setup.
Cons
- Semantic Differences: Revive's "almost compatible" EVM layer has subtle opcode and execution differences that might break viem/wagmi assumptions, causing unexpected errors in reads, writes, or events. References: 1, 2
- Account & Wallet Glitches: Native Polkadot accounts need manual Ethereum mapping; without it, MetaMask connections fail or signing drops in the frontend. Reference: 1
- JSON-RPC Adapter Overhead: The proxy might adds latency and translation bugs on spec mismatches, slowing real-time polling and transaction UX. References: 1, 2
- Tooling & Bytecode Limits: Incomplete EVM bytecode support and partial Hardhat integration force extra hacks, slowing testing and deployment workflows. Reference: 1
Wrapping Up
As a PoC, this frontend nails basics for a Rust contract like Flipper. For beefier stuff with multi-calls, you'll tweak hooks and errors for PVM quirks. Rust PVM's early days mean fast changes from Parity/community. This is great for innovation, but plan for maintenance.
Projects like this are important stepping stone for Polkadot dApps, merging Rust power with web ease. Tools will polish up, speeding adoption. Grab the repo, hack away. It's a killer starting point for frontier tech in Web3. Hit me up if you tweak it!
Top comments (0)