Delphinus Tutorial 1: Create your first zkWasm application


WebAssembly (sometimes abbreviated Wasm) is a binary code format designed for portable applications. Since it is a well-defined format, any language can have Wasm as a compilation target. Consequently, there are now around 40 high-level programming languages that support WebAssembly, including C and C++, Python, Go, Rust, Java, and PHP.

In this tutorial, we will show the following basic approach of making a ZK verifiable Wasm application using Delphinus’s zkWASM tool chain.

  1. Compiling a WASM image for your application.
  2. Generate a zkSNARK circuit for your Wasm.
  3. Run and prove the execution.
  4. Deploy verification contract on Goerli testnet.
  5. Verify your proof on Goerli testnet.

Compile the application into Wasm.

In this example, we will write a “hello-world” program that calculates the length of a string. The code can be written in C or assembly or rust as the following:

void require(int cond); // external host api describes a equal constraint.
unsigned long long wasm_input(int); // external host api for fetch user inputs.

char* string ="hello-world\0";

void zkmain() {
    int sum = wasm_input(1);
    for (int i=0; string[i]!='\0'; i++) {
       sum -= 1;
    require(sum == 0);

There are many ways to compile the above program into Wasm. In this tutorial, we will recommend using the wasm filddle online at For more sophisticated way to compile large C/assembly script/rust projects in Wasm, please refer to our following tutorials or checkout our related SDK (eg. zkWASM-C, zkWASM-Rust and zkWASM-AS).

By clicking the build button of you can compile the above C code into a Wasm image.

Once the image has been compiled, you can download the image binary. In this tutorial, we name the downloaded image as “tutorial-1.wasm”.

Generate a zkSNARK circuit for your Wasm.

There are two ways to generate circuits for you Wasm image. The standard way (use the bare metal zkWASM source code) is to install the git repo and use the command line tool as follows:

cargo run --release -- --function zkMain --wasm tutorial_one setup

However, since this is the first tutorial and to avoid getting our hands on a tedious command line toolchain, we use the easier way to generate the zkSNARK circuit using zkwasm-explorer at In we create a new application and submit our new generated tutor.wasm.

Now we get an application id 9070C…A08BC and can see that the zkwasm prover cloud is generating our circuit for us.

Once everything is done, we see the following.

Run and prove the execution

Now we can generate execution proof by starting a prove task and provide a correct input of our application.

Remark: We can also execute our application and generate proof for it using the command line by using the source code of zkWasm in

After the prove task is submitted, we can query the status and proof at the proof information modal.

Also we can query the proof once the proving task is finished as follows:

As we can see in the modal, the proof contains the inputs, transcripts, aux data (for batch field dividing in smart contracts) and instances of the proof batching circuits. (More information of proof batching can be found at our tutorial of proof batching.)

Deploy verification contract on Goerli testnet

It is very easy to deploy the verification contract on Goerli testnet by the “Depoly verify contract” function in

Verifying proof on Goerli testnet

After we deployed the verification contract by using the “Deploy verify contract” function and provide our image ID). We see that our deployed verification contract is deployed with address (Currently Goerli testnet as default).

After the contract is deployed on the testnet, we can verify the proof through the “verifier on chain” button in the proof modal

or integrate the verify process with our local application via the following typescript code (See Tutorial-4: Integration your application with zkWasm onchain verification).

let image = await zkwasmImageHelper.queryImage(info.task.md5);
let address = image.deployment[0].address;
let verify_contract = new web3.eth.Contract(contract_abi.abi, address);
let args = parseArgs(task.public_inputs).map((x)=> x.toString(10));
let result = await verify_contract.methods.verify(aggregate_proof, instances, aux, [args]).call();

More details can be found in the following tutorial video.