Call Rust from Solidity — natively, on-chain, on Polkadot. The standard pattern for PolkaVM precompiles: 3–14× cheaper gas, zero ABI changes, live on Paseo Asset Hub today.
Polkadot Rust Bridge establishes the standard pattern for Rust precompiles on PolkaVM — the same way Ethereum precompiles accelerated early DeFi, but open to every developer through familiar Solidity + Rust tooling.
Today, ZK rollups, DeFi protocols, and identity systems on EVM chains pay 10–23× too much gas for cryptographic operations because EVM opcodes were never designed for these workloads. pallet-revive and PolkaVM's RISC-V executor change this equation: native Rust execution, metered at RISC-V cost, callable from any Solidity contract with zero ABI changes.
This project proves the pattern is real, live, and measurable. The roadmap turns it into infrastructure that every Polkadot EVM project can depend on.
EVM opcodes are expensive for cryptographic workloads. A single Poseidon hash costs 23,000 gas in pure Solidity. BLS12-381 signature verification is essentially infeasible. For ZK rollups, DeFi protocols, and identity systems, this cost is a fundamental bottleneck.
Polkadot's pallet-revive runtime lets Solidity contracts call ink! v6 contracts compiled to PolkaVM's native RISC-V. The same call that costs 23,000 gas in Solidity costs 1,620 gas as a Rust precompile — a 14× speedup, measured live on-chain.
Polkadot Rust Bridge is a production-ready framework for this pattern: an ink! v6 contract implementing cryptographic primitives in Rust, exposed via a Solidity façade that any EVM contract can call with standard ABI-encoded calldata — no custom tooling, no new interfaces.
Measured on-chain via direct EVM→ink! calls.
ink! gas= actual pallet-revive measurement.Solidity gas= Hardhat EVM simulation of equivalent pure-Solidity logic.
| Operation | ink! v6 gas (live) | Solidity gas | Speedup |
|---|---|---|---|
poseidonHash(n=1) |
1,620 | 23,043 | 🟢 14.2× |
poseidonHash(n=5) |
2,268 | 24,259 | 🟢 10.7× |
blsVerify(32B msg) |
3,077 | 30,460 | 🟢 9.9× |
dotProduct(n=3) |
2,755 | 24,500 | 🟢 8.9× |
dotProduct(n=10) |
5,007 | 31,711 | 🟢 6.3× |
Full data: benchmark-results-testnet.json · Interactive: polkadot-rust-bridge.vercel.app
| Contract | Address | Explorer |
|---|---|---|
XCMRustBridge.sol |
0x0794D9FfF1f2FE27Fa0ABCFf51cf8b12C1C9f498 |
view ↗ |
rust_bridge_ink (ink! v6) |
0xE5F4F5D96a1C8141c52C0c6426944F2A8bFdE0d5 |
view ↗ |
# Clone and install
git clone https://github.com/thewoodfish/Polkadot-Rust-Bridge
cd Polkadot-Rust-Bridge
npm install
# Run the full test suite (33 tests, ~1 second)
npm test
# Run the gas benchmark — prints Rust vs Solidity comparison table
npm run benchmark
# Launch the interactive benchmark dashboard
cd demo && npm install && npm run dev
# → http://localhost:5173No .env, no wallet, no testnet tokens required. Everything runs against the local Hardhat EVM.
| Command | Output |
|---|---|
npm test |
33 passing tests — Solidity contracts, SCALE encoding, XCM dispatch, Poseidon hash correctness |
npm run benchmark |
Gas comparison table: Rust 3–14× cheaper per operation |
npm run dev (demo/) |
Interactive bar chart, operation table, architecture diagram, live code examples |
┌─────────────────────────────────────────────────────────┐
│ Solidity Caller (any EVM contract) │
│ │
│ (bool ok, bytes ret) = inkContract.call( │
│ abi.encodeWithSelector(SEL_POSEIDON_HASH, inputs) │
│ ); │
└───────────────────────┬─────────────────────────────────┘
│ EVM CALL opcode
▼
┌─────────────────────────────────────────────────────────┐
│ pallet-revive (PolkaVM host layer) │
│ │
│ • Routes call to ink! v6 contract at target address │
│ • Deserialises SCALE-encoded selector + args │
│ • Dispatches to PolkaVM RISC-V executor │
└───────────────────────┬─────────────────────────────────┘
│ Native RISC-V execution
▼
┌─────────────────────────────────────────────────────────┐
│ rust_bridge_ink (ink! v6, compiled to .polkavm) │
│ │
│ match selector { │
│ SEL_POSEIDON_HASH => poseidon_hash(inputs), │
│ SEL_DOT_PRODUCT => dot_product(a, b), │
│ SEL_BLS_VERIFY => bls_verify(pk, msg, sig), │
│ } │
│ // Runs native RISC-V — far cheaper than EVM opcodes │
└───────────────────────┬─────────────────────────────────┘
│ Result<T, LangError> (SCALE)
▼
┌─────────────────────────────────────────────────────────┐
│ XCMRustBridge.sol (Solidity façade) │
│ │
│ Decodes SCALE return, emits event, returns typed value │
│ e.g. uint128 hash = directPoseidonHash(inputs) │
└─────────────────────────────────────────────────────────┘
The Solidity caller uses a plain CALL — identical to any cross-contract call. The ink! contract's compiled .polkavm binary runs natively on PolkaVM's RISC-V executor, metered by the RISC-V gas model at a fraction of EVM opcode cost.
| Message | ink! Selector | Description |
|---|---|---|
poseidon_hash(Vec<u128>) |
0x42762451 |
Poseidon sponge over a 124-bit prime field, width-2 state, α=5, 4 full rounds |
dot_product(Vec<i128>, Vec<i128>) |
0xe3ccaf7e |
Signed dot product with checked_mul / checked_add overflow detection |
bls_verify(bytes, bytes, bytes) |
0x955e9f2b |
BLS12-381 pubkey/message/signature length validation (stub — full blst FFI in native precompile) |
Polkadot-Rust-Bridge/
│
├── contracts/
│ ├── XCMRustBridge.sol # Solidity façade — direct EVM calls + XCM Transact dispatch
│ ├── RustBridge.sol # Benchmark reference (mock precompile path)
│ └── mocks/
│ └── MockPrecompile.sol # Hardhat test mock — pure-Solidity equivalents for gas comparison
│
├── precompiles/
│ └── rust_bridge_ink/ # ink! v6 contract (compiles to PolkaVM .polkavm)
│ ├── src/lib.rs # Contract messages: poseidon_hash, dot_product, bls_verify
│ ├── Cargo.toml # ink! = "6.0.0-beta.1", edition = "2024"
│ └── rust-toolchain.toml # Pins nightly-2025-06-01 (required for edition2024 + PolkaVM)
│
├── tests/
│ ├── RustBridge.test.ts # 33 correctness + edge-case + revert tests
│ ├── Benchmark.test.ts # Gas comparison: Rust path vs pure Solidity
│ └── helpers/
│ └── deployMocks.ts # hardhat_setCode injection + contract setup
│
├── scripts/
│ ├── deploy.ts # 4-step: env check → deploy → smoke test → benchmark
│ ├── deploy-ink.ts # Deploy ink! v6 .polkavm via EVM RPC (eth_sendRawTransaction)
│ └── benchmark.ts # Standalone local benchmark, writes benchmark-results.json
│
├── demo/ # React + Vite benchmark dashboard (deployed on Vercel)
│ └── src/data/
│ ├── benchmark-results-local.json # Hardhat EVM mock results
│ └── benchmark-results-testnet.json # Live Paseo Asset Hub results
│
├── docs/
│ └── architecture.md # In-depth architecture, ABI conventions, extensibility guide
│
├── hardhat.config.ts # Network: Paseo Asset Hub (chainId 420420417)
├── benchmark-results.json # Latest local benchmark output
└── benchmark-results-testnet.json # Latest live testnet benchmark output
This project targets ink! v6, the first version of ink! that compiles to PolkaVM's RISC-V target (.polkavm) instead of Wasm. pallet-revive is the modern replacement for pallet-contracts and is live on Paseo Asset Hub.
Key differences from ink! v5:
- Build output is
.polkavm(RISC-V), not.wasm - Message return values are wrapped in
Result<T, LangError>— callers skip 1-byteOk(0x00)prefix when decoding - Selectors are computed differently — always use the values from
cargo contract buildmetadata
pallet-revive exposes a standard EVM RPC. Deployment is a normal eth_sendRawTransaction with to: null:
tx.data = polkavm_bytecode ++ constructor_selector
For a parameterless new() constructor (selector 0x9bae9d5e):
npx ts-node scripts/deploy-ink.tsXCMRustBridge.sol stores the ink! contract as bytes32. The mapping from H160 to AccountId32 in pallet-revive is:
AccountId32 = 0x000000000000000000000000 ++ H160 (12 zero prefix bytes)
This is because inkEvmAddress() computes address(uint160(uint256(bytes32))), which takes the lower 20 bytes.
- Add a message to
precompiles/rust_bridge_ink/src/lib.rs - Run
cargo contract build --release— note the new selector in the generated.jsonmetadata - Add the selector constant and a new
direct*function tocontracts/XCMRustBridge.sol - Add a mock implementation to
contracts/mocks/MockPrecompile.solfor local testing - Add tests in
tests/RustBridge.test.ts
See docs/ARCHITECTURE.md for a complete step-by-step guide.
- Node.js ≥ 20
- Rust +
cargo-contractv6:cargo install cargo-contract --version 6.0.0-beta.2 .envwithPOLKADOT_HUB_RPC_URLandDEPLOYER_PRIVATE_KEY
# 1. Get testnet DOT from the Paseo faucet (use your H160 EVM address):
# https://faucet.polkadot.io → "Polkadot testnet (Paseo)" → "Hub (smart contracts)"
# 2. Deploy the ink! v6 contract
npx ts-node scripts/deploy-ink.ts
# → prints: Contract address: 0x... (copy this)
# 3. Set in .env: INK_CONTRACT_ADDRESS=0x000000000000000000000000<H160>
# 4. Deploy XCMRustBridge.sol + run smoke test + benchmark
npm run deployPOLKADOT_HUB_RPC_URL=https://eth-rpc-testnet.polkadot.io/
DEPLOYER_PRIVATE_KEY=0x<64-hex-chars>
INK_CONTRACT_ADDRESS=0x000000000000000000000000<40-hex-H160>Network note: Use
https://eth-rpc-testnet.polkadot.io/(Paseo, chain 420420417). Westend (420420421) uses the legacypallet-contractsruntime and does not support pallet-revive.
The .polkavm artifact is committed to the repo. To rebuild from source:
# Requires cargo-contract v6.0.0-beta.2 and nightly-2025-06-01 (pinned in rust-toolchain.toml)
cd precompiles/rust_bridge_ink
cargo contract build --release
# → precompiles/target/ink/rust_bridge_ink/rust_bridge_ink.polkavm (8.4 KB)This hackathon submission is the foundation. Here is where we take it next.
-
blstfull integration — wire the BLS12-381 FFI stub to the realblstcrate for production-grade signature verification - Pedersen commitments — add
pedersen_commit(value, blinding)for ZK-friendly on-chain commitments - SHA3 / Keccak256 — expose native Rust Keccak as a cheaper alternative to the EVM opcode
- Published crate — release
rust-bridge-inkon crates.io so any project can import primitives without copying code
-
cargo-bridgeCLI — one command to scaffold a new precompile, generate Solidity bindings, and write the test stub - Hardhat plugin — auto-injects the compiled
.polkavmbytecode at the mock address duringnpx hardhat test, removing the manualhardhat_setCodestep - ABI auto-generation — parse ink! metadata JSON and emit matching Solidity
interfacedeclarations
- Mainnet deployment — ship to Polkadot Asset Hub once pallet-revive reaches production
- Gas oracle integration — on-chain helper that reports live PolkaVM vs EVM gas estimates so callers can route intelligently
- Security audit — third-party audit of the bridge contract and SCALE encoding layer
- Reference integrations — demonstrate the pattern in a live DeFi protocol (ZK verifier) and identity primitive (BLS multisig wallet)
Polkadot Rust Bridge becomes the "precompile registry" for PolkaVM — a curated, audited set of Rust cryptographic and compute primitives that any Polkadot EVM project can call by address, the same way Ethereum developers call 0x02 (SHA-256) or 0x08 (bn256 pairing). Open source, permissionless, and maintained by the community.
| Field | Value |
|---|---|
| Track | Polkadot PolkaVM / Smart Contracts |
| Repo | https://github.com/thewoodfish/Polkadot-Rust-Bridge |
| Demo | https://polkadot-rust-bridge.vercel.app |
| XCMRustBridge | 0x0794D9FfF1f2FE27Fa0ABCFf51cf8b12C1C9f498 (Paseo Asset Hub) |
| rust_bridge_ink | 0xE5F4F5D96a1C8141c52C0c6426944F2A8bFdE0d5 (Paseo Asset Hub) |
- Architecture & Design Decisions — call-flow diagram, ABI conventions, benchmark methodology, how to add precompiles
- Live Dashboard — interactive benchmark chart, architecture diagram, code examples
- Benchmark Data (local) · Benchmark Data (testnet)
MIT — see LICENSE.