Update on cargo-pvm-contract: a Rust SDK for smart contracts
cargo-pvm-contract is a Rust toolchain for writing smart contracts that compile to PVM and run on Polkadot through pallet-revive. The project has been in active development for a while and has reached a point where it’s worth sharing a public progress update.
What it is
Two authoring flavours, same host ABI:
- macro - annotated Rust (
#[contract],#[method],Lazy<T>,Mapping<K, V>) that derives dispatch, storage layout, and ABI encoding at compile time. The higher-level option, easier to start with. - DSL - a thinner abstraction. You write the
call()entry point yourself and chain methods onto aContractBuilder; storage is barehost.set_storage/get_storagewith hand-computed keys. Produces noticeably smaller bytecode once the contract has real surface area.
The choice is about how much plumbing is spelled out by hand vs. generated.
Both flavours are ABI- and storage-compatible with Solidity: function selectors and calldata encoding match solc, and storage uses the same keccak-derived slot keys and packed-field layout as Solidity contracts. The SDK makes that compatibility automatic, with concrete consequences:
- Ethereum tooling works unchanged. Libraries like ethers, viem, and the cast CLI deploy, call, and read SDK contracts - same selectors and storage slots.
- Cross-contract calls between Solidity and Rust work directly, and because the wire format and slot derivation match, an EIP-1967 proxy should be able to swap a Solidity implementation for a Rust one behind the same address with no adapter.
A token in the macro flavour, sketched to show the shape:
#[pvm_contract_sdk::contract(allocator = "bump")]
mod my_token {
use pvm_contract_sdk::{Address, HostApi, Lazy, Mapping, U256};
#[derive(pvm_contract_sdk::SolError)]
pub struct InsufficientBalance;
pub struct MyToken {
total_supply: Lazy<U256>,
balances: Mapping<Address, U256>,
}
impl MyToken {
#[pvm_contract_sdk::method]
pub fn balance_of(&self, account: Address) -> U256 {
self.balances.get(&account)
}
#[pvm_contract_sdk::method]
pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), InsufficientBalance> {
let from = self.caller();
let balance = self.balances.get(&from);
if balance < amount {
return Err(InsufficientBalance);
}
self.balances.insert(&from, &(balance - amount));
self.balances.insert(&to, &(self.balances.get(&to) + amount));
Ok(())
}
fn caller(&self) -> Address {
let mut caller = [0u8; 20];
self.host().caller(&mut caller);
Address(caller)
}
}
}
Why bytecode size matters on PVM
PVM bytecode is structurally larger than EVM bytecode, and every byte of contract code is charged as PoV (proof size) on every call. Smaller contracts aren’t a cosmetic property - they’re a per-call discount that compounds for the lifetime of the contract.
Across our benchmark suite, contracts built with cargo-pvm-contract are competitive with the same contracts compiled from Solidity to PVM. The numbers below are an early checkpoint from one benchmark run, not guarantees - they’ll move as the SDK matures - but the broad picture is consistent:
-
Rust SDK DSL is the smallest PVM implementation on every token-shaped contract - roughly a third smaller than Solidity (PVM) on the ERC-20/721/1155 benchmarks.
-
Rust SDK macro lands in the same ballpark as Solidity (PVM) on token-shaped contracts - within roughly ±15% either way - and is expected to stay around it as more features land.
Benchmark provenance: Rust SDK from
cargo-pvm-contractmainat commit2eec1fe; Solidity (PVM) compiled with theresolcnew_yorkbranch and Solidity (EVM) withsolc0.8.30. Numbers are from a single run on 2026-06-16 and will shift as the SDK and toolchain evolve.
Status: early preview
This is not a 1.0. Several things contracts will eventually want are still being wired up: deeper storage composition, a built-in reentrancy guard, and event-ergonomics polish. Bytecode sizes will move up as those land - the relative position vs. Solidity (PVM) should hold, but absolute numbers are an early checkpoint.
Where to look
-
pallet-revive (the smart-contract pallet): paritytech/polkadot-sdk – substrate/frame/revive
The SDK is being built in the open. Issues, PRs, and API feedback are all welcome - the surface stabilises over the next few months and input from real contract authors is more useful now than later.

