Part 6 of "Building on Polkadot Hub: A Builder's Journal"
TL;DR
Six parts in, here's what we'd tell the next team building on Polkadot Hub: the precompile layer is powerful but incomplete, governance design belongs at the start not the end, and the hardest engineering problem isn't any single contract — it's making two execution layers feel like one.
What We Built: By the Numbers
Before the lessons, a quick inventory of what this series covered:
- A complete token lifecycle platform: creation → bonding curve → graduation → DEX → permanent creator earnings
- EVM smart contracts operating on
pallet_assetsnative assets through precompiles - A custom Uniswap V2 fork (PlazaSwap) with integrated fee collection
- Burn & Earn: 90% LP permanently burned, 10% locked with FeeKeyNFT yield
- TimelockController governance on all upgradeable contracts
- Guardian emergency pause + immutable fee collection
- All contracts deployed and verified on Blockscout
Every feature in this list has two execution paths — one for EVM wallets, one for Polkadot wallets — unified by a frontend abstraction layer.
The Five Lessons
1. Precompiles are the bridge, and the bridge has gaps
ERC-20 precompiles on Hub expose pallet_assets to Solidity code. This is the feature that makes plaza.fun possible. But the precompile interface is incomplete — name(), symbol(), and decimals() panic instead of returning data. approve() fails in certain cross-contract call patterns. These aren't bugs in our code; they're gaps in the platform that every Hub builder will hit.
We reported issues, contributed to fixes, and built workarounds. But we lost weeks to debugging behavior that doesn't exist on any standard EVM chain. If you're building on Hub, budget time for precompile surprises. Test every ERC-20 function you use in the exact call pattern you'll use in production — a function that works in a direct call may fail when called from another contract.
2. Design governance from Day 1
We added TimelockController after the core contracts were done. It works — all upgradeable contracts now require time-delayed upgrades, and anyone can verify pending operations on-chain. But retrofitting governance is harder than building it in from the start.
The deployment scripts had to be rewritten. Ownership transfer had to be verified for every contract individually. Storage layout compatibility had to be checked again. If we started over, the Timelock would be in the first deployment script, not the last.
3. Immutable where possible, upgradeable where necessary
Every upgradeable contract is a contract that can be changed for the worse. The ProtocolFeeCollector is immutable — no owner, no upgrade path, no rescue function. It does one thing (collect fees and deposit them) and it will do that thing forever. That simplicity is its strongest security property.
We made FeeKeyNFT and the DirectAdapter non-upgradeable too, with renounceOwnership explicitly disabled. The pattern: if a contract's job is simple and unlikely to change, make it immutable. Reserve upgradeability for contracts whose requirements will genuinely evolve.
4. The frontend is the real abstraction layer
Polkadot Hub runs two VMs. Users arrive with MetaMask or Talisman. Every feature has two execution paths. The natural instinct is to solve this at the contract level — but contracts can't know which wallet type is calling them, and they shouldn't need to.
The real solution is the frontend. wagmi and polkadot-api run in parallel. A unified wallet store detects the connection type and routes transactions accordingly. The user sees one "Buy" button. Under the hood, the EVM path calls the contract directly; the Substrate path constructs a pallet_revive extrinsic that calls the same contract through a different entry point.
This abstraction layer is the single most important piece of code in our frontend. It's also the hardest to test — every feature needs verification with both wallet types, and some bugs only appear in one path.
5. Atomic operations prevent stuck states
Early graduation was a multi-step process. Early fee distribution used direct transfers only. Early token creation waited indefinitely for the Relayer. Each of these designs had a failure mode where the system could get stuck — a token that can't graduate, a trade that can't settle, a creation that hangs forever.
The fix was always the same: make it atomic, add fallbacks, and provide escape hatches.
- Graduation happens in one transaction with try/catch wrapping. Failure marks the token for retry, not permanent stuck.
- Fee distribution uses push-pull: try direct transfer, fall back to queuing.
- Token creation has a 2-hour timeout with creator-initiated reclaim.
- Admin
forceGraduate()exists as a last resort.
Every state transition should either complete or fail cleanly. Partial completion is the enemy.
What Surprised Us
The decimal mismatch is a constant tax. 18 decimals in EVM, 10 in Substrate. Every calculation that crosses the boundary needs explicit conversion. We thought we'd handle it once in a utility function. In practice, it comes up in dozens of places — display, calculation, validation, event parsing. It's not hard, but it's everywhere, and getting it wrong means silent accounting errors.
MetaMask doesn't know what Polkadot Hub is. MetaMask treats Hub like any EVM chain. It doesn't understand pallet_assets, precompile addresses, or mapped accounts. When a precompile returns unexpected data, MetaMask falls back to NFT display logic. When a native asset doesn't have decimals(), MetaMask shows raw wei values. Every MetaMask-specific workaround we built is technical debt that goes away when the precompile is complete.
The community responded to technical depth. Our Forum post with architecture details got more engagement than any marketing tweet. The Polkadot community — especially governance participants — values builders who share what they learn. The bugs we reported, the GitHub issues we filed, the workarounds we documented — these built more credibility than any announcement.
The Dual VM Decision
Polkadot Hub supports two execution backends: REVM (standard solc EVM bytecode) and PVM (resolc-compiled RISC-V bytecode). We tested both on Paseo testnet.
The results were decisive:
| EVM (REVM) | PVM (resolc) | |
|---|---|---|
| Bytecode size | Original solc output | ~10x bloat |
| Deploy gas | Low | ~10x higher |
| Execution gas | Standard EVM schedule | Extra per_byte + basic_block_compilation overhead |
| Storage deposit | Low (small bytecode) | ~10x higher (large bytecode) |
| Complex call chains (EVM RPC) | ✅ Works | ❌ Reverts |
| Complex call chains (Substrate wallets) | ❌ Weight limit | ❌ Reverts |
PVM's theoretical advantage is native RISC-V execution speed. In practice, resolc produces bytecode that's 10x larger (higher deploy cost, higher storage deposits), the basic_block_compilation overhead adds execution cost, and — critically — complex DeFi call chains fail on both transaction paths.
On REVM, our graduation flow executes at ~1.34M gas via EVM RPC. For Substrate wallet users, our async graduation pattern routes the heavy operation through EVM RPC via the relayer.
Our mainnet deployment will use REVM. When PVM's execution engine matures and resolc output sizes decrease, we'll re-evaluate. The official docs agree: "Most developers should start with REVM."
What We Reported Back
We filed two issues upstream to paritytech/polkadot-sdk:
- #11525 — Complex multi-contract call chains revert under
pallet_revive.callbut succeed via EVM RPC. Through testing, we found this is worse on PVM — with PVM bytecode, the same call chain reverts on both paths. With EVM bytecode, at least EVM RPC works. This was the deciding factor for REVM. - #11526 — Originally filed as a bytecode size limit issue. After deeper investigation, we clarified that the 24KB limit is EIP-170 (EVM path), and PVM's actual limit is 1MB. The issue led to productive dialogue with Parity's team about documentation gaps.
We also found that pallet_assets precompile limitations (name()/symbol()/decimals() panic, approve() cross-contract failure) affect both VMs equally. Our workaround patterns — DirectAdapter for approve, database-backed metadata for token info — are available for any Hub builder to reuse.
What's Next
Mainnet. The contracts are deployed on Paseo Asset Hub testnet via REVM. Mainnet launch is the immediate priority. The audit fixes are in. The governance architecture is deployed. The remaining work is operational — final testing, REVM deployment to mainnet, monitoring setup.
PlazaSwap as general-purpose DeFi infrastructure. PlazaSwap was built for graduation, but it's a complete Uniswap V2 fork — permissionless pair creation, multi-hop routing, multi-quote-token support. It already supports any ERC-20 pair on Hub: DOT/USDt, DOT/USDC, or anything else. As native stablecoins arrive and more tokens deploy on Hub, PlazaSwap becomes the default trading layer. We plan to expand with seed liquidity for major pairs, auto-routing, and pool discovery — making it Hub's first real DEX, not just a launchpad feature.
More integrations. Wallet dApp browser listings (Talisman, SubWallet, Nova). Better mobile experience. The infrastructure for multi-chain expansion exists in the architecture (QuoteTokenRegistry, multi-adapter graduation) but isn't the focus right now.
Continuing to build on Hub. We've filed issues, contributed fixes, and documented workarounds for Hub's EVM compatibility layer. As the platform matures — IERC20Metadata precompile support, better cross-contract call handling, pallet-revive improvements — our workarounds become unnecessary and the developer experience improves for everyone.
One Last Thing
We wrote this series because we think the Polkadot Hub ecosystem needs more builders sharing what they learn. The precompile gaps, the decimal mismatches, the governance patterns — none of this is documented outside of GitHub issues and our own codebase. If one team writes it down, the next team doesn't have to rediscover it.
If you're building on Hub and you've hit something we haven't covered, we'd love to hear about it. The more builders share, the faster the ecosystem matures.
This is Part 6 of "Building on Polkadot Hub: A Builder's Journal."
Start from the beginning: Part 0 — The Plaza Brief
All contracts are verified on Blockscout. Try the testnet at plaza.fun.
Follow @plaza_fun for updates. Forum discussion. · Discord
