Automated generation of ink! v6 smart contract verifiers from Noir zero-knowledge proof circuits for PolkaVM.
ink! Verifier Factory(iVF) generates native PolkaVM (RISC-V) verifier contracts from Noir zero-knowledge circuits, offering a performance-optimized alternative to Solidity verifiers on Pallet-Revive.
Pallet-Revive now supports Solidity verifiers, making ZKPs possible on Polkadot. However, iVF gives you ~25% gas savings, 45% smaller binaries, Rust type safety, and seamless Polkadot ecosystem integration. Choose Solidity for rapid migration, choose ink! for optimal performance.
- One-Command Generation: Transform Noir VK files into production-ready ink! v6 contracts
- Native PolkaVM Integration: Uses Pallet-Revive's BN128 precompiles for optimal performance
- Rust Type Safety: Compile-time guarantees that Solidity can't provide
- Circuit Agnostic: Handles circuits of any complexity (tested from 57 to 128 field elements)
- Production Ready: Comprehensive error handling with detailed diagnostics
- Gas Optimized: Efficient field arithmetic and minimal storage footprint
- Zero External Dependencies: Fully self-contained verification on-chain
- Rust 1.75+ with
riscv64emac-unknown-none-polkavmtarget (for ink! v6) - cargo-contract v6.0.0 (ink! v6 support)
- Noir and nargo CLI
- Node.js 16+ (optional, for testing)
# Clone the repository
git clone https://github.com/Lynette7/ivf.git
cd ivf
# Install Rust and RISC-V target for PolkaVM
rustup default stable
rustup target add riscv64emac-unknown-none-polkavm
# Install cargo-contract with ink! v6 support
cargo install --force --locked --version 6.0.0-beta.1 cargo-contract
# Install Noir (via noirup)
curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash
noirup --version nightly
# Verify installations
cargo contract --version
nargo --version
rustc --version# Create new Noir project
cd noir-circuits
nargo new my_circuit && cd my_circuitEdit src/main.nr:
fn main(secret: Field, public_output: pub Field) {
assert(secret + 1 == public_output);
}# Generate a Prover.toml file to specify our input values
nargo check
# Compile and execute circuit, as well as generate the witness
nargo execute
# Generate VK using Barretenberg
bb write_vk -b ./target/<noir_artifact_name>.json -o ./target --oracle_hash keccakcd ../../ink-generator
cargo run -- \
--vk ../noir-circuits/my_circuit/target/vk \
--output ../generated_verifier/src/lib.rscd ../generated_verifier
# Build the contract (compiles to PolkaVM)
cargo contract build- Deploy the
.contractfile using the contracts UI on Passet Hub network
# Generate proof from your Noir circuit
cd ../noir-circuits/my_circuit
echo 'secret = "5"\npublic_output = "6"' > Prover.toml
nargo prove
# Call verifier contract (example using Polkadot.js)
# proof_bytes = fs.readFileSync('proofs/my_circuit.proof')
# public_inputs = ['0x06'] // 6 in hex
# result = await contract.verify(proof_bytes, public_inputs)The VK file is a binary file containing field elements in big-endian format:
- Metadata (first 3 fields):
circuit_size,log_circuit_size,public_inputs_size - G1 Points (remaining fields): Pairs of coordinates
(x, y)for polynomial commitments
Offset | Content | Size
-------|------------------------|-------
0x0000 | circuit_size | 32 bytes
0x0020 | log_circuit_size | 32 bytes
0x0040 | public_inputs_size | 32 bytes
0x0060 | ql.x | 32 bytes
0x0080 | ql.y | 32 bytes
... | ... | ...
#[ink(message)]
pub fn verify(
&self,
proof: Vec<u8>, // Raw proof bytes from nargo prove
public_inputs: Vec<Vec<u8>> // Public inputs as 32-byte field elements
) -> Result<bool, VerifierError>Returns:
Ok(true)- Proof is validOk(false)- Should never happen (proof is either valid or error)Err(VerifierError::*)- Verification failed with specific error
- Parse Proof: Extract commitments, evaluations, and sumcheck polynomials
- Transcript Generation: Derive Fiat-Shamir challenges from proof data
- Public Input Delta: Compute contribution of public inputs to grand product
- Sumcheck Verification: Verify log(N) rounds of the sumcheck protocol
- Relation Checking: Evaluate all UltraHonk relations at challenge point
- Opening Verification: Verify polynomial openings using KZG commitments
- Pairing Check: Final cryptographic check using bn128pairing precompile
- Elliptic Curve: BN254 (alt_bn128)
- Scalar Field:
p = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - Operations: Point addition, scalar multiplication, pairing checks
- Precompiles: Leverage Pallet-Revive's native implementations for PolkaVM
| Use Solidity (Pallet-Revive) | Use ink! v6 (iVF) |
|---|---|
| Quick Ethereum migration | Building Polkadot-first apps |
| Multi-chain deployment | Maximum performance needed |
| Existing Solidity codebase | Type safety critical |
| Rapid prototyping | Production-grade systems |
| Team only knows Solidity | Deep Polkadot integration |
- Noir by Aztec for the ZKP framework
- ink! for the smart contract language and v6 PolkaVM support
- Barretenberg for the UltraHonk proving system
Pallet-Revive brought ZKPs to Polkadot. iVF makes them native.

