├── packages ├── excubiae │ ├── tasks │ │ └── .gitkeep │ ├── LICENSE │ ├── README.md │ ├── .env.example │ ├── .solcover.js │ ├── .prettierrc.json │ ├── tsconfig.json │ ├── .solhint.json │ ├── contracts │ │ ├── extensions │ │ │ ├── interfaces │ │ │ │ ├── IGitcoinPassportDecoder.sol │ │ │ │ └── IHatsMinimal.sol │ │ │ ├── FreeForAllExcubia.sol │ │ │ ├── ERC721Excubia.sol │ │ │ ├── HatsExcubia.sol │ │ │ ├── GitcoinPassportExcubia.sol │ │ │ ├── SemaphoreExcubia.sol │ │ │ ├── EASExcubia.sol │ │ │ └── ZKEdDSAEventTicketPCDExcubia.sol │ │ ├── package.json │ │ ├── test │ │ │ ├── MockERC721.sol │ │ │ ├── MockGitcoinPassportDecoder.sol │ │ │ ├── MockHats.sol │ │ │ ├── MockSemaphore.sol │ │ │ └── MockEAS.sol │ │ ├── LICENSE │ │ ├── README.md │ │ ├── IExcubia.sol │ │ └── Excubia.sol │ ├── hardhat.config.ts │ ├── package.json │ └── test │ │ ├── FreeForAllExcubia.test.ts │ │ ├── ERC721Excubia.test.ts │ │ ├── HatsExcubia.test.ts │ │ ├── GitcoinPassportExcubia.test.ts │ │ └── EASExcubia.test.ts ├── imt │ ├── LICENSE │ ├── README.md │ ├── .solcover.js │ ├── .env.example │ ├── .prettierrc.json │ ├── contracts │ │ ├── Constants.sol │ │ ├── package.json │ │ ├── test │ │ │ ├── QuinaryIMTTest.sol │ │ │ └── BinaryIMTTest.sol │ │ ├── LICENSE │ │ ├── BinaryIMT.sol │ │ ├── QuinaryIMT.sol │ │ ├── README.md │ │ └── InternalQuinaryIMT.sol │ ├── tsconfig.json │ ├── .solhint.json │ ├── hardhat.config.ts │ ├── package.json │ └── tasks │ │ └── deploy-imt-test.ts ├── lazy-imt │ ├── LICENSE │ ├── README.md │ ├── .env.example │ ├── .solcover.js │ ├── .prettierrc.json │ ├── contracts │ │ ├── Constants.sol │ │ ├── package.json │ │ ├── LICENSE │ │ ├── test │ │ │ └── LazyIMTTest.sol │ │ ├── LazyIMT.sol │ │ └── README.md │ ├── tsconfig.json │ ├── .solhint.json │ ├── hardhat.config.ts │ ├── scripts │ │ └── defaultZeroes.mjs │ ├── package.json │ └── tasks │ │ └── deploy-imt-test.ts ├── lazytower │ ├── LICENSE │ ├── README.md │ ├── .solhintignore │ ├── .env.example │ ├── .solcover.js │ ├── .prettierrc.json │ ├── tsconfig.json │ ├── .solhint.json │ ├── hardhat.config.ts │ ├── test │ │ ├── utils.ts │ │ └── LazyTowerHashChainTest.ts │ ├── contracts │ │ ├── test │ │ │ └── LazyTowerHashChainTest.sol │ │ ├── package.json │ │ ├── LICENSE │ │ ├── LazyTowerHashChain.sol │ │ └── README.md │ ├── package.json │ └── tasks │ │ └── deploy-lazytower-test.ts └── lean-imt │ ├── LICENSE │ ├── README.md │ ├── .env.example │ ├── .solcover.js │ ├── .prettierrc.json │ ├── contracts │ ├── Constants.sol │ ├── package.json │ ├── LICENSE │ ├── test │ │ └── LeanIMTTest.sol │ ├── LeanIMT.sol │ └── README.md │ ├── tsconfig.json │ ├── .solhint.json │ ├── hardhat.config.ts │ ├── package.json │ └── tasks │ └── deploy-imt-test.ts ├── .husky ├── pre-commit ├── commit-msg └── prepare-commit-msg ├── .lintstagedrc.json ├── .prettierrc.json ├── .yarnrc.yml ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ ├── ---package.md │ ├── ---refactoring.md │ ├── ---feature.md │ └── ---bug.md ├── CODEOWNERS ├── sripts │ └── slither-comment.js ├── workflows │ ├── release.yml │ └── main.yml └── pull_request_template.md ├── .prettierignore ├── scripts ├── check-slither.sh └── remove-stable-version-field.ts ├── changelogithub.config.json ├── .commitlintrc.js ├── LICENSE ├── .yarn └── patches │ └── changelogithub-npm-0.13.3-1783949906.patch ├── .gitignore ├── package.json ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md /packages/excubiae/tasks/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/imt/LICENSE: -------------------------------------------------------------------------------- 1 | contracts/LICENSE -------------------------------------------------------------------------------- /packages/excubiae/LICENSE: -------------------------------------------------------------------------------- 1 | contracts/LICENSE -------------------------------------------------------------------------------- /packages/imt/README.md: -------------------------------------------------------------------------------- 1 | contracts/README.md -------------------------------------------------------------------------------- /packages/lazy-imt/LICENSE: -------------------------------------------------------------------------------- 1 | contracts/LICENSE -------------------------------------------------------------------------------- /packages/lazytower/LICENSE: -------------------------------------------------------------------------------- 1 | contracts/LICENSE -------------------------------------------------------------------------------- /packages/lean-imt/LICENSE: -------------------------------------------------------------------------------- 1 | contracts/LICENSE -------------------------------------------------------------------------------- /packages/excubiae/README.md: -------------------------------------------------------------------------------- 1 | contracts/README.md -------------------------------------------------------------------------------- /packages/lazy-imt/README.md: -------------------------------------------------------------------------------- 1 | contracts/README.md -------------------------------------------------------------------------------- /packages/lazytower/README.md: -------------------------------------------------------------------------------- 1 | contracts/README.md -------------------------------------------------------------------------------- /packages/lean-imt/README.md: -------------------------------------------------------------------------------- 1 | contracts/README.md -------------------------------------------------------------------------------- /packages/lazytower/.solhintignore: -------------------------------------------------------------------------------- 1 | contracts/Verifier.sol 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "**/*.{js,ts,md,json,sol,yml,yaml}": "yarn prettier --write" 3 | } 4 | -------------------------------------------------------------------------------- /packages/imt/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | istanbulFolder: "../../coverage/imt" 3 | } 4 | -------------------------------------------------------------------------------- /packages/imt/.env.example: -------------------------------------------------------------------------------- 1 | COINMARKETCAP_API_KEY= 2 | REPORT_GAS=false 3 | REPORT_GAS_OUTPUT_JSON=false 4 | -------------------------------------------------------------------------------- /packages/excubiae/.env.example: -------------------------------------------------------------------------------- 1 | COINMARKETCAP_API_KEY= 2 | REPORT_GAS=false 3 | REPORT_GAS_OUTPUT_JSON=false 4 | -------------------------------------------------------------------------------- /packages/lazy-imt/.env.example: -------------------------------------------------------------------------------- 1 | COINMARKETCAP_API_KEY= 2 | REPORT_GAS=false 3 | REPORT_GAS_OUTPUT_JSON=false 4 | -------------------------------------------------------------------------------- /packages/lazy-imt/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | istanbulFolder: "../../coverage/lazy-imt" 3 | } 4 | -------------------------------------------------------------------------------- /packages/lazytower/.env.example: -------------------------------------------------------------------------------- 1 | COINMARKETCAP_API_KEY= 2 | REPORT_GAS=false 3 | REPORT_GAS_OUTPUT_JSON=false 4 | -------------------------------------------------------------------------------- /packages/lazytower/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | istanbulFolder: "../../coverage/lazytower" 3 | } 4 | -------------------------------------------------------------------------------- /packages/lean-imt/.env.example: -------------------------------------------------------------------------------- 1 | COINMARKETCAP_API_KEY= 2 | REPORT_GAS=false 3 | REPORT_GAS_OUTPUT_JSON=false 4 | -------------------------------------------------------------------------------- /packages/lean-imt/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | istanbulFolder: "../../coverage/lean-imt" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "trailingComma": "none" 5 | } 6 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /packages/excubiae/.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | istanbulFolder: "../../coverage/excubiae", 3 | skipFiles: ["test"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/imt/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "trailingComma": "none", 5 | "plugins": ["prettier-plugin-solidity"] 6 | } 7 | -------------------------------------------------------------------------------- /.husky/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | if [ "$NO_HOOK" != "1" ]; then 5 | exec < /dev/tty && npx czg --hook || true 6 | fi 7 | -------------------------------------------------------------------------------- /packages/excubiae/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "trailingComma": "none", 5 | "plugins": ["prettier-plugin-solidity"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/lazy-imt/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "trailingComma": "none", 5 | "plugins": ["prettier-plugin-solidity"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/lean-imt/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "trailingComma": "none", 5 | "plugins": ["prettier-plugin-solidity"] 6 | } 7 | -------------------------------------------------------------------------------- /packages/lazytower/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "arrowParens": "always", 4 | "trailingComma": "none", 5 | "plugins": ["prettier-plugin-solidity"] 6 | } 7 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | checksumBehavior: update 2 | 3 | compressionLevel: mixed 4 | 5 | enableGlobalCache: false 6 | 7 | nodeLinker: node-modules 8 | 9 | yarnPath: .yarn/releases/yarn-4.2.1.cjs 10 | -------------------------------------------------------------------------------- /packages/lean-imt/contracts/Constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.4; 3 | 4 | uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 5 | -------------------------------------------------------------------------------- /packages/imt/contracts/Constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.4; 3 | 4 | uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 5 | uint8 constant MAX_DEPTH = 32; 6 | -------------------------------------------------------------------------------- /packages/lazy-imt/contracts/Constants.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.4; 3 | 4 | uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 5 | uint8 constant MAX_DEPTH = 32; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | #root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 120 10 | indent_size = 4 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---package.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4E6 Package" 3 | about: Propose a new ZK-Kit Solidity package 4 | title: '' 5 | labels: 'feature :rocket:' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the package you'd like** 11 | A clear and concise description of the type of package you have in mind. 12 | 13 | **Additional context** 14 | Add any other context about the package here. 15 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | package-lock.json 4 | yarn.lock 5 | .yarn 6 | 7 | # testing 8 | coverage 9 | coverage.json 10 | 11 | # hardhat 12 | cache 13 | typechain-types 14 | 15 | # production 16 | dist 17 | build 18 | 19 | # github 20 | .github/ISSUE_TEMPLATE 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # others 32 | target 33 | -------------------------------------------------------------------------------- /scripts/check-slither.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -eu 3 | 4 | CYAN="\033[36m" 5 | RED="\033[31m" 6 | RESET="\033[0m" 7 | 8 | log() { 9 | printf "%b\n" "$1" 10 | } 11 | 12 | main() { 13 | if ! command -v slither >/dev/null; then 14 | log "${RED}error: slither is required but is not installed${RESET}.\nFollow instructions at ${CYAN}https://github.com/crytic/slither#how-to-install${RESET} and try again." 15 | exit 1 16 | fi 17 | } 18 | 19 | main 20 | -------------------------------------------------------------------------------- /changelogithub.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "scopeMap": {}, 3 | "types": { 4 | "feat": { "title": "🚀 Features" }, 5 | "fix": { "title": "🐞 Bug Fixes" }, 6 | "refactor": { "title": "♻️ Refactoring" }, 7 | "perf": { "title": "🏎 Performance" } 8 | }, 9 | "titles": { 10 | "breakingChanges": "🚨 Breaking Changes" 11 | }, 12 | "contributors": true, 13 | "capitalize": true, 14 | "group": true 15 | } 16 | -------------------------------------------------------------------------------- /packages/imt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": ["scripts/**/*", "tasks/**/*", "test/**/*", "typechain-types/**/*"], 12 | "files": ["hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/excubiae/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": ["scripts/**/*", "tasks/**/*", "test/**/*", "typechain-types/**/*"], 12 | "files": ["hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/lazy-imt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": ["scripts/**/*", "tasks/**/*", "test/**/*", "typechain-types/**/*"], 12 | "files": ["hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/lazytower/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": ["scripts/**/*", "tasks/**/*", "test/**/*", "typechain-types/**/*"], 12 | "files": ["hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/lean-imt/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | }, 11 | "include": ["scripts/**/*", "tasks/**/*", "test/**/*", "typechain-types/**/*"], 12 | "files": ["hardhat.config.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. 2 | # They will be added as reviewers for PR related to configuration files. 3 | * @vplasencia 4 | 5 | # When someone opens a pull request that only modifies specific packages, 6 | # only the following users and not the default owners defined above will be 7 | # requested for a review. 8 | /packages/imt/ @vplasencia 9 | /packages/lazy-imt/ @chancehudson 10 | /packages/lazytower/ @LCamel 11 | /packages/lean-imt/ @vplasencia 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---refactoring.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\u267B Refactoring" 3 | about: Suggest any improvements for this project 4 | title: '' 5 | labels: 'refactoring :recycle:' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the improvement you're thinking about** 11 | A clear and concise description of what you think could improve the code. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the improvement request here. 18 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | const fs = require("node:fs") 2 | const path = require("node:path") 3 | 4 | const packages = fs.readdirSync(path.resolve(__dirname, "packages")) 5 | 6 | module.exports = { 7 | extends: ["@commitlint/config-conventional"], 8 | prompt: { 9 | scopes: [...packages], 10 | markBreakingChangeMode: true, 11 | allowCustomIssuePrefix: false, 12 | allowEmptyIssuePrefix: false, 13 | issuePrefixes: [ 14 | { 15 | value: "re", 16 | name: "re: ISSUES related" 17 | } 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/imt/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "compiler-version": ["error", ">=0.8.0"], 6 | "const-name-snakecase": "off", 7 | "no-empty-blocks": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["error", { "ignoreConstructors": true }], 10 | "max-line-length": ["error", 120], 11 | "not-rely-on-time": "off", 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 80 }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/excubiae/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "compiler-version": ["error", ">=0.8.0"], 6 | "const-name-snakecase": "off", 7 | "no-empty-blocks": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["error", { "ignoreConstructors": true }], 10 | "max-line-length": ["error", 120], 11 | "not-rely-on-time": "off", 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 80 }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lazy-imt/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "compiler-version": ["error", ">=0.8.0"], 6 | "const-name-snakecase": "off", 7 | "no-empty-blocks": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["error", { "ignoreConstructors": true }], 10 | "max-line-length": ["error", 120], 11 | "not-rely-on-time": "off", 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 80 }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lazytower/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "compiler-version": ["error", ">=0.8.0"], 6 | "const-name-snakecase": "off", 7 | "no-empty-blocks": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["error", { "ignoreConstructors": true }], 10 | "max-line-length": ["error", 120], 11 | "not-rely-on-time": "off", 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 80 }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lean-imt/.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "compiler-version": ["error", ">=0.8.0"], 6 | "const-name-snakecase": "off", 7 | "no-empty-blocks": "off", 8 | "constructor-syntax": "error", 9 | "func-visibility": ["error", { "ignoreConstructors": true }], 10 | "max-line-length": ["error", 120], 11 | "not-rely-on-time": "off", 12 | "prettier/prettier": [ 13 | "error", 14 | { 15 | "endOfLine": "auto" 16 | } 17 | ], 18 | "reason-string": ["warn", { "maxLength": 80 }] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lazytower/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-toolbox" 2 | import { HardhatUserConfig } from "hardhat/config" 3 | import "./tasks/deploy-lazytower-test" 4 | import "dotenv/config" 5 | 6 | const hardhatConfig: HardhatUserConfig = { 7 | solidity: "0.8.23", 8 | gasReporter: { 9 | currency: "USD", 10 | enabled: process.env.REPORT_GAS === "true", 11 | coinmarketcap: process.env.COINMARKETCAP_API_KEY, 12 | outputJSONFile: "gas-report-lazytower.json", 13 | outputJSON: process.env.REPORT_GAS_OUTPUT_JSON === "true" 14 | }, 15 | typechain: { 16 | target: "ethers-v6" 17 | } 18 | } 19 | 20 | export default hardhatConfig 21 | -------------------------------------------------------------------------------- /packages/lazytower/test/utils.ts: -------------------------------------------------------------------------------- 1 | export default function ShiftTower(W: number, digest: (values: number[]) => number) { 2 | const S: number[][] = [] 3 | const L: number[][] = [] 4 | 5 | function _add(lv: number, v: number): number { 6 | if (lv === L.length) { 7 | S[lv] = [] 8 | L[lv] = [v] 9 | } else if (L[lv].length < W) { 10 | L[lv].push(v) 11 | } else { 12 | const d = digest(L[lv]) 13 | S[lv].push(...L[lv]) 14 | L[lv] = [v] 15 | return _add(lv + 1, d) 16 | } 17 | return lv 18 | } 19 | const add = (item: number) => _add(0, item) 20 | return { W, digest, L, S, add } 21 | } 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature" 3 | about: Suggest an idea for ZK-Kit 4 | title: '' 5 | labels: 'feature :rocket:' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /packages/lazytower/contracts/test/LazyTowerHashChainTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.4; 4 | 5 | import "../LazyTowerHashChain.sol"; 6 | 7 | contract LazyTowerHashChainTest { 8 | using LazyTowerHashChain for LazyTowerHashChainData; 9 | 10 | event Add(uint256 item); 11 | 12 | // map for multiple test cases 13 | mapping(bytes32 => LazyTowerHashChainData) public towers; 14 | 15 | function add(bytes32 _towerId, uint256 _item) external { 16 | towers[_towerId].add(_item); 17 | emit Add(_item); 18 | } 19 | 20 | function getDataForProving(bytes32 _towerId) external view returns (uint256, uint256[] memory, uint256) { 21 | return towers[_towerId].getDataForProving(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/imt/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-toolbox" 2 | import { HardhatUserConfig } from "hardhat/config" 3 | import "./tasks/deploy-imt-test" 4 | import "dotenv/config" 5 | 6 | const hardhatConfig: HardhatUserConfig = { 7 | solidity: { 8 | version: "0.8.23", 9 | settings: { 10 | optimizer: { 11 | enabled: true 12 | } 13 | } 14 | }, 15 | gasReporter: { 16 | currency: "USD", 17 | enabled: process.env.REPORT_GAS === "true", 18 | outputJSONFile: "gas-report-imt.json", 19 | outputJSON: process.env.REPORT_GAS_OUTPUT_JSON === "true" 20 | }, 21 | typechain: { 22 | target: "ethers-v6" 23 | } 24 | } 25 | 26 | export default hardhatConfig 27 | -------------------------------------------------------------------------------- /packages/lazy-imt/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-toolbox" 2 | import { HardhatUserConfig } from "hardhat/config" 3 | import "./tasks/deploy-imt-test" 4 | import "dotenv/config" 5 | 6 | const hardhatConfig: HardhatUserConfig = { 7 | solidity: { 8 | version: "0.8.23", 9 | settings: { 10 | optimizer: { 11 | enabled: true 12 | } 13 | } 14 | }, 15 | gasReporter: { 16 | currency: "USD", 17 | enabled: process.env.REPORT_GAS === "true", 18 | outputJSONFile: "gas-report-lazyimt.json", 19 | outputJSON: process.env.REPORT_GAS_OUTPUT_JSON === "true" 20 | }, 21 | typechain: { 22 | target: "ethers-v6" 23 | } 24 | } 25 | 26 | export default hardhatConfig 27 | -------------------------------------------------------------------------------- /packages/lean-imt/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-toolbox" 2 | import { HardhatUserConfig } from "hardhat/config" 3 | import "./tasks/deploy-imt-test" 4 | import "dotenv/config" 5 | 6 | const hardhatConfig: HardhatUserConfig = { 7 | solidity: { 8 | version: "0.8.23", 9 | settings: { 10 | optimizer: { 11 | enabled: true 12 | } 13 | } 14 | }, 15 | gasReporter: { 16 | currency: "USD", 17 | enabled: process.env.REPORT_GAS === "true", 18 | outputJSONFile: "gas-report-leanimt.json", 19 | outputJSON: process.env.REPORT_GAS_OUTPUT_JSON === "true" 20 | }, 21 | typechain: { 22 | target: "ethers-v6" 23 | } 24 | } 25 | 26 | export default hardhatConfig 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41E Bug" 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: "bug \U0001F41B" 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Technologies (please complete the following information):** 28 | 29 | - Node.js version 30 | - NPM version 31 | - Solidity version 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /packages/lazy-imt/scripts/defaultZeroes.mjs: -------------------------------------------------------------------------------- 1 | import { poseidon } from "circomlibjs" 2 | 3 | const zeroes = [0] 4 | 5 | for (let x = 1; x < 33; x += 1) { 6 | zeroes[x] = poseidon([zeroes[x - 1], zeroes[x - 1]]) 7 | } 8 | 9 | console.log(` 10 | ${Array(33) 11 | .fill(0) 12 | .map((_, i) => `uint256 constant public Z_${i} = ${zeroes[i]};`) 13 | .join("\n ")} 14 | 15 | /* 16 | function defaultZeroes() public pure returns (uint256[32] memory) { 17 | return [${Array(32) 18 | .fill() 19 | .map((_, i) => `Z_${i}`) 20 | .join(",")}]; 21 | } 22 | */ 23 | 24 | function defaultZero(uint256 index) public pure returns (uint256) { 25 | ${Array(33) 26 | .fill() 27 | .map((_, i) => ` if (index == ${i}) return Z_${i};`) 28 | .join("\n")} 29 | revert('badindex'); 30 | } 31 | `) 32 | -------------------------------------------------------------------------------- /.github/sripts/slither-comment.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({ github, context, header, body }) => { 2 | const comment = [header, body].join("\n") 3 | 4 | const { data: comments } = await github.rest.issues.listComments({ 5 | owner: context.repo.owner, 6 | repo: context.repo.repo, 7 | issue_number: context.payload.number 8 | }) 9 | 10 | const botComment = comments.find( 11 | (comment) => 12 | // github-actions bot user 13 | comment.user.id === 41898282 && comment.body.startsWith(header) 14 | ) 15 | 16 | const commentFn = botComment ? "updateComment" : "createComment" 17 | 18 | await github.rest.issues[commentFn]({ 19 | owner: context.repo.owner, 20 | repo: context.repo.repo, 21 | body: comment, 22 | ...(botComment ? { comment_id: botComment.id } : { issue_number: context.payload.number }) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /packages/lean-imt/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-kit/lean-imt.sol", 3 | "version": "2.0.1", 4 | "description": "Incremental Merkle tree implementations in Solidity.", 5 | "license": "MIT", 6 | "files": [ 7 | "*.sol", 8 | "!test/*", 9 | "README.md", 10 | "LICENSE" 11 | ], 12 | "keywords": [ 13 | "blockchain", 14 | "ethereum", 15 | "hardhat", 16 | "smart-contracts", 17 | "solidity", 18 | "libraries", 19 | "merkle-tree", 20 | "incremental-merkle-tree" 21 | ], 22 | "repository": "git@github.com:privacy-scaling-explorations/zk-kit.solidity.git", 23 | "homepage": "https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/imt", 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "dependencies": { 28 | "poseidon-solidity": "0.0.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/lazy-imt/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-kit/lazy-imt.sol", 3 | "version": "2.0.0-beta.12", 4 | "description": "Lazy Incremental Merkle tree implementation in Solidity.", 5 | "license": "MIT", 6 | "files": [ 7 | "*.sol", 8 | "!test/*", 9 | "README.md", 10 | "LICENSE" 11 | ], 12 | "keywords": [ 13 | "blockchain", 14 | "ethereum", 15 | "hardhat", 16 | "smart-contracts", 17 | "solidity", 18 | "libraries", 19 | "merkle-tree", 20 | "incremental-merkle-tree" 21 | ], 22 | "repository": "git@github.com:privacy-scaling-explorations/zk-kit.solidity.git", 23 | "homepage": "https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/lazy-imt", 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "dependencies": { 28 | "poseidon-solidity": "0.0.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/imt/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-kit/imt.sol", 3 | "version": "2.0.0-beta.12", 4 | "description": "Incremental Merkle tree implementation in Solidity.", 5 | "license": "MIT", 6 | "files": [ 7 | "*.sol", 8 | "internal/*", 9 | "!test/*", 10 | "README.md", 11 | "LICENSE" 12 | ], 13 | "keywords": [ 14 | "blockchain", 15 | "ethereum", 16 | "hardhat", 17 | "smart-contracts", 18 | "solidity", 19 | "libraries", 20 | "merkle-tree", 21 | "incremental-merkle-tree" 22 | ], 23 | "repository": "git@github.com:privacy-scaling-explorations/zk-kit.solidity.git", 24 | "homepage": "https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/imt.sol", 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "dependencies": { 29 | "poseidon-solidity": "0.0.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/interfaces/IGitcoinPassportDecoder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL 2 | pragma solidity >=0.8.0; 3 | 4 | /// This interface has been copied & pasted from 5 | /// https://github.com/gitcoinco/eas-proxy/blob/main/contracts/IGitcoinPassportDecoder.sol 6 | /// with commit hash d82a73337216effdba719a625f92cb941b537850. 7 | 8 | /** 9 | * @dev A struct storing a passpor credential 10 | */ 11 | 12 | struct Credential { 13 | string provider; 14 | bytes32 hash; 15 | uint64 time; 16 | uint64 expirationTime; 17 | } 18 | 19 | /** 20 | * @title IGitcoinPassportDecoder 21 | * @notice Minimal interface for consuming GitcoinPassportDecoder data 22 | */ 23 | interface IGitcoinPassportDecoder { 24 | function getPassport(address user) external returns (Credential[] memory); 25 | 26 | function getScore(address user) external view returns (uint256); 27 | 28 | function isHuman(address user) external view returns (bool); 29 | } 30 | -------------------------------------------------------------------------------- /scripts/remove-stable-version-field.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, readFileSync, writeFileSync } from "node:fs" 2 | 3 | async function main() { 4 | let dotIndex = process.argv[2].lastIndexOf(".") 5 | 6 | let folderName = dotIndex !== -1 ? process.argv[2].slice(0, dotIndex) : process.argv[2] 7 | 8 | const projectDirectory = `packages/${folderName}` 9 | 10 | let filePath = `${projectDirectory}/package.json` 11 | 12 | if (existsSync(`${projectDirectory}/contracts/package.json`)) { 13 | filePath = `${projectDirectory}/contracts/package.json` 14 | } 15 | 16 | const content = JSON.parse(readFileSync(filePath, "utf8")) 17 | 18 | if (content.stableVersion) { 19 | delete content.stableVersion 20 | } 21 | 22 | writeFileSync(filePath, JSON.stringify(content, null, 4), "utf8") 23 | } 24 | 25 | main() 26 | .then(() => process.exit(0)) 27 | .catch((error) => { 28 | console.error(error) 29 | process.exit(1) 30 | }) 31 | -------------------------------------------------------------------------------- /packages/imt/contracts/test/QuinaryIMTTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.4; 4 | 5 | import {QuinaryIMT, QuinaryIMTData} from "../QuinaryIMT.sol"; 6 | 7 | contract QuinaryIMTTest { 8 | QuinaryIMTData public data; 9 | 10 | function init(uint256 depth) external { 11 | QuinaryIMT.init(data, depth, 0); 12 | } 13 | 14 | function insert(uint256 leaf) external { 15 | QuinaryIMT.insert(data, leaf); 16 | } 17 | 18 | function update( 19 | uint256 leaf, 20 | uint256 newLeaf, 21 | uint256[4][] calldata proofSiblings, 22 | uint8[] calldata proofPathIndices 23 | ) external { 24 | QuinaryIMT.update(data, leaf, newLeaf, proofSiblings, proofPathIndices); 25 | } 26 | 27 | function remove(uint256 leaf, uint256[4][] calldata proofSiblings, uint8[] calldata proofPathIndices) external { 28 | QuinaryIMT.remove(data, leaf, proofSiblings, proofPathIndices); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/lazytower/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-kit/lazytower.sol", 3 | "version": "0.0.1", 4 | "description": "LazyTower Solidity libraries.", 5 | "license": "MIT", 6 | "files": [ 7 | "**/*.sol", 8 | "!test/", 9 | "README.md" 10 | ], 11 | "keywords": [ 12 | "blockchain", 13 | "ethereum", 14 | "hardhat", 15 | "smart-contracts", 16 | "solidity", 17 | "libraries", 18 | "merkle-tree", 19 | "incremental-merkle-tree", 20 | "lazytower" 21 | ], 22 | "repository": "git@github.com:privacy-scaling-explorations/zk-kit.solidity.git", 23 | "homepage": "https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/lazytower", 24 | "author": { 25 | "name": "LCamel", 26 | "email": "lcamel@gmail.com", 27 | "url": "https://twitter.com/LCamel" 28 | }, 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "dependencies": { 33 | "poseidon-solidity": "0.0.5" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/imt/contracts/test/BinaryIMTTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.4; 4 | 5 | import {BinaryIMT, BinaryIMTData} from "../BinaryIMT.sol"; 6 | 7 | contract BinaryIMTTest { 8 | BinaryIMTData public data; 9 | 10 | function init(uint256 depth) external { 11 | BinaryIMT.init(data, depth, 0); 12 | } 13 | 14 | function initWithDefaultZeroes(uint256 depth) external { 15 | BinaryIMT.initWithDefaultZeroes(data, depth); 16 | } 17 | 18 | function insert(uint256 leaf) external { 19 | BinaryIMT.insert(data, leaf); 20 | } 21 | 22 | function update( 23 | uint256 leaf, 24 | uint256 newLeaf, 25 | uint256[] calldata proofSiblings, 26 | uint8[] calldata proofPathIndices 27 | ) external { 28 | BinaryIMT.update(data, leaf, newLeaf, proofSiblings, proofPathIndices); 29 | } 30 | 31 | function remove(uint256 leaf, uint256[] calldata proofSiblings, uint8[] calldata proofPathIndices) external { 32 | BinaryIMT.remove(data, leaf, proofSiblings, proofPathIndices); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zk-kit/excubiae", 3 | "version": "0.1.1", 4 | "description": "[DEPRECATED] A general purpose on-chain gatekeeping smart contract framework.", 5 | "license": "MIT", 6 | "files": [ 7 | "*.sol", 8 | "!test/*", 9 | "README.md", 10 | "LICENSE" 11 | ], 12 | "keywords": [ 13 | "deprecated", 14 | "blockchain", 15 | "ethereum", 16 | "hardhat", 17 | "smart-contracts", 18 | "solidity", 19 | "libraries", 20 | "gatekeepers", 21 | "credentials" 22 | ], 23 | "repository": "git@github.com:privacy-scaling-explorations/zk-kit.solidity.git", 24 | "homepage": "https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/gatekeepers", 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "dependencies": { 29 | "@ethereum-attestation-service/eas-contracts": "1.7.1", 30 | "@openzeppelin/contracts": "5.0.2", 31 | "@semaphore-protocol/contracts": "4.0.0-beta.16" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/excubiae/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-toolbox" 2 | import { HardhatUserConfig } from "hardhat/config" 3 | import "dotenv/config" 4 | 5 | const TEST_MNEMONIC = "candy maple cake sugar pudding cream honey rich smooth crumble sweet treat" 6 | 7 | const hardhatConfig: HardhatUserConfig = { 8 | networks: { 9 | hardhat: { 10 | accounts: { 11 | mnemonic: TEST_MNEMONIC, 12 | path: "m/44'/60'/0'/0", 13 | initialIndex: 0, 14 | count: 20 15 | } 16 | } 17 | }, 18 | solidity: { 19 | version: "0.8.23", 20 | settings: { 21 | optimizer: { 22 | enabled: true 23 | } 24 | } 25 | }, 26 | gasReporter: { 27 | currency: "USD", 28 | enabled: process.env.REPORT_GAS === "true", 29 | outputJSONFile: "gas-report-excubiae.json", 30 | outputJSON: process.env.REPORT_GAS_OUTPUT_JSON === "true" 31 | }, 32 | typechain: { 33 | target: "ethers-v6" 34 | } 35 | } 36 | 37 | export default hardhatConfig 38 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/test/MockERC721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | /// @title Mock ERC721 Token Contract. 8 | /// @notice This contract is a mock implementation of the ERC721 standard for testing purposes. 9 | /// @dev It simulates the behavior of a real ERC721 contract by providing basic minting functionality. 10 | contract MockERC721 is ERC721, Ownable(msg.sender) { 11 | /// @notice A counter to keep track of the token IDs. 12 | uint256 private _tokenIdCounter; 13 | 14 | /// @notice Constructor to initialize the mock contract with a name and symbol. 15 | constructor() payable ERC721("MockERC721Token", "MockERC721Token") {} 16 | 17 | /// @notice Mints a new token and assigns it to the specified recipient. 18 | /// @param recipient The address that will receive the minted token. 19 | function mintAndGiveToken(address recipient) public onlyOwner { 20 | _safeMint(recipient, _tokenIdCounter++); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Ethereum Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/imt/contracts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethereum Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethereum Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/lazy-imt/contracts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethereum Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/lazytower/contracts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethereum Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/lean-imt/contracts/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethereum Foundation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/lean-imt/contracts/test/LeanIMTTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.4; 4 | 5 | import {LeanIMT, LeanIMTData} from "../LeanIMT.sol"; 6 | 7 | contract LeanIMTTest { 8 | LeanIMTData public data; 9 | 10 | function insert(uint256 leaf) external { 11 | LeanIMT.insert(data, leaf); 12 | } 13 | 14 | function insertMany(uint256[] calldata leaves) external { 15 | LeanIMT.insertMany(data, leaves); 16 | } 17 | 18 | function update(uint256 oldLeaf, uint256 newLeaf, uint256[] calldata siblingNodes) external { 19 | LeanIMT.update(data, oldLeaf, newLeaf, siblingNodes); 20 | } 21 | 22 | function remove(uint256 leaf, uint256[] calldata siblingNodes) external { 23 | LeanIMT.remove(data, leaf, siblingNodes); 24 | } 25 | 26 | function has(uint256 leaf) external view returns (bool) { 27 | return LeanIMT.has(data, leaf); 28 | } 29 | 30 | function indexOf(uint256 leaf) external view returns (uint256) { 31 | return LeanIMT.indexOf(data, leaf); 32 | } 33 | 34 | function root() public view returns (uint256) { 35 | return LeanIMT.root(data); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | tags: 9 | - "*" 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Install Node.js 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: 20 24 | cache: yarn 25 | registry-url: "https://registry.npmjs.org" 26 | 27 | - name: Authentication 28 | run: | 29 | echo npmAuthToken: "$NODE_AUTH_TOKEN" >> ./.yarnrc.yml 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 32 | 33 | - name: Install dependencies 34 | run: yarn 35 | 36 | - name: Publish packages 37 | run: yarn version:publish 38 | env: 39 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 40 | 41 | - run: yarn version:release 42 | env: 43 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 44 | -------------------------------------------------------------------------------- /.yarn/patches/changelogithub-npm-0.13.3-1783949906.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dist/index.mjs b/dist/index.mjs 2 | index 8d005a0e1896ec10e6d09755b5859587882e3f93..580cb17bb6be135b45e8060155d0eb5c2614b02c 100644 3 | --- a/dist/index.mjs 4 | +++ b/dist/index.mjs 5 | @@ -192,7 +192,7 @@ function formatLine(commit, options) { 6 | function formatTitle(name, options) { 7 | if (!options.emoji) 8 | name = name.replace(emojisRE, ""); 9 | - return `###    ${name.trim()}`; 10 | + return `##    ${name.trim()}`; 11 | } 12 | function formatSection(commits, sectionName, options) { 13 | if (!commits.length) 14 | @@ -209,7 +209,8 @@ function formatSection(commits, sectionName, options) { 15 | Object.keys(scopes).sort().forEach((scope) => { 16 | let padding = ""; 17 | let prefix = ""; 18 | - const scopeText = `**${options.scopeMap[scope] || scope}**`; 19 | + const url = `https://github.com/${options.repo}/tree/main/packages/${scope}`; 20 | + const scopeText = `[**@${options.repo.split("/")[1]}/${options.scopeMap[scope] || scope}**](${url})`; 21 | if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) { 22 | lines.push(`- ${scopeText}:`); 23 | padding = " "; 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # IDE 20 | .vscode 21 | .idea 22 | 23 | # Cargo 24 | target 25 | 26 | # Testing 27 | coverage 28 | coverage.json 29 | *.lcov 30 | 31 | # Dependency directories 32 | node_modules/ 33 | 34 | # Parcel cache 35 | .parcel-cache 36 | 37 | # TypeScript cache 38 | *.tsbuildinfo 39 | 40 | # Output of 'npm pack' 41 | *.tgz 42 | 43 | # Microbundle cache 44 | .rpt2_cache/ 45 | .rts2_cache_cjs/ 46 | .rts2_cache_es/ 47 | .rts2_cache_umd/ 48 | 49 | # Yarn Integrity file 50 | .yarn-integrity 51 | 52 | # Generate output 53 | dist 54 | build 55 | 56 | # Hardhat 57 | artifacts 58 | cache 59 | typechain-types 60 | 61 | # dotenv environment variable files 62 | .env 63 | .env.development.local 64 | .env.test.local 65 | .env.production.local 66 | .env.local 67 | 68 | # Optional npm cache directory 69 | .npm 70 | .DS_Store 71 | 72 | # yarn v3 73 | .pnp.* 74 | .yarn/* 75 | !.yarn/patches 76 | !.yarn/plugins 77 | !.yarn/releases 78 | !.yarn/sdks 79 | !.yarn/versions 80 | 81 | .envrc 82 | .tool-versions 83 | -------------------------------------------------------------------------------- /packages/lazy-imt/contracts/test/LazyIMTTest.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.4; 4 | 5 | import {LazyIMT, LazyIMTData} from "../LazyIMT.sol"; 6 | 7 | contract LazyIMTTest { 8 | LazyIMTData public data; 9 | uint256 _root; 10 | 11 | function init(uint8 depth) public { 12 | LazyIMT.init(data, depth); 13 | } 14 | 15 | function reset() public { 16 | LazyIMT.reset(data); 17 | } 18 | 19 | function insert(uint256 leaf) public { 20 | LazyIMT.insert(data, leaf); 21 | } 22 | 23 | function update(uint256 leaf, uint40 index) public { 24 | LazyIMT.update(data, leaf, index); 25 | } 26 | 27 | // for benchmarking the root cost 28 | function benchmarkRoot() public { 29 | _root = LazyIMT.root(data); 30 | } 31 | 32 | function root() public view returns (uint256) { 33 | return LazyIMT.root(data); 34 | } 35 | 36 | function dynamicRoot(uint8 depth) public view returns (uint256) { 37 | return LazyIMT.root(data, depth); 38 | } 39 | 40 | function staticRoot(uint8 depth) public view returns (uint256) { 41 | return LazyIMT.root(data, depth); 42 | } 43 | 44 | function merkleProofElements(uint40 index, uint8 depth) public view returns (uint256[] memory) { 45 | return LazyIMT.merkleProofElements(data, index, depth); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/imt/contracts/BinaryIMT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.4; 3 | 4 | import {InternalBinaryIMT, BinaryIMTData} from "./InternalBinaryIMT.sol"; 5 | 6 | library BinaryIMT { 7 | using InternalBinaryIMT for *; 8 | 9 | function defaultZero(uint256 index) public pure returns (uint256) { 10 | return InternalBinaryIMT._defaultZero(index); 11 | } 12 | 13 | function init(BinaryIMTData storage self, uint256 depth, uint256 zero) public { 14 | InternalBinaryIMT._init(self, depth, zero); 15 | } 16 | 17 | function initWithDefaultZeroes(BinaryIMTData storage self, uint256 depth) public { 18 | InternalBinaryIMT._initWithDefaultZeroes(self, depth); 19 | } 20 | 21 | function insert(BinaryIMTData storage self, uint256 leaf) public returns (uint256) { 22 | return InternalBinaryIMT._insert(self, leaf); 23 | } 24 | 25 | function update( 26 | BinaryIMTData storage self, 27 | uint256 leaf, 28 | uint256 newLeaf, 29 | uint256[] calldata proofSiblings, 30 | uint8[] calldata proofPathIndices 31 | ) public { 32 | InternalBinaryIMT._update(self, leaf, newLeaf, proofSiblings, proofPathIndices); 33 | } 34 | 35 | function remove( 36 | BinaryIMTData storage self, 37 | uint256 leaf, 38 | uint256[] calldata proofSiblings, 39 | uint8[] calldata proofPathIndices 40 | ) public { 41 | InternalBinaryIMT._remove(self, leaf, proofSiblings, proofPathIndices); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/imt/contracts/QuinaryIMT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.4; 3 | 4 | import {InternalQuinaryIMT, QuinaryIMTData} from "./InternalQuinaryIMT.sol"; 5 | 6 | library QuinaryIMT { 7 | using InternalQuinaryIMT for *; 8 | 9 | function init(QuinaryIMTData storage self, uint256 depth, uint256 zero) public { 10 | InternalQuinaryIMT._init(self, depth, zero); 11 | } 12 | 13 | function insert(QuinaryIMTData storage self, uint256 leaf) public { 14 | InternalQuinaryIMT._insert(self, leaf); 15 | } 16 | 17 | function update( 18 | QuinaryIMTData storage self, 19 | uint256 leaf, 20 | uint256 newLeaf, 21 | uint256[4][] calldata proofSiblings, 22 | uint8[] calldata proofPathIndices 23 | ) public { 24 | InternalQuinaryIMT._update(self, leaf, newLeaf, proofSiblings, proofPathIndices); 25 | } 26 | 27 | function remove( 28 | QuinaryIMTData storage self, 29 | uint256 leaf, 30 | uint256[4][] calldata proofSiblings, 31 | uint8[] calldata proofPathIndices 32 | ) public { 33 | InternalQuinaryIMT._remove(self, leaf, proofSiblings, proofPathIndices); 34 | } 35 | 36 | function verify( 37 | QuinaryIMTData storage self, 38 | uint256 leaf, 39 | uint256[4][] calldata proofSiblings, 40 | uint8[] calldata proofPathIndices 41 | ) private view returns (bool) { 42 | return InternalQuinaryIMT._verify(self, leaf, proofSiblings, proofPathIndices); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/excubiae/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "excubiae", 3 | "description": "a flexible & modular framework for general purpose on-chain gatekeepers.", 4 | "private": true, 5 | "scripts": { 6 | "start": "hardhat node", 7 | "compile": "hardhat compile", 8 | "test": "hardhat test", 9 | "test:report-gas": "REPORT_GAS=true hardhat test", 10 | "test:coverage": "hardhat coverage", 11 | "typechain": "hardhat typechain", 12 | "lint": "solhint 'contracts/**/*.sol'", 13 | "slither": "slither . --include-paths contracts --exclude-dependencies --ignore-compile" 14 | }, 15 | "devDependencies": { 16 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", 17 | "@nomicfoundation/hardhat-ethers": "^3.0.0", 18 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 19 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 20 | "@nomicfoundation/hardhat-verify": "^2.0.0", 21 | "@typechain/ethers-v6": "^0.5.0", 22 | "@typechain/hardhat": "^9.0.0", 23 | "@types/chai": "^4.2.0", 24 | "@types/mocha": "^10.0.6", 25 | "@types/node": "^20.10.7", 26 | "chai": "^4.2.0", 27 | "ethers": "^6.4.0", 28 | "hardhat": "^2.19.4", 29 | "hardhat-gas-reporter": "^2.2.0", 30 | "prettier-plugin-solidity": "^1.3.1", 31 | "solhint": "^3.3.6", 32 | "solhint-plugin-prettier": "^0.1.0", 33 | "solidity-coverage": "^0.8.0", 34 | "ts-node": "^10.9.2", 35 | "typechain": "^8.3.0", 36 | "typescript": "^5.3.3" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/lean-imt/contracts/LeanIMT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.4; 3 | 4 | import {InternalLeanIMT, LeanIMTData} from "./InternalLeanIMT.sol"; 5 | 6 | library LeanIMT { 7 | using InternalLeanIMT for *; 8 | 9 | function insert(LeanIMTData storage self, uint256 leaf) public returns (uint256) { 10 | return InternalLeanIMT._insert(self, leaf); 11 | } 12 | 13 | function insertMany(LeanIMTData storage self, uint256[] calldata leaves) public returns (uint256) { 14 | return InternalLeanIMT._insertMany(self, leaves); 15 | } 16 | 17 | function update( 18 | LeanIMTData storage self, 19 | uint256 oldLeaf, 20 | uint256 newLeaf, 21 | uint256[] calldata siblingNodes 22 | ) public returns (uint256) { 23 | return InternalLeanIMT._update(self, oldLeaf, newLeaf, siblingNodes); 24 | } 25 | 26 | function remove( 27 | LeanIMTData storage self, 28 | uint256 oldLeaf, 29 | uint256[] calldata siblingNodes 30 | ) public returns (uint256) { 31 | return InternalLeanIMT._remove(self, oldLeaf, siblingNodes); 32 | } 33 | 34 | function has(LeanIMTData storage self, uint256 leaf) public view returns (bool) { 35 | return InternalLeanIMT._has(self, leaf); 36 | } 37 | 38 | function indexOf(LeanIMTData storage self, uint256 leaf) public view returns (uint256) { 39 | return InternalLeanIMT._indexOf(self, leaf); 40 | } 41 | 42 | function root(LeanIMTData storage self) public view returns (uint256) { 43 | return InternalLeanIMT._root(self); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/imt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "imt.sol", 3 | "private": true, 4 | "scripts": { 5 | "start": "hardhat node", 6 | "compile": "hardhat compile", 7 | "test": "hardhat test", 8 | "test:report-gas": "REPORT_GAS=true hardhat test", 9 | "test:coverage": "hardhat coverage", 10 | "typechain": "hardhat typechain", 11 | "lint": "solhint 'contracts/**/*.sol'", 12 | "slither": "slither . --include-paths contracts --exclude-dependencies --ignore-compile" 13 | }, 14 | "devDependencies": { 15 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", 16 | "@nomicfoundation/hardhat-ethers": "^3.0.0", 17 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 18 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 19 | "@nomicfoundation/hardhat-verify": "^2.0.0", 20 | "@typechain/ethers-v6": "^0.5.0", 21 | "@typechain/hardhat": "^9.0.0", 22 | "@types/chai": "^4.2.0", 23 | "@types/mocha": "^10.0.6", 24 | "@types/node": "^20.10.7", 25 | "@zk-kit/imt": "^2.0.0-beta.5", 26 | "chai": "^4.2.0", 27 | "ethers": "^6.4.0", 28 | "hardhat": "^2.19.4", 29 | "hardhat-gas-reporter": "^2.2.0", 30 | "poseidon-lite": "^0.2.0", 31 | "prettier-plugin-solidity": "^1.3.1", 32 | "solhint": "^3.3.6", 33 | "solhint-plugin-prettier": "^0.1.0", 34 | "solidity-coverage": "^0.8.0", 35 | "ts-node": "^10.9.2", 36 | "typechain": "^8.3.0", 37 | "typescript": "^5.3.3" 38 | }, 39 | "dependencies": { 40 | "poseidon-solidity": "0.0.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/lazy-imt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lazy-imt.sol", 3 | "private": true, 4 | "scripts": { 5 | "start": "hardhat node", 6 | "compile": "hardhat compile", 7 | "test": "hardhat test", 8 | "test:report-gas": "REPORT_GAS=true hardhat test", 9 | "test:coverage": "hardhat coverage", 10 | "typechain": "hardhat typechain", 11 | "lint": "solhint 'contracts/**/*.sol'", 12 | "slither": "slither . --include-paths contracts --exclude-dependencies --ignore-compile" 13 | }, 14 | "devDependencies": { 15 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", 16 | "@nomicfoundation/hardhat-ethers": "^3.0.0", 17 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 18 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 19 | "@nomicfoundation/hardhat-verify": "^2.0.0", 20 | "@typechain/ethers-v6": "^0.5.0", 21 | "@typechain/hardhat": "^9.0.0", 22 | "@types/chai": "^4.2.0", 23 | "@types/mocha": "^10.0.6", 24 | "@types/node": "^20.10.7", 25 | "@zk-kit/imt": "^2.0.0-beta.5", 26 | "chai": "^4.2.0", 27 | "ethers": "^6.4.0", 28 | "hardhat": "^2.19.4", 29 | "hardhat-gas-reporter": "^2.2.0", 30 | "poseidon-lite": "^0.2.0", 31 | "prettier-plugin-solidity": "^1.3.1", 32 | "solhint": "^3.3.6", 33 | "solhint-plugin-prettier": "^0.1.0", 34 | "solidity-coverage": "^0.8.0", 35 | "ts-node": "^10.9.2", 36 | "typechain": "^8.3.0", 37 | "typescript": "^5.3.3" 38 | }, 39 | "dependencies": { 40 | "poseidon-solidity": "0.0.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/lean-imt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lean-imt.sol", 3 | "private": true, 4 | "scripts": { 5 | "start": "hardhat node", 6 | "compile": "hardhat compile", 7 | "test": "hardhat test", 8 | "test:report-gas": "REPORT_GAS=true hardhat test", 9 | "test:coverage": "hardhat coverage", 10 | "typechain": "hardhat typechain", 11 | "lint": "solhint 'contracts/**/*.sol'", 12 | "slither": "slither . --include-paths contracts --exclude-dependencies --ignore-compile" 13 | }, 14 | "devDependencies": { 15 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", 16 | "@nomicfoundation/hardhat-ethers": "^3.0.0", 17 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 18 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 19 | "@nomicfoundation/hardhat-verify": "^2.0.0", 20 | "@typechain/ethers-v6": "^0.5.0", 21 | "@typechain/hardhat": "^9.0.0", 22 | "@types/chai": "^4.2.0", 23 | "@types/mocha": "^10.0.6", 24 | "@types/node": "^20.10.7", 25 | "@zk-kit/lean-imt": "^2.0.1", 26 | "chai": "^4.2.0", 27 | "ethers": "^6.4.0", 28 | "hardhat": "^2.19.4", 29 | "hardhat-gas-reporter": "^2.2.0", 30 | "poseidon-lite": "^0.2.0", 31 | "prettier-plugin-solidity": "^1.3.1", 32 | "solhint": "^3.3.6", 33 | "solhint-plugin-prettier": "^0.1.0", 34 | "solidity-coverage": "^0.8.0", 35 | "ts-node": "^10.9.2", 36 | "typechain": "^8.3.0", 37 | "typescript": "^5.3.3" 38 | }, 39 | "dependencies": { 40 | "poseidon-solidity": "0.0.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/lazytower/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lazytower.sol", 3 | "private": true, 4 | "scripts": { 5 | "start": "hardhat node", 6 | "compile": "hardhat compile", 7 | "deploy:test-contracts": "hardhat deploy:tree-contracts", 8 | "test": "hardhat test", 9 | "test:report-gas": "REPORT_GAS=true hardhat test", 10 | "test:coverage": "hardhat coverage", 11 | "typechain": "hardhat typechain", 12 | "lint": "solhint 'contracts/**/*.sol'", 13 | "slither": "slither . --include-paths contracts --exclude-dependencies --ignore-compile" 14 | }, 15 | "devDependencies": { 16 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.3", 17 | "@nomicfoundation/hardhat-ethers": "^3.0.0", 18 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 19 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 20 | "@nomicfoundation/hardhat-verify": "^2.0.0", 21 | "@typechain/ethers-v6": "^0.5.0", 22 | "@typechain/hardhat": "^9.0.0", 23 | "@types/chai": "^4.2.0", 24 | "@types/mocha": "^10.0.6", 25 | "@types/node": "^20.10.7", 26 | "chai": "^4.2.0", 27 | "ethers": "^6.4.0", 28 | "hardhat": "^2.19.4", 29 | "hardhat-gas-reporter": "^2.2.0", 30 | "poseidon-lite": "^0.2.0", 31 | "prettier-plugin-solidity": "^1.3.1", 32 | "solhint": "^3.3.6", 33 | "solhint-plugin-prettier": "^0.1.0", 34 | "solidity-coverage": "^0.8.0", 35 | "ts-node": "^10.9.2", 36 | "typechain": "^8.3.0", 37 | "typescript": "^5.3.3" 38 | }, 39 | "dependencies": { 40 | "poseidon-solidity": "0.0.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/lazytower/tasks/deploy-lazytower-test.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config" 2 | 3 | task("deploy:lazytower-test", "Deploy a LazyTowerHashChainTest contract") 4 | .addOptionalParam("logs", "Print the logs", true, types.boolean) 5 | .setAction(async ({ logs }, { ethers }): Promise => { 6 | const PoseidonT3Factory = await ethers.getContractFactory("PoseidonT3") 7 | 8 | const poseidonT3 = await PoseidonT3Factory.deploy() 9 | const poseidonT3Address = await poseidonT3.getAddress() 10 | 11 | if (logs) { 12 | console.info(`PoseidonT3 library has been deployed to: ${poseidonT3Address}`) 13 | } 14 | 15 | const LazyTowerLibFactory = await ethers.getContractFactory("LazyTowerHashChain", { 16 | libraries: { 17 | PoseidonT3: poseidonT3Address 18 | } 19 | }) 20 | 21 | const lazyTowerLib = await LazyTowerLibFactory.deploy() 22 | const lazyTowerLibAddress = await lazyTowerLib.getAddress() 23 | 24 | if (logs) { 25 | console.info(`LazyTowerHashChain library has been deployed to: ${lazyTowerLibAddress}`) 26 | } 27 | 28 | const ContractFactory = await ethers.getContractFactory("LazyTowerHashChainTest", { 29 | libraries: { 30 | LazyTowerHashChain: lazyTowerLibAddress 31 | } 32 | }) 33 | 34 | const contract = await ContractFactory.deploy() 35 | const contractAddress = await lazyTowerLib.getAddress() 36 | 37 | if (logs) { 38 | console.info(`Test contract has been deployed to: ${contractAddress}`) 39 | } 40 | 41 | return contract 42 | }) 43 | -------------------------------------------------------------------------------- /packages/imt/tasks/deploy-imt-test.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config" 2 | 3 | task("deploy:imt-test", "Deploy an IMT contract for testing a library") 4 | .addParam("library", "The name of the library", undefined, types.string) 5 | .addOptionalParam("logs", "Print the logs", true, types.boolean) 6 | .addOptionalParam("arity", "The arity of the tree", 2, types.int) 7 | .setAction(async ({ logs, library: libraryName, arity }, { ethers }): Promise => { 8 | const PoseidonFactory = await ethers.getContractFactory(`PoseidonT${arity + 1}`) 9 | 10 | const poseidon = await PoseidonFactory.deploy() 11 | const poseidonAddress = await poseidon.getAddress() 12 | 13 | if (logs) { 14 | console.info(`PoseidonT${arity + 1} library has been deployed to: ${poseidonAddress}`) 15 | } 16 | 17 | const LibraryFactory = await ethers.getContractFactory(libraryName, { 18 | libraries: { 19 | [`PoseidonT${arity + 1}`]: poseidonAddress 20 | } 21 | }) 22 | 23 | const library = await LibraryFactory.deploy() 24 | const libraryAddress = await library.getAddress() 25 | 26 | if (logs) { 27 | console.info(`${libraryName} library has been deployed to: ${libraryAddress}`) 28 | } 29 | 30 | const ContractFactory = await ethers.getContractFactory(`${libraryName}Test`, { 31 | libraries: { 32 | [libraryName]: libraryAddress 33 | } 34 | }) 35 | 36 | const contract = await ContractFactory.deploy() 37 | const contractAddress = await contract.getAddress() 38 | 39 | if (logs) { 40 | console.info(`${libraryName}Test contract has been deployed to: ${contractAddress}`) 41 | } 42 | 43 | return { library, contract } 44 | }) 45 | -------------------------------------------------------------------------------- /packages/lazy-imt/contracts/LazyIMT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.4; 3 | 4 | import {InternalLazyIMT, LazyIMTData} from "./InternalLazyIMT.sol"; 5 | 6 | library LazyIMT { 7 | using InternalLazyIMT for *; 8 | 9 | function init(LazyIMTData storage self, uint8 depth) public { 10 | InternalLazyIMT._init(self, depth); 11 | } 12 | 13 | function defaultZero(uint8 index) public pure returns (uint256) { 14 | return InternalLazyIMT._defaultZero(index); 15 | } 16 | 17 | function reset(LazyIMTData storage self) public { 18 | InternalLazyIMT._reset(self); 19 | } 20 | 21 | function indexForElement(uint8 level, uint40 index) public pure returns (uint40) { 22 | return InternalLazyIMT._indexForElement(level, index); 23 | } 24 | 25 | function insert(LazyIMTData storage self, uint256 leaf) public { 26 | InternalLazyIMT._insert(self, leaf); 27 | } 28 | 29 | function update(LazyIMTData storage self, uint256 leaf, uint40 index) public { 30 | InternalLazyIMT._update(self, leaf, index); 31 | } 32 | 33 | function root(LazyIMTData storage self) public view returns (uint256) { 34 | return InternalLazyIMT._root(self); 35 | } 36 | 37 | function root(LazyIMTData storage self, uint8 depth) public view returns (uint256) { 38 | return InternalLazyIMT._root(self, depth); 39 | } 40 | 41 | function merkleProofElements( 42 | LazyIMTData storage self, 43 | uint40 index, 44 | uint8 depth 45 | ) public view returns (uint256[] memory) { 46 | return InternalLazyIMT._merkleProofElements(self, index, depth); 47 | } 48 | 49 | function _root(LazyIMTData storage self, uint40 numberOfLeaves, uint8 depth) internal view returns (uint256) { 50 | return InternalLazyIMT._root(self, numberOfLeaves, depth); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/lazy-imt/tasks/deploy-imt-test.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config" 2 | 3 | task("deploy:imt-test", "Deploy an IMT contract for testing a library") 4 | .addParam("library", "The name of the library", undefined, types.string) 5 | .addOptionalParam("logs", "Print the logs", true, types.boolean) 6 | .addOptionalParam("arity", "The arity of the tree", 2, types.int) 7 | .setAction(async ({ logs, library: libraryName, arity }, { ethers }): Promise => { 8 | const PoseidonFactory = await ethers.getContractFactory(`PoseidonT${arity + 1}`) 9 | 10 | const poseidon = await PoseidonFactory.deploy() 11 | const poseidonAddress = await poseidon.getAddress() 12 | 13 | if (logs) { 14 | console.info(`PoseidonT${arity + 1} library has been deployed to: ${poseidonAddress}`) 15 | } 16 | 17 | const LibraryFactory = await ethers.getContractFactory(libraryName, { 18 | libraries: { 19 | [`PoseidonT${arity + 1}`]: poseidonAddress 20 | } 21 | }) 22 | 23 | const library = await LibraryFactory.deploy() 24 | const libraryAddress = await library.getAddress() 25 | 26 | if (logs) { 27 | console.info(`${libraryName} library has been deployed to: ${libraryAddress}`) 28 | } 29 | 30 | const ContractFactory = await ethers.getContractFactory(`${libraryName}Test`, { 31 | libraries: { 32 | [libraryName]: libraryAddress 33 | } 34 | }) 35 | 36 | const contract = await ContractFactory.deploy() 37 | const contractAddress = await contract.getAddress() 38 | 39 | if (logs) { 40 | console.info(`${libraryName}Test contract has been deployed to: ${contractAddress}`) 41 | } 42 | 43 | return { library, contract } 44 | }) 45 | -------------------------------------------------------------------------------- /packages/lean-imt/tasks/deploy-imt-test.ts: -------------------------------------------------------------------------------- 1 | import { task, types } from "hardhat/config" 2 | 3 | task("deploy:imt-test", "Deploy an IMT contract for testing a library") 4 | .addParam("library", "The name of the library", undefined, types.string) 5 | .addOptionalParam("logs", "Print the logs", true, types.boolean) 6 | .addOptionalParam("arity", "The arity of the tree", 2, types.int) 7 | .setAction(async ({ logs, library: libraryName, arity }, { ethers }): Promise => { 8 | const PoseidonFactory = await ethers.getContractFactory(`PoseidonT${arity + 1}`) 9 | 10 | const poseidon = await PoseidonFactory.deploy() 11 | const poseidonAddress = await poseidon.getAddress() 12 | 13 | if (logs) { 14 | console.info(`PoseidonT${arity + 1} library has been deployed to: ${poseidonAddress}`) 15 | } 16 | 17 | const LibraryFactory = await ethers.getContractFactory(libraryName, { 18 | libraries: { 19 | [`PoseidonT${arity + 1}`]: poseidonAddress 20 | } 21 | }) 22 | 23 | const library = await LibraryFactory.deploy() 24 | const libraryAddress = await library.getAddress() 25 | 26 | if (logs) { 27 | console.info(`${libraryName} library has been deployed to: ${libraryAddress}`) 28 | } 29 | 30 | const ContractFactory = await ethers.getContractFactory(`${libraryName}Test`, { 31 | libraries: { 32 | [libraryName]: libraryAddress 33 | } 34 | }) 35 | 36 | const contract = await ContractFactory.deploy() 37 | const contractAddress = await contract.getAddress() 38 | 39 | if (logs) { 40 | console.info(`${libraryName}Test contract has been deployed to: ${contractAddress}`) 41 | } 42 | 43 | return { library, contract } 44 | }) 45 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/FreeForAllExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | 6 | /// @title FreeForAll Excubia Contract. 7 | /// @notice This contract extends the Excubia contract to allow free access through the gate. 8 | /// This contract does not perform any checks and allows any passerby to pass the gate. 9 | /// @dev The contract overrides the `_check` function to always return true. 10 | contract FreeForAllExcubia is Excubia { 11 | /// @notice Constructor for the FreeForAllExcubia contract. 12 | constructor() {} 13 | 14 | /// @notice Mapping to track already passed passersby. 15 | mapping(address => bool) public passedPassersby; 16 | 17 | /// @notice The trait of the Excubia contract. 18 | function trait() external pure override returns (string memory) { 19 | return "FreeForAll"; 20 | } 21 | 22 | /// @notice Internal function to handle the gate passing logic. 23 | /// @dev This function calls the parent `_pass` function and then tracks the passerby. 24 | /// @param passerby The address of the entity passing the gate. 25 | /// @param data Additional data required for the pass (not used in this implementation). 26 | function _pass(address passerby, bytes calldata data) internal override { 27 | // Avoiding passing the gate twice with the same address. 28 | if (passedPassersby[passerby]) revert AlreadyPassed(); 29 | 30 | passedPassersby[passerby] = true; 31 | 32 | super._pass(passerby, data); 33 | } 34 | 35 | /// @notice Internal function to handle the gate protection logic. 36 | /// @dev This function always returns true, signaling that any passerby is able to pass the gate. 37 | /// @param passerby The address of the entity attempting to pass the gate. 38 | /// @param data Additional data required for the check (e.g., encoded attestation ID). 39 | function _check(address passerby, bytes calldata data) internal view override { 40 | super._check(passerby, data); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/imt/contracts/README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Incremental Merkle Tree (Solidity) 4 |

5 |

Incremental Merkle tree implementation in Solidity.

6 |

7 | 8 |

9 | 10 | 11 | 12 | 13 | NPM license 14 | 15 | 16 | NPM version 17 | 18 | 19 | Downloads 20 | 21 | 22 | Code style prettier 23 | 24 |

25 | 26 | 33 | 34 | > [!WARNING] 35 | > These library has **not** been audited. 36 | 37 | > [!WARNING] 38 | > If you are looking for the first version of this package, please visit this [link](https://github.com/privacy-scaling-explorations/zk-kit/tree/imt-v1/packages/incremental-merkle-tree.sol). 39 | 40 | --- 41 | 42 | ## 🛠 Install 43 | 44 | ### npm or yarn 45 | 46 | Install the `@zk-kit/imt.sol` package with npm: 47 | 48 | ```bash 49 | npm i @zk-kit/imt.sol --save 50 | ``` 51 | 52 | or yarn: 53 | 54 | ```bash 55 | yarn add @zk-kit/imt.sol 56 | ``` 57 | 58 | ## 📜 Usage 59 | 60 | Please, see the [test contracts](./test) for guidance on utilizing the libraries. 61 | -------------------------------------------------------------------------------- /packages/lazy-imt/contracts/README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Lazy Incremental Merkle Tree (Solidity) 4 |

5 |

Lazy Incremental Merkle tree implementation in Solidity.

6 |

7 | 8 |

9 | 10 | 11 | 12 | 13 | NPM license 14 | 15 | 16 | NPM version 17 | 18 | 19 | Downloads 20 | 21 | 22 | Code style prettier 23 | 24 |

25 | 26 | 33 | 34 | > [!WARNING] 35 | > These library has **not** been audited. 36 | 37 | > [!WARNING] 38 | > If you are looking for the first version of this package, please visit this [link](https://github.com/privacy-scaling-explorations/zk-kit/tree/imt-v1/packages/incremental-merkle-tree.sol). 39 | 40 | --- 41 | 42 | ## 🛠 Install 43 | 44 | ### npm or yarn 45 | 46 | Install the `@zk-kit/lazy-imt.sol` package with npm: 47 | 48 | ```bash 49 | npm i @zk-kit/lazy-imt.sol --save 50 | ``` 51 | 52 | or yarn: 53 | 54 | ```bash 55 | yarn add @zk-kit/lazy-imt.sol 56 | ``` 57 | 58 | ## 📜 Usage 59 | 60 | Please, see the [test contracts](./test) for guidance on utilizing the libraries. 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zk-kit.solidity", 3 | "description": "A monorepo of reusable contracts for zero-knowledge technologies.", 4 | "license": "MIT", 5 | "repository": "git@github.com:privacy-scaling-explorations/zk-kit.solidity.git", 6 | "homepage": "https://github.com/privacy-scaling-explorations/zk-kit.solidity", 7 | "bugs": "https://github.com/privacy-scaling-explorations/zk-kit.solidity/issues", 8 | "private": true, 9 | "scripts": { 10 | "compile": "yarn workspaces foreach -Ap run compile", 11 | "test": "yarn workspaces foreach -Ap run test:coverage", 12 | "version:bump": "yarn workspace @zk-kit/${0} version ${1} && yarn remove:stable-version-field ${0} && NO_HOOK=1 git commit -am \"chore(${0}): v${1}\" && git tag ${0}-v${1}", 13 | "version:publish": "yarn workspaces foreach -A --no-private npm publish --tolerate-republish --access public", 14 | "version:release": "changelogithub", 15 | "format": "prettier -c .", 16 | "format:write": "prettier -w .", 17 | "remove:stable-version-field": "ts-node scripts/remove-stable-version-field.ts ${0} && yarn format:write", 18 | "lint": "yarn workspaces foreach -Ap run lint", 19 | "postinstall": "husky && git config --local core.editor cat", 20 | "slither": "./scripts/check-slither.sh && yarn workspaces foreach -Ap run slither" 21 | }, 22 | "keywords": [ 23 | "solidity", 24 | "monorepo", 25 | "zero-knowledge", 26 | "zk-snarks" 27 | ], 28 | "workspaces": [ 29 | "packages/*", 30 | "packages/*/contracts" 31 | ], 32 | "packageManager": "yarn@4.2.1", 33 | "devDependencies": { 34 | "@commitlint/cli": "^18.6.1", 35 | "@commitlint/config-conventional": "^18.6.2", 36 | "@types/glob": "^7.2.0", 37 | "@types/node": "^20", 38 | "changelogithub": "patch:changelogithub@npm%3A0.13.3#~/.yarn/patches/changelogithub-npm-0.13.3-1783949906.patch", 39 | "czg": "^1.9.1", 40 | "husky": "^9.0.11", 41 | "lint-staged": "^15.2.2", 42 | "prettier": "^3.2.5", 43 | "ts-node": "^10.9.2", 44 | "typescript": "^5.3.3" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | [DEPRECATED] Excubiae 4 |

5 |

A flexible and modular framework for general-purpose on-chain gatekeepers.

6 |

7 | 8 |

9 | No Maintenance 10 |

11 | 12 | > [!NOTE] 13 | > This package has been DEPRECATED. Please, refer to [@excubiae/contracts](https://www.npmjs.com/package/@excubiae/contracts) on [excubiae](https://github.com/privacy-scaling-explorations/excubiae) monorepo. 14 | 15 | --- 16 | 17 | --- 18 | 19 | Excubiae is a generalized framework for on-chain gatekeepers that allows developers to define custom access control mechanisms using different on-chain credentials. By abstracting the gatekeeper logic, excubiae provides a reusable and composable solution for securing decentralised applications. This package provides a pre-defined set of specific excubia (_extensions_) for credentials based on different protocols. 20 | 21 | ## 🛠 Install 22 | 23 | ### npm or yarn 24 | 25 | Install the ` @zk-kit/excubiae` package with npm: 26 | 27 | ```bash 28 | npm i @zk-kit/excubiae --save 29 | ``` 30 | 31 | or yarn: 32 | 33 | ```bash 34 | yarn add @zk-kit/excubiae 35 | ``` 36 | 37 | ## 📜 Usage 38 | 39 | To build your own Excubia: 40 | 41 | 1. Inherit from the [Excubia](./Excubia.sol) abstract contract that conforms to the [IExcubia](./IExcubia.sol) interface. 42 | 2. Implement the `_check()` and `_pass()` methods logic defining your own checks to prevent unwanted access as sybils or avoid to pass the gate twice with the same data / identity. 43 | 44 | ```solidity 45 | // SPDX-License-Identifier: MIT 46 | pragma solidity >=0.8.0; 47 | 48 | import { Excubia } from "excubiae/contracts/Excubia.sol"; 49 | 50 | contract MyExcubia is Excubia { 51 | // ... 52 | 53 | function _pass(address passerby, bytes calldata data) internal override { 54 | // Implement your logic to prevent unwanted access here. 55 | } 56 | 57 | function _check(address passerby, bytes calldata data) internal view override { 58 | // Implement custom access control logic here. 59 | } 60 | 61 | // ... 62 | } 63 | ``` 64 | 65 | Please see the [extensions](./extensions/) folder for more complex reference implementations and the [test contracts](./test) folder for guidance on using the libraries. 66 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/IExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | /// @title IExcubia. 5 | /// @notice Excubia contract interface. 6 | interface IExcubia { 7 | /// @notice Event emitted when someone passes the gate check. 8 | /// @param passerby The address of those who have successfully passed the check. 9 | /// @param gate The address of the excubia-protected contract address. 10 | event GatePassed(address indexed passerby, address indexed gate); 11 | 12 | /// @notice Event emitted when the gate address is set. 13 | /// @param gate The address of the contract set as the gate. 14 | event GateSet(address indexed gate); 15 | 16 | /// @notice Error thrown when an address equal to zero is given. 17 | error ZeroAddress(); 18 | 19 | /// @notice Error thrown when the gate address is not set. 20 | error GateNotSet(); 21 | 22 | /// @notice Error thrown when the callee is not the gate contract. 23 | error GateOnly(); 24 | 25 | /// @notice Error thrown when the gate address has been already set. 26 | error GateAlreadySet(); 27 | 28 | /// @notice Error thrown when the passerby has already passed the gate. 29 | error AlreadyPassed(); 30 | 31 | /// @notice Gets the trait of the Excubia contract. 32 | /// @return The specific trait of the Excubia contract (e.g., SemaphoreExcubia has trait `Semaphore`). 33 | function trait() external pure returns (string memory); 34 | 35 | /// @notice Sets the gate address. 36 | /// @dev Only the owner can set the destination gate address. 37 | /// @param _gate The address of the contract to be set as the gate. 38 | function setGate(address _gate) external; 39 | 40 | /// @notice Enforces the custom gate passing logic. 41 | /// @dev Must call the `check` to handle the logic of checking passerby for specific gate. 42 | /// @param passerby The address of the entity attempting to pass the gate. 43 | /// @param data Additional data required for the check (e.g., encoded token identifier). 44 | function pass(address passerby, bytes calldata data) external; 45 | 46 | /// @dev Defines the custom gate protection logic. 47 | /// @param passerby The address of the entity attempting to pass the gate. 48 | /// @param data Additional data that may be required for the check. 49 | function check(address passerby, bytes calldata data) external view; 50 | } 51 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/test/MockGitcoinPassportDecoder.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {IGitcoinPassportDecoder, Credential} from "../extensions/interfaces/IGitcoinPassportDecoder.sol"; 5 | 6 | /// @title Mock Gitcoin Passport Decoder Contract. 7 | /// @notice This contract is a mock implementation of the IGitcoinPassportDecoder interface for testing purposes. 8 | /// @dev It simulates a Gitcoin Passport Decoder contract providing predefined scores and credentials. 9 | contract MockGitcoinPassportDecoder is IGitcoinPassportDecoder { 10 | /// @notice A mapping to store mocked scores for each user address. 11 | mapping(address => uint256) private mockedScores; 12 | 13 | /// MOCKS /// 14 | /// @notice Constructor to initialize the mock contract with predefined user scores. 15 | /// @param _users An array of user addresses. 16 | /// @param _scores An array of scores corresponding to the user addresses. 17 | constructor(address[] memory _users, uint256[] memory _scores) { 18 | for (uint256 i = 0; i < _users.length; i++) { 19 | mockedScores[_users[i]] = _scores[i]; 20 | } 21 | } 22 | 23 | /// @notice Mock function to get the score of a user. 24 | /// @param user The address of the user. 25 | /// @return The mocked score of the user. 26 | function getScore(address user) external view returns (uint256) { 27 | return mockedScores[user]; 28 | } 29 | 30 | /// @notice Mock function to check if a user is considered human based on their score. 31 | /// @dev check the documentation for more information about (20 is default threshold). 32 | /// @dev https://docs.passport.xyz/building-with-passport/smart-contracts/contract-reference#available-methods 33 | /// @param user The address of the user. 34 | /// @return True if the user's score is greater than 20, false otherwise. 35 | function isHuman(address user) external view returns (bool) { 36 | return mockedScores[user] > 20; 37 | } 38 | 39 | /// STUBS /// 40 | function getPassport(address /*user*/) external pure returns (Credential[] memory) { 41 | Credential[] memory credentials = new Credential[](1); 42 | credentials[0] = Credential({ 43 | provider: "MockProvider", 44 | hash: keccak256("MockHash"), 45 | time: 1234567890123456789, 46 | expirationTime: 1234567890123456789 + 1 days 47 | }); 48 | return credentials; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## Description 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | ## Related Issue(s) 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ## Other information 24 | 25 | 26 | 27 | 28 | ## Checklist 29 | 30 | 31 | 32 | - [ ] I have read and understand the [contributor guidelines](https://github.com/privacy-scaling-explorations/zk-kit.solidity/blob/main/CONTRIBUTING.md) and [code of conduct](https://github.com/privacy-scaling-explorations/zk-kit.solidity/blob/main/CODE_OF_CONDUCT.md). 33 | - [ ] I have performed a self-review of my code 34 | - [ ] I have commented my code, particularly in hard-to-understand areas 35 | - [ ] My changes generate no new warnings 36 | - [ ] I have run `yarn style` without getting any errors 37 | - [ ] I have added tests that prove my fix is effective or that my feature works 38 | - [ ] New and existing unit tests pass locally with my changes 39 | 40 | > [!IMPORTANT] 41 | > We do not accept pull requests for minor grammatical fixes (e.g., correcting typos, rewording sentences) or for fixing broken links, unless they significantly improve clarity or functionality. These contributions, while appreciated, are not a priority for merging. If you notice any of these issues, please create a [GitHub Issue](https://github.com/privacy-scaling-explorations/zk-kit.solidity/issues/new?template=BLANK_ISSUE) to report them so they can be properly tracked and addressed. 42 | -------------------------------------------------------------------------------- /packages/lean-imt/contracts/README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Lean Incremental Merkle Tree (Solidity) 4 |

5 |

Lean Incremental Merkle tree implementation in Solidity.

6 |

7 | 8 |

9 | 10 | 11 | 12 | 13 | NPM license 14 | 15 | 16 | NPM version 17 | 18 | 19 | Downloads 20 | 21 | 22 | Code style prettier 23 | 24 |

25 | 26 | 33 | 34 | > [!NOTE] 35 | > This library has been audited as part of the Semaphore V4 PSE audit: https://semaphore.pse.dev/Semaphore_4.0.0_Audit.pdf. 36 | 37 | The LeanIMT is an optimized binary version of the [IMT](https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/imt) into binary-focused model, eliminating the need for zero values and allowing dynamic depth adjustment. Unlike the IMT, which uses a zero hash for incomplete nodes, the LeanIMT directly adopts the left child's value when a node lacks a right counterpart. The tree's depth dynamically adjusts to the count of leaves, enhancing efficiency by reducing the number of required hash calculations. To understand more about the LeanIMT, take a look at this [visual explanation](https://hackmd.io/@vplasencia/S1whLBN16). 38 | 39 | --- 40 | 41 | ## 🛠 Install 42 | 43 | ### npm or yarn 44 | 45 | Install the `@zk-kit/lean-imt.sol` package with npm: 46 | 47 | ```bash 48 | npm i @zk-kit/lean-imt.sol --save 49 | ``` 50 | 51 | or yarn: 52 | 53 | ```bash 54 | yarn add @zk-kit/lean-imt.sol 55 | ``` 56 | 57 | ## 📜 Usage 58 | 59 | Please, see the [test contracts](./test) for guidance on utilizing the libraries. 60 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/Excubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 5 | import {IExcubia} from "./IExcubia.sol"; 6 | 7 | /// @title Excubia. 8 | /// @notice Abstract base contract which can be extended to implement a specific excubia. 9 | /// @dev Inherit from this contract and implement the `_pass` & `_check` methods to define 10 | /// your custom gatekeeping logic. 11 | abstract contract Excubia is IExcubia, Ownable(msg.sender) { 12 | /// @notice The excubia-protected contract address. 13 | /// @dev The gate can be any contract address that requires a prior check to enable logic. 14 | /// For example, the gate is a Semaphore group that requires the passerby 15 | /// to meet certain criteria before joining. 16 | address public gate; 17 | 18 | /// @dev Modifier to restrict function calls to only from the gate address. 19 | modifier onlyGate() { 20 | if (msg.sender != gate) revert GateOnly(); 21 | _; 22 | } 23 | 24 | /// @inheritdoc IExcubia 25 | function trait() external pure virtual returns (string memory) {} 26 | 27 | /// @inheritdoc IExcubia 28 | function setGate(address _gate) public virtual onlyOwner { 29 | if (_gate == address(0)) revert ZeroAddress(); 30 | if (gate != address(0)) revert GateAlreadySet(); 31 | 32 | gate = _gate; 33 | 34 | emit GateSet(_gate); 35 | } 36 | 37 | /// @inheritdoc IExcubia 38 | function pass(address passerby, bytes calldata data) external onlyGate { 39 | _pass(passerby, data); 40 | } 41 | 42 | /// @inheritdoc IExcubia 43 | function check(address passerby, bytes calldata data) external view { 44 | _check(passerby, data); 45 | } 46 | 47 | /// @notice Internal function to enforce the custom gate passing logic. 48 | /// @dev Calls the `_check` internal logic and emits the relative event if successful. 49 | /// @param passerby The address of the entity attempting to pass the gate. 50 | /// @param data Additional data required for the check (e.g., encoded token identifier). 51 | function _pass(address passerby, bytes calldata data) internal virtual { 52 | _check(passerby, data); 53 | 54 | emit GatePassed(passerby, gate); 55 | } 56 | 57 | /// @notice Internal function to define the custom gate protection logic. 58 | /// @dev Custom logic to determine if the passerby can pass the gate. 59 | /// @param passerby The address of the entity attempting to pass the gate. 60 | /// @param data Additional data that may be required for the check. 61 | function _check(address passerby, bytes calldata data) internal view virtual {} 62 | } 63 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/ERC721Excubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol"; 6 | 7 | /// @title ERC721 Excubia Contract. 8 | /// @notice This contract extends the Excubia contract to integrate with an ERC721 token. 9 | /// This contract checks the ownership of an ERC721 token to permit access through the gate. 10 | /// @dev The contract refers to a contract implementing the ERC721 standard to admit the owner of the token. 11 | contract ERC721Excubia is Excubia { 12 | /// @notice The ERC721 token contract interface. 13 | IERC721 public immutable NFT; 14 | 15 | /// @notice Mapping to track which token IDs have passed by the gate to 16 | /// avoid passing the gate twice with the same token ID. 17 | mapping(uint256 => bool) public passedTokenIds; 18 | 19 | /// @notice Error thrown when the passerby is not the owner of the token. 20 | error UnexpectedTokenOwner(); 21 | 22 | /// @notice Constructor to initialize with target ERC721 contract. 23 | /// @param _erc721 The address of the ERC721 contract. 24 | constructor(address _erc721) { 25 | if (_erc721 == address(0)) revert ZeroAddress(); 26 | 27 | NFT = IERC721(_erc721); 28 | } 29 | 30 | /// @notice The trait of the Excubia contract. 31 | function trait() external pure override returns (string memory) { 32 | return "ERC721"; 33 | } 34 | 35 | /// @notice Internal function to handle the passing logic with check. 36 | /// @dev Calls the parent `_pass` function and stores the NFT ID to avoid passing the gate twice. 37 | /// @param passerby The address of the entity attempting to pass the gate. 38 | /// @param data Additional data required for the check (e.g., encoded token ID). 39 | function _pass(address passerby, bytes calldata data) internal override { 40 | uint256 tokenId = abi.decode(data, (uint256)); 41 | 42 | // Avoiding passing the gate twice with the same token ID. 43 | if (passedTokenIds[tokenId]) revert AlreadyPassed(); 44 | 45 | passedTokenIds[tokenId] = true; 46 | 47 | super._pass(passerby, data); 48 | } 49 | 50 | /// @notice Internal function to handle the gate protection (token ownership check) logic. 51 | /// @dev Checks if the passerby is the owner of the token. 52 | /// @param passerby The address of the entity attempting to pass the gate. 53 | /// @param data Additional data required for the check (e.g., encoded token ID). 54 | function _check(address passerby, bytes calldata data) internal view override { 55 | super._check(passerby, data); 56 | 57 | uint256 tokenId = abi.decode(data, (uint256)); 58 | 59 | // Check if the user owns the token. 60 | if (!(NFT.ownerOf(tokenId) == passerby)) revert UnexpectedTokenOwner(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/test/MockHats.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {IHatsMinimal} from "../extensions/interfaces/IHatsMinimal.sol"; 5 | 6 | /// @title Mock Hats Protocol Contract 7 | /// @notice This contract is a mock implementation of the IHatsMinimal interface for testing purposes. 8 | /// @dev It simulates the behavior of a real Hats protocol contract by providing predefined functionality 9 | /// for minting and checking hats. 10 | contract MockHats is IHatsMinimal { 11 | /// @notice A mapping to store the hats worn by each wearer address. 12 | mapping(address => uint256[]) private mockedWearers; 13 | 14 | /// @notice Constructor to initialize the mock contract with predefined hats and wearers. 15 | /// @param _hatsIds An array of hat IDs. 16 | /// @param _wearers An array of wearer addresses corresponding to the hat IDs. 17 | constructor(uint256[] memory _hatsIds, address[] memory _wearers) { 18 | for (uint256 i = 0; i < _hatsIds.length; i++) { 19 | mintHat(_hatsIds[i], _wearers[i]); 20 | } 21 | } 22 | 23 | /// @notice Mock function to mint a hat for a wearer. 24 | /// @dev This function simulates the minting of a hat by adding the hat ID to the wearer's list of hats. 25 | /// @param _hatId The ID of the hat to mint. 26 | /// @param _wearer The address of the wearer to mint the hat for. 27 | /// @return success A boolean indicating the success of the operation (always returns true). 28 | function mintHat(uint256 _hatId, address _wearer) public returns (bool success) { 29 | mockedWearers[_wearer].push(_hatId); 30 | return true; 31 | } 32 | 33 | /// @notice Mock function to check if an account is wearing a specific hat. 34 | /// @dev This function checks if the hat ID is present in the wearer's list of hats. 35 | /// @param account The address of the account to check. 36 | /// @param hat The ID of the hat to check. 37 | /// @return True if the account is wearing the hat, false otherwise. 38 | function isWearerOfHat(address account, uint256 hat) external view returns (bool) { 39 | uint256[] memory hats = mockedWearers[account]; 40 | for (uint256 i = 0; i < hats.length; i++) { 41 | if (hats[i] == hat) { 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | 48 | /// STUBS /// 49 | function mintTopHat( 50 | address /*_target*/, 51 | string calldata /*_details*/, 52 | string calldata /*_imageURI*/ 53 | ) external pure returns (uint256) { 54 | return 0; 55 | } 56 | 57 | function createHat( 58 | uint256 /*_admin*/, 59 | string calldata /*_details*/, 60 | uint32 /*_maxSupply*/, 61 | address /*_eligibility*/, 62 | address /*_toggle*/, 63 | bool /*_mutable*/, 64 | string calldata /*_imageURI*/ 65 | ) external pure returns (uint256) { 66 | return 0; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /packages/lazytower/test/LazyTowerHashChainTest.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai" 2 | import { Contract, encodeBytes32String } from "ethers" 3 | import { run } from "hardhat" 4 | import { poseidon2 } from "poseidon-lite" 5 | import ShiftTower from "./utils" 6 | 7 | describe("LazyTowerHashChainTest", () => { 8 | let contract: Contract 9 | 10 | before(async () => { 11 | contract = await run("deploy:lazytower-test", { logs: false }) 12 | }) 13 | 14 | it("Should produce correct levelLengths, digests and digest of digests", async () => { 15 | const lazyTowerId = encodeBytes32String("test1") 16 | 17 | const N = 150 18 | for (let i = 0; i < N; i += 1) { 19 | await contract.add(lazyTowerId, i) 20 | } 21 | 22 | const [levelLengths, digests, digestOfDigests] = await contract.getDataForProving(lazyTowerId) 23 | 24 | expect(levelLengths).to.equal(0x2112) 25 | 26 | expect(digests[0]).to.equal( 27 | BigInt("7484852499570635450337779587061833141700590058395918107227385307780465498841") 28 | ) 29 | expect(digests[1]).to.equal( 30 | BigInt("18801712394745483811033456933953954791894699812924877968490149877093764724813") 31 | ) 32 | expect(digests[2]).to.equal( 33 | BigInt("18495397265763935736123111771752209927150052777598404957994272011704245682779") 34 | ) 35 | expect(digests[3]).to.equal( 36 | BigInt("11606235313340788975553986881206148975708550071371494991713397040288897077102") 37 | ) 38 | for (let i = 4; i < digests.length; i += 1) { 39 | expect(digests[i]).to.equal(BigInt("0")) 40 | } 41 | 42 | expect(digestOfDigests).to.equal( 43 | BigInt("19260615748091768530426964318883829655407684674262674118201416393073357631548") 44 | ) 45 | }) 46 | 47 | // TODO: this times out in CI 48 | it.skip("Should have the same output as the Javascript fixture", async () => { 49 | const lazyTowerId = encodeBytes32String("test2") 50 | 51 | const H2 = (a: bigint, b: bigint) => poseidon2([a, b]) 52 | const W = 4 53 | const shiftTower = ShiftTower(W, (vs: any[]) => vs.reduce(H2)) 54 | for (let i = 0; i < 150; i += 1) { 55 | shiftTower.add(i) 56 | 57 | const tx = contract.add(lazyTowerId, i) 58 | 59 | // event 60 | await expect(tx).to.emit(contract, "Add").withArgs(i) 61 | 62 | // levelLengths and digest 63 | const [levelLengths, digests, digestOfDigests] = await contract.getDataForProving(lazyTowerId) 64 | 65 | expect(levelLengths).to.equal(shiftTower.L.map((l) => l.length).reduce((s, v, lv) => s + (v << (lv * 4)))) 66 | 67 | const D = shiftTower.L.map((l: any[]) => l.reduce(H2)) 68 | for (let lv = 0; lv < digests.length; lv += 1) { 69 | expect(digests[lv]).to.equal(D[lv] ?? 0) 70 | } 71 | 72 | expect(digestOfDigests).to.equal(D.reverse().reduce(H2)) 73 | } 74 | }) 75 | 76 | it("Should reject values not in the field", async () => { 77 | const lazyTowerId = encodeBytes32String("test3") 78 | 79 | let item = BigInt("21888242871839275222246405745257275088548364400416034343698204186575808495616") 80 | 81 | const tx = contract.add(lazyTowerId, item) 82 | await expect(tx).to.emit(contract, "Add").withArgs(item) 83 | 84 | item += BigInt(1) 85 | const tx2 = contract.add(lazyTowerId, item) 86 | await expect(tx2).to.be.revertedWith("LazyTower: item must be < SNARK_SCALAR_FIELD") 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/interfaces/IHatsMinimal.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | /// This interface has been copied & pasted from MACI. 5 | /// https://github.com/privacy-scaling-explorations/maci/blob/dev/contracts/contracts/interfaces/IHats.sol 6 | /// with commit hash bb429aece0eed2eed5d526e2a23522722c42ba5c. 7 | /// credits to Spencer Graham (https://github.com/spengrah) for writing this. 8 | 9 | /// @title IHatsMinimal 10 | /// @notice Minimal interface for the Hats Protocol contract. 11 | /// @dev Includes only the functions required for the HatsExcubia and associated tests. 12 | interface IHatsMinimal { 13 | /// @notice Creates and mints a Hat that is its own admin, i.e. a "topHat" 14 | /// @dev A topHat has no eligibility and no toggle 15 | /// @param _target The address to which the newly created topHat is minted 16 | /// @param _details A description of the Hat [optional]. Should not be larger than 7000 bytes 17 | /// (enforced in changeHatDetails) 18 | /// @param _imageURI The image uri for this top hat and the fallback for its 19 | /// downstream hats [optional]. Should not be larger than 7000 bytes 20 | /// (enforced in changeHatImageURI) 21 | /// @return topHatId The id of the newly created topHat 22 | function mintTopHat( 23 | address _target, 24 | string calldata _details, 25 | string calldata _imageURI 26 | ) external returns (uint256); 27 | 28 | /// @notice Creates a new hat. The msg.sender must wear the `_admin` hat. 29 | /// @dev Initializes a new Hat struct, but does not mint any tokens. 30 | /// @param _details A description of the Hat. Should not be larger than 7000 bytes (enforced in changeHatDetails) 31 | /// @param _maxSupply The total instances of the Hat that can be worn at once 32 | /// @param _admin The id of the Hat that will control who wears the newly created hat 33 | /// @param _eligibility The address that can report on the Hat wearer's status 34 | /// @param _toggle The address that can deactivate the Hat 35 | /// @param _mutable Whether the hat's properties are changeable after creation 36 | /// @param _imageURI The image uri for this hat and the fallback for its 37 | /// downstream hats [optional]. Should not be larger than 7000 bytes (enforced in changeHatImageURI) 38 | /// @return newHatId The id of the newly created Hat 39 | function createHat( 40 | uint256 _admin, 41 | string calldata _details, 42 | uint32 _maxSupply, 43 | address _eligibility, 44 | address _toggle, 45 | bool _mutable, 46 | string calldata _imageURI 47 | ) external returns (uint256); 48 | 49 | /// @notice Mints an ERC1155-similar token of the Hat to an eligible recipient, who then "wears" the hat 50 | /// @dev The msg.sender must wear an admin Hat of `_hatId`, and the recipient must be eligible to wear `_hatId` 51 | /// @param _hatId The id of the Hat to mint 52 | /// @param _wearer The address to which the Hat is minted 53 | /// @return success Whether the mint succeeded 54 | function mintHat(uint256 _hatId, address _wearer) external returns (bool success); 55 | 56 | /// @notice Checks whether a given address wears a given Hat 57 | /// @dev Convenience function that wraps `balanceOf` 58 | /// @param account The address in question 59 | /// @param hat The id of the Hat that the `_user` might wear 60 | /// @return isWearer Whether the `_user` wears the Hat. 61 | function isWearerOfHat(address account, uint256 hat) external view returns (bool); 62 | } 63 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/HatsExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | import {IHatsMinimal} from "./interfaces/IHatsMinimal.sol"; 6 | 7 | /// @title Hats Excubia Contract. 8 | /// @notice This contract extends the Excubia contract to integrate with the Hats protocol. 9 | /// This contract checks if a user is wearing a specific hat to permit access through the gate. 10 | /// @dev The contract uses a specific set of hats to admit the passerby wearing any of those hats. 11 | contract HatsExcubia is Excubia { 12 | /// @notice The Hats contract interface. 13 | IHatsMinimal public immutable HATS; 14 | 15 | /// @notice Mapping to track which hats are considered valid for passing the gate. 16 | mapping(uint256 => bool) public criterionHat; 17 | /// @notice Mapping to track which users have already passed through the gate. 18 | mapping(address => bool) public passedUsers; 19 | 20 | /// @notice Error thrown when the user is not wearing the required hat. 21 | error NotWearingCriterionHat(); 22 | /// @notice Error thrown when the specified hat is not a criterion hat. 23 | error NotCriterionHat(); 24 | /// @notice Error thrown when the array of criterion hats is empty. 25 | error ZeroCriterionHats(); 26 | 27 | /// @notice Constructor to initialize the contract with the target Hats contract and criterion hats. 28 | /// @param _hats The address of the Hats contract. 29 | /// @param _criterionHats An array of hat IDs that are considered as criteria for passing the gate. 30 | constructor(address _hats, uint256[] memory _criterionHats) { 31 | if (_hats == address(0)) revert ZeroAddress(); 32 | if (_criterionHats.length == 0) revert ZeroCriterionHats(); 33 | 34 | HATS = IHatsMinimal(_hats); 35 | 36 | uint256 numberOfCriterionHats = _criterionHats.length; 37 | 38 | for (uint256 i = 0; i < numberOfCriterionHats; ++i) { 39 | criterionHat[_criterionHats[i]] = true; 40 | } 41 | } 42 | 43 | /// @notice The trait of the Excubia contract. 44 | function trait() external pure override returns (string memory) { 45 | return "Hats"; 46 | } 47 | 48 | /// @notice Internal function to handle the passing logic with check. 49 | /// @dev Calls the parent `_pass` function and stores the user to avoid passing the gate twice. 50 | /// @param passerby The address of the entity attempting to pass the gate. 51 | /// @param data Additional data required for the check. 52 | function _pass(address passerby, bytes calldata data) internal override { 53 | // Avoiding passing the gate twice for the same user. 54 | if (passedUsers[passerby]) revert AlreadyPassed(); 55 | 56 | passedUsers[passerby] = true; 57 | 58 | super._pass(passerby, data); 59 | } 60 | 61 | /// @notice Internal function to handle the gate protection (hat check) logic. 62 | /// @dev Checks if the user is wearing one of the criterion hats. 63 | /// @param passerby The address of the entity attempting to pass the gate. 64 | /// @param data Additional data required for the check. 65 | function _check(address passerby, bytes calldata data) internal view override { 66 | super._check(passerby, data); 67 | 68 | uint256 hat = abi.decode(data, (uint256)); 69 | 70 | // Check if the hat is a criterion hat. 71 | if (!criterionHat[hat]) revert NotCriterionHat(); 72 | 73 | // Check if the user is wearing the criterion hat. 74 | if (!HATS.isWearerOfHat(passerby, hat)) revert NotWearingCriterionHat(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/GitcoinPassportExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | import {IGitcoinPassportDecoder} from "./interfaces/IGitcoinPassportDecoder.sol"; 6 | 7 | /// @title Gitcoin Passport Excubia Contract. 8 | /// @notice This contract extends the Excubia contract to integrate with the Gitcoin Passport Decoder. 9 | /// This contract checks the Gitcoin Passport user score to permit access through the gate. 10 | /// The Gitcoin Passport smart contract stack is built on top of Ethereum Attestation Service (EAS) contracts. 11 | /// @dev The contract uses a fixed threshold score to admit only passersby with a passport score 12 | /// equal to or greater than the fixed threshold based on their score (see _check() for more). 13 | contract GitcoinPassportExcubia is Excubia { 14 | /// @notice The factor used to scale the score. 15 | /// @dev https://docs.passport.xyz/building-with-passport/smart-contracts/contract-reference#available-methods 16 | uint256 public constant FACTOR = 100; 17 | 18 | /// @notice The Gitcoin Passport Decoder contract interface. 19 | IGitcoinPassportDecoder public immutable DECODER; 20 | 21 | /// @notice The minimum threshold score required to pass the gate. 22 | uint256 public immutable THRESHOLD_SCORE; 23 | 24 | /// @notice Mapping to track which users have already passed through the gate. 25 | mapping(address => bool) public passedUsers; 26 | 27 | /// @notice Error thrown when the user's score is insufficient to pass the gate. 28 | error InsufficientScore(); 29 | 30 | /// @notice Error thrown when the threshold score is negative or zero. 31 | error NegativeOrZeroThresholdScore(); 32 | 33 | /// @notice Constructor to initialize the contract with the target decoder and threshold score. 34 | /// @param _decoder The address of the Gitcoin Passport Decoder contract. 35 | /// @param _thresholdScore The minimum threshold score required to pass the gate. 36 | constructor(address _decoder, uint256 _thresholdScore) { 37 | if (_decoder == address(0)) revert ZeroAddress(); 38 | if (_thresholdScore <= 0) revert NegativeOrZeroThresholdScore(); 39 | 40 | DECODER = IGitcoinPassportDecoder(_decoder); 41 | THRESHOLD_SCORE = _thresholdScore; 42 | } 43 | 44 | /// @notice The trait of the Excubia contract. 45 | function trait() external pure override returns (string memory) { 46 | return "GitcoinPassport"; 47 | } 48 | 49 | /// @notice Internal function to handle the passing logic with check. 50 | /// @dev Calls the parent `_pass` function and stores the user to avoid passing the gate twice. 51 | /// @param passerby The address of the entity attempting to pass the gate. 52 | /// @param data Additional data required for the check. 53 | function _pass(address passerby, bytes calldata data) internal override { 54 | if (passedUsers[passerby]) revert AlreadyPassed(); 55 | 56 | passedUsers[passerby] = true; 57 | 58 | super._pass(passerby, data); 59 | } 60 | 61 | /// @notice Internal function to handle the gate protection (score check) logic. 62 | /// @dev Checks if the user's Gitcoin Passport score meets the threshold. 63 | /// @param passerby The address of the entity attempting to pass the gate. 64 | /// @param data Additional data required for the check. 65 | function _check(address passerby, bytes calldata data) internal view override { 66 | super._check(passerby, data); 67 | 68 | if ((DECODER.getScore(passerby) / FACTOR) < THRESHOLD_SCORE) revert InsufficientScore(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/test/MockSemaphore.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {ISemaphore} from "@semaphore-protocol/contracts/interfaces/ISemaphore.sol"; 5 | 6 | /// @title Mock Semaphore Contract 7 | /// @notice This contract is a mock implementation of the ISemaphore interface for testing purposes. 8 | /// @dev It simulates the behavior of a real Semaphore contract by simulating the storage and verification 9 | /// of a set of predefined mocked proofs. 10 | contract MockSemaphore is ISemaphore { 11 | /// @dev Gets a group id and returns the relative group. 12 | mapping(uint256 => bool) public mockedGroups; 13 | 14 | /// @notice A mapping to store mocked proofs by their unique nullifiers. 15 | mapping(uint256 => bool) private mockedProofs; 16 | 17 | /// @dev Counter to assign an incremental id to the groups. 18 | /// This counter is used to keep track of the number of groups created. 19 | uint256 public groupCounter; 20 | 21 | /// MOCKS /// 22 | /// @notice Constructor to initialize the mock contract with predefined proofs. 23 | /// @param _groupIds An array of identifiers of groups to be intended as the contract managed groups. 24 | /// @param _nullifiers An array of nullifiers to be mocked as proofs. 25 | /// @param _validities An array of booleans to mock the validity of proofs associated with the nullifiers. 26 | constructor(uint256[] memory _groupIds, uint256[] memory _nullifiers, bool[] memory _validities) { 27 | for (uint256 i = 0; i < _groupIds.length; i++) { 28 | mockedGroups[_groupIds[i]] = true; 29 | groupCounter++; 30 | } 31 | 32 | for (uint256 i = 0; i < _nullifiers.length; i++) { 33 | mockedProofs[_nullifiers[i]] = _validities[i]; 34 | } 35 | } 36 | 37 | function verifyProof(uint256 groupId, SemaphoreProof calldata proof) external view returns (bool) { 38 | return mockedGroups[groupId] && mockedProofs[proof.nullifier]; 39 | } 40 | 41 | /// STUBS /// 42 | // The following functions are stubs and do not perform any meaningful operations. 43 | // They are placeholders to comply with the IEAS interface. 44 | function createGroup() external pure override returns (uint256) { 45 | return 0; 46 | } 47 | 48 | function createGroup(address /*admin*/) external pure override returns (uint256) { 49 | return 0; 50 | } 51 | 52 | function createGroup(address /*admin*/, uint256 /*merkleTreeDuration*/) external pure override returns (uint256) { 53 | return 0; 54 | } 55 | 56 | function updateGroupAdmin(uint256 /*groupId*/, address /*newAdmin*/) external override {} 57 | 58 | function acceptGroupAdmin(uint256 /*groupId*/) external override {} 59 | 60 | function updateGroupMerkleTreeDuration(uint256 /*groupId*/, uint256 /*newMerkleTreeDuration*/) external override {} 61 | 62 | function addMember(uint256 groupId, uint256 identityCommitment) external override {} 63 | 64 | function addMembers(uint256 groupId, uint256[] calldata identityCommitments) external override {} 65 | 66 | function updateMember( 67 | uint256 /*groupId*/, 68 | uint256 /*oldIdentityCommitment*/, 69 | uint256 /*newIdentityCommitment*/, 70 | uint256[] calldata /*merkleProofSiblings*/ 71 | ) external override {} 72 | 73 | function removeMember( 74 | uint256 /*groupId*/, 75 | uint256 /*identityCommitment*/, 76 | uint256[] calldata /*merkleProofSiblings*/ 77 | ) external override {} 78 | 79 | function validateProof(uint256 /*groupId*/, SemaphoreProof calldata /*proof*/) external override {} 80 | } 81 | -------------------------------------------------------------------------------- /packages/lazytower/contracts/LazyTowerHashChain.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.4; 3 | 4 | import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol"; 5 | // CAPACITY = W * (W**0 + W**1 + ... + W**(H - 1)) = W * (W**H - 1) / (W - 1) 6 | // 4 * (4**24 - 1) / (4 - 1) = 375_299_968_947_540; 7 | uint256 constant H = 24; 8 | uint256 constant W = 4; 9 | 10 | uint256 constant bitsPerLevel = 4; 11 | uint256 constant levelBitmask = 15; // (1 << bitsPerLevel) - 1 12 | uint256 constant ones = 0x111111111111111111111111; // H ones 13 | 14 | // Each LazyTower has certain properties and data that will 15 | // be used to add new items. 16 | struct LazyTowerHashChainData { 17 | uint256 levelLengths; // length of each level 18 | uint256[H] digests; // digest of each level 19 | uint256[H] digestOfDigests; // digest of digests 20 | } 21 | 22 | /// @title LazyTower. 23 | /// @dev The LazyTower allows to calculate the digest of digests each time an item is added, ensuring 24 | /// the integrity of the LazyTower. 25 | library LazyTowerHashChain { 26 | uint256 internal constant SNARK_SCALAR_FIELD = 27 | 21888242871839275222246405745257275088548364400416034343698204186575808495617; 28 | 29 | function findLowestNonFullLevelThenInc( 30 | uint256 levelLengths 31 | ) internal pure returns (uint256 level, bool isHead, bool isTop, uint256 newLevelLengths) { 32 | // find the lowest non-full level 33 | uint256 levelLength; 34 | while (true) { 35 | levelLength = levelLengths & levelBitmask; 36 | if (levelLength < W) break; 37 | level++; 38 | levelLengths >>= bitsPerLevel; 39 | } 40 | 41 | isHead = (levelLength == 0); 42 | isTop = ((levelLengths >> bitsPerLevel) == 0); 43 | 44 | // increment the non-full levelLength(s) by one 45 | // all full levels below become ones 46 | uint256 fullLevelBits = level * bitsPerLevel; 47 | uint256 onesMask = (1 << fullLevelBits) - 1; 48 | newLevelLengths = ((levelLengths + 1) << fullLevelBits) + (onesMask & ones); 49 | } 50 | 51 | /// @dev Add an item. 52 | /// @param self: LazyTower data 53 | /// @param item: item to be added 54 | function add(LazyTowerHashChainData storage self, uint256 item) public { 55 | require(item < SNARK_SCALAR_FIELD, "LazyTower: item must be < SNARK_SCALAR_FIELD"); 56 | 57 | uint256 level; 58 | bool isHead; 59 | bool isTop; 60 | (level, isHead, isTop, self.levelLengths) = findLowestNonFullLevelThenInc(self.levelLengths); 61 | 62 | uint256 digest; 63 | uint256 digestOfDigests; 64 | uint256 toAdd; 65 | 66 | // append at the first non-full level 67 | toAdd = (level == 0) ? item : self.digests[level - 1]; 68 | digest = isHead ? toAdd : PoseidonT3.hash([self.digests[level], toAdd]); 69 | digestOfDigests = isTop ? digest : PoseidonT3.hash([self.digestOfDigests[level + 1], digest]); 70 | self.digests[level] = digest; 71 | self.digestOfDigests[level] = digestOfDigests; 72 | 73 | // the rest of levels are all full 74 | while (level != 0) { 75 | level--; 76 | 77 | toAdd = (level == 0) ? item : self.digests[level - 1]; 78 | digest = toAdd; 79 | digestOfDigests = PoseidonT3.hash([digestOfDigests, digest]); // top-down 80 | self.digests[level] = digest; 81 | self.digestOfDigests[level] = digestOfDigests; 82 | } 83 | } 84 | 85 | function getDataForProving( 86 | LazyTowerHashChainData storage self 87 | ) external view returns (uint256, uint256[] memory, uint256) { 88 | uint256 len = self.digests.length; 89 | uint256[] memory digests = new uint256[](len); // for returning a dynamic array 90 | for (uint256 i = 0; i < len; i++) { 91 | digests[i] = self.digests[i]; 92 | } 93 | return (self.levelLengths, digests, self.digestOfDigests[0]); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/SemaphoreExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | import {ISemaphore} from "@semaphore-protocol/contracts/interfaces/ISemaphore.sol"; 6 | 7 | /// @title Semaphore Excubia Contract 8 | /// @notice This contract extends the Excubia contract to integrate with the Semaphore protocol. 9 | /// It verifies the passerby Semaphore group membership proofs to grant access through the gate. 10 | /// @dev To allow only specific Semaphore identities from a group, the contract stores the specific group identifier. 11 | /// To avoid identities from passing twice, nullifiers are stored upon successful verification of the proofs. 12 | contract SemaphoreExcubia is Excubia { 13 | /// @notice The Semaphore contract interface. 14 | ISemaphore public immutable SEMAPHORE; 15 | /// @notice The specific group identifier that proofs must match to pass the gate. 16 | /// @dev Used as a `scope` to ensure consistency during proof membership verification. 17 | uint256 public immutable GROUP_ID; 18 | 19 | /// @notice Mapping to track which nullifiers have been used to avoid passing the 20 | /// gate twice using the same Semaphore identity. 21 | /// @dev The nullifier is derived from the hash of the secret and group identifier, 22 | /// ensuring that the same identity cannot pass twice using the same group. 23 | mapping(uint256 => bool) public passedNullifiers; 24 | 25 | /// @notice Error thrown when the group identifier does not match the expected one. 26 | error InvalidGroup(); 27 | 28 | /// @notice Error thrown when the proof is invalid. 29 | error InvalidProof(); 30 | 31 | /// @notice Error thrown when the proof scope does not match the expected group identifier. 32 | error UnexpectedScope(); 33 | 34 | /// @notice Constructor to initialize with target Semaphore contract and specific group identifier. 35 | /// @param _semaphore The address of the Semaphore contract. 36 | /// @param _groupId The group identifier that proofs must match. 37 | constructor(address _semaphore, uint256 _groupId) { 38 | if (_semaphore == address(0)) revert ZeroAddress(); 39 | 40 | SEMAPHORE = ISemaphore(_semaphore); 41 | 42 | if (ISemaphore(_semaphore).groupCounter() <= _groupId) revert InvalidGroup(); 43 | 44 | GROUP_ID = _groupId; 45 | } 46 | 47 | /// @notice The trait of the Excubia contract. 48 | function trait() external pure override returns (string memory) { 49 | return "Semaphore"; 50 | } 51 | 52 | /// @notice Internal function to handle the passing logic with check. 53 | /// @dev Calls the parent `_pass` function and stores the nullifier to avoid passing the gate twice. 54 | /// @param passerby The address of the entity attempting to pass the gate. 55 | /// @param data Additional data required for the check (ie., encoded Semaphore proof). 56 | function _pass(address passerby, bytes calldata data) internal override { 57 | ISemaphore.SemaphoreProof memory proof = abi.decode(data, (ISemaphore.SemaphoreProof)); 58 | 59 | // Avoiding passing the gate twice using the same nullifier. 60 | if (passedNullifiers[proof.nullifier]) revert AlreadyPassed(); 61 | 62 | passedNullifiers[proof.nullifier] = true; 63 | 64 | super._pass(passerby, data); 65 | } 66 | 67 | /// @notice Internal function to handle the gate protection (proof check) logic. 68 | /// @dev Checks if the proof matches the group ID, scope, and is valid. 69 | /// @param passerby The address of the entity attempting to pass the gate. 70 | /// @param data Additional data required for the check (i.e., encoded Semaphore proof). 71 | function _check(address passerby, bytes calldata data) internal view override { 72 | super._check(passerby, data); 73 | 74 | ISemaphore.SemaphoreProof memory proof = abi.decode(data, (ISemaphore.SemaphoreProof)); 75 | 76 | if (GROUP_ID != proof.scope) revert UnexpectedScope(); 77 | 78 | if (!SEMAPHORE.verifyProof(GROUP_ID, proof)) revert InvalidProof(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/EASExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | import {IEAS} from "@ethereum-attestation-service/eas-contracts/contracts/IEAS.sol"; 6 | import {Attestation} from "@ethereum-attestation-service/eas-contracts/contracts/Common.sol"; 7 | 8 | /// @title EAS Excubia Contract. 9 | /// @notice This contract extends the Excubia contract to integrate with the Ethereum Attestation Service (EAS). 10 | /// This contract checks an EAS attestation to permit access through the gate. 11 | /// @dev The contract uses a specific attestation schema & attester to admit the recipient of the attestation. 12 | contract EASExcubia is Excubia { 13 | /// @notice The Ethereum Attestation Service contract interface. 14 | IEAS public immutable EAS; 15 | /// @notice The specific schema ID that attestations must match to pass the gate. 16 | bytes32 public immutable SCHEMA; 17 | /// @notice The trusted attester address whose attestations are considered 18 | /// the only ones valid to pass the gate. 19 | address public immutable ATTESTER; 20 | 21 | /// @notice Mapping to track which attestations have passed the gate to 22 | /// avoid passing it twice using the same attestation. 23 | mapping(bytes32 => bool) public passedAttestations; 24 | 25 | /// @notice Error thrown when the attestation does not match the designed schema. 26 | error UnexpectedSchema(); 27 | 28 | /// @notice Error thrown when the attestation does not match the designed trusted attester. 29 | error UnexpectedAttester(); 30 | 31 | /// @notice Error thrown when the attestation does not match the passerby as recipient. 32 | error UnexpectedRecipient(); 33 | 34 | /// @notice Error thrown when the attestation has been revoked. 35 | error RevokedAttestation(); 36 | 37 | /// @notice Constructor to initialize with target EAS contract with specific attester and schema. 38 | /// @param _eas The address of the EAS contract. 39 | /// @param _attester The address of the trusted attester. 40 | /// @param _schema The schema ID that attestations must match. 41 | constructor(address _eas, address _attester, bytes32 _schema) { 42 | if (_eas == address(0) || _attester == address(0)) revert ZeroAddress(); 43 | 44 | EAS = IEAS(_eas); 45 | ATTESTER = _attester; 46 | SCHEMA = _schema; 47 | } 48 | 49 | /// @notice The trait of the Excubia contract. 50 | function trait() external pure override returns (string memory) { 51 | return "EAS"; 52 | } 53 | 54 | /// @notice Internal function to handle the passing logic with check. 55 | /// @dev Calls the parent `_pass` function and stores the attestation to avoid pass the gate twice. 56 | /// @param passerby The address of the entity attempting to pass the gate. 57 | /// @param data Additional data required for the check (e.g., encoded attestation ID). 58 | function _pass(address passerby, bytes calldata data) internal override { 59 | bytes32 attestationId = abi.decode(data, (bytes32)); 60 | 61 | // Avoiding passing the gate twice using the same attestation. 62 | if (passedAttestations[attestationId]) revert AlreadyPassed(); 63 | 64 | passedAttestations[attestationId] = true; 65 | 66 | super._pass(passerby, data); 67 | } 68 | 69 | /// @notice Internal function to handle the gate protection (attestation check) logic. 70 | /// @dev Checks if the attestation matches the schema, attester, recipient, and is not revoked. 71 | /// @param passerby The address of the entity attempting to pass the gate. 72 | /// @param data Additional data required for the check (e.g., encoded attestation ID). 73 | function _check(address passerby, bytes calldata data) internal view override { 74 | super._check(passerby, data); 75 | 76 | bytes32 attestationId = abi.decode(data, (bytes32)); 77 | 78 | Attestation memory attestation = EAS.getAttestation(attestationId); 79 | 80 | if (attestation.schema != SCHEMA) revert UnexpectedSchema(); 81 | if (attestation.attester != ATTESTER) revert UnexpectedAttester(); 82 | if (attestation.recipient != passerby) revert UnexpectedRecipient(); 83 | if (attestation.revocationTime != 0) revert RevokedAttestation(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/lazytower/contracts/README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | LazyTower (Solidity) 4 |

5 |

LazyTower Solidity library.

6 |

7 | 8 |

9 | 10 | 11 | 12 | 13 | NPM license 14 | 15 | 16 | NPM version 17 | 18 | 19 | Downloads 20 | 21 | 22 | Code style prettier 23 | 24 |

25 | 26 | 33 | 34 | > [!WARNING] 35 | > These library has **not** been audited. 36 | 37 | --- 38 | 39 | ## 🛠 Install 40 | 41 | ### npm or yarn 42 | 43 | Install the `@zk-kit/lazytower.sol` package with npm: 44 | 45 | ```bash 46 | npm i @zk-kit/lazytower.sol --save 47 | ``` 48 | 49 | or yarn: 50 | 51 | ```bash 52 | yarn add @zk-kit/lazytower.sol 53 | ``` 54 | 55 | ## 📜 Usage 56 | 57 | ### Importing and using the library 58 | 59 | ```solidity 60 | // SPDX-License-Identifier: MIT 61 | 62 | pragma solidity ^0.8.4; 63 | 64 | import "../LazyTowerHashChain.sol"; 65 | 66 | contract LazyTowerHashChainTest { 67 | using LazyTowerHashChain for LazyTowerHashChainData; 68 | 69 | event Add(uint256 item); 70 | 71 | // map for multiple test cases 72 | mapping(bytes32 => LazyTowerHashChainData) public towers; 73 | 74 | function add(bytes32 _towerId, uint256 _item) external { 75 | towers[_towerId].add(_item); 76 | emit Add(_item); 77 | } 78 | 79 | function getDataForProving(bytes32 _towerId) external view returns (uint256, uint256[] memory, uint256) { 80 | return towers[_towerId].getDataForProving(); 81 | } 82 | } 83 | ``` 84 | 85 | ### Creating an Hardhat task to deploy the contract 86 | 87 | ```typescript 88 | import { Contract } from "ethers" 89 | import { task, types } from "hardhat/config" 90 | 91 | task("deploy:lazytower-test", "Deploy a LazyTowerHashChainTest contract") 92 | .addOptionalParam("logs", "Print the logs", true, types.boolean) 93 | .setAction(async ({ logs }, { ethers }): Promise => { 94 | const PoseidonT3Factory = await ethers.getContractFactory("PoseidonT3") 95 | const PoseidonT3 = await PoseidonT3Factory.deploy() 96 | 97 | if (logs) { 98 | console.info(`PoseidonT3 library has been deployed to: ${PoseidonT3.address}`) 99 | } 100 | 101 | const LazyTowerLibFactory = await ethers.getContractFactory("LazyTowerHashChain", { 102 | libraries: { 103 | PoseidonT3: PoseidonT3.address 104 | } 105 | }) 106 | const lazyTowerLib = await LazyTowerLibFactory.deploy() 107 | 108 | await lazyTowerLib.deployed() 109 | 110 | if (logs) { 111 | console.info(`LazyTowerHashChain library has been deployed to: ${lazyTowerLib.address}`) 112 | } 113 | 114 | const ContractFactory = await ethers.getContractFactory("LazyTowerHashChainTest", { 115 | libraries: { 116 | LazyTowerHashChain: lazyTowerLib.address 117 | } 118 | }) 119 | 120 | const contract = await ContractFactory.deploy() 121 | 122 | await contract.deployed() 123 | 124 | if (logs) { 125 | console.info(`Test contract has been deployed to: ${contract.address}`) 126 | } 127 | 128 | return contract 129 | }) 130 | ``` 131 | 132 | ## Contacts 133 | 134 | ### Developers 135 | 136 | - e-mail : lcamel@gmail.com 137 | - github : [@LCamel](https://github.com/LCamel) 138 | - website : https://www.facebook.com/LCamel 139 | -------------------------------------------------------------------------------- /packages/excubiae/test/FreeForAllExcubia.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai" 2 | import { ethers } from "hardhat" 3 | import { Signer, ZeroAddress, ZeroHash } from "ethers" 4 | import { FreeForAllExcubia, FreeForAllExcubia__factory } from "../typechain-types" 5 | 6 | describe("FreeForAllExcubia", function () { 7 | let FreeForAllExcubiaContract: FreeForAllExcubia__factory 8 | let freeForAllExcubia: FreeForAllExcubia 9 | 10 | let signer: Signer 11 | let signerAddress: string 12 | 13 | let gate: Signer 14 | let gateAddress: string 15 | 16 | before(async function () { 17 | ;[signer, gate] = await ethers.getSigners() 18 | signerAddress = await signer.getAddress() 19 | gateAddress = await gate.getAddress() 20 | 21 | FreeForAllExcubiaContract = await ethers.getContractFactory("FreeForAllExcubia") 22 | freeForAllExcubia = await FreeForAllExcubiaContract.deploy() 23 | }) 24 | 25 | describe("constructor()", function () { 26 | it("Should deploy the FreeForAllExcubia contract correctly", async function () { 27 | expect(freeForAllExcubia).to.not.eq(undefined) 28 | }) 29 | }) 30 | 31 | describe("trait()", function () { 32 | it("should return the trait of the Excubia contract", async () => { 33 | expect(await freeForAllExcubia.trait()).to.be.equal("FreeForAll") 34 | }) 35 | }) 36 | 37 | describe("setGate()", function () { 38 | it("should fail to set the gate when the caller is not the owner", async () => { 39 | const [, notOwnerSigner] = await ethers.getSigners() 40 | 41 | await expect(freeForAllExcubia.connect(notOwnerSigner).setGate(gateAddress)).to.be.revertedWithCustomError( 42 | freeForAllExcubia, 43 | "OwnableUnauthorizedAccount" 44 | ) 45 | }) 46 | 47 | it("should fail to set the gate when the gate address is zero", async () => { 48 | await expect(freeForAllExcubia.setGate(ZeroAddress)).to.be.revertedWithCustomError( 49 | freeForAllExcubia, 50 | "ZeroAddress" 51 | ) 52 | }) 53 | 54 | it("Should set the gate contract address correctly", async function () { 55 | const tx = await freeForAllExcubia.setGate(gateAddress) 56 | const receipt = await tx.wait() 57 | const event = FreeForAllExcubiaContract.interface.parseLog( 58 | receipt?.logs[0] as unknown as { topics: string[]; data: string } 59 | ) as unknown as { 60 | args: { 61 | gate: string 62 | } 63 | } 64 | 65 | expect(receipt?.status).to.eq(1) 66 | expect(event.args.gate).to.eq(gateAddress) 67 | expect(await freeForAllExcubia.gate()).to.eq(gateAddress) 68 | }) 69 | 70 | it("Should fail to set the gate if already set", async function () { 71 | await expect(freeForAllExcubia.setGate(gateAddress)).to.be.revertedWithCustomError( 72 | freeForAllExcubia, 73 | "GateAlreadySet" 74 | ) 75 | }) 76 | }) 77 | 78 | describe("check()", function () { 79 | it("should check", async () => { 80 | // `data` parameter value can be whatever (e.g., ZeroHash default). 81 | await expect(freeForAllExcubia.check(signerAddress, ZeroHash)).to.not.be.reverted 82 | 83 | // check does NOT change the state of the contract (see pass()). 84 | expect(await freeForAllExcubia.passedPassersby(signerAddress)).to.be.false 85 | }) 86 | }) 87 | 88 | describe("pass()", function () { 89 | it("should throw when the callee is not the gate", async () => { 90 | await expect( 91 | // `data` parameter value can be whatever (e.g., ZeroHash default). 92 | freeForAllExcubia.connect(signer).pass(signerAddress, ZeroHash) 93 | ).to.be.revertedWithCustomError(freeForAllExcubia, "GateOnly") 94 | }) 95 | 96 | it("should pass", async () => { 97 | // `data` parameter value can be whatever (e.g., ZeroHash default). 98 | const tx = await freeForAllExcubia.connect(gate).pass(signerAddress, ZeroHash) 99 | const receipt = await tx.wait() 100 | const event = FreeForAllExcubiaContract.interface.parseLog( 101 | receipt?.logs[0] as unknown as { topics: string[]; data: string } 102 | ) as unknown as { 103 | args: { 104 | passerby: string 105 | gate: string 106 | } 107 | } 108 | 109 | expect(receipt?.status).to.eq(1) 110 | expect(event.args.passerby).to.eq(signerAddress) 111 | expect(event.args.gate).to.eq(gateAddress) 112 | expect(await freeForAllExcubia.passedPassersby(signerAddress)).to.be.true 113 | }) 114 | 115 | it("should prevent to pass twice", async () => { 116 | await expect( 117 | // `data` parameter value can be whatever (e.g., ZeroHash default). 118 | freeForAllExcubia.connect(gate).pass(signerAddress, ZeroHash) 119 | ).to.be.revertedWithCustomError(freeForAllExcubia, "AlreadyPassed") 120 | }) 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /packages/excubiae/contracts/extensions/ZKEdDSAEventTicketPCDExcubia.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.8.0; 3 | 4 | import {Excubia} from "../Excubia.sol"; 5 | import {ZKEdDSAEventTicketPCDVerifier} from "./verifiers/ZKEdDSAEventTicketPCDVerifier.sol"; 6 | 7 | /// @title ZKEdDSA Event Ticket PCD Excubia Contract. 8 | /// @notice This contract extends the Excubia contract to integrate with ZK EdDSA Event Ticket PCD. 9 | /// This contract verifies a ZK EdDSA Event Ticket PCD proof to permit access through the gate. 10 | /// You can find more about on the Zupass repository https://github.com/proofcarryingdata/zupass. 11 | /// @dev The contract uses specific event ID and signers to check against the verifier 12 | /// in order to admit the recipient (passerby) of the proof. 13 | contract ZKEdDSAEventTicketPCDExcubia is Excubia { 14 | /// @notice The valid event ID that proofs must match to pass the gate. 15 | uint256 public immutable VALID_EVENT_ID; 16 | /// @notice The first valid signer whose signatures are considered valid to pass the gate. 17 | uint256 public immutable VALID_SIGNER_1; 18 | /// @notice The second valid signer whose signatures are considered valid to pass the gate. 19 | uint256 public immutable VALID_SIGNER_2; 20 | 21 | /// @notice The ZKEdDSA Event Ticket PCD Verifier contract. 22 | ZKEdDSAEventTicketPCDVerifier public immutable VERIFIER; 23 | 24 | /// @notice Mapping to track which tickets have already passed the checks 25 | /// to avoid passing the gate twice with the same ticket. 26 | mapping(uint256 => bool) public passedZKEdDSAEventTicketPCDs; 27 | 28 | /// @notice Error thrown when the proof is invalid. 29 | error InvalidProof(); 30 | 31 | /// @notice Error thrown when the event ID in the proof does not match the valid event ID. 32 | error InvalidEventId(); 33 | 34 | /// @notice Error thrown when the signers in the proof do not match the valid signers. 35 | error InvalidSigners(); 36 | 37 | /// @notice Error thrown when the watermark in the proof does not match the passerby address. 38 | error InvalidWatermark(); 39 | 40 | /// @notice Constructor to initialize with target verifier, valid event ID, and valid signers. 41 | /// @param _verifier The address of the ZKEdDSA Event Ticket PCD Verifier contract. 42 | /// @param _validEventId The valid event ID that proofs must match. 43 | /// @param _validSigner1 The first valid signer whose signatures are considered valid. 44 | /// @param _validSigner2 The second valid signer whose signatures are considered valid. 45 | constructor(address _verifier, uint256 _validEventId, uint256 _validSigner1, uint256 _validSigner2) { 46 | if (_verifier == address(0)) revert ZeroAddress(); 47 | 48 | VERIFIER = ZKEdDSAEventTicketPCDVerifier(_verifier); 49 | VALID_EVENT_ID = _validEventId; 50 | VALID_SIGNER_1 = _validSigner1; 51 | VALID_SIGNER_2 = _validSigner2; 52 | } 53 | 54 | /// @notice The trait of the Excubia contract. 55 | function trait() external pure override returns (string memory) { 56 | return "ZKEdDSAEventTicketPCD"; 57 | } 58 | 59 | /// @notice Internal function to handle the passing logic with check. 60 | /// @dev Calls the parent `_pass` function and stores the ticket ID to avoid passing the gate twice. 61 | /// @param passerby The address of the entity attempting to pass the gate. 62 | /// @param data Additional data required for the check (i.e., encoded proof). 63 | function _pass(address passerby, bytes calldata data) internal override { 64 | // Decode the given data bytes. 65 | (, , , uint256[38] memory _pubSignals) = abi.decode(data, (uint256[2], uint256[2][2], uint256[2], uint256[38])); 66 | 67 | // Avoiding passing the gate twice using the same nullifier. 68 | /// @dev Ticket ID is stored at _pubSignals index 0. 69 | if (passedZKEdDSAEventTicketPCDs[_pubSignals[0]]) revert AlreadyPassed(); 70 | 71 | passedZKEdDSAEventTicketPCDs[_pubSignals[0]] = true; 72 | 73 | super._pass(passerby, data); 74 | } 75 | 76 | /// @notice Internal function to handle the gate protection (proof check) logic. 77 | /// @dev Checks if the proof matches the event ID, signers, watermark, and is valid. 78 | /// @param passerby The address of the entity attempting to pass the gate. 79 | /// @param data Additional data required for the check (i.e., encoded proof). 80 | function _check(address passerby, bytes calldata data) internal view override { 81 | super._check(passerby, data); 82 | 83 | // Decode the given data bytes. 84 | (uint256[2] memory _pA, uint256[2][2] memory _pB, uint256[2] memory _pC, uint256[38] memory _pubSignals) = abi 85 | .decode(data, (uint256[2], uint256[2][2], uint256[2], uint256[38])); 86 | 87 | // Signers are stored at index 13 and 14. 88 | if (_pubSignals[13] != VALID_SIGNER_1 || _pubSignals[14] != VALID_SIGNER_2) revert InvalidSigners(); 89 | 90 | // Event ID is stored at index 15. 91 | if (_pubSignals[15] != VALID_EVENT_ID) revert InvalidEventId(); 92 | 93 | // Watermark is stored at index 37. 94 | if (_pubSignals[37] != uint256(uint160(passerby))) revert InvalidWatermark(); 95 | 96 | // Proof verification. 97 | if (!VERIFIER.verifyProof(_pA, _pB, _pC, _pubSignals)) revert InvalidProof(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | :tada: Thank you for being interested in contributing to the ZK-kit project! :tada: 4 | 5 | Feel welcome and read the following sections in order to know how to ask questions and how to work on something. 6 | 7 | All members of our community are expected to follow our [Code of Conduct](/CODE_OF_CONDUCT.md). Please make sure you are welcoming and friendly in all of our spaces. 8 | 9 | We're really glad you're reading this, because we need volunteer developers to help this project come to fruition. 👏 10 | 11 | ## Issues 12 | 13 | The best way to contribute to our projects is by opening a [new issue](https://github.com/privacy-scaling-explorations/zk-kit.solidity/issues/new/choose) or tackling one of the issues listed [here](https://github.com/privacy-scaling-explorations/zk-kit.solidity/contribute). 14 | 15 | ## Pull Requests 16 | 17 | Pull requests are great if you want to add a feature or fix a bug. Here's a quick guide: 18 | 19 | 1. Fork the repo. 20 | 21 | 2. Run the tests. We only take pull requests with passing tests. 22 | 23 | 3. Add a test for your change. Only refactoring and documentation changes require no new tests. 24 | 25 | 4. Make sure to check out the [Style Guide](/CONTRIBUTING.md#style-guide) and ensure that your code complies with the rules. 26 | 27 | 5. Make the test pass. 28 | 29 | 6. Commit your changes. 30 | 31 | 7. Push to your fork and submit a pull request on our `main` branch. Please provide us with some explanation of why you made the changes you made. For new features make sure to explain a standard use case to us. 32 | 33 | > [!NOTE] 34 | > When a new package is created or a new feature is added to the repository, the contributor will be added to the `.github/CODEOWNERS` file to review and approve any future changes to their code. 35 | 36 | > [!IMPORTANT] 37 | > We do not accept pull requests for minor grammatical fixes (e.g., correcting typos, rewording sentences) or for fixing broken links, unless they significantly improve clarity or functionality. These contributions, while appreciated, are not a priority for merging. If you notice any of these issues, please create a [GitHub Issue](https://github.com/privacy-scaling-explorations/zk-kit.solidity/issues/new?template=BLANK_ISSUE) to report them so they can be properly tracked and addressed. 38 | 39 | ## CI (Github Actions) Tests 40 | 41 | We use GitHub Actions to test each PR before it is merged. 42 | 43 | When you submit your PR (or later change that code), a CI build will automatically be kicked off. A note will be added to the PR, and will indicate the current status of the build. 44 | 45 | ## Style Guide 46 | 47 | ### Code rules 48 | 49 | We always use ESLint and Prettier. To check that your code follows the rules, simply run the npm script `yarn lint`. 50 | 51 | ### Commits rules 52 | 53 | For commits it is recommended to use [Conventional Commits](https://www.conventionalcommits.org). 54 | 55 | Don't worry if it looks complicated. In our repositories, `git commit` opens an interactive app to create your conventional commit. 56 | 57 | Each commit message consists of a **header**, a **body** and a **footer**. The **header** has a special format that includes a **type**, a **scope** and a **subject**: 58 | 59 | (): 60 | 61 | 62 | 63 |