Noir 101 for Solidity Devs

In this tutorial, we will create a simple “Secret Public Contract”, using Noir and Foundry. The contract will be deployed with an execute function which can only be executed with a secret calldata, meaning that only if you know the secret function and prove it, you can execute the contract functionality.

You can see the complete code used in this tutorial in the following repository.

Getting Started

Noir is a Domain Specific Language for SNARK proving systems. Its design choices are influenced heavily by Rust and focus on a simple, familiar syntax. While Noir can be used for different purposes this tutorial focuses on Solidity development. We will use Noir to create the circuits and Foundry to test the Solidity contracts.

Installing Noir

As Noir evolves constantly, the recommended way to install it is by using noirup which installs Nargo, a command line tool for interacting with Noir programs (compiling, proving, verifying, creating contracts, etc). In OSX or Linux run the following command on the terminal:

$ curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash

You can check that the installation was successful by running nargo --version. Other options for installation are available in the official documentation.

Installing Foundry

Just like Noir, the recommended way to install Foundry is by using foundryup. Run the following command to install it:

curl -L https://foundry.paradigm.xyz | bash

As it’s outside the scope of this tutorial, you can read more information about Foundry on the official documentation site.

Installing Noir Language Support (Optional)

If you use VS Code I will suggest installing the Noir Language Support extension, It adds syntax highlighting, compilation errors and warnings, and useful snippets.

Creating a Project

With Nargo and Foundry installed, we can make a project. While we can use the forge init projectName and nargo new projectName commands, we would need to do some file ordering and cleaning, so I will suggest you use the Noir starter with the Foundry repo.

You can fork it or use it as inspiration to order the files from scratch. Just keep in mind that you will need to edit the foundry.toml file and add ffi and fs_permissions for Foundry tests to access the proofs generated by noir.

[profile.default]
src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read-write", path = "./"},{ access = "read-write", path = "/tmp/"}]
ffi = true

# See more config options https://github.com/foundry-rs/foundry/tree/master/config

Creating the Circuits

We are ready to create our circuit, move to the circuits/src folder and create a new Noir file main.nr. This is a pretty simple circuit, only checks that the values sent from the contract are the ones we define in secret. We use the Pedersen hash function to create a unique hash based on the parameters then we compare them with the hash of our secret parameters to see if they match.

main.nr
main.nr

We can then build the output files by running:

$ nargo check 

This will generate two files: Prover.toml which holds input values and Verifier.toml which has public values. We fill in the values on the Prover.toml if we already know which function we want to call and prove the valid execution by creating a proof running:

nargo prove nameOfProof

This is going to generate a new folder proofs and it will contain a proof file with the name that you specify, depending on your computer and code this command might take a few seconds to execute. We can then go ahead and verify the correct execution of our program by running the following command:

$ nargo verify nameOfProof

If there are no errors, no message will be shown. We can finally generate the verifier contract by running.

$ nargo codegen-verifier

Which will create the contract folder and the plonk_vk.sol contract, which we can later use to verify our contracts.

Creating the Contracts

Head back to the project folder and create two solidity files in the contract folder. MyToken.sol is going to be a simple ERC20 Token with a public function mint that anyone can call, add the solmate library running:

$ forge install transmissions11/solmate
MyToken.sol
MyToken.sol

And now let’s create SecretCaller.sol where we will use our plonk_vk.sol file to verify that the user making the call knows the secret.

SecretCaller.sol
SecretCaller.sol

We import the verifier contract and assign it to the constructor, we can call the verify function in the secretCall function. We must pass the proof as bytes and generate the publicInputs as an array of bytes32 and we pass it to the verifier.

SecretCaller.sol
SecretCaller.sol

Once we verify that the proof is correct we can execute the contract call with the information we sent.

Testing the Contracts

As we now have the contracts we want to test, let’s fill the Prover.toml file with the correct values (you can use console.log on the test files to see the values) generate a proof, and test everything using Foundry. Start creating the SecretCaller.t.sol file under the test directory and let’s discuss what’s happening:

SecretCaller.t.sol
SecretCaller.t.sol

We import the contracts that we are going to test, deploy them and use a helper function to grab the proof we generated in the previous steps, then, we are going to create two test functions that send correct and bad data and see if we can execute the secretCall function.

SecretCall.t.sol
SecretCall.t.sol

We can then run the $ forge test command in the console and the two tests should pass.


That’s all! remember that this code is for testing purposes and if someone makes a contract call, the data will be public on-chain meaning that anyone can replicate the call.

This tutorial was built using the Noir version 0.7.1. As expected with an evolving language many changes are expected to come, I’ll suggest you follow the official Twitter account and join the official discord in case you have any doubts. Feel free to send me a DM on Twitter (X) with questions or comments @crisgarner.

Subscribe to crisgarner.eth
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.