Documentation Index
Fetch the complete documentation index at: https://companyname-a7d5b98e-closes-1950-ai-ai-ai-ai-ai-ai-ai.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
This guide shows how to create, compile, and test a simple Circom scheme and verify a ZK-proof using the zk-SNARK Groth16 protocol.
Prerequisites
Project setup
-
Create a new project using Blueprint:
npm create ton@latest ZkSimple
cd ZkSimple
-
Install libraries for working with ZK-proofs:
npm install snarkjs @types/snarkjs
-
Install the verifier export utility for TON:
npm install export-ton-verifier@latest
This utility exports verifier contracts for FunC, Tolk, and Tact.
Create the directory circuits/Multiplier and the file Multiplier.circom:
mkdir -p circuits/Multiplier
cd circuits/Multiplier
pragma circom 2.2.2;
template Multiplier() {
signal input a;
signal input b;
signal output c;
c <== a*b;
}
component main = Multiplier();
This circuit proves knowledge of two numbers a and b, whose product is equal to the public output c, without revealing a and b themselves.
Compile
Run in circuits/Multiplier:
circom Multiplier.circom --r1cs --wasm --sym --prime bls12381
After compilation, the following files will appear:
Multiplier.r1cs — circuit constraints (R1CS)
Multiplier.sym — symbolic signal map
Multiplier.wasm — artifact for generating proof
Check constraints:
snarkjs r1cs info Multiplier.r1cs
Output example:
[INFO] snarkJS: Curve: bls12-381
[INFO] snarkJS: # of Wires: 4
[INFO] snarkJS: # of Constraints: 1
[INFO] snarkJS: # of Private Inputs: 2
[INFO] snarkJS: # of Public Inputs: 0
[INFO] snarkJS: # of Outputs: 1
Trusted setup (Groth16)
The trusted setup is a one-time ceremony that generates the proving and verification keys for a circuit. It’s called “trusted” because if the setup parameters are compromised, proofs could be forged. For production use, participate in a multi-party trusted setup ceremony. For local development and testing, a simplified single-party setup is sufficient. For local tests, perform a simplified trusted setup ceremony.
The “power of tau” parameter (10) has to be chosen
- as low as possible, because it affects execution time;
- high enough, because the more constraints in the scheme, the higher the parameter required.
# first phase
snarkjs powersoftau new bls12-381 10 pot10_0000.ptau -v
snarkjs powersoftau contribute pot10_0000.ptau pot10_0001.ptau --name="First contribution" -v -e="some random text"
# second phase (depends on the compiled scheme)
snarkjs powersoftau prepare phase2 pot10_0001.ptau pot10_final.ptau -v
snarkjs groth16 setup Multiplier.r1cs pot10_final.ptau Multiplier_0000.zkey
snarkjs zkey contribute Multiplier_0000.zkey Multiplier_final.zkey --name="1st Contributor" -v -e="some random text"
# export verification key
snarkjs zkey export verificationkey Multiplier_final.zkey verification_key.json
Clear up unnecessary artifacts:
rm pot10_0000.ptau pot10_0001.ptau pot10_final.ptau Multiplier_0000.zkey
Export the verifier contract
Return to the Blueprint project root before running these commands if you are still in circuits/Multiplier:
# export Tolk contract (default for Groth16)
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.tolk
# export FunC contract
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.fc --func
# export Tact contract
npx export-ton-verifier ./circuits/Multiplier/Multiplier_final.zkey ./contracts/verifier_multiplier.tact --tact
For FunC and Tolk, generate the wrapper that matches your contract target in ./wrappers/:
# Copy the TypeScript wrapper for Tolk
npx export-ton-verifier import-wrapper ./wrappers/Verifier_tolk.ts --groth16 --force
# Copy the TypeScript wrapper for FunC
npx export-ton-verifier import-wrapper ./wrappers/Verifier_func.ts --groth16 --func --force
These commands copy TypeScript wrapper files that provide type-safe methods for interacting with the verifier contract. If you keep both wrappers in the same project, update the test import to match the wrapper you generated: use Verifier_tolk for Tolk and Verifier_func for FunC.
Testing and verification
In tests/ZkSimple.spec.ts:
import * as snarkjs from 'snarkjs';
import path from 'path';
import { dictFromInputList, groth16CompressProof } from 'export-ton-verifier';
// for Tact (After running `npx blueprint build --all`)
import { Verifier } from '../build/Verifier_tact/tact_Verifier';
// for Tolk
import { Verifier } from '../wrappers/Verifier_tolk';
// for FunC
import { Verifier } from '../wrappers/Verifier_func';
Local verification:
const wasmPath = path.join(__dirname, '../circuits/Multiplier', 'Multiplier.wasm');
const zkeyPath = path.join(__dirname, '../circuits/Multiplier', 'Multiplier_final.zkey');
const verificationKey = require('../circuits/Multiplier/verification_key.json');
const input = { a: '342', b: '1245' };
const { proof, publicSignals } = await snarkjs.groth16.fullProve(input, wasmPath, zkeyPath);
const okLocal = await snarkjs.groth16.verify(verificationKey, publicSignals, proof);
On-chain verification:
const { pi_a, pi_b, pi_c, pubInputs } = await groth16CompressProof(proof, publicSignals);
// Quick check via get-method: verifies the proof locally without changing blockchain state.
expect(await verifier.getVerify({ pi_a, pi_b, pi_c, pubInputs })).toBe(true);
// Send the proof to the contract in a message
// The contract will run verification; handling the result/flow is up to the developer using this template.
await verifier.sendVerify(deployer.getSender(), { pi_a, pi_b, pi_c, pubInputs, value: toNano('0.15') });
Other Languages
This tutorial follows the path Circom → snarkjs → export-ton-verifier → TON.
The same workflow applies to other stacks — the key requirement is to obtain a proof and a verification key in snarkjs format.
In the example repository — zk-ton-examples — there are already templates for noname, gnark, and arkworks: proofs can be generated in any of these stacks, then converted into snarkjs format and verified both locally and on-chain in the same way.
The idea is always the same: generate proof.json and verification_key.json in snarkjs format, then use export-ton-verifier and perform verification in TON.
Arkworks (Rust)
Use the arkworks library to generate the proof and verification key, then convert them into snarkjs format with ark-snarkjs.
- Set up an Arkworks project:
cargo init
cargo add ark-bls12-381 ark-ff ark-groth16 ark-r1cs-std ark-relations ark-snark ark-snarkjs ark-std rand@0.8.5
-
Write the circuit in Rust. Implement the circuit logic using
arkworks primitives, similar to how a Circom circuit would be written. Learn how to write constraints in arkworks by following the Arkworks R1CS tutorial. A working example of a simple multiplication circuit can be found in the zk-ton-examples repository.
-
Compile, generate proof, and perform trusted setup following the same workflow as in the Circom section above.
-
Export the proof and verification key to JSON using
ark-snarkjs:
use ark_snarkjs::{export_proof, export_vk};
use ark_bls12_381::{Bls12_381, Fr};
let _ = export_proof::<Bls12_381, _>(&proof, &public_inputs, "json/proof.json");
let _ = export_vk::<Bls12_381, _>(
¶ms.vk,
public_inputs.len(),
"json/verification_key.json",
);
The directory and files will be created automatically.
- Export the verifier contract:
# Tolk contract (default)
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.tolk
# FunC contract
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.fc --func
# Tact contract
npx export-ton-verifier ./circuits/Arkworks/MulCircuit/json/verification_key.json ./contracts/verifier_ark.tact --tact
gnark (Go)
Use the gnark library to generate the proof and verification key, then convert them into snarkjs format with gnark-to-snarkjs.
-
Set up a
gnark project. You can find an example circuit in the gnark repository. A working example of a cubic circuit can be found in the zk-ton-examples repository.
-
Add
gnark-to-snarkjs as a dependency:
go get github.com/mysteryon88/gnark-to-snarkjs@latest
- Export the proof and verification key:
{
proof_out, _ := os.Create("proof.json")
defer proof_out.Close()
_ = gnarktosnarkjs.ExportProof(proof, []string{"35"}, proof_out)
}
{
out, _ := os.Create("verification_key.json")
defer out.Close()
_ = gnarktosnarkjs.ExportVerifyingKey(vk, out)
}
- Export the verifier contract:
# Tolk contract (default)
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.tolk
# FunC contract
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.fc --func
# Tact contract
npx export-ton-verifier ./circuits/cubic-gnark/verification_key.json ./contracts/verifier_cubic.tact --tact
Conclusion
This guide demonstrates a minimal example: circuit → trusted setup → verifier export → verification in TON. This workflow can be extended to support more complex circuits and real-world applications.
Useful Links