A starter template for building and deploying Solidity contracts to both EVM and PolkaVM (Polkadot) from a single project, using Hardhat + resolc + Foundry.
Click "Use this template" on GitHub to create your own repository, drop your contracts into src/, and deploy to Anvil, a local PolkaVM node, or a testnet with one command.
The same Solidity compiles two ways:
- EVM via
solc: Anvil, Base Sepolia, Ethereum Sepolia, any EVM chain. - PolkaVM via
resolc(@parity/hardhat-polkadot): a local revive dev node, Polkadot Hub testnet.
This template was produced under the ZK Email Kusama grant (Milestone 2) as the reusable project-structure / tooling outcome, stripped of the zkemail-specific verifier contracts and shipped with a plain Counter example so anyone can reuse it.
| Path | Purpose |
|---|---|
src/Counter.sol |
Example contract. Replace with your own. |
hh-ignition/modules/Counter.ts |
Hardhat Ignition deployment module (primary deploy path). |
test/Counter.t.sol |
Foundry test. |
hardhat.config.ts |
solc + resolc build targets and EVM / PolkaVM networks. |
foundry.toml |
Foundry build/test config. |
bin/setup-dev-node.sh |
Downloads the local PolkaVM dev node + eth-rpc adapter. |
- Node.js + Yarn
- Foundry (
forge,anvil,cast) for tests and the EVM local flow - macOS (arm64) or Linux (x86_64) for the bundled PolkaVM dev-node binaries
Install dependencies:
yarnCopy .env.example to .env and fill in what you need:
| Variable | Required | Description |
|---|---|---|
PRIVATE_KEY |
For deploys | EOA private key used to broadcast transactions (Hardhat accounts when set). |
ETHERSCAN_API_KEY |
Verification only | API key for an Etherscan-compatible explorer. |
localEvm and localPvm use a fixed http://127.0.0.1:8545 URL; PRIVATE_KEY is only attached when set, so local PolkaVM dev defaults work even when you omit it.
yarn build # hardhat compile (solc + resolc)forge testTerminal 1, fetch the dev-node binaries (once) and start the node:
yarn setup-pvm-node # download dev-node + eth-rpc binaries (once)
yarn pvm-node # start the local PolkaVM node + Eth-RPC adapterThis starts the revive dev node and an Eth-RPC adapter on 127.0.0.1:8545.
Terminal 2, build and deploy against the localPvm network:
yarn build
yarn deploy localPvmTerminal 1:
anvilTerminal 2, build and deploy against the localEvm network:
yarn build
yarn deploy localEvmNo PRIVATE_KEY needed: with none set, Hardhat uses Anvil's default unlocked account #0 (same as the localPvm flow). Set PRIVATE_KEY in .env only if you want to deploy from a specific account.
The deployer account needs testnet funds to deploy. Create a fresh keypair (Foundry's cast ships with this repo):
cast wallet new
# Successfully created new keypair.
# Address: 0xYourAddress...
# Private key: 0xYourPrivateKey...Put the private key into .env (add RPC_URL to override the default RPC for the target network):
PRIVATE_KEY=<the private key from your cast wallet new output above>
Fund the address with testnet PAS, then confirm it arrived:
- Faucet: https://faucet.polkadot.io/, select the Polkadot Hub TestNet (Paseo Asset Hub) and paste your address.
- Explorer:
https://blockscout-testnet.polkadot.io/address/<your address>
For the EVM testnets below, use that chain's own faucet (e.g. a Base Sepolia or Ethereum Sepolia faucet) to fund the same address.
Pass a network keyed by chain ID from hardhat.config.ts:
yarn deploy 420420417 # Polkadot Hub testnet (PolkaVM)
yarn deploy 84532 # Base Sepolia (EVM)
yarn deploy 11155111 # Ethereum Sepolia (EVM)Hardhat Ignition stores deployment artifacts under hh-ignition/deployments.
For EVM networks with a working Etherscan-compatible API (e.g. Base Sepolia):
yarn verify chain-84532Source-code verification is not currently possible for PolkaVM deployments. The contract is resolc-compiled to PolkaVM/RISC-V bytecode; the Blockscout explorer's verification API and @nomicfoundation/hardhat-verify both only support EVM solc/Vyper bytecode, and @parity/hardhat-polkadot does not yet provide a resolc-aware verify task. This is a PolkaVM tooling gap, not a deployment issue.
The contract is still fully visible on Blockscout (address, PolkaVM bytecode, transactions) and is exercisable via its read/write methods.
- Add your contract under
src/(removeCounter.solif you don't need it). - Update
hh-ignition/modules/Counter.ts(or add a new module) to deploy it, passing any constructor args viam.contract("YourContract", [args]). - Point the
deployscript inpackage.jsonat your module. - Update the Foundry
test/files to match.
| Command | Description |
|---|---|
yarn build |
Compile contracts with Hardhat (solc + resolc). |
yarn deploy <network> |
Deploy with Hardhat Ignition to the given network. |
yarn verify chain-<id> |
Verify an Ignition deployment. |
yarn setup-pvm-node |
Download the local PolkaVM dev node + eth-rpc adapter. |
yarn pvm-node |
Start the local PolkaVM node + Eth-RPC adapter. |
forge test |
Run Foundry tests. |