├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .gitattributes ├── .github ├── actions │ ├── setup-foundry │ │ └── action.yml │ └── setup-nargo │ │ └── action.yml ├── scripts │ └── latest.js └── workflows │ ├── vite_hardhat.yaml │ └── with_foundry.yaml ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── npx.js ├── package.json ├── vite-hardhat ├── .gitignore ├── .prettierignore ├── .prettierrc ├── README.md ├── hardhat.config.cts ├── netlify.toml ├── package.json ├── packages │ ├── noir │ │ ├── .gitignore │ │ ├── Nargo.toml │ │ ├── Prover.toml │ │ ├── compile.ts │ │ └── src │ │ │ └── main.nr │ └── vite │ │ ├── App.css │ │ ├── components │ │ └── index.tsx │ │ ├── hooks │ │ ├── useOffChainVerification.tsx │ │ ├── useOnChainVerification.tsx │ │ └── useProofGeneration.tsx │ │ ├── index.html │ │ ├── index.tsx │ │ ├── package.json │ │ ├── vite.config.js │ │ └── wagmi.config.ts ├── tests │ ├── uh.test.ts │ └── up.test.ts └── tsconfig.json └── with-foundry ├── .env.example ├── .gitignore ├── .gitmodules ├── README.md ├── circuits ├── Nargo.toml ├── Prover.toml └── src │ └── main.nr ├── contract └── Starter.sol ├── foundry.toml ├── script ├── Starter.s.sol └── Verify.s.sol └── test └── Starter.t.sol /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 node:lts-bookworm-slim 2 | SHELL ["/bin/bash", "-c"] 3 | RUN apt update && apt install -y curl bash git tar gzip libc++-dev unzip 4 | RUN curl -fsSL https://bun.sh/install | bash 5 | ENTRYPOINT ["bun"] 6 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-dockerfile 3 | { 4 | "name": "Existing Dockerfile", 5 | "build": { 6 | "context": ".", 7 | "dockerfile": "Dockerfile" 8 | }, 9 | "customizations": { 10 | // Configure properties specific to VS Code. 11 | "vscode": { 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": {}, 14 | "extensions": ["noir-lang.vscode-noir"] 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.nr linguist-language=Rust -------------------------------------------------------------------------------- /.github/actions/setup-foundry/action.yml: -------------------------------------------------------------------------------- 1 | name: Install Foundry 2 | description: Installs the workspace's foundry 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Install Foundry 8 | uses: foundry-rs/foundry-toolchain@v1 9 | -------------------------------------------------------------------------------- /.github/actions/setup-nargo/action.yml: -------------------------------------------------------------------------------- 1 | name: Install Nargo 2 | description: Installs the workspace's nargo 3 | inputs: 4 | version: 5 | description: The version of the project to install dependencies for 6 | required: true 7 | 8 | runs: 9 | using: composite 10 | steps: 11 | - name: Install Nargo 12 | uses: noir-lang/noirup@v0.1.2 13 | with: 14 | toolchain: ${{ inputs.version }} 15 | 16 | - name: Use Nargo 17 | run: nargo --version 18 | shell: bash 19 | -------------------------------------------------------------------------------- /.github/scripts/latest.js: -------------------------------------------------------------------------------- 1 | const GITHUB_PAGES = 3; 2 | 3 | async function main() { 4 | const fetchOpts = { 5 | params: { per_page: 100 }, 6 | headers: {}, 7 | }; 8 | 9 | if (process.env.GITHUB_TOKEN) 10 | fetchOpts.headers = { Authorization: `token ${process.env.GITHUB_TOKEN}` }; 11 | 12 | const versions = []; 13 | for (let i = 0; i < GITHUB_PAGES; i++) { 14 | const res = await fetch( 15 | `https://api.github.com/repos/noir-lang/noir/releases?page=${i + 1}`, 16 | fetchOpts, 17 | ); 18 | 19 | const data = await res.json(); 20 | 21 | const filtered = data.filter( 22 | release => !release.tag_name.includes('aztec') && !release.tag_name.includes('nightly'), 23 | ); 24 | versions.push(...filtered); 25 | } 26 | 27 | const latestStable = versions.find(release => !release.prerelease).tag_name.substring(1); 28 | 29 | /** 30 | * TODO: test the prerelease! 31 | * 32 | * The problem with the prerelease is that if the test runs for both the stable and the prerelease, 33 | * and the prerelease has a breaking change, then the stable will fail. 34 | * 35 | * If we update the starter to match the prerelease, then the stable will fail. 36 | * 37 | * This means that if there is a breaking change in a prerelease, we will ALWAYS get a warning 😄, which defeats the purpose. 38 | * 39 | * A solution would be to have a separate "prerelease" branch that is updated with the prerelease. And the CI runs on that branch. 40 | * However, Noir hasn't yet reached a state where, for example, there is ALWAYS a prerelease newer than the stable. 41 | * Sometimes the stable is the last one, and there is a prerelease buried somewhere that never got the honor of being promoted to stable. 42 | * 43 | * So for now, we will just ignore the prerelease. 44 | */ 45 | 46 | // const latestPreRelease = versions.find(release => release.prerelease).tag_name.substring(1); 47 | 48 | const workflowOutput = JSON.stringify({ 49 | stable: latestStable, 50 | // prerelease: latestPreRelease, 51 | }); 52 | console.log(workflowOutput); // DON'T REMOVE, GITHUB WILL CAPTURE THIS OUTPUT 53 | } 54 | 55 | main(); 56 | -------------------------------------------------------------------------------- /.github/workflows/vite_hardhat.yaml: -------------------------------------------------------------------------------- 1 | name: PR - vite-hardhat 2 | on: 3 | pull_request: 4 | paths: 5 | - 'vite-hardhat/**' 6 | 7 | jobs: 8 | test-vite-hardhat: 9 | runs-on: ubuntu-latest 10 | defaults: 11 | run: 12 | working-directory: vite-hardhat 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: Set up bun 18 | uses: oven-sh/setup-bun@v1 19 | 20 | - name: Get latest version 21 | id: versions_step 22 | run: | 23 | output=$(node ../.github/scripts/latest.js) 24 | echo "Output from Node.js script: $output" 25 | 26 | STABLE=$(echo $output | jq -r '.stable') 27 | echo "::set-output name=stable::$STABLE" 28 | 29 | - name: Install dependencies 30 | run: bun install 31 | 32 | - name: Compile 33 | run: bunx hardhat compile 34 | 35 | - name: Run tests 36 | run: bun run test 37 | -------------------------------------------------------------------------------- /.github/workflows/with_foundry.yaml: -------------------------------------------------------------------------------- 1 | name: PR - with-foundry 2 | on: 3 | pull_request: 4 | paths: 5 | - 'with-foundry/**' 6 | 7 | jobs: 8 | test-with-foundry: 9 | defaults: 10 | run: 11 | working-directory: with-foundry 12 | 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | submodules: recursive 18 | 19 | - name: Install libc++ 20 | run: | 21 | sudo apt-get update 22 | sudo apt-get install -y libc++-dev libc++abi-dev 23 | 24 | - name: Get latest version 25 | id: versions_step 26 | run: | 27 | output=$(node ../.github/scripts/latest.js) 28 | echo "Output from Node.js script: $output" 29 | 30 | STABLE=$(echo $output | jq -r '.stable') 31 | echo "::set-output name=stable::$STABLE" 32 | 33 | - name: Set up nargo 34 | uses: ./.github/actions/setup-nargo 35 | with: 36 | version: ${{ steps.versions_step.outputs.stable }} 37 | 38 | - name: Set up foundry 39 | uses: ./.github/actions/setup-foundry 40 | 41 | - name: Install bb 42 | run: | 43 | curl -L https://raw.githubusercontent.com/AztecProtocol/aztec-packages/master/barretenberg/cpp/installation/install | bash 44 | echo "PATH=$PATH:/home/runner/.bb" >> $GITHUB_ENV 45 | shell: bash 46 | 47 | - name: Use bbup 48 | run: | 49 | bbup -v 0.63.1 50 | shell: bash 51 | 52 | - name: Generate verifier contract 53 | run: | 54 | nargo compile && bb write_vk -b ./target/with_foundry.json && bb contract 55 | working-directory: with-foundry/circuits 56 | 57 | - name: Generate proof 58 | run: | 59 | nargo execute witness 60 | bb prove -b ./target/with_foundry.json -w ./target/witness.gz -o ./target/with_foundry.proof 61 | working-directory: with-foundry/circuits 62 | 63 | - name: Test with Foundry 64 | run: | 65 | forge test --optimize --optimizer-runs 5000 --evm-version cancun 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store 3 | 4 | node_modules 5 | package-lock.json 6 | 7 | # To use with nektos/act 8 | .github/event.json 9 | bun.lockb 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "with-foundry/lib/foundry-noir-helper"] 2 | path = with-foundry/lib/foundry-noir-helper 3 | url = https://github.com/0xnonso/foundry-noir-helper 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "printWidth": 100, 6 | "proseWrap": "always" 7 | } 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via an issue. 4 | 5 | ## Contributing to an existing example project 6 | 7 | If you would like to contribute to an existing example project, please open an issue and tag one of the original 8 | authors or maintainers ([@critesjosh](https://github.com/critesjosh), [@signorecello](https://github.com/signorecello)) with your suggestion. 9 | 10 | ## Adding a new example project 11 | 12 | We are currently not accepting new contributions with starters. However, the Noir team encourages everyone to contribute to [awesome-noir](https://github.com/noir-lang/awesome-noir). 13 | 14 | If you want to contribute with unique ideas, check out Awesome Noir to see what's been done so far, or what's the status of each of the projects. Some unique ideas for boilerplates or examples, if you don't know where to start, would be: 15 | 16 | - Something integrating Angular 17 | - A React Native starter 18 | - A Svelte example 19 | - A server-side-rendered Go or Python app 20 | 21 | Basically anything that combines different technologies would be adding a lot to the ecosystem. If you'd like to discuss further, please message [@signorecello](https://github.com/signorecello) or [@critesjosh](https://github.com/critesjosh)! 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2.0 2 | 3 | Copyright 2025 Spilsbury Holdings Ltd 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Noir Starters 2 | 3 | This is a reference repo to help you get started with writing zero-knowledge circuits with [Noir](https://noir-lang.org/). 4 | 5 | Each project is an example you can use as template. Feel free to mix them in order to find the best combination of technology that suits your needs. 6 | 7 | ## Getting started 8 | 9 | If you have [node](https://nodejs.org/en/download) installed, just open a terminal and run: 10 | 11 | ```bash 12 | npx create-noir 13 | ``` 14 | 15 | ### Templates 16 | 17 | - Foundry: [`./with-foundry`](./with-foundry) 18 | - Vite + Hardhat: [`./vite-hardhat`](./vite-hardhat) 19 | 20 | ## Example Projects 21 | 22 | You can view more complete example projects written in Noir in the [Noir Examples](https://github.com/noir-lang/noir-examples) repo. 23 | 24 | ## Support 25 | 26 | Need help? Join the [Noir Discord](https://discord.gg/JtqzkdeQ6G) or reach out on [Twitter](https://twitter.com/NoirLang). 27 | 28 | ## Contributing 29 | 30 | We welcome contributions! Check out the [contributing guidelines](./CONTRIBUTING.md) for more info. 31 | -------------------------------------------------------------------------------- /npx.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { Command } from 'commander'; 3 | import select from '@inquirer/select'; 4 | import input from '@inquirer/input'; 5 | const program = new Command(); 6 | import tiged from 'tiged'; 7 | 8 | program.action(async () => { 9 | const appType = await select({ 10 | message: 'Please choose an option:', 11 | choices: [ 12 | { value: 'vite-hardhat', name: 'Browser App using Vite' }, 13 | { value: 'with-foundry', name: 'Solidity App using Foundry' }, 14 | ], 15 | }); 16 | 17 | console.log(`You chose: ${appType}`); 18 | 19 | const appName = await input({ 20 | message: 'Your app name:', 21 | default: 'my-noir-app', 22 | }); 23 | 24 | const emitter = tiged(`noir-lang/noir-starter/${appType}`, { 25 | disableCache: true, 26 | force: true, 27 | verbose: true, 28 | }); 29 | 30 | emitter.on('info', info => { 31 | console.log(info.message); 32 | }); 33 | 34 | emitter.clone(`./${appName}`).then(() => { 35 | console.log('done'); 36 | }); 37 | }); 38 | 39 | program.parse(); 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-noir", 3 | "version": "0.1.3", 4 | "type": "module", 5 | "description": "This is a reference repo to help you get started with writing zero-knowledge circuits with [Noir](https://noir-lang.org/).", 6 | "bin": "npx.js", 7 | "author": "", 8 | "license": "Apache-2.0", 9 | "dependencies": { 10 | "@inquirer/input": "^1.2.16", 11 | "@inquirer/select": "^1.3.3", 12 | "commander": "^11.1.0", 13 | "tiged": "^2.12.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vite-hardhat/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | .env 4 | .tmp 5 | 6 | # dependencies 7 | /node_modules 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | /.next/ 16 | /out/ 17 | 18 | # production 19 | /build 20 | 21 | # misc 22 | .DS_Store 23 | *.pem 24 | 25 | # debug 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | .pnpm-debug.log* 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | 41 | # hardhat 42 | cache 43 | 44 | # artifacts 45 | typechain-types 46 | proofs 47 | 48 | # noir 49 | crs 50 | 51 | # other 52 | .vscode 53 | .DS_Store 54 | artifacts 55 | .yarn/ 56 | 57 | noir/target/ 58 | contracts 59 | dist 60 | deployment.json 61 | bun.lockb 62 | -------------------------------------------------------------------------------- /vite-hardhat/.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | -------------------------------------------------------------------------------- /vite-hardhat/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "singleQuote": true, 4 | "trailingComma": "all", 5 | "printWidth": 100, 6 | "proseWrap": "always" 7 | } 8 | -------------------------------------------------------------------------------- /vite-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # Noir with Vite and Hardhat 2 | 3 | [![Netlify Status](https://api.netlify.com/api/v1/badges/e4bd1ebc-6be1-4ed2-8be8-18f70382ae22/deploy-status)](https://app.netlify.com/sites/noir-vite-hardhat/deploys) 4 | 5 | This example uses [Vite](https://vite.dev/) as the frontend framework, and 6 | [Hardhat](https://hardhat.org/) to deploy and test. 7 | 8 | ## Getting Started 9 | 10 | Want to get started in a pinch? Start your project in a free Github Codespace! 11 | 12 | [![Start your project in a free Github Codespace!](https://github.com/codespaces/badge.svg)](https://codespaces.new/noir-lang/noir-starter/tree/main) 13 | 14 | ## Locally 15 | 16 | 1. Install your favorite package manager. We'll use [bun](https://bun.sh/docs/installation) but feel 17 | free to use `yarn` or others: 18 | 19 | ```bash 20 | curl -fsSL https://bun.sh/install | bash 21 | ``` 22 | 23 | 2. Install dependencies: 24 | 25 | ```bash 26 | bun i 27 | ``` 28 | 29 | 3. Run a node 30 | 31 | ```bash 32 | bunx hardhat node 33 | ``` 34 | 35 | 4. Deploy the verifier contract (UltraPlonk) 36 | 37 | ```bash 38 | bun run deploy 39 | ``` 40 | 41 | 5. Run the dev environment 42 | 43 | ```bash 44 | bun dev 45 | ``` 46 | 47 | ### Testing 48 | 49 | You can run the [example test file](./test/index.test.ts) with 50 | 51 | ```bash 52 | bun run test 53 | ``` 54 | 55 | This test shows the basic usage of Noir in a TypeScript Node.js environment. It also starts its own network and deploys the verifier contract. 56 | 57 | If you want to test only `UltraHonk`, you can run: 58 | 59 | ```bash 60 | bun run test:uh 61 | ``` 62 | 63 | ### Deploying on other networks 64 | 65 | The default scripting targets a local environment. For convenience, we added some configurations for 66 | deployment on various other networks. You can see the existing list by running: 67 | 68 | ```bash 69 | bunx hardhat vars setup 70 | ``` 71 | 72 | If you want to deploy on any of them, just pass in a private key, for example for the holesky 73 | network: 74 | 75 | ```bash 76 | bunx hardhat vars set holesky 77 | ``` 78 | 79 | You can then deploy on that network by passing the `--network` flag. For example: 80 | 81 | ```bash 82 | bunx hardhat deploy --network holesky # deploys on holesky 83 | ``` 84 | 85 | Feel free to add more networks, as long as they're supported by `wagmi` 86 | ([list here](https://wagmi.sh/react/api/chains#available-chains)). Just make sure you: 87 | 88 | - Have funds in these accounts 89 | - Add their configuration in the `networks` property in `hardhat.config.cts` 90 | - Use the name that wagmi expects (for example `ethereum` won't work, as `wagmi` calls it `mainnet`) 91 | -------------------------------------------------------------------------------- /vite-hardhat/hardhat.config.cts: -------------------------------------------------------------------------------- 1 | import '@nomicfoundation/hardhat-toolbox-viem'; 2 | import '@nomicfoundation/hardhat-viem'; 3 | import '@nomicfoundation/hardhat-chai-matchers'; 4 | import 'hardhat-noirenberg'; 5 | 6 | import { writeFileSync } from 'fs'; 7 | import { Chain } from 'viem'; 8 | import { task, vars } from 'hardhat/config'; 9 | import { HardhatUserConfig } from 'hardhat/types/config'; 10 | import fs from 'fs'; 11 | import { resolve } from 'path'; 12 | 13 | const config: HardhatUserConfig = { 14 | solidity: { 15 | version: '0.8.28', 16 | settings: { 17 | optimizer: { enabled: true, runs: 5000 }, 18 | }, 19 | }, 20 | defaultNetwork: 'localhost', 21 | networks: { 22 | localhost: { 23 | url: 'http://127.0.0.1:8545', 24 | chainId: 31337, 25 | accounts: vars.has('localhost') 26 | ? [vars.get('localhost')] 27 | : ['0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'], 28 | }, 29 | scrollSepolia: { 30 | url: 'https://sepolia-rpc.scroll.io', 31 | accounts: vars.has('scrollSepolia') ? [vars.get('scrollSepolia')] : [], 32 | }, 33 | holesky: { 34 | url: 'https://holesky.drpc.org', 35 | accounts: vars.has('holesky') ? [vars.get('holesky')] : [], 36 | }, 37 | }, 38 | paths: { 39 | root: 'packages', 40 | tests: 'tests', 41 | }, 42 | }; 43 | 44 | task('deploy', 'Deploys a verifier contract') 45 | .addOptionalPositionalParam('provingSystem') 46 | .setAction(async (taskArgs, hre) => { 47 | const contractsDir = resolve('packages', 'contracts'); 48 | if (fs.existsSync(contractsDir)) fs.rmdirSync(contractsDir, { recursive: true }); 49 | 50 | hre.config.noirenberg = { provingSystem: taskArgs.provingSystem || 'UltraPlonk' }; 51 | await hre.run('compile'); 52 | 53 | let verifier; 54 | if (taskArgs.provingSystem === 'UltraHonk') { 55 | verifier = await hre.viem.deployContract('HonkVerifier'); 56 | } else { 57 | verifier = await hre.viem.deployContract('UltraVerifier'); 58 | } 59 | 60 | const networkConfig = (await import(`viem/chains`))[hre.network.name] as Chain; 61 | const config = { 62 | name: hre.network.name, 63 | address: verifier.address, 64 | networkConfig: { 65 | ...networkConfig, 66 | id: hre.network.config.chainId, 67 | }, 68 | }; 69 | 70 | console.log( 71 | `Attached to address ${verifier.address} at network ${hre.network.name} with chainId ${networkConfig.id}...`, 72 | ); 73 | writeFileSync('deployment.json', JSON.stringify(config), { flag: 'w' }); 74 | }); 75 | 76 | export default config; 77 | -------------------------------------------------------------------------------- /vite-hardhat/netlify.toml: -------------------------------------------------------------------------------- 1 | [[headers]] 2 | for = "/*" 3 | [headers.values] 4 | Cross-Origin-Opener-Policy = "same-origin" 5 | Cross-Origin-Embedder-Policy = "false" 6 | -------------------------------------------------------------------------------- /vite-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-hardhat", 3 | "description": "A template repository to get started with writing zero knowledge programs with Noir.", 4 | "private": true, 5 | "workspaces": [ 6 | "packages/*" 7 | ], 8 | "scripts": { 9 | "deploy": "bunx hardhat deploy", 10 | "dev": "bun --filter vite dev", 11 | "test:up": "bun test ./tests/up.test.ts", 12 | "test:uh": "bun test ./tests/uh.test.ts", 13 | "test": "bun test:up && bun test:uh", 14 | "node": "bunx hardhat node" 15 | }, 16 | "type": "module", 17 | "devDependencies": { 18 | "@types/bun": "^1.1.12", 19 | "hardhat": "^2.19.2" 20 | }, 21 | "dependencies": { 22 | "@noir-lang/noir_js": "1.0.0-beta.0", 23 | "@noir-lang/noir_wasm": "1.0.0-beta.0", 24 | "@noir-lang/types": "1.0.0-beta.0", 25 | "@aztec/bb.js": "0.63.1", 26 | "@nomicfoundation/hardhat-ignition": "^0.15.5", 27 | "@nomicfoundation/hardhat-ignition-viem": "^0.15.5", 28 | "commander": "^12.1.0", 29 | "dotenv": "^16.0.3", 30 | "shelljs": "^0.8.5", 31 | "@nomicfoundation/hardhat-ethers": "^3.0.6", 32 | "@nomicfoundation/hardhat-network-helpers": "^1.0.11", 33 | "@nomicfoundation/hardhat-toolbox-viem": "3.0.0", 34 | "@nomicfoundation/hardhat-verify": "^2.0.8", 35 | "@nomicfoundation/hardhat-viem": "2.0.2", 36 | "@types/mocha": "^10.0.1", 37 | "@types/shelljs": "^0.8.7", 38 | "hardhat-gas-reporter": "^1.0.9", 39 | "solidity-coverage": "^0.8.5", 40 | "hardhat-noirenberg": "0.2.0", 41 | "typescript": "^5.6.3" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /vite-hardhat/packages/noir/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /vite-hardhat/packages/noir/Nargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "noirstarter" 3 | type = "bin" 4 | 5 | [dependencies] 6 | -------------------------------------------------------------------------------- /vite-hardhat/packages/noir/Prover.toml: -------------------------------------------------------------------------------- 1 | x = 1 2 | y = 2 3 | -------------------------------------------------------------------------------- /vite-hardhat/packages/noir/compile.ts: -------------------------------------------------------------------------------- 1 | import { compile, createFileManager } from '@noir-lang/noir_wasm'; 2 | import { CompiledCircuit } from '@noir-lang/types'; 3 | 4 | export async function getCircuit() { 5 | const fm = createFileManager('/'); 6 | const main = (await fetch(new URL(`./src/main.nr`, import.meta.url))) 7 | .body as ReadableStream; 8 | const nargoToml = (await fetch(new URL(`./Nargo.toml`, import.meta.url))) 9 | .body as ReadableStream; 10 | 11 | fm.writeFile('./src/main.nr', main); 12 | fm.writeFile('./Nargo.toml', nargoToml); 13 | const result = await compile(fm); 14 | if (!('program' in result)) { 15 | throw new Error('Compilation failed'); 16 | } 17 | return result.program as CompiledCircuit; 18 | } 19 | -------------------------------------------------------------------------------- /vite-hardhat/packages/noir/src/main.nr: -------------------------------------------------------------------------------- 1 | fn main(x : Field, y : pub Field) { 2 | assert(x != y); 3 | } 4 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/App.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | .faded { 6 | opacity: 0.5; 7 | } 8 | 9 | .spinner { 10 | position: fixed; 11 | top: 50%; 12 | left: 50%; 13 | /* bring your own prefixes */ 14 | transform: translate(-50%, -50%); 15 | } 16 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/components/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import React from 'react'; 3 | 4 | import { useOnChainVerification } from '../hooks/useOnChainVerification.js'; 5 | import { useProofGeneration } from '../hooks/useProofGeneration.js'; 6 | import { useOffChainVerification } from '../hooks/useOffChainVerification.js'; 7 | 8 | function Component() { 9 | const [input, setInput] = useState<{ x: string; y: string } | undefined>(); 10 | const { noir, proofData, backend } = useProofGeneration(input); 11 | useOffChainVerification(backend!, noir, proofData); 12 | const verifyButton = useOnChainVerification(proofData); 13 | 14 | const submit = (e: React.FormEvent) => { 15 | e.preventDefault(); 16 | const elements = e.currentTarget.elements; 17 | if (!elements) return; 18 | 19 | const x = elements.namedItem('x') as HTMLInputElement; 20 | const y = elements.namedItem('y') as HTMLInputElement; 21 | 22 | setInput({ x: x.value, y: y.value }); 23 | }; 24 | 25 | return ( 26 | <> 27 |
28 |

Example starter

29 |

This circuit checks that x and y are different (yey!)

30 |

Try it!

31 | 32 | 33 | 34 |
35 | {verifyButton} 36 | 37 | ); 38 | } 39 | 40 | export default Component; 41 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/hooks/useOffChainVerification.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ProofData } from '@noir-lang/types'; 4 | import { useEffect } from 'react'; 5 | import { toast } from 'react-toastify'; 6 | import { UltraPlonkBackend } from '@aztec/bb.js'; 7 | import { Noir } from '@noir-lang/noir_js'; 8 | 9 | export function useOffChainVerification( 10 | backend: UltraPlonkBackend, 11 | noir?: Noir, 12 | proofData?: ProofData, 13 | ) { 14 | useEffect(() => { 15 | if (!proofData || !noir) return; 16 | 17 | toast.promise(backend.verifyProof(proofData), { 18 | pending: 'Verifying proof off-chain', 19 | success: 'Proof verified off-chain', 20 | error: 'Error verifying proof off-chain', 21 | }); 22 | }, [proofData]); 23 | } 24 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/hooks/useOnChainVerification.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { ProofData } from '@noir-lang/types'; 3 | import { useAccount, useConnect, useDisconnect, useSwitchChain } from 'wagmi'; 4 | import { bytesToHex } from 'viem'; 5 | import { useEffect, useState } from 'react'; 6 | import { Id, toast } from 'react-toastify'; 7 | import { useReadUltraVerifierVerify } from '../artifacts/generated.js'; 8 | import deployment from '../../../deployment.json'; 9 | 10 | export function useOnChainVerification(proofData?: ProofData) { 11 | const { connect, connectors } = useConnect(); 12 | const { disconnect } = useDisconnect(); 13 | const { isConnected } = useAccount(); 14 | const [args, setArgs] = useState<[`0x${string}`, `0x${string}`[]] | undefined>(); 15 | 16 | const { chains, switchChain } = useSwitchChain(); 17 | const { data, error } = useReadUltraVerifierVerify({ 18 | args, 19 | query: { 20 | enabled: !!args, 21 | }, 22 | }); 23 | 24 | const [onChainToast, setOnChainToast] = useState(0); 25 | 26 | useEffect(() => { 27 | switchChain({ chainId: chains[0].id }); 28 | if (!proofData || !isConnected) { 29 | return; 30 | } 31 | 32 | setArgs([bytesToHex(proofData.proof), proofData.publicInputs as `0x${string}`[]]); 33 | 34 | if (!onChainToast) 35 | setOnChainToast(toast.loading('Verifying proof on-chain', { autoClose: 10000 })); 36 | }, [isConnected, proofData]); 37 | 38 | useEffect(() => { 39 | if (data) { 40 | toast.update(onChainToast, { 41 | type: 'success', 42 | render: 'Proof verified on-chain!', 43 | isLoading: false, 44 | }); 45 | } else if (error) { 46 | toast.update(onChainToast, { 47 | type: 'error', 48 | render: 'Error verifying proof on-chain!', 49 | isLoading: false, 50 | }); 51 | console.error(error.message); 52 | } 53 | }, [data, error]); 54 | 55 | if (!isConnected) { 56 | return ( 57 |
58 | 66 |
67 | ); 68 | } else { 69 | return ( 70 |
71 | 72 |
73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/hooks/useProofGeneration.tsx: -------------------------------------------------------------------------------- 1 | import { toast } from 'react-toastify'; 2 | import { useEffect, useState } from 'react'; 3 | import { getCircuit } from '../../noir/compile.js'; 4 | import { UltraPlonkBackend } from '@aztec/bb.js'; 5 | import { Noir } from '@noir-lang/noir_js'; 6 | import { ProofData } from '@noir-lang/types'; 7 | 8 | export function useProofGeneration(inputs?: { [key: string]: string }) { 9 | const [proofData, setProofData] = useState(); 10 | const [backend, setBackend] = useState(); 11 | const [noir, setNoir] = useState(); 12 | 13 | const proofGeneration = async () => { 14 | if (!inputs) return; 15 | const circuit = await getCircuit(); 16 | const backend = new UltraPlonkBackend(circuit.bytecode, { 17 | threads: navigator.hardwareConcurrency, 18 | }); 19 | const noir = new Noir(circuit); 20 | 21 | await toast.promise(noir.init, { 22 | pending: 'Initializing Noir...', 23 | success: 'Noir initialized!', 24 | error: 'Error initializing Noir', 25 | }); 26 | 27 | const { witness } = await toast.promise(noir.execute(inputs), { 28 | pending: 'Generating witness...', 29 | success: 'Witness generated!', 30 | error: 'Error generating witness', 31 | }); 32 | 33 | const data = await toast.promise(backend.generateProof(witness), { 34 | pending: 'Generating proof', 35 | success: 'Proof generated', 36 | error: 'Error generating proof', 37 | }); 38 | 39 | setProofData(data); 40 | setNoir(noir); 41 | setBackend(backend); 42 | }; 43 | 44 | useEffect(() => { 45 | if (!inputs) return; 46 | proofGeneration(); 47 | }, [inputs]); 48 | 49 | return { noir, proofData, backend }; 50 | } 51 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vite + React + TS 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/index.tsx: -------------------------------------------------------------------------------- 1 | // @ts-ignore 2 | import acvm from '@noir-lang/acvm_js/web/acvm_js_bg.wasm?url'; 3 | // @ts-ignore 4 | import noirc from '@noir-lang/noirc_abi/web/noirc_abi_wasm_bg.wasm?url'; 5 | import initNoirC from '@noir-lang/noirc_abi'; 6 | import initACVM from '@noir-lang/acvm_js'; 7 | // @ts-ignore 8 | await Promise.all([initACVM(fetch(acvm)), initNoirC(fetch(noirc))]); 9 | 10 | import React, { ReactNode, useEffect } from 'react'; 11 | import ReactDOM from 'react-dom/client'; 12 | import './App.css'; 13 | import 'react-toastify/dist/ReactToastify.css'; 14 | import { ToastContainer } from 'react-toastify'; 15 | import Component from './components/index.jsx'; 16 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 17 | import { WagmiProvider, createConfig, http } from 'wagmi'; 18 | import { defineChain, createClient } from 'viem'; 19 | import { injected } from 'wagmi/connectors'; 20 | import { networkConfig } from '../../deployment.json'; 21 | 22 | const queryClient = new QueryClient(); 23 | 24 | const { id, name, nativeCurrency, rpcUrls } = networkConfig; 25 | const chain = defineChain({ 26 | id, 27 | name, 28 | nativeCurrency, 29 | rpcUrls, 30 | }); 31 | 32 | const config = createConfig({ 33 | connectors: [injected()], 34 | chains: [chain], 35 | client({ chain }) { 36 | return createClient({ chain, transport: http() }); 37 | }, 38 | }); 39 | 40 | export function Providers({ children }: { children: React.ReactNode }) { 41 | const [mounted, setMounted] = React.useState(false); 42 | React.useEffect(() => setMounted(true), []); 43 | return ( 44 | 45 | {mounted && children} 46 | 47 | ); 48 | } 49 | 50 | ReactDOM.createRoot(document.getElementById('root')!).render( 51 | 52 | 53 | 54 | , 55 | ); 56 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite", 3 | "type": "module", 4 | "description": "A template repository to get started with writing zero knowledge programs with Noir.", 5 | "scripts": { 6 | "dev": "bun wagmi && vite dev", 7 | "wagmi": "wagmi generate" 8 | }, 9 | "dependencies": { 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-toastify": "^9.1.1", 13 | "viem": "2.x", 14 | "wagmi": "2.10.0" 15 | }, 16 | "devDependencies": { 17 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.7", 18 | "@tanstack/react-query": "5.44.0", 19 | "@tanstack/react-query-persist-client": "5.0.5", 20 | "@types/react-dom": "^18.3.1", 21 | "@tanstack/react-query-devtools": "5.0.5", 22 | "@types/bun": "^1.1.12", 23 | "@types/node": "^18.15.5", 24 | "@types/react": "^18.0.26", 25 | "@vitejs/plugin-react-swc": "^3.5.0", 26 | "@wagmi/cli": "^2.1.10", 27 | "chai": "^4.2.0", 28 | "ts-node": "^10.9.1", 29 | "typechain": "^8.1.0", 30 | "typescript": "^4.9.3", 31 | "vite": "^5.0.6" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/vite.config.js: -------------------------------------------------------------------------------- 1 | import react from '@vitejs/plugin-react-swc'; 2 | 3 | export default { 4 | optimizeDeps: { 5 | esbuildOptions: { 6 | target: 'esnext', 7 | }, 8 | }, 9 | plugins: [react()], 10 | }; 11 | -------------------------------------------------------------------------------- /vite-hardhat/packages/vite/wagmi.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from '@wagmi/cli'; 2 | import { react, hardhat } from '@wagmi/cli/plugins'; 3 | import deployment from '../../deployment.json'; 4 | 5 | export default defineConfig({ 6 | out: 'artifacts/generated.ts', 7 | plugins: [ 8 | react(), 9 | hardhat({ 10 | project: '.', 11 | artifacts: '../artifacts', 12 | deployments: { 13 | UltraVerifier: { 14 | [deployment.networkConfig.id]: deployment.address as `0x${string}`, 15 | }, 16 | }, 17 | }), 18 | ], 19 | }); 20 | -------------------------------------------------------------------------------- /vite-hardhat/tests/uh.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, beforeAll, describe, test } from 'bun:test'; 2 | import { Noir } from '@noir-lang/noir_js'; 3 | import { ProofData } from '@noir-lang/types'; 4 | import { UltraHonkBackend } from '@aztec/bb.js'; 5 | import fs from 'fs'; 6 | import { resolve } from 'path'; 7 | 8 | describe('UltraHonk', () => { 9 | let correctProof: ProofData; 10 | let noir: Noir; 11 | let backend: UltraHonkBackend; 12 | 13 | beforeAll(async () => { 14 | const contractsDir = resolve('packages', 'contracts'); 15 | if (fs.existsSync(contractsDir)) fs.rmdirSync(contractsDir, { recursive: true }); 16 | 17 | const hre = require('hardhat'); 18 | 19 | hre.run('node'); 20 | hre.config.noirenberg = { provingSystem: 'UltraHonk' }; 21 | 22 | console.log(hre); 23 | ({ noir, backend } = await hre.noirenberg.compile()); 24 | await hre.noirenberg.getSolidityVerifier(); 25 | }); 26 | 27 | test('Should generate valid proof for correct input', async () => { 28 | const input = { x: 1, y: 2 }; 29 | const { witness } = await noir.execute(input); 30 | correctProof = await backend.generateProof(witness); 31 | expect(correctProof.proof instanceof Uint8Array).toBeTrue; 32 | }); 33 | 34 | test('Should verify valid proof for correct input', async () => { 35 | const verification = await backend.verifyProof(correctProof); 36 | expect(verification).toBeTrue; 37 | }); 38 | 39 | test('Should fail to generate valid proof for incorrect input', async () => { 40 | try { 41 | const input = { x: 1, y: 1 }; 42 | const { witness } = await noir.execute(input); 43 | const incorrectProof = await backend.generateProof(witness); 44 | } catch (err) { 45 | expect(err instanceof Error).toBeTrue; 46 | const error = err as Error; 47 | expect(error.message).toContain('Cannot satisfy constraint'); 48 | } 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /vite-hardhat/tests/up.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, beforeAll, describe, test } from 'bun:test'; 2 | import { Noir } from '@noir-lang/noir_js'; 3 | import { ProofData } from '@noir-lang/types'; 4 | import { UltraPlonkBackend } from '@aztec/bb.js'; 5 | import fs from 'fs'; 6 | import { resolve } from 'path'; 7 | import { bytesToHex } from 'viem'; 8 | 9 | describe('UltraPlonk', () => { 10 | let correctProof: ProofData; 11 | let noir: Noir; 12 | let backend: UltraPlonkBackend; 13 | 14 | beforeAll(async () => { 15 | const contractsDir = resolve('packages', 'contracts'); 16 | if (fs.existsSync(contractsDir)) fs.rmdirSync(contractsDir, { recursive: true }); 17 | 18 | const hre = require('hardhat'); 19 | hre.run('node'); 20 | hre.config.noirenberg = { provingSystem: 'UltraPlonk' }; 21 | 22 | ({ noir, backend } = await hre.noirenberg.compile()); 23 | await hre.noirenberg.getSolidityVerifier(); 24 | }); 25 | 26 | test('Should generate valid proof for correct input', async () => { 27 | const input = { x: 1, y: 2 }; 28 | const { witness } = await noir.execute(input); 29 | correctProof = await backend.generateProof(witness); 30 | expect(correctProof.proof instanceof Uint8Array).toBeTrue; 31 | }); 32 | 33 | test('Should verify valid proof for correct input', async () => { 34 | const verification = await backend.verifyProof(correctProof); 35 | expect(verification).toBeTrue; 36 | }); 37 | 38 | test('Should verify valid proof for correct input on a smart contract', async () => { 39 | const hre = require('hardhat'); 40 | 41 | const verifier = await hre.viem.deployContract('UltraVerifier'); 42 | const res = await verifier.read.verify([ 43 | bytesToHex(correctProof.proof), 44 | correctProof.publicInputs as `0x${string}`[], 45 | ]); 46 | expect(res).toBeTrue; 47 | }); 48 | 49 | test('Should fail to generate valid proof for incorrect input', async () => { 50 | try { 51 | const input = { x: 1, y: 1 }; 52 | const { witness } = await noir.execute(input); 53 | const incorrectProof = await backend.generateProof(witness); 54 | } catch (err) { 55 | expect(err instanceof Error).toBeTrue; 56 | const error = err as Error; 57 | expect(error.message).toContain('Cannot satisfy constraint'); 58 | } 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /vite-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "NodeNext", 13 | "moduleResolution": "NodeNext", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "preserve", 18 | "noImplicitAny": true, 19 | "strictNullChecks": true, 20 | "incremental": true 21 | }, 22 | "ts-node": { 23 | "experimentalResolver": true, 24 | "files": true 25 | }, 26 | "exclude": ["dist", "node_modules"], 27 | "include": ["packages", "./tests"], 28 | "files": ["hardhat.config.cts"] 29 | } 30 | -------------------------------------------------------------------------------- /with-foundry/.env.example: -------------------------------------------------------------------------------- 1 | ANVIL_RPC= 2 | LOCALHOST_PRIVATE_KEY= 3 | SEPOLIA_RPC=https://rpc2.sepolia.org 4 | -------------------------------------------------------------------------------- /with-foundry/.gitignore: -------------------------------------------------------------------------------- 1 | cache 2 | out 3 | proofs 4 | broadcast 5 | .env 6 | plonk_vk.sol 7 | circuits/target/* 8 | -------------------------------------------------------------------------------- /with-foundry/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/forge-std"] 2 | path = lib/forge-std 3 | url = https://github.com/foundry-rs/forge-std 4 | 5 | [submodule "lib/foundry-noir-helper"] 6 | path = lib/foundry-noir-helper 7 | url = https://github.com/0xnonso/foundry-noir-helper 8 | -------------------------------------------------------------------------------- /with-foundry/README.md: -------------------------------------------------------------------------------- 1 | # Noir with Foundry 2 | 3 | This example uses Foundry to deploy and test a verifier. 4 | 5 | ## Getting Started 6 | 7 | Want to get started in a pinch? Start your project in a free Github Codespace! 8 | 9 | [![Start your project in a free Github Codespace!](https://github.com/codespaces/badge.svg)](https://codespaces.new/noir-lang/noir-starter) 10 | 11 | In the meantime, follow these simple steps to work on your own machine: 12 | 13 | Install [noirup](https://noir-lang.org/docs/getting_started/noir_installation) with 14 | 15 | 1. Install [noirup](https://noir-lang.org/docs/getting_started/noir_installation): 16 | 17 | ```bash 18 | curl -L https://raw.githubusercontent.com/noir-lang/noirup/main/install | bash 19 | ``` 20 | 21 | 2. Install Nargo: 22 | 23 | ```bash 24 | noirup 25 | ``` 26 | 27 | 3. Install foundryup and follow the instructions on screen. You should then have all the foundry 28 | tools like `forge`, `cast`, `anvil` and `chisel`. 29 | 30 | ```bash 31 | curl -L https://foundry.paradigm.xyz | bash 32 | ``` 33 | 34 | 4. Install foundry dependencies by running `forge install 0xnonso/foundry-noir-helper --no-commit`. 35 | 36 | 5. Install `bbup`, the tool for managing Barretenberg versions, by following the instructions 37 | [here](https://github.com/AztecProtocol/aztec-packages/blob/master/barretenberg/bbup/README.md#installation). 38 | 39 | 6. Then run `bbup`. 40 | 41 | ## Generate verifier contract and proof 42 | 43 | ### Contract 44 | 45 | The deployment assumes a verifier contract has been generated by nargo. In order to do this, run: 46 | 47 | ```bash 48 | cd circuits 49 | nargo compile 50 | bb write_vk -b ./target/with_foundry.json 51 | bb contract 52 | ``` 53 | 54 | A file named `contract.sol` should appear in the `circuits/target` folder. 55 | 56 | ### Test with Foundry 57 | 58 | We're ready to test with Foundry. There's a basic test inside the `test` folder that deploys the 59 | verifier contract, the `Starter` contract and two bytes32 arrays correspondent to good and bad 60 | solutions to your circuit. 61 | 62 | By running the following command, forge will compile the contract with 5000 rounds of optimization 63 | and the London EVM version. **You need to use these optimizer settings to suppress the "stack too 64 | deep" error on the solc compiler**. Then it will run the test, expecting it to pass with correct 65 | inputs, and fail with wrong inputs: 66 | 67 | ```bash 68 | forge test --optimize --optimizer-runs 5000 --evm-version cancun 69 | ``` 70 | 71 | #### Testing On-chain 72 | 73 | You can test that the Noir Solidity verifier contract works on a given chain by running the 74 | `Verify.s.sol` script against the appropriate RPC endpoint. 75 | 76 | ```bash 77 | forge script script/Verify.s.sol --rpc-url $RPC_ENDPOINT --broadcast 78 | ``` 79 | 80 | If that doesn't work, you can add the network to Metamask and deploy and test via 81 | [Remix](https://remix.ethereum.org/). 82 | 83 | Note that some EVM network infrastructure may behave differently and this script may fail for 84 | reasons unrelated to the compatibility of the verifier contract. 85 | 86 | ### Deploy with Foundry 87 | 88 | This template also has a script to help you deploy on your own network. But for that you need to run 89 | your own node or, alternatively, deploy on a testnet. 90 | 91 | #### (Option 1) Run a local node 92 | 93 | If you want to deploy locally, run a node by opening a terminal and running 94 | 95 | ```bash 96 | anvil 97 | ``` 98 | 99 | This should start a local node listening on `http://localhost:8545`. It will also give you many 100 | private keys. 101 | 102 | Edit your `.env` file to look like: 103 | 104 | ``` 105 | ANVIL_RPC=http://localhost:8545 106 | LOCALHOST_PRIVATE_KEY= 107 | ``` 108 | 109 | #### (Option 2) Prepare for testnet 110 | 111 | Pick a testnet like Sepolia or Goerli. Generate a private key and use a faucet (like 112 | [this one for Sepolia](https://sepoliafaucet.com/)) to get some coins in there. 113 | 114 | Edit your `.env` file to look like: 115 | 116 | ```env 117 | SEPOLIA_RPC=https://rpc2.sepolia.org 118 | LOCALHOST_PRIVATE_KEY= 119 | ``` 120 | 121 | #### Run the deploy script 122 | 123 | You need to source your `.env` file before deploying. Do that with: 124 | 125 | ```bash 126 | source .env 127 | ``` 128 | 129 | Then run the deployment with: 130 | 131 | ```bash 132 | forge script script/Starter.s.sol --rpc-url $ANVIL_RPC --broadcast --verify 133 | ``` 134 | 135 | Replace `$ANVIL_RPC` with the testnet RPC, if you're deploying on a testnet. 136 | 137 | ## Developing on this template 138 | 139 | This template doesn't include settings you may need to deal with syntax highlighting and 140 | IDE-specific settings (i.e. VScode). Please follow the instructions on the 141 | [Foundry book](https://book.getfoundry.sh/config/vscode) to set that up. 142 | 143 | It's **highly recommended** you get familiar with [Foundry](https://book.getfoundry.sh) before 144 | developing on this template. 145 | -------------------------------------------------------------------------------- /with-foundry/circuits/Nargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name="with_foundry" 3 | type="bin" 4 | authors = ["critesjosh"] 5 | compiler_version = ">=0.31.0" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /with-foundry/circuits/Prover.toml: -------------------------------------------------------------------------------- 1 | return = 3 2 | x = 3 3 | y = 3 4 | -------------------------------------------------------------------------------- /with-foundry/circuits/src/main.nr: -------------------------------------------------------------------------------- 1 | fn main(x: Field, y: pub Field) -> pub Field { 2 | assert(x == y); 3 | y 4 | } 5 | -------------------------------------------------------------------------------- /with-foundry/contract/Starter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.17; 2 | 3 | import "../circuits/target/contract.sol"; 4 | 5 | contract Starter { 6 | UltraVerifier public verifier; 7 | 8 | constructor(UltraVerifier _verifier) { 9 | verifier = _verifier; 10 | } 11 | 12 | function verifyEqual(bytes calldata proof, bytes32[] calldata y) public view returns (bool) { 13 | bool proofResult = verifier.verify(proof, y); 14 | require(proofResult, "Proof is not valid"); 15 | return proofResult; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /with-foundry/foundry.toml: -------------------------------------------------------------------------------- 1 | [profile.default] 2 | src = "src" 3 | out = "out" 4 | libs = ["lib"] 5 | fs_permissions = [{ access = "read-write", path = "./"},{ access = "read-write", path = "/tmp/"}] 6 | ffi = true 7 | 8 | [profile.remappings] 9 | # ds-test = "lib/forge-std/lib/ds-test/src/" 10 | forge-std = "lib/foundry-noir-helper/lib/forge-std/src/" 11 | # foundry-noir-helper = "lib/foundry-noir-helper/src/" 12 | 13 | # See more config options https://github.com/foundry-rs/foundry/tree/master/crates/config 14 | -------------------------------------------------------------------------------- /with-foundry/script/Starter.s.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.17; 2 | 3 | import "forge-std/Script.sol"; 4 | import "../circuits/target/contract.sol"; 5 | import "../contract/Starter.sol"; 6 | 7 | contract StarterScript is Script { 8 | Starter public starter; 9 | UltraVerifier public verifier; 10 | 11 | function setUp() public {} 12 | 13 | function run() public { 14 | uint256 deployerPrivateKey = vm.envUint("LOCALHOST_PRIVATE_KEY"); 15 | vm.startBroadcast(deployerPrivateKey); 16 | 17 | verifier = new UltraVerifier(); 18 | starter = new Starter(verifier); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /with-foundry/script/Verify.s.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.17; 2 | 3 | import "forge-std/Script.sol"; 4 | import "../circuits/target/contract.sol"; 5 | import "../contract/Starter.sol"; 6 | 7 | contract VerifyScript is Script { 8 | Starter public starter; 9 | UltraVerifier public verifier; 10 | 11 | function setUp() public {} 12 | 13 | function run() public returns (bool) { 14 | uint256 deployerPrivateKey = vm.envUint("LOCALHOST_PRIVATE_KEY"); 15 | vm.startBroadcast(deployerPrivateKey); 16 | 17 | verifier = new UltraVerifier(); 18 | starter = new Starter(verifier); 19 | 20 | string memory proof = vm.readLine("./circuits/proofs/with_foundry.proof"); 21 | bytes memory proofBytes = vm.parseBytes(proof); 22 | 23 | bytes32[] memory correct = new bytes32[](2); 24 | correct[0] = bytes32(0x0000000000000000000000000000000000000000000000000000000000000003); 25 | correct[1] = correct[0]; 26 | 27 | bool equal = starter.verifyEqual(proofBytes, correct); 28 | return equal; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /with-foundry/test/Starter.t.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.17; 2 | 3 | import "../contract/Starter.sol"; 4 | import "../circuits/target/contract.sol"; 5 | import "forge-std/console.sol"; 6 | 7 | import "forge-std/Test.sol"; 8 | import {NoirHelper} from "foundry-noir-helper/NoirHelper.sol"; 9 | 10 | 11 | contract StarterTest is Test { 12 | Starter public starter; 13 | UltraVerifier public verifier; 14 | NoirHelper public noirHelper; 15 | 16 | function setUp() public { 17 | noirHelper = new NoirHelper(); 18 | verifier = new UltraVerifier(); 19 | starter = new Starter(verifier); 20 | } 21 | 22 | function test_verifyProof() public { 23 | noirHelper.withInput("x", 1).withInput("y", 1).withInput("return", 1); 24 | (bytes32[] memory publicInputs, bytes memory proof) = noirHelper.generateProof("test_verifyProof", 2); 25 | starter.verifyEqual(proof, publicInputs); 26 | } 27 | 28 | function test_wrongProof() public { 29 | noirHelper.clean(); 30 | noirHelper.withInput("x", 1).withInput("y", 5).withInput("return", 5); 31 | (bytes32[] memory publicInputs, bytes memory proof) = noirHelper.generateProof("test_wrongProof", 2); 32 | vm.expectRevert(); 33 | starter.verifyEqual(proof, publicInputs); 34 | } 35 | 36 | // function test_all() public { 37 | // // forge runs tests in parallel which messes with the read/writes to the proof file 38 | // // Run tests in wrapper to force them run sequentially 39 | // verifyProof(); 40 | // wrongProof(); 41 | // } 42 | 43 | } 44 | --------------------------------------------------------------------------------