├── .env.example ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .solcover.js ├── .solhint.json ├── .solhintignore ├── LICENSE.md ├── README.md ├── audit ├── avacloud-eerc-audit.pdf └── avacloud-eerc-circom-audit.pdf ├── biome.json ├── circom ├── .DS_Store ├── build │ ├── burn │ │ ├── burn.wasm │ │ ├── burn.zkey │ │ └── burn_verification_key.json │ ├── mint │ │ ├── mint.wasm │ │ ├── mint.zkey │ │ └── mint_verification_key.json │ ├── registration │ │ ├── circuit_final.zkey │ │ ├── registration.wasm │ │ └── registration_verification_key.json │ ├── transfer │ │ ├── transfer.wasm │ │ ├── transfer.zkey │ │ └── transfer_verification_key.json │ └── withdraw │ │ ├── circuit_final.zkey │ │ ├── withdraw.wasm │ │ └── withdraw_verification_key.json ├── burn.circom ├── circomlib │ ├── aliascheck.circom │ ├── babyjub.circom │ ├── bitify.circom │ ├── comparators.circom │ ├── compconstant.circom │ ├── escalarmulany.circom │ ├── escalarmulfix.circom │ ├── montgomery.circom │ ├── mux3.circom │ ├── poseidon.circom │ ├── poseidon_constants.circom │ ├── poseidon_constants_old.circom │ └── poseidon_old.circom ├── components.circom ├── mint.circom ├── registration.circom ├── transfer.circom └── withdraw.circom ├── contracts ├── EncryptedERC.sol ├── EncryptedUserBalances.sol ├── Registrar.sol ├── auditor │ └── AuditorManager.sol ├── errors │ └── Errors.sol ├── interfaces │ ├── IEncryptedERC.sol │ ├── IRegistrar.sol │ └── verifiers │ │ ├── IBurnVerifier.sol │ │ ├── IMintVerifier.sol │ │ ├── IRegistrationVerifier.sol │ │ ├── ITransferVerifier.sol │ │ └── IWithdrawVerifier.sol ├── libraries │ └── BabyJubJub.sol ├── prod │ ├── BurnVerifier.sol │ ├── MintVerifier.sol │ ├── RegistrationVerifier.sol │ ├── TransferVerifier.sol │ └── WithdrawVerifier.sol ├── tokens │ ├── FeeERC20.sol │ ├── SimpleERC20.sol │ └── TokenTracker.sol ├── types │ └── Types.sol └── verifiers │ ├── BurnCircuitGroth16Verifier.sol │ ├── MintCircuitGroth16Verifier.sol │ ├── RegistrationCircuitGroth16Verifier.sol │ ├── TransferCircuitGroth16Verifier.sol │ └── WithdrawCircuitGroth16Verifier.sol ├── hardhat.config.ts ├── images └── banner.png ├── package-lock.json ├── package.json ├── scripts ├── constants.ts ├── deploy-converter.ts └── deploy-standalone.ts ├── src ├── constants.ts ├── index.ts ├── jub │ ├── index.ts │ └── jub.ts └── poseidon │ ├── index.ts │ └── poseidon.ts ├── test ├── EncryptedERC-Converter.ts ├── EncryptedERC-Standalone.ts ├── helpers.ts └── user.ts ├── tsconfig.json └── zk ├── .gitignore ├── Makefile ├── cmd └── main.go ├── go.mod ├── go.sum └── pkg ├── babyjub └── babyjub.go ├── circuits ├── components.go ├── mint_circuit.go ├── registration_circuit.go ├── structs.go ├── transfer_circuit.go └── withdraw_circuit.go ├── hardhat ├── helpers.go ├── mint.go ├── register.go ├── transfer.go └── withdraw.go ├── helpers └── helpers.go ├── poseidon ├── poseidon.go ├── poseidon_constants.go └── poseidon_decryption.go └── utils ├── helpers.go └── poseidon_constants.go /.env.example: -------------------------------------------------------------------------------- 1 | COINMARKETCAP_API_KEY="" 2 | REPORT_GAS="" 3 | FORKING="" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "CI" 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | ci: 12 | strategy: 13 | matrix: 14 | node-version: [18.x] 15 | go-version: ["1.22"] 16 | 17 | runs-on: "ubuntu-latest" 18 | steps: 19 | - name: "Check out the repo" 20 | uses: "actions/checkout@v4" 21 | 22 | - name: Setup Node.js 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version-file: ".nvmrc" 26 | 27 | - name: "Install the dependencies" 28 | run: "npm install" 29 | 30 | - name: Hardhat coverage 31 | run: | 32 | npx hardhat coverage 33 | 34 | - name: "Run linter" 35 | run: "npm run lint" 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | *.DS_Store 4 | # Hardhat files 5 | /cache 6 | /artifacts 7 | 8 | # TypeChain files 9 | /typechain 10 | /typechain-types 11 | 12 | # solidity-coverage files 13 | /coverage 14 | /coverage.json 15 | 16 | # Hardhat Ignition default folder for deployments against a local node 17 | ignition/deployments/chain-31337 18 | 19 | .vscode 20 | 21 | # hardhat-gas-report 22 | gas-report.txt 23 | 24 | # zkit 25 | generated-types/ 26 | zkit/ -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v22.14.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | contracts/verifiers/* 2 | contracts/prod/* 3 | circom/build/* -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-solidity"], 3 | "overrides": [ 4 | { 5 | "files": "*.sol", 6 | "options": { 7 | "parser": "solidity-parse", 8 | "printWidth": 80, 9 | "tabWidth": 4, 10 | "useTabs": false, 11 | "singleQuote": false, 12 | "bracketSpacing": false 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: [ 3 | "SimpleERC20.sol", 4 | "FeeERC20.sol", 5 | "x", 6 | "verifiers" 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "compiler-version": [ 5 | "error", 6 | "0.8.27" 7 | ], 8 | "no-unused-vars": "error", 9 | "func-visibility": [ 10 | "error", 11 | { 12 | "ignoreConstructors": true 13 | } 14 | ], 15 | "private-vars-leading-underscore": [ 16 | "warn", 17 | { 18 | "strict": true 19 | } 20 | ], 21 | "reason-string": [ 22 | "warn", 23 | { 24 | "maxLength": 75 25 | } 26 | ], 27 | "gas-custom-errors": "off", 28 | "ordering": "error", 29 | "immutable-vars-naming": [ 30 | "warn", 31 | { 32 | "immutablesAsConstants": false 33 | } 34 | ], 35 | "func-named-parameters": [ 36 | "error", 37 | 5 38 | ], 39 | "one-contract-per-file": "off", 40 | "no-console": "off" 41 | } 42 | } -------------------------------------------------------------------------------- /.solhintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | contracts/verifiers/* 3 | contracts/prod/* 4 | circom/build/* 5 | 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2025, Ava Labs, Inc. All rights reserved. 2 | 3 | Ecosystem License 4 | Version: 1.1 5 | 6 | Subject to the terms herein, Ava Labs, Inc. (**“Ava Labs”**) hereby grants you 7 | a limited, royalty-free, worldwide, non-sublicensable, non-transferable, 8 | non-exclusive license to use, copy, modify, create derivative works based on, 9 | and redistribute the Software, in source code, binary, or any other form, 10 | including any modifications or derivative works of the Software (collectively, 11 | **“Licensed Software”**), in each case subject to this Ecosystem License 12 | (**“License”**). 13 | 14 | This License applies to all copies, modifications, derivative works, and any 15 | other form or usage of the Licensed Software. You will include and display 16 | this License, without modification, with all uses of the Licensed Software, 17 | regardless of form. 18 | 19 | You will use the Licensed Software solely (i) in connection with the Avalanche 20 | Public Blockchain platform, having a NetworkID of 1 (Mainnet) or 5 (Fuji), and 21 | associated blockchains, comprised exclusively of the Avalanche X-Chain, 22 | C-Chain, P-Chain and any subnets linked to the P-Chain (“Avalanche Authorized 23 | Platform”) or (ii) for non-production, testing or research purposes within the 24 | Avalanche ecosystem, in each case, without any commercial application 25 | (“Non-Commercial Use”); provided that this License does not permit use of the 26 | Licensed Software in connection with (a) any forks of the Avalanche Authorized 27 | Platform or (b) in any manner not operationally connected to the Avalanche 28 | Authorized Platform other than, for the avoidance of doubt, the limited 29 | exception for Non-Commercial Use. Ava Labs may publicly announce changes or 30 | additions to the Avalanche Authorized Platform, which may expand or modify 31 | usage of the Licensed Software. Upon such announcement, the Avalanche 32 | Authorized Platform will be deemed to be the then-current iteration of such 33 | platform. 34 | 35 | You hereby acknowledge and agree to the terms set forth at 36 | www.avalabs.org/important-notice. 37 | 38 | If you use the Licensed Software in violation of this License, this License 39 | will automatically terminate and Ava Labs reserves all rights to seek any 40 | remedy for such violation. 41 | 42 | Except for uses explicitly permitted in this License, Ava Labs retains all 43 | rights in the Licensed Software, including without limitation the ability to 44 | modify it. 45 | 46 | Except as required or explicitly permitted by this License, you will not use 47 | any Ava Labs names, logos, or trademarks without Ava Labs’ prior written 48 | consent. 49 | 50 | You may use this License for software other than the “Licensed Software” 51 | specified above, as long as the only change to this License is the definition 52 | of the term “Licensed Software.” 53 | 54 | The Licensed Software may reference third party components. You acknowledge 55 | and agree that these third party components may be governed by a separate 56 | license or terms and that you will comply with them. 57 | 58 | **TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE LICENSED SOFTWARE IS PROVIDED 59 | ON AN “AS IS” BASIS, AND AVA LABS EXPRESSLY DISCLAIMS AND EXCLUDES ALL 60 | REPRESENTATIONS, WARRANTIES AND OTHER TERMS AND CONDITIONS, WHETHER EXPRESS OR 61 | IMPLIED, INCLUDING WITHOUT LIMITATION BY OPERATION OF LAW OR BY CUSTOM, 62 | STATUTE OR OTHERWISE, AND INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTY, 63 | TERM, OR CONDITION OF NON-INFRINGEMENT, MERCHANTABILITY, TITLE, OR FITNESS FOR 64 | PARTICULAR PURPOSE. YOU USE THE LICENSED SOFTWARE AT YOUR OWN RISK. AVA LABS 65 | EXPRESSLY DISCLAIMS ALL LIABILITY (INCLUDING FOR ALL DIRECT, CONSEQUENTIAL OR 66 | OTHER DAMAGES OR LOSSES) RELATED TO ANY USE OF THE LICENSED SOFTWARE.** 67 | -------------------------------------------------------------------------------- /audit/avacloud-eerc-audit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/audit/avacloud-eerc-audit.pdf -------------------------------------------------------------------------------- /audit/avacloud-eerc-circom-audit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/audit/avacloud-eerc-circom-audit.pdf -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": [ 11 | "typechain-types", 12 | "coverage", 13 | "artifacts", 14 | ".vscode", 15 | "generated-types" 16 | ] 17 | }, 18 | "formatter": { 19 | "enabled": true, 20 | "indentStyle": "tab" 21 | }, 22 | "organizeImports": { 23 | "enabled": true 24 | }, 25 | "linter": { 26 | "enabled": true, 27 | "rules": { 28 | "recommended": true 29 | } 30 | }, 31 | "javascript": { 32 | "formatter": { 33 | "quoteStyle": "double" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /circom/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/.DS_Store -------------------------------------------------------------------------------- /circom/build/burn/burn.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/burn/burn.wasm -------------------------------------------------------------------------------- /circom/build/burn/burn.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/burn/burn.zkey -------------------------------------------------------------------------------- /circom/build/burn/burn_verification_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 19, 5 | "vk_alpha_1": [ 6 | "20491192805390485299153009773594534940189261866228447918068658471970481763042", 7 | "9383485363053290200918347156157836566562967994039712273449902621266178545958", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "6375614351688725206403948262868962793625744043794305715222011528459656738731", 13 | "4252822878758300859123897981450591353533073413197771768651442665752259397132" 14 | ], 15 | [ 16 | "10505242626370262277552901082094356697409835680220590971873171140371331206856", 17 | "21847035105528745403288232691147584728191162732299865338377159692350059136679" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "17951494579165795533532847235529611089521789751209994758862962564682445617412", 41 | "20500123508036250241732436039045855491142840147741568076165900093824331281316" 42 | ], 43 | [ 44 | "15268404118840851015046775717571916089738924216088942953684197626467386210997", 45 | "5834387578477044714458263369208539598498352288497332887384107712494722576583" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "2029413683389138792403550203267699914886160938906632433982220835551125967885", 56 | "21072700047562757817161031222997517981543347628379360635925549008442030252106" 57 | ], 58 | [ 59 | "5940354580057074848093997050200682056184807770593307860589430076672439820312", 60 | "12156638873931618554171829126792193045421052652279363021382169897324752428276" 61 | ], 62 | [ 63 | "7898200236362823042373859371574133993780991612861777490112507062703164551277", 64 | "7074218545237549455313236346927434013100842096812539264420499035217050630853" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "7077479683546002997211712695946002074877511277312570035766170199895071832130", 70 | "10093483419865920389913245021038182291233451549023025229112148274109565435465" 71 | ], 72 | [ 73 | "4595479056700221319381530156280926371456704509942304414423590385166031118820", 74 | "19831328484489333784475432780421641293929726139240675179672856274388269393268" 75 | ], 76 | [ 77 | "11934129596455521040620786944827826205713621633706285934057045369193958244500", 78 | "8037395052364110730298837004334506829870972346962140206007064471173334027475" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "11786268729161045937652164714990691490770027441460947932012316631529733292940", 85 | "9581132102330540074693305331254898451475664668584360899026097918947512977366", 86 | "1" 87 | ], 88 | [ 89 | "914822366018062648285730205402683692599853766866159406178623354781735659599", 90 | "8553697750564092712813746981763759579862680918228138684307182805192139236520", 91 | "1" 92 | ], 93 | [ 94 | "17908121343774581376092646765067170799970925894057937307165335210962871507135", 95 | "6739483966006459958647545663755815825611388802168101513616676265213547687212", 96 | "1" 97 | ], 98 | [ 99 | "143359351657650308208480824138891765346610788279862658068353643164441844115", 100 | "1688658345879791302522420761526578526163188876279486731177660400972657648742", 101 | "1" 102 | ], 103 | [ 104 | "18309718863517978042660293994537440591428742486455947599492279373595114219500", 105 | "3543366565414834921232477531525858456841505305202548147687587105557694958104", 106 | "1" 107 | ], 108 | [ 109 | "2179968961403977829543241099157331775731416225717055667057125732267317798366", 110 | "14579564811228528586131093319854876770716135551807922280189147940915226636750", 111 | "1" 112 | ], 113 | [ 114 | "12128037778350917133400346467296434075039661406517118820895803963198786951800", 115 | "12810377956478161485381774828600271058686148075894654796038454545352947160776", 116 | "1" 117 | ], 118 | [ 119 | "9593873745453136272405518724864540111195676150385738265238288297101461351389", 120 | "18140226050853380485925948980064648409518762214351351505488025177062130412015", 121 | "1" 122 | ], 123 | [ 124 | "5344462067192893979551241744484644250693230460027867247351105468329395690915", 125 | "2435072349815413116277252545573449685604686772441816336584542983683379409914", 126 | "1" 127 | ], 128 | [ 129 | "15867459271796662494529143386082298582993967528167005628586571617103329782767", 130 | "1031216327552632558464991513003642749331915406636029936411939647607628738052", 131 | "1" 132 | ], 133 | [ 134 | "11710830742293210758218398274130454937288722189717294688374583983853957500247", 135 | "18095934287133238566717320534781010028273178446261522698705303702525394211898", 136 | "1" 137 | ], 138 | [ 139 | "5451223849987641096615559250356903902465481513182931751171182631482949381911", 140 | "6791511973211811945927811502706661666842895298126970397039460184655612620453", 141 | "1" 142 | ], 143 | [ 144 | "12576390828389735220141209658777087276380270144557056528857559882798323629447", 145 | "19191122649652045476856774223103624470066531140701020233373995430155185392289", 146 | "1" 147 | ], 148 | [ 149 | "3208115974817470421120218397409974778673673537533531169452261659740974257278", 150 | "1563495302870822079389756646952495877672019554537528044396737285924026906103", 151 | "1" 152 | ], 153 | [ 154 | "15803266454704411593372706477399336420388826076293995292354385277988446601281", 155 | "14061261779941396671642560007918916383355024387096318566313366433584709024499", 156 | "1" 157 | ], 158 | [ 159 | "1473892103720096233092115851822223251992401285317974909943916318348184767365", 160 | "3670020837101119839757171869792531530222770849398108379916390350369899561438", 161 | "1" 162 | ], 163 | [ 164 | "8909686676336745813415338575513600352109241710831786249590248291624151036333", 165 | "4561980313802046241453616743996179799784948333408123666456078054975402233930", 166 | "1" 167 | ], 168 | [ 169 | "9644420134970614917918276763660740255881705883613568754990717610748161142338", 170 | "6057246608400625128574045490366205109267182635495031148770746354579442521952", 171 | "1" 172 | ], 173 | [ 174 | "3478588600416828411411103709929443364350875935722379535054531373495869197302", 175 | "14312139497598775136457694532354226575027823182198251149840842365373433448408", 176 | "1" 177 | ], 178 | [ 179 | "16478469522390352876826812932849485876750021274758447084770343652372421581875", 180 | "5525105724620928136283079314841429313946285054350434613540639344526895834484", 181 | "1" 182 | ] 183 | ] 184 | } -------------------------------------------------------------------------------- /circom/build/mint/mint.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/mint/mint.wasm -------------------------------------------------------------------------------- /circom/build/mint/mint.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/mint/mint.zkey -------------------------------------------------------------------------------- /circom/build/mint/mint_verification_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 24, 5 | "vk_alpha_1": [ 6 | "20491192805390485299153009773594534940189261866228447918068658471970481763042", 7 | "9383485363053290200918347156157836566562967994039712273449902621266178545958", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "6375614351688725206403948262868962793625744043794305715222011528459656738731", 13 | "4252822878758300859123897981450591353533073413197771768651442665752259397132" 14 | ], 15 | [ 16 | "10505242626370262277552901082094356697409835680220590971873171140371331206856", 17 | "21847035105528745403288232691147584728191162732299865338377159692350059136679" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "12917761706367159448552669332158549008424338800753983174002760426851264899916", 41 | "6506135815943976343670204718908252778100436948044695952269504614489235747522" 42 | ], 43 | [ 44 | "921807516250667254679639816527091672038652453691410152532432179406742682074", 45 | "21054759061354342502671069523347944444365476693281335142489232816218318279843" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "2029413683389138792403550203267699914886160938906632433982220835551125967885", 56 | "21072700047562757817161031222997517981543347628379360635925549008442030252106" 57 | ], 58 | [ 59 | "5940354580057074848093997050200682056184807770593307860589430076672439820312", 60 | "12156638873931618554171829126792193045421052652279363021382169897324752428276" 61 | ], 62 | [ 63 | "7898200236362823042373859371574133993780991612861777490112507062703164551277", 64 | "7074218545237549455313236346927434013100842096812539264420499035217050630853" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "7077479683546002997211712695946002074877511277312570035766170199895071832130", 70 | "10093483419865920389913245021038182291233451549023025229112148274109565435465" 71 | ], 72 | [ 73 | "4595479056700221319381530156280926371456704509942304414423590385166031118820", 74 | "19831328484489333784475432780421641293929726139240675179672856274388269393268" 75 | ], 76 | [ 77 | "11934129596455521040620786944827826205713621633706285934057045369193958244500", 78 | "8037395052364110730298837004334506829870972346962140206007064471173334027475" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "10121904041541460154551260390411876526139574666949788432677764574044027742051", 85 | "6395114105467352463516423639828272877440036954754220055848039516989817256674", 86 | "1" 87 | ], 88 | [ 89 | "5586762719201810155133862083740714325655748243483738877755810028542797644739", 90 | "9387879719287370926595730382657598610335084850078203728936667449453246554359", 91 | "1" 92 | ], 93 | [ 94 | "19420434315212518030589186167981244595406343548992012458488730749825996100239", 95 | "3482657374383353276279298693752239555763142972311841530862645391131143881873", 96 | "1" 97 | ], 98 | [ 99 | "21667521945257749121492909465236139091096077691835364325355606431375269082282", 100 | "12156175257782822331801928906256367913221851960540994586932487722549923413345", 101 | "1" 102 | ], 103 | [ 104 | "16828357126590671631798447095302651492589140460428270542352941484237941156483", 105 | "3785759973408012513975276660945174958105045591429994983968533282503371539336", 106 | "1" 107 | ], 108 | [ 109 | "21244321325522105477060254124915502034148248502931960755663601081500950076300", 110 | "14687177467139662957844534389099557537735007475047254307829149459337997284150", 111 | "1" 112 | ], 113 | [ 114 | "21404447565616225355770439075122853382066792760583316781611395109968504614761", 115 | "3777910210505077428584254669424713454396973987622630869249106067166590299357", 116 | "1" 117 | ], 118 | [ 119 | "5644690004940482546460856256670502914882531219591199090065888636589494736039", 120 | "16345970805899097192278870928687506101715169625232631539981164972434652655022", 121 | "1" 122 | ], 123 | [ 124 | "16593367704572145491497340027058421406291597779030039134573703992464863830646", 125 | "19275772679724763916884927502599327291258343548738975909520313332884772129528", 126 | "1" 127 | ], 128 | [ 129 | "18338635157990571739660769292280252719367811451620901782236478705200001456515", 130 | "12580365946981294431067603413481570211192375767222833113101936660967490853342", 131 | "1" 132 | ], 133 | [ 134 | "19880773599746588943745194809450382516728304373879942615347513263173728275196", 135 | "128548754750873577554444615682746852358621588347676854260473817368140033519", 136 | "1" 137 | ], 138 | [ 139 | "6476719107464483530164368276818653110527411272303410163053488615734788766586", 140 | "13331539055996208679678758084320772815107338352425913859433335410845899223811", 141 | "1" 142 | ], 143 | [ 144 | "17393436839515851750885277096607160266036302533767007170590322671069079326483", 145 | "17336491525577930609097646312281682071744277090982994234361498925040239739760", 146 | "1" 147 | ], 148 | [ 149 | "14059139663320156276001577403575266145309546640724899667521029234074221180403", 150 | "5963232262834884334488206091864494084893490405377454589196552297795638041488", 151 | "1" 152 | ], 153 | [ 154 | "13813470258321934033051704279698252148783024920181797002453328082368105474789", 155 | "19760333797981660449134124218356560284106192590380149679255903109561686546313", 156 | "1" 157 | ], 158 | [ 159 | "11858117840318587547791069185551586430435948995333725070770764697634898894992", 160 | "16621799951101200543458233987158204772158254202860685491749532125765298740749", 161 | "1" 162 | ], 163 | [ 164 | "13584356250500421508084566767718022822120888453124821650989966819848963029582", 165 | "21121442557480691564262522113115528175177021588076736568194595390503813944019", 166 | "1" 167 | ], 168 | [ 169 | "4326226245481542040767942040881999777137473163343274779447151141265545792486", 170 | "21183580388477113613358837765893868421637670220571970210021457037640835173809", 171 | "1" 172 | ], 173 | [ 174 | "2704745980008452624320271214496436840853635777289985169457654318338905712053", 175 | "5498942242451865915343845370075402718280213375705928811712272696867696623122", 176 | "1" 177 | ], 178 | [ 179 | "18021323881247358977456866382503667963535769536490271165761105041938333860259", 180 | "15056011404667104875167366478630077579580289596849055349190254216447781136049", 181 | "1" 182 | ], 183 | [ 184 | "2428670635260390602473903592482933886252233168437493591205178349351514000281", 185 | "862350060646299115005420081148519933461348146447694025974629367115211964519", 186 | "1" 187 | ], 188 | [ 189 | "20557922324599650576215164022162024912213509843003042307546636524489843547931", 190 | "1061522392679391742130691544383963509531829000498209937975610885874122363387", 191 | "1" 192 | ], 193 | [ 194 | "9114256858842496488535186312068922854228150055952688114444862375910254881995", 195 | "20868937436899006110617091404782344741103549613810098330797141428653796030423", 196 | "1" 197 | ], 198 | [ 199 | "392332888128429361037702708407695212788446855280618226506466973682041689462", 200 | "10986782868722900357976121669302445297980271976862420955075995610762868745084", 201 | "1" 202 | ], 203 | [ 204 | "12335561474727855982123382035622477594232361573497391299243618736725476900631", 205 | "20340286770637453607265533856091115758478742882346824576410498508028320071146", 206 | "1" 207 | ] 208 | ] 209 | } -------------------------------------------------------------------------------- /circom/build/registration/circuit_final.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/registration/circuit_final.zkey -------------------------------------------------------------------------------- /circom/build/registration/registration.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/registration/registration.wasm -------------------------------------------------------------------------------- /circom/build/registration/registration_verification_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 5, 5 | "vk_alpha_1": [ 6 | "20491192805390485299153009773594534940189261866228447918068658471970481763042", 7 | "9383485363053290200918347156157836566562967994039712273449902621266178545958", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "6375614351688725206403948262868962793625744043794305715222011528459656738731", 13 | "4252822878758300859123897981450591353533073413197771768651442665752259397132" 14 | ], 15 | [ 16 | "10505242626370262277552901082094356697409835680220590971873171140371331206856", 17 | "21847035105528745403288232691147584728191162732299865338377159692350059136679" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "2824093012045694268556486522753590084359236940213189502690301706344424715581", 41 | "19753714125923452630385433581442722093941065264298048276246815338797167448169" 42 | ], 43 | [ 44 | "12986835757414062294165220819491853575831232959693796161862995405626568533639", 45 | "18067530836866733450125362089378459710977233400131368676354166923678178030850" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "2029413683389138792403550203267699914886160938906632433982220835551125967885", 56 | "21072700047562757817161031222997517981543347628379360635925549008442030252106" 57 | ], 58 | [ 59 | "5940354580057074848093997050200682056184807770593307860589430076672439820312", 60 | "12156638873931618554171829126792193045421052652279363021382169897324752428276" 61 | ], 62 | [ 63 | "7898200236362823042373859371574133993780991612861777490112507062703164551277", 64 | "7074218545237549455313236346927434013100842096812539264420499035217050630853" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "7077479683546002997211712695946002074877511277312570035766170199895071832130", 70 | "10093483419865920389913245021038182291233451549023025229112148274109565435465" 71 | ], 72 | [ 73 | "4595479056700221319381530156280926371456704509942304414423590385166031118820", 74 | "19831328484489333784475432780421641293929726139240675179672856274388269393268" 75 | ], 76 | [ 77 | "11934129596455521040620786944827826205713621633706285934057045369193958244500", 78 | "8037395052364110730298837004334506829870972346962140206007064471173334027475" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "5049073754175979375715331231494813434614104647476713784192278874636518287456", 85 | "13973039660095243213304729343482928815131903560133937124822026621516335683252", 86 | "1" 87 | ], 88 | [ 89 | "6960760860997719127050640389023071189120419160885047633543132031266205842967", 90 | "14311210142759362992805316832152765774097518712372467130307901891389375007789", 91 | "1" 92 | ], 93 | [ 94 | "14970325264892984291437720194401230916657388050759523602370378142660744831477", 95 | "15860538555168123807647719982845297214031403618163443664157964964439662885432", 96 | "1" 97 | ], 98 | [ 99 | "9095633778879314058949553908878900274830732566924382454865365752356228820208", 100 | "872555237120135122336016443589602918886936394020805627011933688108936774726", 101 | "1" 102 | ], 103 | [ 104 | "16383214040587918206822141493152567743771449527138865305823970747014581995205", 105 | "15807972914519003321398888297328267389104135252699104466520087670194802499103", 106 | "1" 107 | ], 108 | [ 109 | "19865793340402973866436804410911022538640660662316164831277333170166538152800", 110 | "1466380730966029920868782474285618825793197367831481952454164420798011324299", 111 | "1" 112 | ] 113 | ] 114 | } -------------------------------------------------------------------------------- /circom/build/transfer/transfer.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/transfer/transfer.wasm -------------------------------------------------------------------------------- /circom/build/transfer/transfer.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/transfer/transfer.zkey -------------------------------------------------------------------------------- /circom/build/transfer/transfer_verification_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 32, 5 | "vk_alpha_1": [ 6 | "20491192805390485299153009773594534940189261866228447918068658471970481763042", 7 | "9383485363053290200918347156157836566562967994039712273449902621266178545958", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "6375614351688725206403948262868962793625744043794305715222011528459656738731", 13 | "4252822878758300859123897981450591353533073413197771768651442665752259397132" 14 | ], 15 | [ 16 | "10505242626370262277552901082094356697409835680220590971873171140371331206856", 17 | "21847035105528745403288232691147584728191162732299865338377159692350059136679" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "19123758782558680960137049284286660834755919438304772090386326942336280507607", 41 | "3763145489265154227084511171694601212878632438189047805035774181671200721579" 42 | ], 43 | [ 44 | "4894238513642303098263860622238471992887790214981922367145986542927713649242", 45 | "2055449651368268424611099662557640196156614451951214953872156041652270884888" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "2029413683389138792403550203267699914886160938906632433982220835551125967885", 56 | "21072700047562757817161031222997517981543347628379360635925549008442030252106" 57 | ], 58 | [ 59 | "5940354580057074848093997050200682056184807770593307860589430076672439820312", 60 | "12156638873931618554171829126792193045421052652279363021382169897324752428276" 61 | ], 62 | [ 63 | "7898200236362823042373859371574133993780991612861777490112507062703164551277", 64 | "7074218545237549455313236346927434013100842096812539264420499035217050630853" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "7077479683546002997211712695946002074877511277312570035766170199895071832130", 70 | "10093483419865920389913245021038182291233451549023025229112148274109565435465" 71 | ], 72 | [ 73 | "4595479056700221319381530156280926371456704509942304414423590385166031118820", 74 | "19831328484489333784475432780421641293929726139240675179672856274388269393268" 75 | ], 76 | [ 77 | "11934129596455521040620786944827826205713621633706285934057045369193958244500", 78 | "8037395052364110730298837004334506829870972346962140206007064471173334027475" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "10534947708174160215201992304779041355498470735561003306499717122566742770626", 85 | "977905257499899758791217822505228003462337033122097368830210745360601555342", 86 | "1" 87 | ], 88 | [ 89 | "3280711512314934753992505317526746468153049821370324146752079659712174232300", 90 | "9336028462433315123867796333994191785575550026414826950323988687234821492567", 91 | "1" 92 | ], 93 | [ 94 | "15162904757480315297547642942646568889101029164132171480256830884259150251283", 95 | "5543236365374398176471504758808152977042933422387633848583378303299793270756", 96 | "1" 97 | ], 98 | [ 99 | "6084599581719503190421548874454410642759752303455385862653438619168850972257", 100 | "18221408640134590893699663063879232072826509494821669770579541736533993832423", 101 | "1" 102 | ], 103 | [ 104 | "13515294784299896617263177420364080405138679960108803709127908717931838641125", 105 | "18537954088397403593567974022084871913708328503334967617431436055023979447391", 106 | "1" 107 | ], 108 | [ 109 | "18052165982957660268255521614825223786335394780677647206458385784232406504157", 110 | "15194585058280412021740640814141568454746627730826424655106679430538982053654", 111 | "1" 112 | ], 113 | [ 114 | "6795673837897345581582685299965371845759418273539377917021912839836254998739", 115 | "21657993441803516491832636271681043591945604671357149970048825080204553686618", 116 | "1" 117 | ], 118 | [ 119 | "11289069591450532499401972724447572002376453520281915368481681110785600567292", 120 | "11928539623033527208857415982521538988739084305734749009299860340216923817607", 121 | "1" 122 | ], 123 | [ 124 | "21069306450658538733134282147277445263698906490240400327689099847099016892805", 125 | "17386225215792739261329655602621235879743955668381844924635942683420074576174", 126 | "1" 127 | ], 128 | [ 129 | "14345437456753227245495940686700720956058390526322202366808609527984033660993", 130 | "20783457650276182735214466418096310752840639854110597710747660731724327511323", 131 | "1" 132 | ], 133 | [ 134 | "4486441340824192164364882510367580608217147444088109552210786362403055241932", 135 | "16553383197162304205323300390384875515644589603157855319848881923653657055218", 136 | "1" 137 | ], 138 | [ 139 | "7299849271075286253602519481881832740498497504048928918662655218108429284825", 140 | "21860902462897801250346538563498742432578812847135047747638191825760100188844", 141 | "1" 142 | ], 143 | [ 144 | "20812311364493619358550828916571084941809811622004047558332288367068444292636", 145 | "12016931719373400562450857278580603432222260403491989130190760226223433669838", 146 | "1" 147 | ], 148 | [ 149 | "5497135883066110411842507436361453534364438777740194801977407015457789050083", 150 | "960289848958742655061746467902157315103658957325800360115467733778920704760", 151 | "1" 152 | ], 153 | [ 154 | "5231929274251912956972062967045818685875910122905492872577246977634648737843", 155 | "2405680064306334170931852015314781533305421897621973941278348820986549273557", 156 | "1" 157 | ], 158 | [ 159 | "14432423838507076163732475853088150200901869051870980078541847122928808702858", 160 | "13162477912034036392091090217125050953003275307601447359448369192560307791075", 161 | "1" 162 | ], 163 | [ 164 | "6035380084134788387796489655621652133939237219692536713085830198890102531157", 165 | "2399413675745655961934247765244583346698339793807331712093749883838255391396", 166 | "1" 167 | ], 168 | [ 169 | "1755840344064269003982904724784588829875883176859314530482477354204358128073", 170 | "2254035658088032093438783815005800506175183609989063203400053587352866305963", 171 | "1" 172 | ], 173 | [ 174 | "10798890848385583150817432575812471927781281538956290209396088794995757251699", 175 | "17954866604704083608550796134404236420626288308545111154506695506614087706302", 176 | "1" 177 | ], 178 | [ 179 | "16347505387353659907142234702135154337293088908179815677326286365566466652892", 180 | "8145400935245335001708439352534792707071085106081878093341920760316939810339", 181 | "1" 182 | ], 183 | [ 184 | "1675146216777597460950545026188423914140684419320163593588767283439513220964", 185 | "3350896492191726464487860464991880712916569207980460035951208489640794805493", 186 | "1" 187 | ], 188 | [ 189 | "14963360443843679002951981438607054782948196458205149005110445616986857962149", 190 | "5453682123596332752358242170390357649813170313370080322693195792615766005582", 191 | "1" 192 | ], 193 | [ 194 | "15857139261281078612816698520673496528634524033018907015066869560007114561368", 195 | "1980632957785357477112204530352463186795117003870653324765557649080191593069", 196 | "1" 197 | ], 198 | [ 199 | "16478788219117512612181000397725550334291930466463033672428118170129909755975", 200 | "448496014898525130300582250347934155241571361910686004171961088395557906873", 201 | "1" 202 | ], 203 | [ 204 | "1666825517524796314004984207461413135984918930374439646195676102947777182322", 205 | "4231769516076965497747593863600552237823540573882692739018851158660080939768", 206 | "1" 207 | ], 208 | [ 209 | "3076619425752586210204729303861073992338095413231945893603783564036991027253", 210 | "18163258048301667325957382686787210971933697470347155712452125397431467657627", 211 | "1" 212 | ], 213 | [ 214 | "12240984649194028123658663640999248201069935674255565459899301410323450592735", 215 | "7409006959659622318645744477945350299438380296542336739366936950900796739795", 216 | "1" 217 | ], 218 | [ 219 | "14527565647042473833164468956000362795519314198262866225352631554103417406048", 220 | "19163038429636451765415902370901493252440686868311522712756801834049736087286", 221 | "1" 222 | ], 223 | [ 224 | "4563064787502578607504522503795653524780441771903390561095040321594991012805", 225 | "17132961897233615551711030277418083026528303676258815957139396120116493761491", 226 | "1" 227 | ], 228 | [ 229 | "10825384216191058605866379876945565560465943245071973007006264312285398033060", 230 | "21577743268709413526652653885555866375927505893806393223914213433533358336358", 231 | "1" 232 | ], 233 | [ 234 | "6397191691971602863357148514208654665119261190406272901198661597143240375", 235 | "19034368636483100848266322777417046441872774162624857636955426992047727086319", 236 | "1" 237 | ], 238 | [ 239 | "13887187887323344570981409187274970320617448786085046258948210932080686653886", 240 | "18715879836330833238341482186679228139561235361790447129420164142460558259244", 241 | "1" 242 | ], 243 | [ 244 | "11249333327764660601508194401900817731177098928541819675032435010243826533510", 245 | "18594519484247875970157793842670472272992161543336964034550143549078316481776", 246 | "1" 247 | ] 248 | ] 249 | } -------------------------------------------------------------------------------- /circom/build/withdraw/circuit_final.zkey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/withdraw/circuit_final.zkey -------------------------------------------------------------------------------- /circom/build/withdraw/withdraw.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/circom/build/withdraw/withdraw.wasm -------------------------------------------------------------------------------- /circom/build/withdraw/withdraw_verification_key.json: -------------------------------------------------------------------------------- 1 | { 2 | "protocol": "groth16", 3 | "curve": "bn128", 4 | "nPublic": 16, 5 | "vk_alpha_1": [ 6 | "20491192805390485299153009773594534940189261866228447918068658471970481763042", 7 | "9383485363053290200918347156157836566562967994039712273449902621266178545958", 8 | "1" 9 | ], 10 | "vk_beta_2": [ 11 | [ 12 | "6375614351688725206403948262868962793625744043794305715222011528459656738731", 13 | "4252822878758300859123897981450591353533073413197771768651442665752259397132" 14 | ], 15 | [ 16 | "10505242626370262277552901082094356697409835680220590971873171140371331206856", 17 | "21847035105528745403288232691147584728191162732299865338377159692350059136679" 18 | ], 19 | [ 20 | "1", 21 | "0" 22 | ] 23 | ], 24 | "vk_gamma_2": [ 25 | [ 26 | "10857046999023057135944570762232829481370756359578518086990519993285655852781", 27 | "11559732032986387107991004021392285783925812861821192530917403151452391805634" 28 | ], 29 | [ 30 | "8495653923123431417604973247489272438418190587263600148770280649306958101930", 31 | "4082367875863433681332203403145435568316851327593401208105741076214120093531" 32 | ], 33 | [ 34 | "1", 35 | "0" 36 | ] 37 | ], 38 | "vk_delta_2": [ 39 | [ 40 | "9134393277137786117877602562175203472042554058536880581948679621094751253663", 41 | "17937261609492175473978423283366042492782788118934277743325590061927472686823" 42 | ], 43 | [ 44 | "13560247966092290370160513680671248860780213214350811711788627488150106599867", 45 | "21830579691680039608885512644157042138865859432216493093304779889887319651703" 46 | ], 47 | [ 48 | "1", 49 | "0" 50 | ] 51 | ], 52 | "vk_alphabeta_12": [ 53 | [ 54 | [ 55 | "2029413683389138792403550203267699914886160938906632433982220835551125967885", 56 | "21072700047562757817161031222997517981543347628379360635925549008442030252106" 57 | ], 58 | [ 59 | "5940354580057074848093997050200682056184807770593307860589430076672439820312", 60 | "12156638873931618554171829126792193045421052652279363021382169897324752428276" 61 | ], 62 | [ 63 | "7898200236362823042373859371574133993780991612861777490112507062703164551277", 64 | "7074218545237549455313236346927434013100842096812539264420499035217050630853" 65 | ] 66 | ], 67 | [ 68 | [ 69 | "7077479683546002997211712695946002074877511277312570035766170199895071832130", 70 | "10093483419865920389913245021038182291233451549023025229112148274109565435465" 71 | ], 72 | [ 73 | "4595479056700221319381530156280926371456704509942304414423590385166031118820", 74 | "19831328484489333784475432780421641293929726139240675179672856274388269393268" 75 | ], 76 | [ 77 | "11934129596455521040620786944827826205713621633706285934057045369193958244500", 78 | "8037395052364110730298837004334506829870972346962140206007064471173334027475" 79 | ] 80 | ] 81 | ], 82 | "IC": [ 83 | [ 84 | "17592896514411238838534259943087906039445412467749501855660552694930011809103", 85 | "12732983330708765219970549274392016128270173019293005848551474757324971194427", 86 | "1" 87 | ], 88 | [ 89 | "4884577932082347864149139524102040008488705064603684844915539314596644874132", 90 | "5456447315604083878474265800827509892021228387863831389892962189069605516459", 91 | "1" 92 | ], 93 | [ 94 | "17313454373712045907554165434725963402117845429222578159130609582778779362163", 95 | "4407461008783085705651216674025467181197484228135456843989535495240785812495", 96 | "1" 97 | ], 98 | [ 99 | "10893636774628876142899763192646905724708189468282917758613650147010478133301", 100 | "148896310776292842756025446759984436974180847194558332931999839598427070960", 101 | "1" 102 | ], 103 | [ 104 | "10775675593870925743575084889624246391515781987722804772444693341240701433005", 105 | "21091396295200083161363697039830865943762788335711072967082811732416412142212", 106 | "1" 107 | ], 108 | [ 109 | "3277805223442060778292405741942548007218490041051348547437304074109624107356", 110 | "14423944753390168508001610935122911632893435927783118919600891838924075393335", 111 | "1" 112 | ], 113 | [ 114 | "16279740598746831017838829127065220413108847820004536437353222843036288927209", 115 | "11934056246616513677892280054066225650223628502415902228226616908265848867449", 116 | "1" 117 | ], 118 | [ 119 | "5071131858486439523644545889310168311903353944707113398976996997278587799687", 120 | "11022965843210062064457659632129711893317662131236104884153566306811626505122", 121 | "1" 122 | ], 123 | [ 124 | "19131780283367531273448020668619041357172962162156115675741827445246630873287", 125 | "10224436589841763650559926912214172567856653421053590774767009207269607380898", 126 | "1" 127 | ], 128 | [ 129 | "9015890207964856811111785067351720194541561850029096381425406768947368358563", 130 | "6047643904429654109574184105636835293535463759429215740849034643735922891159", 131 | "1" 132 | ], 133 | [ 134 | "14203417831592538293633573944212232646895161398477212007798306234115933198031", 135 | "16402598342815946882326203254821041228554468765346033642071484587006914175418", 136 | "1" 137 | ], 138 | [ 139 | "20547058030012031602382050197843576579725635037139342207584317836070120910810", 140 | "18032163858711245709516724511475426102219042616668426511217798974437856519723", 141 | "1" 142 | ], 143 | [ 144 | "148439843878200889608411203087042067351611646430696078100079098168514978714", 145 | "9307113381205316554384865850704068163340346564569932871492676068849222362101", 146 | "1" 147 | ], 148 | [ 149 | "21753481289618352374517009260504147501427757590648179575845005895425333005720", 150 | "15435923224398640375717587240417911857458317269020496236194087111795302601904", 151 | "1" 152 | ], 153 | [ 154 | "15556836099875422007583907774078621468033908465380872159150755233143098695951", 155 | "1129744811704326861736580949783332840905388382080396668503461990830533174097", 156 | "1" 157 | ], 158 | [ 159 | "13442230315665714018082790733663853073869607935268797374915472095839891294991", 160 | "6217288894318109502763318183559473125294772219565374069029777927475585687108", 161 | "1" 162 | ], 163 | [ 164 | "13992512973627636194543618755294557224102658899504040367095379192332931057711", 165 | "5489859536562727184655955989301069690715491550780438666774255315815483859767", 166 | "1" 167 | ] 168 | ] 169 | } -------------------------------------------------------------------------------- /circom/burn.circom: -------------------------------------------------------------------------------- 1 | pragma circom 2.1.9; 2 | 3 | include "./components.circom"; 4 | 5 | template BurnCircuit () { 6 | signal input ValueToBurn; 7 | 8 | signal input SenderPrivateKey; 9 | signal input SenderPublicKey[2]; 10 | signal input SenderBalance; 11 | signal input SenderBalanceC1[2]; 12 | signal input SenderBalanceC2[2]; 13 | 14 | signal input SenderVTBC1[2]; 15 | signal input SenderVTBC2[2]; 16 | 17 | signal input AuditorPublicKey[2]; 18 | signal input AuditorPCT[4]; 19 | signal input AuditorPCTAuthKey[2]; 20 | signal input AuditorPCTNonce; 21 | signal input AuditorPCTRandom; 22 | 23 | // Verify that the transfer amount is less than or equal to the sender's balance and is less than the base order 24 | var baseOrder = 2736030358979909402780800718157159386076813972158567259200215660948447373041; 25 | 26 | component bitCheck1 = Num2Bits(252); 27 | bitCheck1.in <== ValueToBurn; 28 | 29 | component bitCheck2 = Num2Bits(252); 30 | bitCheck2.in <== baseOrder; 31 | 32 | component lt = LessThan(252); 33 | lt.in[0] <== ValueToBurn; 34 | lt.in[1] <== baseOrder; 35 | lt.out === 1; 36 | 37 | component bitCheck3 = Num2Bits(252); 38 | bitCheck3.in <== SenderBalance + 1; 39 | 40 | component checkValue = LessThan(252); 41 | checkValue.in[0] <== ValueToBurn; 42 | checkValue.in[1] <== SenderBalance + 1; 43 | checkValue.out === 1; 44 | 45 | 46 | // Verify that the sender's public key is well-formed 47 | component checkSenderPK = CheckPublicKey(); 48 | checkSenderPK.privKey <== SenderPrivateKey; 49 | checkSenderPK.pubKey[0] <== SenderPublicKey[0]; 50 | checkSenderPK.pubKey[1] <== SenderPublicKey[1]; 51 | 52 | // Verify that the sender's encrypted balance is well-formed 53 | component checkSenderBalance = CheckValue(); 54 | checkSenderBalance.value <== SenderBalance; 55 | checkSenderBalance.privKey <== SenderPrivateKey; 56 | checkSenderBalance.valueC1[0] <== SenderBalanceC1[0]; 57 | checkSenderBalance.valueC1[1] <== SenderBalanceC1[1]; 58 | checkSenderBalance.valueC2[0] <== SenderBalanceC2[0]; 59 | checkSenderBalance.valueC2[1] <== SenderBalanceC2[1]; 60 | 61 | // Verify that the sender's encrypted value to burn is the burn amount 62 | component checkSenderVTB = CheckValue(); 63 | checkSenderVTB.value <== ValueToBurn; 64 | checkSenderVTB.privKey <== SenderPrivateKey; 65 | checkSenderVTB.valueC1[0] <== SenderVTBC1[0]; 66 | checkSenderVTB.valueC1[1] <== SenderVTBC1[1]; 67 | checkSenderVTB.valueC2[0] <== SenderVTBC2[0]; 68 | checkSenderVTB.valueC2[1] <== SenderVTBC2[1]; 69 | 70 | // Verify auditor's encrypted summary includes the burn amount and is encrypted with the auditor's public key 71 | component checkAuditorPCT = CheckPCT(); 72 | checkAuditorPCT.publicKey[0] <== AuditorPublicKey[0]; 73 | checkAuditorPCT.publicKey[1] <== AuditorPublicKey[1]; 74 | checkAuditorPCT.pct <== AuditorPCT; 75 | checkAuditorPCT.authKey[0] <== AuditorPCTAuthKey[0]; 76 | checkAuditorPCT.authKey[1] <== AuditorPCTAuthKey[1]; 77 | checkAuditorPCT.nonce <== AuditorPCTNonce; 78 | checkAuditorPCT.random <== AuditorPCTRandom; 79 | checkAuditorPCT.value <== ValueToBurn; 80 | } 81 | 82 | component main { public [ SenderPublicKey, AuditorPublicKey, SenderBalanceC1, SenderBalanceC2, SenderVTBC1, SenderVTBC2, AuditorPCT, AuditorPCTAuthKey, AuditorPCTNonce ] } = BurnCircuit(); -------------------------------------------------------------------------------- /circom/circomlib/aliascheck.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | include "compconstant.circom"; 22 | 23 | 24 | template AliasCheck() { 25 | 26 | signal input in[254]; 27 | 28 | component compConstant = CompConstant(-1); 29 | 30 | for (var i=0; i<254; i++) in[i] ==> compConstant.in[i]; 31 | 32 | compConstant.out === 0; 33 | } 34 | -------------------------------------------------------------------------------- /circom/circomlib/babyjub.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | include "bitify.circom"; 22 | include "escalarmulfix.circom"; 23 | 24 | template BabyAdd() { 25 | signal input x1; 26 | signal input y1; 27 | signal input x2; 28 | signal input y2; 29 | signal output xout; 30 | signal output yout; 31 | 32 | signal beta; 33 | signal gamma; 34 | signal delta; 35 | signal tau; 36 | 37 | var a = 168700; 38 | var d = 168696; 39 | 40 | beta <== x1*y2; 41 | gamma <== y1*x2; 42 | delta <== (-a*x1+y1)*(x2 + y2); 43 | tau <== beta * gamma; 44 | 45 | xout <-- (beta + gamma) / (1+ d*tau); 46 | (1+ d*tau) * xout === (beta + gamma); 47 | 48 | yout <-- (delta + a*beta - gamma) / (1-d*tau); 49 | (1-d*tau)*yout === (delta + a*beta - gamma); 50 | } 51 | 52 | template BabyDbl() { 53 | signal input x; 54 | signal input y; 55 | signal output xout; 56 | signal output yout; 57 | 58 | component adder = BabyAdd(); 59 | adder.x1 <== x; 60 | adder.y1 <== y; 61 | adder.x2 <== x; 62 | adder.y2 <== y; 63 | 64 | adder.xout ==> xout; 65 | adder.yout ==> yout; 66 | } 67 | 68 | 69 | template BabyCheck() { 70 | signal input x; 71 | signal input y; 72 | 73 | signal x2; 74 | signal y2; 75 | 76 | var a = 168700; 77 | var d = 168696; 78 | 79 | x2 <== x*x; 80 | y2 <== y*y; 81 | 82 | a*x2 + y2 === 1 + d*x2*y2; 83 | } 84 | 85 | // Extracts the public key from private key 86 | template BabyPbk() { 87 | signal input in; 88 | signal output Ax; 89 | signal output Ay; 90 | 91 | var BASE8[2] = [ 92 | 5299619240641551281634865583518297030282874472190772894086521144482721001553, 93 | 16950150798460657717958625567821834550301663161624707787222815936182638968203 94 | ]; 95 | 96 | component pvkBits = Num2Bits(253); 97 | pvkBits.in <== in; 98 | 99 | component mulFix = EscalarMulFix(253, BASE8); 100 | 101 | var i; 102 | for (i=0; i<253; i++) { 103 | mulFix.e[i] <== pvkBits.out[i]; 104 | } 105 | Ax <== mulFix.out[0]; 106 | Ay <== mulFix.out[1]; 107 | } 108 | -------------------------------------------------------------------------------- /circom/circomlib/bitify.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | include "comparators.circom"; 22 | include "aliascheck.circom"; 23 | 24 | 25 | template Num2Bits(n) { 26 | signal input in; 27 | signal output out[n]; 28 | var lc1=0; 29 | 30 | var e2=1; 31 | for (var i = 0; i> i) & 1; 33 | out[i] * (out[i] -1 ) === 0; 34 | lc1 += out[i] * e2; 35 | e2 = e2+e2; 36 | } 37 | 38 | lc1 === in; 39 | } 40 | 41 | template Num2Bits_strict() { 42 | signal input in; 43 | signal output out[254]; 44 | 45 | component aliasCheck = AliasCheck(); 46 | component n2b = Num2Bits(254); 47 | in ==> n2b.in; 48 | 49 | for (var i=0; i<254; i++) { 50 | n2b.out[i] ==> out[i]; 51 | n2b.out[i] ==> aliasCheck.in[i]; 52 | } 53 | } 54 | 55 | template Bits2Num(n) { 56 | signal input in[n]; 57 | signal output out; 58 | var lc1=0; 59 | 60 | var e2 = 1; 61 | for (var i = 0; i out; 67 | } 68 | 69 | template Bits2Num_strict() { 70 | signal input in[254]; 71 | signal output out; 72 | 73 | component aliasCheck = AliasCheck(); 74 | component b2n = Bits2Num(254); 75 | 76 | for (var i=0; i<254; i++) { 77 | in[i] ==> b2n.in[i]; 78 | in[i] ==> aliasCheck.in[i]; 79 | } 80 | 81 | b2n.out ==> out; 82 | } 83 | 84 | template Num2BitsNeg(n) { 85 | signal input in; 86 | signal output out[n]; 87 | var lc1=0; 88 | 89 | component isZero; 90 | 91 | isZero = IsZero(); 92 | 93 | var neg = n == 0 ? 0 : 2**n - in; 94 | 95 | for (var i = 0; i> i) & 1; 97 | out[i] * (out[i] -1 ) === 0; 98 | lc1 += out[i] * 2**i; 99 | } 100 | 101 | in ==> isZero.in; 102 | 103 | 104 | 105 | lc1 + isZero.out * 2**n === 2**n - in; 106 | } 107 | -------------------------------------------------------------------------------- /circom/circomlib/comparators.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | template IsZero() { 22 | signal input in; 23 | signal output out; 24 | 25 | signal inv; 26 | 27 | inv <-- in!=0 ? 1/in : 0; 28 | 29 | out <== -in*inv +1; 30 | in*out === 0; 31 | } 32 | 33 | template LessThan(n) { 34 | assert(n <= 252); 35 | signal input in[2]; 36 | signal output out; 37 | 38 | component n2b = Num2Bits(n+1); 39 | 40 | n2b.in <== in[0]+ (1<. 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | include "bitify.circom"; 22 | 23 | // Returns 1 if in (in binary) > ct 24 | template CompConstant(ct) { 25 | signal input in[254]; 26 | signal output out; 27 | 28 | signal parts[127]; 29 | signal sout; 30 | 31 | var clsb; 32 | var cmsb; 33 | var slsb; 34 | var smsb; 35 | 36 | var sum=0; 37 | 38 | var b = (1 << 128) -1; 39 | var a = 1; 40 | var e = 1; 41 | var i; 42 | 43 | for (i=0;i<127; i++) { 44 | clsb = (ct >> (i*2)) & 1; 45 | cmsb = (ct >> (i*2+1)) & 1; 46 | slsb = in[i*2]; 47 | smsb = in[i*2+1]; 48 | 49 | if ((cmsb==0)&&(clsb==0)) { 50 | parts[i] <== -b*smsb*slsb + b*smsb + b*slsb; 51 | } else if ((cmsb==0)&&(clsb==1)) { 52 | parts[i] <== a*smsb*slsb - a*slsb + b*smsb - a*smsb + a; 53 | } else if ((cmsb==1)&&(clsb==0)) { 54 | parts[i] <== b*smsb*slsb - a*smsb + a; 55 | } else { 56 | parts[i] <== -a*smsb*slsb + a; 57 | } 58 | 59 | sum = sum + parts[i]; 60 | 61 | b = b -e; 62 | a = a +e; 63 | e = e*2; 64 | } 65 | 66 | sout <== sum; 67 | 68 | component num2bits = Num2Bits(135); 69 | 70 | num2bits.in <== sout; 71 | 72 | out <== num2bits.out[127]; 73 | } 74 | -------------------------------------------------------------------------------- /circom/circomlib/escalarmulany.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | include "montgomery.circom"; 22 | include "babyjub.circom"; 23 | include "comparators.circom"; 24 | 25 | template Multiplexor2() { 26 | signal input sel; 27 | signal input in[2][2]; 28 | signal output out[2]; 29 | 30 | out[0] <== (in[1][0] - in[0][0])*sel + in[0][0]; 31 | out[1] <== (in[1][1] - in[0][1])*sel + in[0][1]; 32 | } 33 | 34 | template BitElementMulAny() { 35 | signal input sel; 36 | signal input dblIn[2]; 37 | signal input addIn[2]; 38 | signal output dblOut[2]; 39 | signal output addOut[2]; 40 | 41 | component doubler = MontgomeryDouble(); 42 | component adder = MontgomeryAdd(); 43 | component selector = Multiplexor2(); 44 | 45 | 46 | sel ==> selector.sel; 47 | 48 | dblIn[0] ==> doubler.in[0]; 49 | dblIn[1] ==> doubler.in[1]; 50 | doubler.out[0] ==> adder.in1[0]; 51 | doubler.out[1] ==> adder.in1[1]; 52 | addIn[0] ==> adder.in2[0]; 53 | addIn[1] ==> adder.in2[1]; 54 | addIn[0] ==> selector.in[0][0]; 55 | addIn[1] ==> selector.in[0][1]; 56 | adder.out[0] ==> selector.in[1][0]; 57 | adder.out[1] ==> selector.in[1][1]; 58 | 59 | doubler.out[0] ==> dblOut[0]; 60 | doubler.out[1] ==> dblOut[1]; 61 | selector.out[0] ==> addOut[0]; 62 | selector.out[1] ==> addOut[1]; 63 | } 64 | 65 | // p is montgomery point 66 | // n must be <= 248 67 | // returns out in twisted edwards 68 | // Double is in montgomery to be linked; 69 | 70 | template SegmentMulAny(n) { 71 | signal input e[n]; 72 | signal input p[2]; 73 | signal output out[2]; 74 | signal output dbl[2]; 75 | 76 | component bits[n-1]; 77 | 78 | component e2m = Edwards2Montgomery(); 79 | 80 | p[0] ==> e2m.in[0]; 81 | p[1] ==> e2m.in[1]; 82 | 83 | var i; 84 | 85 | bits[0] = BitElementMulAny(); 86 | e2m.out[0] ==> bits[0].dblIn[0]; 87 | e2m.out[1] ==> bits[0].dblIn[1]; 88 | e2m.out[0] ==> bits[0].addIn[0]; 89 | e2m.out[1] ==> bits[0].addIn[1]; 90 | e[1] ==> bits[0].sel; 91 | 92 | for (i=1; i bits[i].dblIn[0]; 96 | bits[i-1].dblOut[1] ==> bits[i].dblIn[1]; 97 | bits[i-1].addOut[0] ==> bits[i].addIn[0]; 98 | bits[i-1].addOut[1] ==> bits[i].addIn[1]; 99 | e[i+1] ==> bits[i].sel; 100 | } 101 | 102 | bits[n-2].dblOut[0] ==> dbl[0]; 103 | bits[n-2].dblOut[1] ==> dbl[1]; 104 | 105 | component m2e = Montgomery2Edwards(); 106 | 107 | bits[n-2].addOut[0] ==> m2e.in[0]; 108 | bits[n-2].addOut[1] ==> m2e.in[1]; 109 | 110 | component eadder = BabyAdd(); 111 | 112 | m2e.out[0] ==> eadder.x1; 113 | m2e.out[1] ==> eadder.y1; 114 | -p[0] ==> eadder.x2; 115 | p[1] ==> eadder.y2; 116 | 117 | component lastSel = Multiplexor2(); 118 | 119 | e[0] ==> lastSel.sel; 120 | eadder.xout ==> lastSel.in[0][0]; 121 | eadder.yout ==> lastSel.in[0][1]; 122 | m2e.out[0] ==> lastSel.in[1][0]; 123 | m2e.out[1] ==> lastSel.in[1][1]; 124 | 125 | lastSel.out[0] ==> out[0]; 126 | lastSel.out[1] ==> out[1]; 127 | } 128 | 129 | // This function assumes that p is in the subgroup and it is different to 0 130 | 131 | template EscalarMulAny(n) { 132 | signal input e[n]; // Input in binary format 133 | signal input p[2]; // Point (Twisted format) 134 | signal output out[2]; // Point (Twisted format) 135 | 136 | var nsegments = (n-1)\148 +1; 137 | var nlastsegment = n - (nsegments-1)*148; 138 | 139 | component segments[nsegments]; 140 | component doublers[nsegments-1]; 141 | component m2e[nsegments-1]; 142 | component adders[nsegments-1]; 143 | component zeropoint = IsZero(); 144 | zeropoint.in <== p[0]; 145 | 146 | var s; 147 | var i; 148 | var nseg; 149 | 150 | for (s=0; s segments[s].e[i]; 158 | } 159 | 160 | if (s==0) { 161 | // force G8 point if input point is zero 162 | segments[s].p[0] <== p[0] + (5299619240641551281634865583518297030282874472190772894086521144482721001553 - p[0])*zeropoint.out; 163 | segments[s].p[1] <== p[1] + (16950150798460657717958625567821834550301663161624707787222815936182638968203 - p[1])*zeropoint.out; 164 | } else { 165 | doublers[s-1] = MontgomeryDouble(); 166 | m2e[s-1] = Montgomery2Edwards(); 167 | adders[s-1] = BabyAdd(); 168 | 169 | segments[s-1].dbl[0] ==> doublers[s-1].in[0]; 170 | segments[s-1].dbl[1] ==> doublers[s-1].in[1]; 171 | 172 | doublers[s-1].out[0] ==> m2e[s-1].in[0]; 173 | doublers[s-1].out[1] ==> m2e[s-1].in[1]; 174 | 175 | m2e[s-1].out[0] ==> segments[s].p[0]; 176 | m2e[s-1].out[1] ==> segments[s].p[1]; 177 | 178 | if (s==1) { 179 | segments[s-1].out[0] ==> adders[s-1].x1; 180 | segments[s-1].out[1] ==> adders[s-1].y1; 181 | } else { 182 | adders[s-2].xout ==> adders[s-1].x1; 183 | adders[s-2].yout ==> adders[s-1].y1; 184 | } 185 | segments[s].out[0] ==> adders[s-1].x2; 186 | segments[s].out[1] ==> adders[s-1].y2; 187 | } 188 | } 189 | 190 | if (nsegments == 1) { 191 | segments[0].out[0]*(1-zeropoint.out) ==> out[0]; 192 | segments[0].out[1]+(1-segments[0].out[1])*zeropoint.out ==> out[1]; 193 | } else { 194 | adders[nsegments-2].xout*(1-zeropoint.out) ==> out[0]; 195 | adders[nsegments-2].yout+(1-adders[nsegments-2].yout)*zeropoint.out ==> out[1]; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /circom/circomlib/escalarmulfix.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | include "mux3.circom"; 22 | include "montgomery.circom"; 23 | include "babyjub.circom"; 24 | 25 | /* 26 | Window of 3 elements, it calculates 27 | out = base + base*in[0] + 2*base*in[1] + 4*base*in[2] 28 | out4 = 4*base 29 | 30 | The result should be compensated. 31 | */ 32 | 33 | /* 34 | 35 | The scalar is s = a0 + a1*2^3 + a2*2^6 + ...... + a81*2^243 36 | First We calculate Q = B + 2^3*B + 2^6*B + ......... + 2^246*B 37 | 38 | Then we calculate S1 = 2*2^246*B + (1 + a0)*B + (2^3 + a1)*B + .....+ (2^243 + a81)*B 39 | 40 | And Finaly we compute the result: RES = SQ - Q 41 | 42 | As you can see the input of the adders cannot be equal nor zero, except for the last 43 | substraction that it's done in montgomery. 44 | 45 | A good way to see it is that the accumulator input of the adder >= 2^247*B and the other input 46 | is the output of the windows that it's going to be <= 2^246*B 47 | */ 48 | template WindowMulFix() { 49 | signal input in[3]; 50 | signal input base[2]; 51 | signal output out[2]; 52 | signal output out8[2]; // Returns 8*Base (To be linked) 53 | 54 | component mux = MultiMux3(2); 55 | 56 | mux.s[0] <== in[0]; 57 | mux.s[1] <== in[1]; 58 | mux.s[2] <== in[2]; 59 | 60 | component dbl2 = MontgomeryDouble(); 61 | component adr3 = MontgomeryAdd(); 62 | component adr4 = MontgomeryAdd(); 63 | component adr5 = MontgomeryAdd(); 64 | component adr6 = MontgomeryAdd(); 65 | component adr7 = MontgomeryAdd(); 66 | component adr8 = MontgomeryAdd(); 67 | 68 | // in[0] -> 1*BASE 69 | 70 | mux.c[0][0] <== base[0]; 71 | mux.c[1][0] <== base[1]; 72 | 73 | // in[1] -> 2*BASE 74 | dbl2.in[0] <== base[0]; 75 | dbl2.in[1] <== base[1]; 76 | mux.c[0][1] <== dbl2.out[0]; 77 | mux.c[1][1] <== dbl2.out[1]; 78 | 79 | // in[2] -> 3*BASE 80 | adr3.in1[0] <== base[0]; 81 | adr3.in1[1] <== base[1]; 82 | adr3.in2[0] <== dbl2.out[0]; 83 | adr3.in2[1] <== dbl2.out[1]; 84 | mux.c[0][2] <== adr3.out[0]; 85 | mux.c[1][2] <== adr3.out[1]; 86 | 87 | // in[3] -> 4*BASE 88 | adr4.in1[0] <== base[0]; 89 | adr4.in1[1] <== base[1]; 90 | adr4.in2[0] <== adr3.out[0]; 91 | adr4.in2[1] <== adr3.out[1]; 92 | mux.c[0][3] <== adr4.out[0]; 93 | mux.c[1][3] <== adr4.out[1]; 94 | 95 | // in[4] -> 5*BASE 96 | adr5.in1[0] <== base[0]; 97 | adr5.in1[1] <== base[1]; 98 | adr5.in2[0] <== adr4.out[0]; 99 | adr5.in2[1] <== adr4.out[1]; 100 | mux.c[0][4] <== adr5.out[0]; 101 | mux.c[1][4] <== adr5.out[1]; 102 | 103 | // in[5] -> 6*BASE 104 | adr6.in1[0] <== base[0]; 105 | adr6.in1[1] <== base[1]; 106 | adr6.in2[0] <== adr5.out[0]; 107 | adr6.in2[1] <== adr5.out[1]; 108 | mux.c[0][5] <== adr6.out[0]; 109 | mux.c[1][5] <== adr6.out[1]; 110 | 111 | // in[6] -> 7*BASE 112 | adr7.in1[0] <== base[0]; 113 | adr7.in1[1] <== base[1]; 114 | adr7.in2[0] <== adr6.out[0]; 115 | adr7.in2[1] <== adr6.out[1]; 116 | mux.c[0][6] <== adr7.out[0]; 117 | mux.c[1][6] <== adr7.out[1]; 118 | 119 | // in[7] -> 8*BASE 120 | adr8.in1[0] <== base[0]; 121 | adr8.in1[1] <== base[1]; 122 | adr8.in2[0] <== adr7.out[0]; 123 | adr8.in2[1] <== adr7.out[1]; 124 | mux.c[0][7] <== adr8.out[0]; 125 | mux.c[1][7] <== adr8.out[1]; 126 | 127 | out8[0] <== adr8.out[0]; 128 | out8[1] <== adr8.out[1]; 129 | 130 | out[0] <== mux.out[0]; 131 | out[1] <== mux.out[1]; 132 | } 133 | 134 | 135 | /* 136 | This component does a multiplication of a escalar times a fix base 137 | Signals: 138 | e: The scalar in bits 139 | base: the base point in edwards format 140 | out: The result 141 | dbl: Point in Edwards to be linked to the next segment. 142 | */ 143 | 144 | template SegmentMulFix(nWindows) { 145 | signal input e[nWindows*3]; 146 | signal input base[2]; 147 | signal output out[2]; 148 | signal output dbl[2]; 149 | 150 | var i; 151 | var j; 152 | 153 | // Convert the base to montgomery 154 | 155 | component e2m = Edwards2Montgomery(); 156 | e2m.in[0] <== base[0]; 157 | e2m.in[1] <== base[1]; 158 | 159 | component windows[nWindows]; 160 | component adders[nWindows]; 161 | component cadders[nWindows]; 162 | 163 | // In the last step we add an extra doubler so that numbers do not match. 164 | component dblLast = MontgomeryDouble(); 165 | 166 | for (i=0; i out[0]; 222 | cAdd.yout ==> out[1]; 223 | 224 | windows[nWindows-1].out8[0] ==> dbl[0]; 225 | windows[nWindows-1].out8[1] ==> dbl[1]; 226 | } 227 | 228 | 229 | /* 230 | This component multiplies a escalar times a fixed point BASE (twisted edwards format) 231 | Signals 232 | e: The escalar in binary format 233 | out: The output point in twisted edwards 234 | */ 235 | template EscalarMulFix(n, BASE) { 236 | signal input e[n]; // Input in binary format 237 | signal output out[2]; // Point (Twisted format) 238 | 239 | var nsegments = (n-1)\246 +1; // 249 probably would work. But I'm not sure and for security I keep 246 240 | var nlastsegment = n - (nsegments-1)*249; 241 | 242 | component segments[nsegments]; 243 | 244 | component m2e[nsegments-1]; 245 | component adders[nsegments-1]; 246 | 247 | var s; 248 | var i; 249 | var nseg; 250 | var nWindows; 251 | 252 | for (s=0; s m2e[s-1].in[0]; 275 | segments[s-1].dbl[1] ==> m2e[s-1].in[1]; 276 | 277 | m2e[s-1].out[0] ==> segments[s].base[0]; 278 | m2e[s-1].out[1] ==> segments[s].base[1]; 279 | 280 | if (s==1) { 281 | segments[s-1].out[0] ==> adders[s-1].x1; 282 | segments[s-1].out[1] ==> adders[s-1].y1; 283 | } else { 284 | adders[s-2].xout ==> adders[s-1].x1; 285 | adders[s-2].yout ==> adders[s-1].y1; 286 | } 287 | segments[s].out[0] ==> adders[s-1].x2; 288 | segments[s].out[1] ==> adders[s-1].y2; 289 | } 290 | } 291 | 292 | if (nsegments == 1) { 293 | segments[0].out[0] ==> out[0]; 294 | segments[0].out[1] ==> out[1]; 295 | } else { 296 | adders[nsegments-2].xout ==> out[0]; 297 | adders[nsegments-2].yout ==> out[1]; 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /circom/circomlib/montgomery.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | 20 | /* 21 | Source: https://en.wikipedia.org/wiki/Montgomery_curve 22 | 23 | 1 + y 1 + y 24 | [u, v] = [ ------- , ---------- ] 25 | 1 - y (1 - y)x 26 | 27 | */ 28 | pragma circom 2.1.9; 29 | 30 | template Edwards2Montgomery() { 31 | signal input in[2]; 32 | signal output out[2]; 33 | 34 | out[0] <-- (1 + in[1]) / (1 - in[1]); 35 | out[1] <-- out[0] / in[0]; 36 | 37 | 38 | out[0] * (1-in[1]) === (1 + in[1]); 39 | out[1] * in[0] === out[0]; 40 | } 41 | 42 | /* 43 | 44 | u u - 1 45 | [x, y] = [ ---, ------- ] 46 | v u + 1 47 | 48 | */ 49 | template Montgomery2Edwards() { 50 | signal input in[2]; 51 | signal output out[2]; 52 | 53 | out[0] <-- in[0] / in[1]; 54 | out[1] <-- (in[0] - 1) / (in[0] + 1); 55 | 56 | out[0] * in[1] === in[0]; 57 | out[1] * (in[0] + 1) === in[0] - 1; 58 | } 59 | 60 | 61 | /* 62 | x2 - x1 63 | lamda = --------- 64 | y2 - y1 65 | 66 | x3 + A + x1 + x2 67 | x3 = B * lamda^2 - A - x1 -x2 => lamda^2 = ------------------ 68 | B 69 | 70 | y3 = (2*x1 + x2 + A)*lamda - B*lamda^3 - y1 => 71 | 72 | 73 | => y3 = lamda * ( 2*x1 + x2 + A - x3 - A - x1 - x2) - y1 => 74 | 75 | => y3 = lamda * ( x1 - x3 ) - y1 76 | 77 | ---------- 78 | 79 | y2 - y1 80 | lamda = --------- 81 | x2 - x1 82 | 83 | x3 = B * lamda^2 - A - x1 -x2 84 | 85 | y3 = lamda * ( x1 - x3 ) - y1 86 | 87 | */ 88 | 89 | template MontgomeryAdd() { 90 | signal input in1[2]; 91 | signal input in2[2]; 92 | signal output out[2]; 93 | 94 | var a = 168700; 95 | var d = 168696; 96 | 97 | var A = (2 * (a + d)) / (a - d); 98 | var B = 4 / (a - d); 99 | 100 | signal lamda; 101 | 102 | lamda <-- (in2[1] - in1[1]) / (in2[0] - in1[0]); 103 | lamda * (in2[0] - in1[0]) === (in2[1] - in1[1]); 104 | 105 | out[0] <== B*lamda*lamda - A - in1[0] -in2[0]; 106 | out[1] <== lamda * (in1[0] - out[0]) - in1[1]; 107 | } 108 | 109 | /* 110 | 111 | x1_2 = x1*x1 112 | 113 | 3*x1_2 + 2*A*x1 + 1 114 | lamda = --------------------- 115 | 2*B*y1 116 | 117 | x3 = B * lamda^2 - A - x1 -x1 118 | 119 | y3 = lamda * ( x1 - x3 ) - y1 120 | 121 | */ 122 | template MontgomeryDouble() { 123 | signal input in[2]; 124 | signal output out[2]; 125 | 126 | var a = 168700; 127 | var d = 168696; 128 | 129 | var A = (2 * (a + d)) / (a - d); 130 | var B = 4 / (a - d); 131 | 132 | signal lamda; 133 | signal x1_2; 134 | 135 | x1_2 <== in[0] * in[0]; 136 | 137 | lamda <-- (3*x1_2 + 2*A*in[0] + 1 ) / (2*B*in[1]); 138 | lamda * (2*B*in[1]) === (3*x1_2 + 2*A*in[0] + 1 ); 139 | 140 | out[0] <== B*lamda*lamda - A - 2*in[0]; 141 | out[1] <== lamda * (in[0] - out[0]) - in[1]; 142 | } 143 | -------------------------------------------------------------------------------- /circom/circomlib/mux3.circom: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 0KIMS association. 3 | 4 | This file is part of circom (Zero Knowledge Circuit Compiler). 5 | 6 | circom is a free software: you can redistribute it and/or modify it 7 | under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | circom is distributed in the hope that it will be useful, but WITHOUT 12 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 13 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 14 | License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with circom. If not, see . 18 | */ 19 | pragma circom 2.1.9; 20 | 21 | template MultiMux3(n) { 22 | signal input c[n][8]; // Constants 23 | signal input s[3]; // Selector 24 | signal output out[n]; 25 | 26 | signal a210[n]; 27 | signal a21[n]; 28 | signal a20[n]; 29 | signal a2[n]; 30 | 31 | signal a10[n]; 32 | signal a1[n]; 33 | signal a0[n]; 34 | signal a[n]; 35 | 36 | // 4 constrains for the intermediary variables 37 | signal s10; 38 | s10 <== s[1] * s[0]; 39 | 40 | for (var i=0; i0) { 95 | ark[0].in[j] <== inputs[j-1]; 96 | } else { 97 | ark[0].in[j] <== initialState; 98 | } 99 | } 100 | 101 | for (var r = 0; r < nRoundsF\2-1; r++) { 102 | for (var j=0; j0) { 67 | ark[i].in[j] <== inputs[j-1]; 68 | } else { 69 | ark[i].in[j] <== 0; 70 | } 71 | } else { 72 | ark[i].in[j] <== mix[i-1].out[j]; 73 | } 74 | } 75 | 76 | if (i < nRoundsF/2 || i >= nRoundsP + nRoundsF/2) { 77 | k = i < nRoundsF/2 ? i : i - nRoundsP; 78 | mix[i] = Mix(t, M); 79 | for (var j=0; j Point userPublicKey) public userPublicKeys; 35 | 36 | /// @notice Mapping of registration hashes to registration status 37 | /// @dev Used to prevent duplicate registrations 38 | mapping(uint256 registrationHash => bool isRegistered) public isRegistered; 39 | 40 | /////////////////////////////////////////////////// 41 | /// Events /// 42 | /////////////////////////////////////////////////// 43 | 44 | /// @notice Emitted when a user is registered 45 | /// @param user Address of the user 46 | /// @param publicKey Public key of the user 47 | event Register(address indexed user, Point publicKey); 48 | 49 | /////////////////////////////////////////////////// 50 | /// Constructor /// 51 | /////////////////////////////////////////////////// 52 | 53 | /** 54 | * @notice Initializes the Registrar contract 55 | * @param registrationVerifier_ Address of the registration verifier contract 56 | */ 57 | constructor(address registrationVerifier_) { 58 | registrationVerifier = IRegistrationVerifier(registrationVerifier_); 59 | } 60 | 61 | /////////////////////////////////////////////////// 62 | /// External /// 63 | /////////////////////////////////////////////////// 64 | 65 | /** 66 | * @notice Registers a user with their public key 67 | * @param proof The zero-knowledge proof proving the validity of the registration 68 | * @dev This function: 69 | * 1. Verifies the sender matches the account in the proof 70 | * 2. Checks the chain ID matches 71 | * 3. Validates the registration hash 72 | * 4. Verifies the zero-knowledge proof 73 | * 5. Registers the user with their public key 74 | * 75 | * Requirements: 76 | * - Sender must match the account in the proof 77 | * - Chain ID must match 78 | * - Registration hash must be valid 79 | * - User must not be already registered 80 | * - Proof must be valid 81 | */ 82 | function register(RegisterProof calldata proof) external { 83 | // extract public inputs 84 | uint256[5] memory input = proof.publicSignals; 85 | 86 | address account = address(uint160(input[2])); 87 | 88 | // check if the sender matches the account in the proof 89 | if (msg.sender != account) { 90 | revert InvalidSender(); 91 | } 92 | 93 | // check if the chain ID matches 94 | if (block.chainid != input[3]) { 95 | revert InvalidChainId(); 96 | } 97 | 98 | // check if the registration hash is valid 99 | uint256 registrationHash = input[4]; 100 | if (registrationHash >= BabyJubJub.Q) { 101 | revert InvalidRegistrationHash(); 102 | } 103 | 104 | // check if the user is already registered 105 | if (isRegistered[registrationHash] && isUserRegistered(account)) { 106 | revert UserAlreadyRegistered(); 107 | } 108 | 109 | // Verify the proof 110 | _verifyProof(proof); 111 | 112 | _register(account, Point({x: input[0], y: input[1]}), registrationHash); 113 | } 114 | 115 | /** 116 | * @notice Checks if a user is registered 117 | * @param user The address of the user to check 118 | * @return bool True if the user is registered, false otherwise 119 | * @dev A user is considered registered if their public key is not the zero point (0,0) 120 | */ 121 | function isUserRegistered(address user) public view returns (bool) { 122 | return userPublicKeys[user].x != 0 && userPublicKeys[user].y != 0; 123 | } 124 | 125 | /** 126 | * @notice Gets the public key of a user 127 | * @param user The address of the user 128 | * @return publicKey The public key of the user as a uint256 array 129 | * @dev Returns the x and y coordinates of the user's public key 130 | */ 131 | function getUserPublicKey( 132 | address user 133 | ) public view returns (uint256[2] memory publicKey) { 134 | return [userPublicKeys[user].x, userPublicKeys[user].y]; 135 | } 136 | 137 | /////////////////////////////////////////////////// 138 | /// Internal /// 139 | /////////////////////////////////////////////////// 140 | 141 | /** 142 | * @notice Registers a user with their public key 143 | * @param user The address of the user 144 | * @param publicKey The public key of the user 145 | * @param registrationHash The registration hash 146 | * @dev This function: 147 | * 1. Sets the user's public key 148 | * 2. Marks the registration hash as used 149 | * 3. Emits a Register event 150 | */ 151 | function _register( 152 | address user, 153 | Point memory publicKey, 154 | uint256 registrationHash 155 | ) internal { 156 | userPublicKeys[user] = publicKey; 157 | isRegistered[registrationHash] = true; 158 | emit Register(user, publicKey); 159 | } 160 | 161 | /** 162 | * @notice Verifies a registration proof 163 | * @param proof_ The proof to verify 164 | * @dev This function: 165 | * 1. Extracts the proof points and public inputs 166 | * 2. Calls the verifier contract to verify the proof 167 | * 3. Reverts if the proof is invalid 168 | */ 169 | function _verifyProof(RegisterProof calldata proof_) internal view { 170 | uint256[2] memory pointA_ = proof_.proofPoints.a; 171 | uint256[2][2] memory pointB_ = proof_.proofPoints.b; 172 | uint256[2] memory pointC_ = proof_.proofPoints.c; 173 | uint256[5] memory input = proof_.publicSignals; 174 | 175 | // Verify the proof 176 | bool verified_ = registrationVerifier.verifyProof( 177 | pointA_, 178 | pointB_, 179 | pointC_, 180 | input 181 | ); 182 | 183 | if (!verified_) { 184 | revert InvalidProof(); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /contracts/auditor/AuditorManager.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | pragma solidity 0.8.27; 6 | 7 | import {Point} from "../types/Types.sol"; 8 | import {ZeroAddress} from "../errors/Errors.sol"; 9 | 10 | /** 11 | * @title AuditorManager 12 | * @notice Abstract contract that manages auditor-related functionality for encrypted ERC operations 13 | * @dev This contract is responsible for: 14 | * 1. Storing and managing the auditor's address and public key 15 | * 2. Providing access control for auditor-related operations 16 | * 3. Emitting events when auditor information changes 17 | * 18 | * The auditor is a crucial component in the encrypted ERC system that: 19 | * - Ensures compliance with regulatory requirements 20 | * - Provides oversight for private operations 21 | */ 22 | abstract contract AuditorManager { 23 | /////////////////////////////////////////////////// 24 | /// State Variables /// 25 | /////////////////////////////////////////////////// 26 | 27 | /// @notice The address of the current auditor 28 | /// @dev This address is used to identify the auditor and for access control 29 | address public auditor = address(0); 30 | 31 | /// @notice The public key of the current auditor 32 | /// @dev This is used in zero-knowledge proofs to validate auditor signatures 33 | /// The point (0,1) is considered invalid as it's the identity point in the elliptic curve 34 | Point public auditorPublicKey = Point({x: 0, y: 0}); 35 | 36 | /////////////////////////////////////////////////// 37 | /// Events /// 38 | /////////////////////////////////////////////////// 39 | 40 | /** 41 | * @notice Emitted when the auditor's information is updated 42 | * @param oldAuditor The previous auditor's address 43 | * @param newAuditor The new auditor's address 44 | */ 45 | event AuditorChanged( 46 | address indexed oldAuditor, 47 | address indexed newAuditor 48 | ); 49 | 50 | /////////////////////////////////////////////////// 51 | /// Modifiers /// 52 | /////////////////////////////////////////////////// 53 | 54 | /** 55 | * @notice Ensures that an auditor is properly 56 | * @dev This modifier checks two conditions: 57 | * 1. The auditor's public key is valid (not the identity point) 58 | * 2. The auditor's address is not the zero address 59 | * 60 | * Requirements: 61 | * - Auditor public key must be set (not the identity point) 62 | * - Auditor address must be set (not zero address) 63 | */ 64 | modifier onlyIfAuditorSet() { 65 | require( 66 | auditorPublicKey.x != 0 && auditorPublicKey.y != 1, 67 | "Auditor public key not set" 68 | ); 69 | require(auditor != address(0), "Auditor not set"); 70 | _; 71 | } 72 | 73 | /////////////////////////////////////////////////// 74 | /// External /// 75 | /////////////////////////////////////////////////// 76 | 77 | /** 78 | * @notice Checks if the auditor's public key is properly set 79 | * @return bool True if the auditor's public key is set and valid 80 | * @dev This function is used to verify if the contract is ready for 81 | * operations that require auditor validation 82 | */ 83 | function isAuditorKeySet() external view returns (bool) { 84 | return auditorPublicKey.x != 0 && auditorPublicKey.y != 1; 85 | } 86 | 87 | /////////////////////////////////////////////////// 88 | /// Internal /// 89 | /////////////////////////////////////////////////// 90 | 91 | /** 92 | * @notice Updates the auditor's information 93 | * @param newAuditor The address of the new auditor 94 | * @param publicKey The public key of the new auditor 95 | * @dev This function: 96 | * 1. Validates the new auditor's address 97 | * 2. Updates the auditor's information 98 | * 3. Emits an event to track the change 99 | * 100 | * Requirements: 101 | * - newAuditor must not be the zero address 102 | * - publicKey must be a valid point on the elliptic curve 103 | */ 104 | function _updateAuditor( 105 | address newAuditor, 106 | uint256[2] memory publicKey 107 | ) internal { 108 | address oldAuditor = auditor; 109 | // check if the auditor is the zero address 110 | if (newAuditor == address(0)) { 111 | revert ZeroAddress(); 112 | } 113 | 114 | auditor = newAuditor; 115 | auditorPublicKey = Point({x: publicKey[0], y: publicKey[1]}); 116 | 117 | emit AuditorChanged(oldAuditor, newAuditor); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /contracts/errors/Errors.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | error UserAlreadyRegistered(); 9 | error UserNotRegistered(); 10 | error UnauthorizedAccess(); 11 | error AuditorKeyNotSet(); 12 | error InvalidProof(); 13 | error InvalidOperation(); 14 | error TransferFailed(); 15 | error UnknownToken(); 16 | error InvalidChainId(); 17 | error InvalidNullifier(); 18 | error InvalidSender(); 19 | error InvalidRegistrationHash(); 20 | error ZeroAddress(); 21 | error TokenBlacklisted(address token); 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IEncryptedERC.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface IEncryptedERC { 9 | /** 10 | * @notice Sets the balance percentage for a user and token. 11 | * @param user User address 12 | * @param tokenId Token ID 13 | * @param pct Balance percentage array 14 | * @dev Only the registrar can set the balance percentage 15 | */ 16 | function setUserBalancePCT( 17 | address user, 18 | uint256 tokenId, 19 | uint256[7] memory pct 20 | ) external; 21 | } 22 | -------------------------------------------------------------------------------- /contracts/interfaces/IRegistrar.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface IRegistrar { 9 | /** 10 | * @dev Returns the public key of a user. 11 | * @param user Address of the user. 12 | * @return publicKey The public key of the user as an array of two uint256 values. 13 | */ 14 | function getUserPublicKey( 15 | address user 16 | ) external view returns (uint256[2] memory publicKey); 17 | 18 | /** 19 | * @dev Returns true if the user is registered. 20 | * @param user Address of the user. 21 | * @return isRegistered True if the user is registered, false otherwise. 22 | */ 23 | function isUserRegistered(address user) external view returns (bool); 24 | } 25 | -------------------------------------------------------------------------------- /contracts/interfaces/verifiers/IBurnVerifier.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface IBurnVerifier { 9 | function verifyProof( 10 | uint256[2] memory pointA_, 11 | uint256[2][2] memory pointB_, 12 | uint256[2] memory pointC_, 13 | uint256[19] memory publicSignals_ 14 | ) external view returns (bool verified_); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/verifiers/IMintVerifier.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface IMintVerifier { 9 | function verifyProof( 10 | uint256[2] memory pointA_, 11 | uint256[2][2] memory pointB_, 12 | uint256[2] memory pointC_, 13 | uint256[24] memory publicSignals_ 14 | ) external view returns (bool verified_); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/verifiers/IRegistrationVerifier.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface IRegistrationVerifier { 9 | function verifyProof( 10 | uint256[2] memory pointA_, 11 | uint256[2][2] memory pointB_, 12 | uint256[2] memory pointC_, 13 | uint256[5] memory publicSignals_ 14 | ) external view returns (bool verified_); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/verifiers/ITransferVerifier.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface ITransferVerifier { 9 | function verifyProof( 10 | uint256[2] memory pointA_, 11 | uint256[2][2] memory pointB_, 12 | uint256[2] memory pointC_, 13 | uint256[32] memory publicSignals_ 14 | ) external view returns (bool verified_); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/interfaces/verifiers/IWithdrawVerifier.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | interface IWithdrawVerifier { 9 | function verifyProof( 10 | uint256[2] memory pointA_, 11 | uint256[2][2] memory pointB_, 12 | uint256[2] memory pointC_, 13 | uint256[16] memory publicSignals_ 14 | ) external view returns (bool verified_); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/libraries/BabyJubJub.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | // Structs 9 | import {Point, EGCT} from "../types/Types.sol"; 10 | 11 | /** 12 | * @dev BabyJubJub curve operations 13 | */ 14 | library BabyJubJub { 15 | // Curve parameters 16 | // E: A^2 + y^2 = 1 + Dx^2y^2 (mod Q) 17 | uint256 internal constant A = 168700; 18 | uint256 internal constant D = 168696; 19 | uint256 public constant Q = 20 | 21888242871839275222246405745257275088548364400416034343698204186575808495617; 21 | uint256 internal constant H = 22 | 10944121435919637611123202872628637544274182200208017171849102093287904247808; 23 | uint256 internal constant R = 24 | 2736030358979909402780800718157159386076813972158567259200215660948447373041; 25 | 26 | /** 27 | * @dev Subtract a BabyJubJub point from another BabyJubJub point 28 | * @param _point1 the point which will be subtracted from 29 | * @param _point2 point to subtract 30 | * @return result 31 | */ 32 | function _sub( 33 | Point memory _point1, 34 | Point memory _point2 35 | ) public view returns (Point memory) { 36 | return _add(_point1, negate(_point2)); 37 | } 38 | 39 | /** 40 | * @dev Add 2 points on BabyJubJub curve 41 | * Formulae for adding 2 points on a twisted Edwards curve: 42 | * x3 = (x1y2 + y1x2) / (1 + dx1x2y1y2) 43 | * y3 = (y1y2 - ax1x2) / (1 - dx1x2y1y2) 44 | * @param _point1 first point 45 | * @param _point2 second point 46 | * @return resulting point 47 | */ 48 | function _add( 49 | Point memory _point1, 50 | Point memory _point2 51 | ) public view returns (Point memory) { 52 | uint256 x1x2 = mulmod(_point1.x, _point2.x, Q); 53 | uint256 y1y2 = mulmod(_point1.y, _point2.y, Q); 54 | 55 | uint256 dx1x2y1y2 = mulmod(D, mulmod(x1x2, y1y2, Q), Q); 56 | 57 | uint256 x3Num = addmod( 58 | mulmod(_point1.x, _point2.y, Q), 59 | mulmod(_point1.y, _point2.x, Q), 60 | Q 61 | ); 62 | uint256 y3Num = submod(y1y2, mulmod(A, x1x2, Q)); 63 | 64 | return 65 | Point({ 66 | x: mulmod(x3Num, invmod(addmod(1, dx1x2y1y2, Q)), Q), 67 | y: mulmod(y3Num, invmod(submod(1, dx1x2y1y2)), Q) 68 | }); 69 | } 70 | 71 | /** 72 | * @dev Multiply a BabyJubJub point by a scalar 73 | * Use the double and add algorithm 74 | * @param _point point be multiplied by a scalar 75 | * @param _scalar scalar value 76 | * @return resulting point 77 | */ 78 | function scalarMultiply( 79 | Point memory _point, 80 | uint256 _scalar 81 | ) public view returns (Point memory) { 82 | // Initial scalar remainder 83 | uint256 remaining = _scalar % R; 84 | 85 | // Copy initial point so that we don't mutate it 86 | Point memory initial = _point; 87 | 88 | // Initialize result 89 | Point memory result = Point({x: 0, y: 1}); 90 | 91 | // Loop while remainder is greater than 0 92 | while (remaining != 0) { 93 | // If the right-most binary digit is 1 (number is odd) add initial point to result 94 | if ((remaining & 1) != 0) { 95 | result = _add(result, initial); 96 | } 97 | 98 | // Double initial point 99 | initial = double(initial); 100 | 101 | // Shift bits to the right 102 | remaining = remaining >> 1; 103 | } 104 | 105 | return result; 106 | } 107 | 108 | /** 109 | * 110 | * @param _publicKey Public Key that will be used in encryption 111 | * @param _msg Message in scalar form to be encrypted 112 | */ 113 | function elGamalEncryption( 114 | Point memory _publicKey, 115 | uint256 _msg 116 | ) public view returns (EGCT memory) { 117 | uint256 random = 1; 118 | Point memory b8 = base8(); 119 | 120 | Point memory c1 = scalarMultiply(b8, random); 121 | Point memory pkr = scalarMultiply(_publicKey, random); 122 | Point memory pMsg = scalarMultiply(b8, _msg); 123 | 124 | Point memory c2 = _add(pkr, pMsg); 125 | 126 | return EGCT({c1: c1, c2: c2}); 127 | } 128 | 129 | // elgamal encryption with a given message 130 | function encrypt( 131 | Point memory _publicKey, 132 | uint256 _msg 133 | ) public view returns (EGCT memory) { 134 | return elGamalEncryption(_publicKey, _msg); 135 | } 136 | 137 | /** 138 | * @dev Default generator 139 | */ 140 | function base8() public pure returns (Point memory) { 141 | return 142 | Point({ 143 | x: 5299619240641551281634865583518297030282874472190772894086521144482721001553, 144 | y: 16950150798460657717958625567821834550301663161624707787222815936182638968203 145 | }); 146 | } 147 | 148 | /** 149 | * @dev Double a point on BabyJubJub curve 150 | * @param _p point to double 151 | * @return doubled point 152 | */ 153 | function double(Point memory _p) internal view returns (Point memory) { 154 | return _add(_p, _p); 155 | } 156 | 157 | /** 158 | * @dev Compute modular inverse of a number 159 | * @param _a the value to be inverted in mod Q 160 | */ 161 | function invmod(uint256 _a) internal view returns (uint256) { 162 | // We can use Euler's theorem instead of the extended Euclidean algorithm 163 | // Since m = Q and Q is prime we have: a^-1 = a^(m - 2) (mod m) 164 | return 165 | expmod( 166 | _a, 167 | 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff 168 | ); 169 | } 170 | 171 | /** 172 | * @dev Exponentiation modulo Q 173 | * @param _base the base of the exponentiation 174 | * @param _exponent the exponent 175 | * @return result 176 | */ 177 | function expmod( 178 | uint256 _base, 179 | uint256 _exponent 180 | ) internal view returns (uint256) { 181 | uint256 result; 182 | 183 | // solhint-disable-next-line no-inline-assembly 184 | assembly { 185 | let 186 | localQ 187 | := 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001 188 | let memPtr := mload(0x40) 189 | mstore(memPtr, 0x20) // Length of base _b 190 | mstore(add(memPtr, 0x20), 0x20) // Length of exponent _e 191 | mstore(add(memPtr, 0x40), 0x20) // Length of modulus Q 192 | mstore(add(memPtr, 0x60), _base) // Base _b 193 | mstore(add(memPtr, 0x80), _exponent) // Exponent _e 194 | mstore(add(memPtr, 0xa0), localQ) // Modulus Q 195 | 196 | // The bigModExp precompile is at 0x05 197 | let success := staticcall(gas(), 0x05, memPtr, 0xc0, memPtr, 0x20) 198 | switch success 199 | case 0 { 200 | revert(0x0, 0x0) 201 | } 202 | default { 203 | result := mload(memPtr) 204 | } 205 | } 206 | 207 | return result; 208 | } 209 | 210 | /** 211 | * @dev Negate a BabyJubJub point 212 | * @param _point point to negate 213 | * @return p = -(_p) 214 | */ 215 | function negate(Point memory _point) internal pure returns (Point memory) { 216 | return Point({x: Q - _point.x, y: _point.y}); 217 | } 218 | 219 | /** 220 | * @dev Modular subtract (mod n). 221 | * @param _a The first number 222 | * @param _b The number to be subtracted 223 | * @return result 224 | */ 225 | function submod(uint256 _a, uint256 _b) internal pure returns (uint256) { 226 | return addmod(_a, Q - _b, Q); 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /contracts/prod/RegistrationVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | /* 3 | Copyright 2021 0KIMS association. 4 | 5 | This file is generated with [snarkJS](https://github.com/iden3/snarkjs). 6 | 7 | snarkJS is a free software: you can redistribute it and/or modify it 8 | under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | snarkJS is distributed in the hope that it will be useful, but WITHOUT 13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 15 | License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with snarkJS. If not, see . 19 | */ 20 | 21 | pragma solidity >=0.7.0 <0.9.0; 22 | 23 | contract RegistrationVerifier { 24 | // Scalar field size 25 | uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 26 | // Base field size 27 | uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; 28 | 29 | // Verification Key data 30 | uint256 constant alphax = 20491192805390485299153009773594534940189261866228447918068658471970481763042; 31 | uint256 constant alphay = 9383485363053290200918347156157836566562967994039712273449902621266178545958; 32 | uint256 constant betax1 = 4252822878758300859123897981450591353533073413197771768651442665752259397132; 33 | uint256 constant betax2 = 6375614351688725206403948262868962793625744043794305715222011528459656738731; 34 | uint256 constant betay1 = 21847035105528745403288232691147584728191162732299865338377159692350059136679; 35 | uint256 constant betay2 = 10505242626370262277552901082094356697409835680220590971873171140371331206856; 36 | uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; 37 | uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; 38 | uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; 39 | uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; 40 | uint256 constant deltax1 = 19753714125923452630385433581442722093941065264298048276246815338797167448169; 41 | uint256 constant deltax2 = 2824093012045694268556486522753590084359236940213189502690301706344424715581; 42 | uint256 constant deltay1 = 18067530836866733450125362089378459710977233400131368676354166923678178030850; 43 | uint256 constant deltay2 = 12986835757414062294165220819491853575831232959693796161862995405626568533639; 44 | 45 | 46 | uint256 constant IC0x = 5049073754175979375715331231494813434614104647476713784192278874636518287456; 47 | uint256 constant IC0y = 13973039660095243213304729343482928815131903560133937124822026621516335683252; 48 | 49 | uint256 constant IC1x = 6960760860997719127050640389023071189120419160885047633543132031266205842967; 50 | uint256 constant IC1y = 14311210142759362992805316832152765774097518712372467130307901891389375007789; 51 | 52 | uint256 constant IC2x = 14970325264892984291437720194401230916657388050759523602370378142660744831477; 53 | uint256 constant IC2y = 15860538555168123807647719982845297214031403618163443664157964964439662885432; 54 | 55 | uint256 constant IC3x = 9095633778879314058949553908878900274830732566924382454865365752356228820208; 56 | uint256 constant IC3y = 872555237120135122336016443589602918886936394020805627011933688108936774726; 57 | 58 | uint256 constant IC4x = 16383214040587918206822141493152567743771449527138865305823970747014581995205; 59 | uint256 constant IC4y = 15807972914519003321398888297328267389104135252699104466520087670194802499103; 60 | 61 | uint256 constant IC5x = 19865793340402973866436804410911022538640660662316164831277333170166538152800; 62 | uint256 constant IC5y = 1466380730966029920868782474285618825793197367831481952454164420798011324299; 63 | 64 | 65 | // Memory data 66 | uint16 constant pVk = 0; 67 | uint16 constant pPairing = 128; 68 | 69 | uint16 constant pLastMem = 896; 70 | 71 | function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[5] calldata _pubSignals) public view returns (bool) { 72 | assembly { 73 | function checkField(v) { 74 | if iszero(lt(v, r)) { 75 | mstore(0, 0) 76 | return(0, 0x20) 77 | } 78 | } 79 | 80 | // G1 function to multiply a G1 value(x,y) to value in an address 81 | function g1_mulAccC(pR, x, y, s) { 82 | let success 83 | let mIn := mload(0x40) 84 | mstore(mIn, x) 85 | mstore(add(mIn, 32), y) 86 | mstore(add(mIn, 64), s) 87 | 88 | success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) 89 | 90 | if iszero(success) { 91 | mstore(0, 0) 92 | return(0, 0x20) 93 | } 94 | 95 | mstore(add(mIn, 64), mload(pR)) 96 | mstore(add(mIn, 96), mload(add(pR, 32))) 97 | 98 | success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) 99 | 100 | if iszero(success) { 101 | mstore(0, 0) 102 | return(0, 0x20) 103 | } 104 | } 105 | 106 | function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk { 107 | let _pPairing := add(pMem, pPairing) 108 | let _pVk := add(pMem, pVk) 109 | 110 | mstore(_pVk, IC0x) 111 | mstore(add(_pVk, 32), IC0y) 112 | 113 | // Compute the linear combination vk_x 114 | 115 | g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0))) 116 | 117 | g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32))) 118 | 119 | g1_mulAccC(_pVk, IC3x, IC3y, calldataload(add(pubSignals, 64))) 120 | 121 | g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96))) 122 | 123 | g1_mulAccC(_pVk, IC5x, IC5y, calldataload(add(pubSignals, 128))) 124 | 125 | 126 | // -A 127 | mstore(_pPairing, calldataload(pA)) 128 | mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q)) 129 | 130 | // B 131 | mstore(add(_pPairing, 64), calldataload(pB)) 132 | mstore(add(_pPairing, 96), calldataload(add(pB, 32))) 133 | mstore(add(_pPairing, 128), calldataload(add(pB, 64))) 134 | mstore(add(_pPairing, 160), calldataload(add(pB, 96))) 135 | 136 | // alpha1 137 | mstore(add(_pPairing, 192), alphax) 138 | mstore(add(_pPairing, 224), alphay) 139 | 140 | // beta2 141 | mstore(add(_pPairing, 256), betax1) 142 | mstore(add(_pPairing, 288), betax2) 143 | mstore(add(_pPairing, 320), betay1) 144 | mstore(add(_pPairing, 352), betay2) 145 | 146 | // vk_x 147 | mstore(add(_pPairing, 384), mload(add(pMem, pVk))) 148 | mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32)))) 149 | 150 | 151 | // gamma2 152 | mstore(add(_pPairing, 448), gammax1) 153 | mstore(add(_pPairing, 480), gammax2) 154 | mstore(add(_pPairing, 512), gammay1) 155 | mstore(add(_pPairing, 544), gammay2) 156 | 157 | // C 158 | mstore(add(_pPairing, 576), calldataload(pC)) 159 | mstore(add(_pPairing, 608), calldataload(add(pC, 32))) 160 | 161 | // delta2 162 | mstore(add(_pPairing, 640), deltax1) 163 | mstore(add(_pPairing, 672), deltax2) 164 | mstore(add(_pPairing, 704), deltay1) 165 | mstore(add(_pPairing, 736), deltay2) 166 | 167 | 168 | let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20) 169 | 170 | isOk := and(success, mload(_pPairing)) 171 | } 172 | 173 | let pMem := mload(0x40) 174 | mstore(0x40, add(pMem, pLastMem)) 175 | 176 | // Validate that all evaluations ∈ F 177 | 178 | checkField(calldataload(add(_pubSignals, 0))) 179 | 180 | checkField(calldataload(add(_pubSignals, 32))) 181 | 182 | checkField(calldataload(add(_pubSignals, 64))) 183 | 184 | checkField(calldataload(add(_pubSignals, 96))) 185 | 186 | checkField(calldataload(add(_pubSignals, 128))) 187 | 188 | 189 | // Validate all evaluations 190 | let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem) 191 | 192 | mstore(0, isValid) 193 | return(0, 0x20) 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /contracts/tokens/FeeERC20.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | import {SimpleERC20} from "./SimpleERC20.sol"; 9 | 10 | /** 11 | * @title FeeERC20 12 | * @dev ERC20 token with a fee mechanism for testing 13 | */ 14 | contract FeeERC20 is SimpleERC20 { 15 | uint256 public feeRate; 16 | address public feeCollector; 17 | 18 | constructor( 19 | string memory name, 20 | string memory symbol, 21 | uint8 decimal, 22 | uint256 feeRates, 23 | address feeCollectors 24 | ) SimpleERC20(name, symbol, decimal) { 25 | feeRate = feeRates; 26 | feeCollector = feeCollectors; 27 | } 28 | 29 | /** 30 | * @dev Override transferFrom to apply a fee 31 | * @param sender The address to transfer from 32 | * @param recipient The address to transfer to 33 | * @param amount The amount to transfer 34 | * @return A boolean that indicates if the operation was successful 35 | */ 36 | function transferFrom( 37 | address sender, 38 | address recipient, 39 | uint256 amount 40 | ) public virtual override returns (bool) { 41 | address spender = _msgSender(); 42 | 43 | // Calculate fee 44 | uint256 fee = (amount * feeRate) / 100; 45 | uint256 amountAfterFee = amount - fee; 46 | 47 | // Deduct allowance 48 | _spendAllowance(sender, spender, amount); 49 | 50 | // Transfer amount after fee to recipient 51 | _transfer(sender, recipient, amountAfterFee); 52 | 53 | // Transfer fee to fee collector 54 | if (fee > 0) { 55 | _transfer(sender, feeCollector, fee); 56 | } 57 | 58 | return true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/tokens/SimpleERC20.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 9 | 10 | contract SimpleERC20 is ERC20 { 11 | // token decimals 12 | uint8 public decimals_; 13 | 14 | constructor( 15 | string memory name, 16 | string memory symbol, 17 | uint8 decimal 18 | ) ERC20(name, symbol) { 19 | decimals_ = decimal; 20 | } 21 | 22 | function mint(address to, uint256 amount) public { 23 | _mint(to, amount); 24 | } 25 | 26 | function decimals() public view virtual override returns (uint8) { 27 | return decimals_; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/tokens/TokenTracker.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol"; 9 | import {TokenBlacklisted, InvalidOperation} from "../errors/Errors.sol"; 10 | 11 | /** 12 | * @title TokenTracker 13 | * @notice Contract for tracking ERC20 tokens in the encrypted ERC system 14 | * @dev This contract manages: 15 | * 1. Token registration and identification 16 | * 2. Token blacklisting for security 17 | * 3. Contract Mode (converter vs standalone) 18 | * 19 | * The contract can operate in two modes: 20 | * - Converter Mode: Wraps existing ERC20 tokens into encrypted tokens 21 | * - Standalone Mode: Operates as a standalone encrypted token 22 | */ 23 | contract TokenTracker is Ownable2Step { 24 | /////////////////////////////////////////////////// 25 | /// State Variables /// 26 | /////////////////////////////////////////////////// 27 | 28 | /// @notice The next available token ID 29 | /// @dev Token IDs start from 1, with 0 reserved for the standalone version 30 | uint256 public nextTokenId = 1; 31 | 32 | /// @notice Indicates if the contract is operating in converter mode 33 | bool public isConverter; 34 | 35 | /// @notice Mapping from token address to token ID 36 | mapping(address tokenAddress => uint256 tokenId) public tokenIds; 37 | 38 | /// @notice Mapping from token ID to token address 39 | mapping(uint256 tokenId => address tokenAddress) public tokenAddresses; 40 | 41 | /// @notice Array of all registered token addresses 42 | address[] public tokens; 43 | 44 | /// @notice Mapping to track blacklisted tokens 45 | mapping(address tokenAddress => bool isBlacklisted) 46 | public blacklistedTokens; 47 | 48 | /////////////////////////////////////////////////// 49 | /// Modifiers /// 50 | /////////////////////////////////////////////////// 51 | 52 | /** 53 | * @notice Ensures the function is only called in converter mode 54 | * @dev Reverts with InvalidOperation if called in standalone mode 55 | */ 56 | modifier onlyForConverter() { 57 | if (!isConverter) { 58 | revert InvalidOperation(); 59 | } 60 | _; 61 | } 62 | 63 | /** 64 | * @notice Ensures the function is only called in standalone mode 65 | * @dev Reverts with InvalidOperation if called in converter mode 66 | */ 67 | modifier onlyForStandalone() { 68 | if (isConverter) { 69 | revert InvalidOperation(); 70 | } 71 | _; 72 | } 73 | 74 | /** 75 | * @notice Ensures the token is not blacklisted 76 | * @param tokenAddress Address of the token to check 77 | * @dev Reverts with TokenBlacklisted if the token is blacklisted 78 | */ 79 | modifier revertIfBlacklisted(address tokenAddress) { 80 | if (blacklistedTokens[tokenAddress]) { 81 | revert TokenBlacklisted(tokenAddress); 82 | } 83 | _; 84 | } 85 | 86 | /////////////////////////////////////////////////// 87 | /// Constructor /// 88 | /////////////////////////////////////////////////// 89 | 90 | /** 91 | * @notice Initializes the TokenTracker contract 92 | * @param isConverter_ Determines if the contract operates in converter mode 93 | * @dev Sets the initial mode of operation and initializes the owner 94 | */ 95 | constructor(bool isConverter_) Ownable(msg.sender) { 96 | isConverter = isConverter_; 97 | } 98 | 99 | /////////////////////////////////////////////////// 100 | /// External /// 101 | /////////////////////////////////////////////////// 102 | 103 | /** 104 | * @notice Sets the blacklist status of a token 105 | * @param token Address of the token to blacklist/unblacklist 106 | * @param blacklisted True to blacklist, false to unblacklist 107 | * @dev Only the owner can call this function 108 | */ 109 | function setTokenBlacklist( 110 | address token, 111 | bool blacklisted 112 | ) external onlyOwner { 113 | blacklistedTokens[token] = blacklisted; 114 | } 115 | 116 | /** 117 | * @notice Returns an array of all registered token addresses 118 | * @return Array of token addresses 119 | * @dev Used for enumeration and listing all supported tokens 120 | */ 121 | function getTokens() external view returns (address[] memory) { 122 | return tokens; 123 | } 124 | 125 | /////////////////////////////////////////////////// 126 | /// Internal /// 127 | /////////////////////////////////////////////////// 128 | 129 | /** 130 | * @notice Adds a new token to the tracker 131 | * @param tokenAddress Address of the token to add 132 | * @dev This function: 133 | * 1. Assigns a new token ID 134 | * 2. Updates the token mappings 135 | * 3. Adds the token to the tokens array 136 | * 4. Increments the next token ID 137 | */ 138 | function _addToken(address tokenAddress) internal { 139 | uint256 newTokenId = nextTokenId; 140 | tokenIds[tokenAddress] = newTokenId; 141 | tokenAddresses[newTokenId] = tokenAddress; 142 | tokens.push(tokenAddress); 143 | nextTokenId++; 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /contracts/types/Types.sol: -------------------------------------------------------------------------------- 1 | // (c) 2025, Ava Labs, Inc. All rights reserved. 2 | // See the file LICENSE for licensing terms. 3 | 4 | // SPDX-License-Identifier: Ecosystem 5 | 6 | pragma solidity 0.8.27; 7 | 8 | struct Point { 9 | uint256 x; 10 | uint256 y; 11 | } 12 | 13 | struct CreateEncryptedERCParams { 14 | // registrar contract address for fetching users public key 15 | address registrar; 16 | // eERC is converter mode or not 17 | bool isConverter; 18 | // eERC Token 19 | string name; 20 | string symbol; 21 | uint8 decimals; 22 | // verifiers 23 | address mintVerifier; 24 | address withdrawVerifier; 25 | address transferVerifier; 26 | address burnVerifier; 27 | } 28 | 29 | struct AmountPCT { 30 | uint256[7] pct; 31 | uint256 index; 32 | } 33 | 34 | struct EncryptedBalance { 35 | EGCT eGCT; 36 | mapping(uint256 index => BalanceHistory history) balanceList; 37 | uint256 nonce; 38 | uint256 transactionIndex; 39 | uint256[7] balancePCT; // user balance pcts 40 | AmountPCT[] amountPCTs; // user amount pcts 41 | } 42 | 43 | struct BalanceHistory { 44 | uint256 index; 45 | bool isValid; 46 | } 47 | 48 | struct EGCT { 49 | Point c1; 50 | Point c2; 51 | } 52 | 53 | /// @dev The proof base is used to verify the proof 54 | struct ProofPoints { 55 | uint256[2] a; 56 | uint256[2][2] b; 57 | uint256[2] c; 58 | } 59 | 60 | struct RegisterProof { 61 | ProofPoints proofPoints; 62 | uint256[5] publicSignals; 63 | } 64 | 65 | struct MintProof { 66 | ProofPoints proofPoints; 67 | uint256[24] publicSignals; 68 | } 69 | 70 | struct TransferProof { 71 | ProofPoints proofPoints; 72 | uint256[32] publicSignals; 73 | } 74 | 75 | struct BurnProof { 76 | ProofPoints proofPoints; 77 | uint256[19] publicSignals; 78 | } 79 | 80 | struct WithdrawProof { 81 | ProofPoints proofPoints; 82 | uint256[16] publicSignals; 83 | } 84 | 85 | struct TransferInputs { 86 | EGCT providedBalance; 87 | EGCT senderEncryptedAmount; 88 | EGCT receiverEncryptedAmount; 89 | uint256[7] amountPCT; 90 | } 91 | -------------------------------------------------------------------------------- /contracts/verifiers/RegistrationCircuitGroth16Verifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | /* AUTOGENERATED FILE BY HARDHAT-ZKIT. DO NOT EDIT. */ 4 | 5 | pragma solidity >=0.7.0 <0.9.0; 6 | 7 | contract RegistrationCircuitGroth16Verifier { 8 | // @dev scalar field size 9 | uint256 public constant SCALAR_FIELD_SIZE = 10 | 21888242871839275222246405745257275088548364400416034343698204186575808495617; 11 | /// @dev base field size 12 | uint256 public constant BASE_FIELD_SIZE = 13 | 21888242871839275222246405745257275088696311157297823662689037894645226208583; 14 | 15 | /// @dev verification key data 16 | uint256 public constant ALPHA_X = 17 | 20491192805390485299153009773594534940189261866228447918068658471970481763042; 18 | uint256 public constant ALPHA_Y = 19 | 9383485363053290200918347156157836566562967994039712273449902621266178545958; 20 | uint256 public constant BETA_X1 = 21 | 4252822878758300859123897981450591353533073413197771768651442665752259397132; 22 | uint256 public constant BETA_X2 = 23 | 6375614351688725206403948262868962793625744043794305715222011528459656738731; 24 | uint256 public constant BETA_Y1 = 25 | 21847035105528745403288232691147584728191162732299865338377159692350059136679; 26 | uint256 public constant BETA_Y2 = 27 | 10505242626370262277552901082094356697409835680220590971873171140371331206856; 28 | uint256 public constant GAMMA_X1 = 29 | 11559732032986387107991004021392285783925812861821192530917403151452391805634; 30 | uint256 public constant GAMMA_X2 = 31 | 10857046999023057135944570762232829481370756359578518086990519993285655852781; 32 | uint256 public constant GAMMA_Y1 = 33 | 4082367875863433681332203403145435568316851327593401208105741076214120093531; 34 | uint256 public constant GAMMA_Y2 = 35 | 8495653923123431417604973247489272438418190587263600148770280649306958101930; 36 | uint256 public constant DELTA_X1 = 37 | 11559732032986387107991004021392285783925812861821192530917403151452391805634; 38 | uint256 public constant DELTA_X2 = 39 | 10857046999023057135944570762232829481370756359578518086990519993285655852781; 40 | uint256 public constant DELTA_Y1 = 41 | 4082367875863433681332203403145435568316851327593401208105741076214120093531; 42 | uint256 public constant DELTA_Y2 = 43 | 8495653923123431417604973247489272438418190587263600148770280649306958101930; 44 | 45 | uint256 public constant IC0_X = 46 | 4004410872179300339480249405398939298715031489893009961199535208964457923750; 47 | uint256 public constant IC0_Y = 48 | 11142026210898871476346451274761099606196839371161747578101583645702654533240; 49 | uint256 public constant IC1_X = 50 | 14970325264892984291437720194401230916657388050759523602370378142660744831477; 51 | uint256 public constant IC1_Y = 52 | 15860538555168123807647719982845297214031403618163443664157964964439662885432; 53 | uint256 public constant IC2_X = 54 | 2280562765509182195246897364500489648120102222444059313572774422753200337271; 55 | uint256 public constant IC2_Y = 56 | 7147694953124310609924568435428058789638619830198023240430532891482445253803; 57 | uint256 public constant IC3_X = 58 | 7737404298715916349870992960929602974683638711993694006376159661700137192127; 59 | uint256 public constant IC3_Y = 60 | 7116770325362339113448473214465082117296435085200119804036017348236401720128; 61 | uint256 public constant IC4_X = 62 | 3130277824222995531291107528843021785954629147236040284065307643519664903928; 63 | uint256 public constant IC4_Y = 64 | 11742475342174768235971584303810158858484260897318069476115756668898865449280; 65 | uint256 public constant IC5_X = 66 | 14791539702458079086636207858304521437578092734215012107895193807307152746110; 67 | uint256 public constant IC5_Y = 68 | 12489284483607948781669905789845942689563255773386215312172350852214666005897; 69 | 70 | /// @dev memory pointer sizes 71 | uint16 public constant P_PUBLIC_SIGNALS_ACCUMULATOR_SIZE = 128; 72 | uint16 public constant P_TOTAL_SIZE = 896; 73 | 74 | function verifyProof( 75 | uint256[2] memory pointA_, 76 | uint256[2][2] memory pointB_, 77 | uint256[2] memory pointC_, 78 | uint256[5] memory publicSignals_ 79 | ) public view returns (bool verified_) { 80 | assembly { 81 | function checkField(signal_) -> res_ { 82 | res_ := lt(signal_, SCALAR_FIELD_SIZE) 83 | } 84 | 85 | function g1MulAdd(pR_, x_, y_, s_) -> res_ { 86 | let pointer_ := mload(64) // free pointer 87 | 88 | mstore(pointer_, x_) 89 | mstore(add(pointer_, 32), y_) 90 | mstore(add(pointer_, 64), s_) 91 | 92 | res_ := staticcall(6000, 7, pointer_, 96, pointer_, 64) // ecMul 93 | res_ := and(res_, gt(returndatasize(), 0)) // check that multiplication succeeded 94 | 95 | if iszero(res_) { 96 | leave 97 | } 98 | 99 | mstore(add(pointer_, 64), mload(pR_)) 100 | mstore(add(pointer_, 96), mload(add(pR_, 32))) 101 | 102 | res_ := staticcall(150, 6, pointer_, 128, pR_, 64) // ecAdd 103 | res_ := and(res_, gt(returndatasize(), 0)) // check that addition succeeded 104 | } 105 | 106 | function checkPairing(pA_, pB_, pC_, pubSignals_, pointer_) -> res_ { 107 | let pPairing_ := add(pointer_, P_PUBLIC_SIGNALS_ACCUMULATOR_SIZE) 108 | 109 | mstore(pointer_, IC0_X) 110 | mstore(add(pointer_, 32), IC0_Y) 111 | 112 | /// @dev compute the linear combination of public signals 113 | if iszero(g1MulAdd(pointer_, IC1_X, IC1_Y, mload(add(pubSignals_, 0)))) { 114 | leave 115 | } 116 | if iszero(g1MulAdd(pointer_, IC2_X, IC2_Y, mload(add(pubSignals_, 32)))) { 117 | leave 118 | } 119 | if iszero(g1MulAdd(pointer_, IC3_X, IC3_Y, mload(add(pubSignals_, 64)))) { 120 | leave 121 | } 122 | if iszero(g1MulAdd(pointer_, IC4_X, IC4_Y, mload(add(pubSignals_, 96)))) { 123 | leave 124 | } 125 | if iszero(g1MulAdd(pointer_, IC5_X, IC5_Y, mload(add(pubSignals_, 128)))) { 126 | leave 127 | } 128 | 129 | /// @dev -A 130 | mstore(pPairing_, mload(pA_)) 131 | mstore( 132 | add(pPairing_, 32), 133 | mod(sub(BASE_FIELD_SIZE, mload(add(pA_, 32))), BASE_FIELD_SIZE) 134 | ) 135 | 136 | /// @dev B 137 | mstore(add(pPairing_, 64), mload(mload(pB_))) 138 | mstore(add(pPairing_, 96), mload(add(mload(pB_), 32))) 139 | mstore(add(pPairing_, 128), mload(mload(add(pB_, 32)))) 140 | mstore(add(pPairing_, 160), mload(add(mload(add(pB_, 32)), 32))) 141 | 142 | /// @dev alpha1 143 | mstore(add(pPairing_, 192), ALPHA_X) 144 | mstore(add(pPairing_, 224), ALPHA_Y) 145 | 146 | /// @dev beta2 147 | mstore(add(pPairing_, 256), BETA_X1) 148 | mstore(add(pPairing_, 288), BETA_X2) 149 | mstore(add(pPairing_, 320), BETA_Y1) 150 | mstore(add(pPairing_, 352), BETA_Y2) 151 | 152 | /// @dev public signals 153 | mstore(add(pPairing_, 384), mload(pointer_)) 154 | mstore(add(pPairing_, 416), mload(add(pointer_, 32))) 155 | 156 | /// @dev gamma2 157 | mstore(add(pPairing_, 448), GAMMA_X1) 158 | mstore(add(pPairing_, 480), GAMMA_X2) 159 | mstore(add(pPairing_, 512), GAMMA_Y1) 160 | mstore(add(pPairing_, 544), GAMMA_Y2) 161 | 162 | /// @dev C 163 | mstore(add(pPairing_, 576), mload(pC_)) 164 | mstore(add(pPairing_, 608), mload(add(pC_, 32))) 165 | 166 | /// @dev delta2 167 | mstore(add(pPairing_, 640), DELTA_X1) 168 | mstore(add(pPairing_, 672), DELTA_X2) 169 | mstore(add(pPairing_, 704), DELTA_Y1) 170 | mstore(add(pPairing_, 736), DELTA_Y2) 171 | 172 | res_ := staticcall(181000, 8, pPairing_, 768, pPairing_, 32) // ecPairing 173 | res_ := and(res_, mload(pPairing_)) // check that pairing succeeded 174 | } 175 | 176 | let pointer_ := mload(64) // free pointer 177 | mstore(64, add(pointer_, P_TOTAL_SIZE)) 178 | 179 | /// @dev check that all public signals are in F 180 | verified_ := 1 181 | verified_ := and(verified_, checkField(mload(add(publicSignals_, 0)))) 182 | verified_ := and(verified_, checkField(mload(add(publicSignals_, 32)))) 183 | verified_ := and(verified_, checkField(mload(add(publicSignals_, 64)))) 184 | verified_ := and(verified_, checkField(mload(add(publicSignals_, 96)))) 185 | verified_ := and(verified_, checkField(mload(add(publicSignals_, 128)))) 186 | 187 | /// @dev check pairings 188 | if not(iszero(verified_)) { 189 | verified_ := checkPairing(pointA_, pointB_, pointC_, publicSignals_, pointer_) 190 | } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomicfoundation/hardhat-chai-matchers"; 2 | import "@nomicfoundation/hardhat-ethers"; 3 | import "@solarity/chai-zkit"; 4 | import "@solarity/hardhat-zkit"; 5 | import "@typechain/hardhat"; 6 | import "hardhat-gas-reporter"; 7 | import type { HardhatUserConfig } from "hardhat/config"; 8 | import "solidity-coverage"; 9 | 10 | import dotenv from "dotenv"; 11 | dotenv.config(); 12 | 13 | const RPC_URL = process.env.RPC_URL || "https://api.avax.network/ext/bc/C/rpc"; 14 | 15 | const config: HardhatUserConfig = { 16 | solidity: { 17 | version: "0.8.27", 18 | settings: { 19 | optimizer: { 20 | enabled: true, 21 | runs: 200, 22 | }, 23 | }, 24 | }, 25 | networks: { 26 | hardhat: { 27 | forking: { 28 | url: RPC_URL, 29 | blockNumber: 59121339, 30 | enabled: !!process.env.FORKING, 31 | }, 32 | }, 33 | }, 34 | gasReporter: { 35 | enabled: !!process.env.REPORT_GAS, 36 | currency: "USD", 37 | coinmarketcap: process.env.COINMARKETCAP_API_KEY, 38 | excludeContracts: ["contracts/mocks/"], 39 | outputFile: "gas-report.txt", 40 | L1: "avalanche", 41 | showMethodSig: true, 42 | }, 43 | zkit: { 44 | compilerVersion: "2.1.9", 45 | circuitsDir: "circom", 46 | compilationSettings: { 47 | artifactsDir: "zkit/artifacts", 48 | onlyFiles: [], 49 | skipFiles: [], 50 | c: false, 51 | json: false, 52 | optimization: "O2", 53 | }, 54 | setupSettings: { 55 | contributionSettings: { 56 | provingSystem: "groth16", 57 | contributions: 0, 58 | }, 59 | onlyFiles: [], 60 | skipFiles: [], 61 | ptauDir: undefined, 62 | ptauDownload: true, 63 | }, 64 | verifiersSettings: { 65 | verifiersDir: "contracts/verifiers", 66 | verifiersType: "sol", 67 | }, 68 | typesDir: "generated-types/zkit", 69 | quiet: false, 70 | }, 71 | }; 72 | 73 | export default config; 74 | -------------------------------------------------------------------------------- /images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ava-labs/EncryptedERC/885843e3be6523a09a475ac63ecbc81de06910f9/images/banner.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "encryptederc", 3 | "version": "1.0.0", 4 | "devDependencies": { 5 | "@biomejs/biome": "^1.9.4", 6 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.8", 7 | "@nomicfoundation/hardhat-ethers": "^3.0.8", 8 | "@openzeppelin/contracts": "^5.1.0", 9 | "@solarity/chai-zkit": "^0.3.1", 10 | "@solarity/hardhat-zkit": "^0.5.15", 11 | "@typechain/hardhat": "^9.1.0", 12 | "@types/jest": "^29.5.14", 13 | "@types/mocha": "^10.0.10", 14 | "@zk-kit/baby-jubjub": "^1.0.3", 15 | "dotenv": "^16.4.7", 16 | "hardhat": "^2.22.15", 17 | "hardhat-gas-reporter": "^2.2.2", 18 | "maci-crypto": "^2.0.0", 19 | "poseidon-lite": "^0.3.0", 20 | "prettier": "^3.5.3", 21 | "prettier-plugin-solidity": "^1.4.2", 22 | "solhint": "^5.0.5", 23 | "solidity-coverage": "^0.8.14" 24 | }, 25 | "scripts": { 26 | "test": "mocha 'src/**/*.test.js'", 27 | "postinstall": "npx hardhat compile & npx hardhat zkit make --force && npx hardhat zkit verifiers", 28 | "lint:sol": "solhint '**/*.sol' --config ./.solhint.json --ignore-path ./.solhintignore --max-warnings 0 && npx prettier --check '**/*.sol' --config ./.prettierrc", 29 | "lint:ts": "npx biome lint .", 30 | "lint": "npm run lint:sol && npm run lint:ts" 31 | }, 32 | "keywords": [], 33 | "author": "", 34 | "repository": { 35 | "url": "https://github.com/ava-labs/EncryptedERC", 36 | "type": "git" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /scripts/constants.ts: -------------------------------------------------------------------------------- 1 | export const DECIMALS = 2; 2 | -------------------------------------------------------------------------------- /scripts/deploy-converter.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { deployLibrary, deployVerifiers } from "../test/helpers"; 3 | import { EncryptedERC__factory } from "../typechain-types"; 4 | import { DECIMALS } from "./constants"; 5 | 6 | const main = async () => { 7 | // get deployer 8 | const [deployer] = await ethers.getSigners(); 9 | 10 | // deploy verifiers 11 | // if true, deploys verifiers for prod, generated with proper trusted setup 12 | const { 13 | registrationVerifier, 14 | mintVerifier, 15 | withdrawVerifier, 16 | transferVerifier, 17 | burnVerifier, 18 | } = await deployVerifiers(deployer); 19 | 20 | // deploy babyjub library 21 | const babyJubJub = await deployLibrary(deployer); 22 | 23 | // deploy registrar contract 24 | const registrarFactory = await ethers.getContractFactory("Registrar"); 25 | const registrar = await registrarFactory.deploy(registrationVerifier); 26 | await registrar.waitForDeployment(); 27 | 28 | // deploy eERC20 29 | const encryptedERCFactory = new EncryptedERC__factory({ 30 | "contracts/libraries/BabyJubJub.sol:BabyJubJub": babyJubJub, 31 | }); 32 | const encryptedERC_ = await encryptedERCFactory.connect(deployer).deploy({ 33 | registrar: registrar.target, 34 | isConverter: true, // This is a converter eERC 35 | name: "", 36 | symbol: "", 37 | mintVerifier, 38 | withdrawVerifier, 39 | transferVerifier, 40 | burnVerifier, 41 | decimals: DECIMALS, 42 | }); 43 | await encryptedERC_.waitForDeployment(); 44 | 45 | // also deploys new erc20 46 | const erc20Factory = await ethers.getContractFactory("SimpleERC20"); 47 | const erc20 = await erc20Factory.deploy("Test", "TEST", 18); 48 | await erc20.waitForDeployment(); 49 | 50 | // mints some amount to deployer as well 51 | const tx = await erc20.mint(deployer.address, ethers.parseEther("10000")); 52 | await tx.wait(); 53 | 54 | console.log("ERC20 deployed at:", erc20.target); 55 | console.log("Minted 10000 erc20 to deployer"); 56 | 57 | console.table({ 58 | registrationVerifier, 59 | mintVerifier, 60 | withdrawVerifier, 61 | transferVerifier, 62 | babyJubJub, 63 | registrar: registrar.target, 64 | encryptedERC: encryptedERC_.target, 65 | }); 66 | }; 67 | 68 | main().catch((error) => { 69 | console.error(error); 70 | process.exitCode = 1; 71 | }); 72 | -------------------------------------------------------------------------------- /scripts/deploy-standalone.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | import { deployLibrary, deployVerifiers } from "../test/helpers"; 3 | import { EncryptedERC__factory } from "../typechain-types"; 4 | import { DECIMALS } from "./constants"; 5 | 6 | const main = async () => { 7 | // get deployer 8 | const [deployer] = await ethers.getSigners(); 9 | 10 | // deploy verifiers 11 | // if true, deploys verifiers for prod, generated with proper trusted setup 12 | const { 13 | registrationVerifier, 14 | mintVerifier, 15 | withdrawVerifier, 16 | transferVerifier, 17 | burnVerifier, 18 | } = await deployVerifiers(deployer); 19 | 20 | // deploy babyjub library 21 | const babyJubJub = await deployLibrary(deployer); 22 | 23 | // deploy registrar contract 24 | const registrarFactory = await ethers.getContractFactory("Registrar"); 25 | const registrar = await registrarFactory.deploy(registrationVerifier); 26 | await registrar.waitForDeployment(); 27 | 28 | // deploy eERC20 29 | const encryptedERCFactory = new EncryptedERC__factory({ 30 | "contracts/libraries/BabyJubJub.sol:BabyJubJub": babyJubJub, 31 | }); 32 | const encryptedERC_ = await encryptedERCFactory.connect(deployer).deploy({ 33 | registrar: registrar.target, 34 | isConverter: false, // This is a standalone eERC 35 | name: "Test", 36 | symbol: "TEST", 37 | mintVerifier, 38 | withdrawVerifier, 39 | transferVerifier, 40 | burnVerifier, 41 | decimals: DECIMALS, 42 | }); 43 | await encryptedERC_.waitForDeployment(); 44 | 45 | console.table({ 46 | registrationVerifier, 47 | mintVerifier, 48 | withdrawVerifier, 49 | transferVerifier, 50 | babyJubJub, 51 | registrar: registrar.target, 52 | encryptedERC: encryptedERC_.target, 53 | }); 54 | }; 55 | 56 | main().catch((error) => { 57 | console.error(error); 58 | process.exitCode = 1; 59 | }); 60 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const BASE_POINT_ORDER = 2 | 2736030358979909402780800718157159386076813972158567259200215660948447373041n; 3 | export const BN254_SCALAR_FIELD = 4 | 21888242871839275222246405745257275088548364400416034343698204186575808495617n; 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./jub"; 2 | export * from "./poseidon"; 3 | export * from "./constants"; 4 | -------------------------------------------------------------------------------- /src/jub/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./jub"; 2 | -------------------------------------------------------------------------------- /src/jub/jub.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Base8, 3 | Fr, 4 | type Point, 5 | addPoint, 6 | mulPointEscalar, 7 | } from "@zk-kit/baby-jubjub"; 8 | import { formatPrivKeyForBabyJub, genRandomBabyJubValue } from "maci-crypto"; 9 | import { BASE_POINT_ORDER } from "../constants"; 10 | 11 | /** 12 | * Implements El-Gamal encryption on BabyJubJub curve 13 | * @param publicKey BabyJubJub public key 14 | * @param point Point to encrypt 15 | * @param random Randomness for the encryption 16 | * @returns [c1,c2] - returns 2 different points as a ciphertext 17 | */ 18 | export const encryptPoint = ( 19 | publicKey: bigint[], 20 | point: bigint[], 21 | random = genRandomBabyJubValue(), 22 | ): [Point, Point] => { 23 | const c1 = mulPointEscalar(Base8, random); 24 | const pky = mulPointEscalar(publicKey as Point, random); 25 | const c2 = addPoint(point as Point, pky); 26 | 27 | return [c1, c2]; 28 | }; 29 | 30 | /** 31 | * Implements El-Gamal encryption on scalar message on BabyJubJub curve 32 | * @param publicKey Public key to encrypt the message 33 | * @param message Message to encrypt 34 | * @param random Randomness for the encryption 35 | * @returns { cipher: [c1,c2], random: bigint } - returns 2 different points as a ciphertext and the randomness used 36 | */ 37 | export const encryptMessage = ( 38 | publicKey: bigint[], 39 | message: bigint, 40 | random = genRandomBabyJubValue(), 41 | ): { cipher: [bigint[], bigint[]]; random: bigint } => { 42 | let encRandom = random; 43 | if (encRandom >= BASE_POINT_ORDER) { 44 | encRandom = genRandomBabyJubValue() / 100n; 45 | } 46 | const p = mulPointEscalar(Base8, message); 47 | 48 | return { 49 | cipher: encryptPoint(publicKey, p, encRandom), 50 | random: encRandom, 51 | }; 52 | }; 53 | 54 | /** 55 | * Implements El-Gamal decryption on BabyJubJub curve 56 | * @param privateKey - Private key to decrypt the point 57 | * @param c1 - First part of the cipher 58 | * @param c2 - Second part of the cipher 59 | * @returns Point - returns the decrypted point 60 | */ 61 | export const decryptPoint = ( 62 | privateKey: bigint, 63 | c1: bigint[], 64 | c2: bigint[], 65 | ): bigint[] => { 66 | const privKey = formatPrivKeyForBabyJub(privateKey); 67 | 68 | const c1x = mulPointEscalar(c1 as Point, privKey); 69 | const c1xInverse = [Fr.e(c1x[0] * -1n), c1x[1]]; 70 | return addPoint(c2 as Point, c1xInverse as Point); 71 | }; 72 | -------------------------------------------------------------------------------- /src/poseidon/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./poseidon"; 2 | -------------------------------------------------------------------------------- /src/poseidon/poseidon.ts: -------------------------------------------------------------------------------- 1 | import { Base8, type Point, mulPointEscalar } from "@zk-kit/baby-jubjub"; 2 | import { 3 | formatPrivKeyForBabyJub, 4 | genRandomBabyJubValue, 5 | poseidonDecrypt, 6 | poseidonEncrypt, 7 | } from "maci-crypto"; 8 | import { randomBytes } from "node:crypto"; 9 | import { BASE_POINT_ORDER } from "../constants"; 10 | 11 | /** 12 | * Generates a random nonce 13 | * @returns A cryptographically secure random number 14 | */ 15 | export const randomNonce = (): bigint => { 16 | const bytes = randomBytes(16); 17 | // add 1 to make sure it's non-zero 18 | return BigInt(`0x${bytes.toString("hex")}`) + 1n; 19 | }; 20 | 21 | /** 22 | * 23 | * @param inputs Input array to encrypt 24 | * @param publicKey Public key 25 | * @returns ciphertext - Encrypted message 26 | * @returns nonce - Nonce used for the poseidon encryption 27 | * @returns encRandom - Randomness used for the encryption 28 | * @returns poseidonEncryptionKey - Encryption key (publicKey * encRandom) 29 | * @returns authKey - Authentication key (Base8 * encRandom) 30 | */ 31 | export const processPoseidonEncryption = ( 32 | inputs: bigint[], 33 | publicKey: bigint[], 34 | ) => { 35 | const nonce = randomNonce(); 36 | 37 | let encRandom = genRandomBabyJubValue(); 38 | if (encRandom >= BASE_POINT_ORDER) { 39 | encRandom = genRandomBabyJubValue() / 10n; 40 | } 41 | 42 | const poseidonEncryptionKey = mulPointEscalar( 43 | publicKey as Point, 44 | encRandom, 45 | ); 46 | const authKey = mulPointEscalar(Base8, encRandom); 47 | const ciphertext = poseidonEncrypt(inputs, poseidonEncryptionKey, nonce); 48 | 49 | return { ciphertext, nonce, encRandom, poseidonEncryptionKey, authKey }; 50 | }; 51 | 52 | /** 53 | * Decrypts a message encrypted with Poseidon 54 | * @param ciphertext Encrypted message 55 | * @param authKey Authentication key 56 | * @param nonce Nonce used for the poseidon encryption 57 | * @param privateKey Private key 58 | * @param length Length of the original input array 59 | * @returns Decrypted message as an array 60 | */ 61 | export const processPoseidonDecryption = ( 62 | ciphertext: bigint[], 63 | authKey: bigint[], 64 | nonce: bigint, 65 | privateKey: bigint, 66 | length: number, 67 | ) => { 68 | const sharedKey = mulPointEscalar( 69 | authKey as Point, 70 | formatPrivKeyForBabyJub(privateKey), 71 | ); 72 | 73 | const decrypted = poseidonDecrypt(ciphertext, sharedKey, nonce, length); 74 | 75 | return decrypted.slice(0, length); 76 | }; 77 | -------------------------------------------------------------------------------- /test/user.ts: -------------------------------------------------------------------------------- 1 | import type { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/dist/src/signer-with-address"; 2 | import { Base8, mulPointEscalar, subOrder } from "@zk-kit/baby-jubjub"; 3 | import { formatPrivKeyForBabyJub, genPrivKey, hash2 } from "maci-crypto"; 4 | import { poseidon3 } from "poseidon-lite"; 5 | 6 | export const AUDITOR_SECRET_KEY = 7 | 12847321338015819245445518144028570538408927360876901642159872299055545378037n; 8 | 9 | export class User { 10 | privateKey: bigint; 11 | formattedPrivateKey: bigint; 12 | publicKey: bigint[]; 13 | signer: SignerWithAddress; 14 | 15 | constructor(signer: SignerWithAddress) { 16 | this.signer = signer; 17 | // gen private key 18 | this.privateKey = genPrivKey(); 19 | // format private key for baby jubjub 20 | this.formattedPrivateKey = 21 | formatPrivKeyForBabyJub(this.privateKey) % subOrder; 22 | // gen public key 23 | this.publicKey = mulPointEscalar(Base8, this.formattedPrivateKey).map((x) => 24 | BigInt(x), 25 | ); 26 | } 27 | 28 | get address() { 29 | const address = hash2(this.publicKey); 30 | return address; 31 | } 32 | 33 | /** 34 | * 35 | * @param chainId Chain ID of the network 36 | * @returns The registration hash for the user CRH(CHAIN_ID | PRIVATE_KEY | ADDRESS) 37 | */ 38 | genRegistrationHash(chainId: bigint) { 39 | const registrationHash = poseidon3([ 40 | chainId, 41 | this.formattedPrivateKey, 42 | BigInt(this.signer.address), 43 | ]); 44 | 45 | return registrationHash; 46 | } 47 | } 48 | 49 | export class BurnUser { 50 | privateKey: bigint; 51 | publicKey: bigint[]; 52 | 53 | constructor() { 54 | this.privateKey = BigInt(0); 55 | this.publicKey = [0n, 1n]; 56 | } 57 | 58 | get address() { 59 | const address = "0x1111111111111111111111111111111111111111"; 60 | return address; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /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": [ 12 | "./src/**/*.ts", 13 | "./test/**/*.ts", 14 | "./scripts/**/*.ts", 15 | "./typechain-types" 16 | ], 17 | "exclude": [], 18 | "files": ["./hardhat.config.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /zk/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .DS_Store 3 | 4 | # env variables 5 | .env 6 | 7 | # env variables for the tests 8 | .env.test 9 | 10 | vendor/ -------------------------------------------------------------------------------- /zk/Makefile: -------------------------------------------------------------------------------- 1 | export GO111MODULE=on 2 | 3 | SOURCE_DIRS = circuits cmd hardhat utils 4 | 5 | .PHONY: vendor clean build mod-clean 6 | 7 | all: mod-clean vendor build 8 | 9 | vendor: 10 | go mod vendor 11 | 12 | clean: 13 | rm -rf build/ 14 | 15 | build: 16 | go build -o ./build/encryptedERC ./cmd/ 17 | 18 | mod-clean: 19 | go mod tidy 20 | -------------------------------------------------------------------------------- /zk/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/ava-labs/EncryptedERC/pkg/hardhat" 7 | "github.com/ava-labs/EncryptedERC/pkg/helpers" 8 | ) 9 | 10 | /* 11 | Input structure 12 | { 13 | privateInputs: [], 14 | publicInputs: [], 15 | } 16 | */ 17 | 18 | func main() { 19 | operation := flag.String("operation", "", "Circuit Name [REGISTER,TRANSFER,MINT,WITHDRAW]") 20 | input := flag.String("input", "", "Stringified JSON input") 21 | output := flag.String("output", "", "Name of the circuit output file (output.json)") 22 | csPath := flag.String("cs", "", "Path to the circuit cs.r1cs") 23 | pkPath := flag.String("pk", "", "Path to the circuit pk.pk") 24 | isNew := flag.Bool("new", false, "Generate new circuit") 25 | shouldExtract := flag.Bool("extract", false, "Extract the circuit") 26 | 27 | flag.Parse() 28 | 29 | pp := helpers.TestingParams{Input: *input, Output: *output, CsPath: *csPath, PkPath: *pkPath, IsNew: *isNew, Extract: *shouldExtract} 30 | 31 | switch *operation { 32 | case "REGISTER": 33 | hardhat.Register(pp) 34 | case "MINT": 35 | hardhat.Mint(pp) 36 | case "WITHDRAW": 37 | hardhat.Withdraw(pp) 38 | case "TRANSFER": 39 | hardhat.Transfer(pp) 40 | default: 41 | panic("Invalid operation") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /zk/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ava-labs/EncryptedERC 2 | 3 | go 1.22.6 4 | 5 | require ( 6 | github.com/consensys/gnark v0.11.0 7 | github.com/consensys/gnark-crypto v0.14.0 8 | github.com/iden3/go-iden3-crypto v0.0.17 9 | ) 10 | 11 | require ( 12 | github.com/bits-and-blooms/bitset v1.14.2 // indirect 13 | github.com/blang/semver/v4 v4.0.0 // indirect 14 | github.com/consensys/bavard v0.1.13 // indirect 15 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 16 | github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect 17 | github.com/ingonyama-zk/icicle v1.1.0 // indirect 18 | github.com/ingonyama-zk/iciclegnark v0.1.0 // indirect 19 | github.com/mattn/go-colorable v0.1.13 // indirect 20 | github.com/mattn/go-isatty v0.0.20 // indirect 21 | github.com/mmcloughlin/addchain v0.4.0 // indirect 22 | github.com/ronanh/intcomp v1.1.0 // indirect 23 | github.com/rs/zerolog v1.33.0 // indirect 24 | github.com/x448/float16 v0.8.4 // indirect 25 | golang.org/x/crypto v0.31.0 // indirect 26 | golang.org/x/sync v0.8.0 // indirect 27 | golang.org/x/sys v0.28.0 // indirect 28 | rsc.io/tmplfunc v0.0.3 // indirect 29 | ) 30 | -------------------------------------------------------------------------------- /zk/go.sum: -------------------------------------------------------------------------------- 1 | github.com/bits-and-blooms/bitset v1.14.2 h1:YXVoyPndbdvcEVcseEovVfp0qjJp7S+i5+xgp/Nfbdc= 2 | github.com/bits-and-blooms/bitset v1.14.2/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 3 | github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= 4 | github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= 5 | github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= 6 | github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= 7 | github.com/consensys/gnark v0.11.0 h1:YlndnlbRAoIEA+aIIHzNIW4P0dCIOM9/jCVzsXf356c= 8 | github.com/consensys/gnark v0.11.0/go.mod h1:2LbheIOxsBI1a9Ck1XxUoy6PRnH28mSI9qrvtN2HwDY= 9 | github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= 10 | github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= 11 | github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= 15 | github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= 16 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 17 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 18 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 19 | github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= 20 | github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= 21 | github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 22 | github.com/iden3/go-iden3-crypto v0.0.17 h1:NdkceRLJo/pI4UpcjVah4lN/a3yzxRUGXqxbWcYh9mY= 23 | github.com/iden3/go-iden3-crypto v0.0.17/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= 24 | github.com/ingonyama-zk/icicle v1.1.0 h1:a2MUIaF+1i4JY2Lnb961ZMvaC8GFs9GqZgSnd9e95C8= 25 | github.com/ingonyama-zk/icicle v1.1.0/go.mod h1:kAK8/EoN7fUEmakzgZIYdWy1a2rBnpCaZLqSHwZWxEk= 26 | github.com/ingonyama-zk/iciclegnark v0.1.0 h1:88MkEghzjQBMjrYRJFxZ9oR9CTIpB8NG2zLeCJSvXKQ= 27 | github.com/ingonyama-zk/iciclegnark v0.1.0/go.mod h1:wz6+IpyHKs6UhMMoQpNqz1VY+ddfKqC/gRwR/64W6WU= 28 | github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= 29 | github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= 30 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 31 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 32 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 33 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 34 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 35 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 36 | github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= 37 | github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= 38 | github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= 39 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 40 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 41 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 42 | github.com/ronanh/intcomp v1.1.0 h1:i54kxmpmSoOZFcWPMWryuakN0vLxLswASsGa07zkvLU= 43 | github.com/ronanh/intcomp v1.1.0/go.mod h1:7FOLy3P3Zj3er/kVrU/pl+Ql7JFZj7bwliMGketo0IU= 44 | github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= 45 | github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= 46 | github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= 47 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 48 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 49 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 50 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 51 | golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= 52 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= 53 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 54 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 55 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 56 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 57 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 58 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 59 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 60 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 61 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 62 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 63 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 64 | rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= 65 | rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= 66 | -------------------------------------------------------------------------------- /zk/pkg/babyjub/babyjub.go: -------------------------------------------------------------------------------- 1 | package babyjub 2 | 3 | import ( 4 | "math/big" 5 | 6 | tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" 7 | "github.com/consensys/gnark/frontend" 8 | "github.com/consensys/gnark/std/algebra/native/twistededwards" 9 | ) 10 | 11 | type BjWrapper struct { 12 | Curve twistededwards.Curve 13 | BasePointOrder *big.Int 14 | api frontend.API 15 | base8 twistededwards.Point 16 | } 17 | 18 | // creates new wrapper for babyjub curve operations 19 | func NewBjWrapper(api frontend.API, curveId tedwards.ID) *BjWrapper { 20 | curve, err := twistededwards.NewEdCurve(api, curveId) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | // Set the order of the babyjub curve 26 | basePointOrder, _ := big.NewInt(0).SetString("2736030358979909402780800718157159386076813972158567259200215660948447373041", 10) 27 | 28 | // Set the x and y coordinates for the base point (base8) of the babyjub curve 29 | baseX, _ := big.NewInt(0).SetString("5299619240641551281634865583518297030282874472190772894086521144482721001553", 10) 30 | baseY, _ := big.NewInt(0).SetString("16950150798460657717958625567821834550301663161624707787222815936182638968203", 10) 31 | 32 | // Set the curve parameters for the babyjub curve being used 33 | curve.Params().A = big.NewInt(168700) 34 | curve.Params().D = big.NewInt(168696) 35 | curve.Params().Base = [2]*big.Int{baseX, baseY} 36 | 37 | return &BjWrapper{ 38 | Curve: curve, 39 | BasePointOrder: basePointOrder, 40 | api: api, 41 | base8: twistededwards.Point{X: frontend.Variable(baseX), Y: frontend.Variable(baseY)}, 42 | } 43 | } 44 | 45 | // multiplies the base point with provided scalar value using scalar multiplication 46 | // same operation as generating public key from secret key 47 | func (bj *BjWrapper) MulWithBasePoint(s frontend.Variable) twistededwards.Point { 48 | var p twistededwards.Point 49 | points := bj.Curve.Params().Base 50 | p.X = points[0] 51 | p.Y = points[1] 52 | 53 | res := bj.Curve.ScalarMul(p, s) 54 | 55 | return res 56 | } 57 | 58 | // asserts that two points are equal and also in the curve 59 | func (bj *BjWrapper) AssertPoint(p1 twistededwards.Point, x, y frontend.Variable) { 60 | bj.Curve.AssertIsOnCurve(p1) 61 | 62 | p2 := twistededwards.Point{X: x, Y: y} 63 | bj.Curve.AssertIsOnCurve(p2) 64 | 65 | bj.api.AssertIsEqual(p1.X, p2.X) 66 | bj.api.AssertIsEqual(p1.Y, p2.Y) 67 | } 68 | 69 | // multiplies the provided point with a scalar value 70 | func (bj *BjWrapper) MulWithScalar(x, y, s frontend.Variable) twistededwards.Point { 71 | r := bj.Curve.ScalarMul( 72 | twistededwards.Point{X: x, Y: y}, 73 | s, 74 | ) 75 | bj.Curve.AssertIsOnCurve(r) 76 | return r 77 | } 78 | 79 | // el gamal decryption on the babyjub curve 80 | func (bj *BjWrapper) ElGamalDecrypt(c1, c2 [2]frontend.Variable, secretKey frontend.Variable) twistededwards.Point { 81 | var c1x, c1xInverse, decrypted, c2Point twistededwards.Point 82 | 83 | c1x.X = c1[0] 84 | c1x.Y = c1[1] 85 | 86 | c2Point.X = c2[0] 87 | c2Point.Y = c2[1] 88 | 89 | c1x = bj.Curve.ScalarMul(c1x, secretKey) 90 | c1xInverse = bj.Curve.Neg(c1x) 91 | decrypted = bj.Curve.Add(c1xInverse, c2Point) 92 | 93 | bj.Curve.AssertIsOnCurve(c1x) 94 | bj.Curve.AssertIsOnCurve(decrypted) 95 | bj.Curve.AssertIsOnCurve(c1xInverse) 96 | 97 | return decrypted 98 | } 99 | 100 | // function encrypts message with El-Gamal encryption scheme 101 | func (bj *BjWrapper) ElGamalEncrypt(publicKey, msg twistededwards.Point, random frontend.Variable) (c1 twistededwards.Point, c2 twistededwards.Point) { 102 | var tc2 twistededwards.Point 103 | 104 | c1 = bj.Curve.ScalarMul(bj.base8, random) 105 | tc2 = bj.Curve.ScalarMul(publicKey, random) 106 | c2 = bj.Curve.Add(tc2, msg) 107 | 108 | bj.Curve.AssertIsOnCurve(tc2) 109 | 110 | return c1, c2 111 | } 112 | -------------------------------------------------------------------------------- /zk/pkg/circuits/components.go: -------------------------------------------------------------------------------- 1 | package circuits 2 | 3 | import ( 4 | "github.com/ava-labs/EncryptedERC/pkg/babyjub" 5 | "github.com/ava-labs/EncryptedERC/pkg/poseidon" 6 | "github.com/consensys/gnark/frontend" 7 | ) 8 | 9 | // KeyHolder is an interface for any type that has a private key and public key 10 | type KeyHolder interface { 11 | GetPrivateKey() frontend.Variable 12 | GetPublicKeyX() frontend.Variable 13 | GetPublicKeyY() frontend.Variable 14 | } 15 | 16 | /* 17 | CheckPublicKey checks if the given private key generates the given public key over the BabyJubjub curve 18 | */ 19 | func CheckPublicKey(api frontend.API, bj *babyjub.BjWrapper, keyHolder KeyHolder) { 20 | api.AssertIsLessOrEqual(keyHolder.GetPrivateKey(), api.Sub(bj.BasePointOrder, 1)) 21 | 22 | generatedPublicKey := bj.MulWithBasePoint(keyHolder.GetPrivateKey()) 23 | bj.AssertPoint(generatedPublicKey, keyHolder.GetPublicKeyX(), keyHolder.GetPublicKeyY()) 24 | } 25 | 26 | /* 27 | CheckPublicKey checks if the given private key generates the given public key over the BabyJubjub curve 28 | */ 29 | func CheckRegistrationPublicKey(api frontend.API, bj *babyjub.BjWrapper, sender RegistrationSender) { 30 | api.AssertIsLessOrEqual(sender.PrivateKey, api.Sub(bj.BasePointOrder, 1)) 31 | 32 | generatedSenderPublicKey := bj.MulWithBasePoint(sender.PrivateKey) 33 | bj.AssertPoint(generatedSenderPublicKey, sender.PublicKey.P.X, sender.PublicKey.P.Y) 34 | } 35 | 36 | /* 37 | CheckBalance checks if the sender's balance is a well-formed ElGamal ciphertext by decryption 38 | */ 39 | func CheckBalance(api frontend.API, bj *babyjub.BjWrapper, sender Sender) { 40 | api.AssertIsLessOrEqual(sender.Balance, api.Sub(bj.BasePointOrder, 1)) 41 | 42 | decSenderBalanceP := bj.ElGamalDecrypt([2]frontend.Variable{sender.BalanceEGCT.C1.X, sender.BalanceEGCT.C1.Y}, [2]frontend.Variable{sender.BalanceEGCT.C2.X, sender.BalanceEGCT.C2.Y}, sender.PrivateKey) 43 | givenSenderBalanceP := bj.MulWithBasePoint(sender.Balance) 44 | bj.AssertPoint(givenSenderBalanceP, decSenderBalanceP.X, decSenderBalanceP.Y) 45 | } 46 | 47 | /* 48 | CheckPositiveValue verifies if the sender's value is the encryption of the given value by decryption 49 | */ 50 | func CheckPositiveValue(api frontend.API, bj *babyjub.BjWrapper, sender Sender, value frontend.Variable) { 51 | api.AssertIsLessOrEqual(value, api.Sub(bj.BasePointOrder, 1)) 52 | 53 | positiveValueP := bj.MulWithBasePoint(value) 54 | decSenderValueP := bj.ElGamalDecrypt([2]frontend.Variable{sender.ValueEGCT.C1.X, sender.ValueEGCT.C1.Y}, [2]frontend.Variable{sender.ValueEGCT.C2.X, sender.ValueEGCT.C2.Y}, sender.PrivateKey) 55 | bj.AssertPoint(positiveValueP, decSenderValueP.X, decSenderValueP.Y) 56 | } 57 | 58 | /* 59 | CheckValue verifies if the receiver's value is the encryption of the given value by re-encrypting it and comparing the result with the given ciphertext 60 | */ 61 | func CheckValue(api frontend.API, bj *babyjub.BjWrapper, receiver Receiver, value frontend.Variable) { 62 | api.AssertIsLessOrEqual(value, api.Sub(bj.BasePointOrder, 1)) 63 | 64 | api.AssertIsLessOrEqual(receiver.ValueRandom.R, api.Sub(bj.BasePointOrder, 1)) 65 | 66 | reEncC1, reEncC2 := bj.ElGamalEncrypt(receiver.PublicKey.P, bj.MulWithBasePoint(value), receiver.ValueRandom.R) 67 | bj.AssertPoint(receiver.ValueEGCT.C1, reEncC1.X, reEncC1.Y) 68 | bj.AssertPoint(receiver.ValueEGCT.C2, reEncC2.X, reEncC2.Y) 69 | } 70 | 71 | /* 72 | CheckPCTReceiver verifies if the given receiver's Poseidon ciphertext is well-formed by re-encryption 73 | */ 74 | func CheckPCTReceiver(api frontend.API, bj *babyjub.BjWrapper, receiver Receiver, value frontend.Variable) { 75 | api.AssertIsLessOrEqual(receiver.PCT.Random, api.Sub(bj.BasePointOrder, 1)) 76 | 77 | poseidonAuthKey := bj.MulWithBasePoint(receiver.PCT.Random) 78 | bj.AssertPoint(poseidonAuthKey, receiver.PCT.AuthKey.X, receiver.PCT.AuthKey.Y) 79 | 80 | // r * pk 81 | poseidonEncryptionKey := bj.MulWithScalar(receiver.PublicKey.P.X, receiver.PublicKey.P.Y, receiver.PCT.Random) 82 | // Decrypt the ciphertext 83 | decrypted := poseidon.PoseidonDecryptSingle(api, [2]frontend.Variable{poseidonEncryptionKey.X, poseidonEncryptionKey.Y}, receiver.PCT.Nonce, receiver.PCT.Ciphertext) 84 | 85 | api.AssertIsEqual(decrypted[0], value) 86 | 87 | } 88 | 89 | /* 90 | CheckPCTAuditor verifies if the given auditor's Poseidon ciphertext is well-formed by re-encryption 91 | */ 92 | func CheckPCTAuditor(api frontend.API, bj *babyjub.BjWrapper, auditor Auditor, value frontend.Variable) { 93 | api.AssertIsLessOrEqual(auditor.PCT.Random, api.Sub(bj.BasePointOrder, 1)) 94 | 95 | poseidonAuthKey := bj.MulWithBasePoint(auditor.PCT.Random) 96 | bj.AssertPoint(poseidonAuthKey, auditor.PCT.AuthKey.X, auditor.PCT.AuthKey.Y) 97 | 98 | // r * pk 99 | poseidonEncryptionKey := bj.MulWithScalar(auditor.PublicKey.P.X, auditor.PublicKey.P.Y, auditor.PCT.Random) 100 | 101 | // Decrypt the ciphertext 102 | decrypted := poseidon.PoseidonDecryptSingle(api, [2]frontend.Variable{poseidonEncryptionKey.X, poseidonEncryptionKey.Y}, auditor.PCT.Nonce, auditor.PCT.Ciphertext) 103 | api.AssertIsEqual(decrypted[0], value) 104 | } 105 | 106 | /* 107 | CheckRegistrationHash verifies if the given registration hash is well-formed 108 | */ 109 | func CheckRegistrationHash(api frontend.API, sender RegistrationSender) { 110 | pos := poseidon.NewPoseidonHash(api) 111 | hash := poseidon.Hash3(pos, sender.ChainID, sender.PrivateKey, sender.Address) 112 | api.AssertIsEqual(hash, sender.RegistrationHash) 113 | } 114 | 115 | /* 116 | CheckNullifierHash verifies if the given nullifier hash is well-formed 117 | */ 118 | func CheckNullifierHash(api frontend.API, auditor Auditor, nullifier MintNullifier) { 119 | pos := poseidon.NewPoseidonHash(api) 120 | hash := poseidon.CalculateNullifierHash(pos, nullifier.ChainID, auditor.PCT.Ciphertext[:]) 121 | api.AssertIsEqual(hash, nullifier.NullifierHash) 122 | } 123 | 124 | func (s Sender) GetPrivateKey() frontend.Variable { 125 | return s.PrivateKey 126 | } 127 | 128 | func (s Sender) GetPublicKeyX() frontend.Variable { 129 | return s.PublicKey.P.X 130 | } 131 | 132 | func (s Sender) GetPublicKeyY() frontend.Variable { 133 | return s.PublicKey.P.Y 134 | } 135 | 136 | func (s RegistrationSender) GetPrivateKey() frontend.Variable { 137 | return s.PrivateKey 138 | } 139 | 140 | func (s RegistrationSender) GetPublicKeyX() frontend.Variable { 141 | return s.PublicKey.P.X 142 | } 143 | 144 | func (s RegistrationSender) GetPublicKeyY() frontend.Variable { 145 | return s.PublicKey.P.Y 146 | } 147 | -------------------------------------------------------------------------------- /zk/pkg/circuits/mint_circuit.go: -------------------------------------------------------------------------------- 1 | package circuits 2 | 3 | import ( 4 | "github.com/ava-labs/EncryptedERC/pkg/babyjub" 5 | tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" 6 | "github.com/consensys/gnark/frontend" 7 | ) 8 | 9 | type MintCircuit struct { 10 | Receiver Receiver 11 | Auditor Auditor 12 | MintNullifier MintNullifier 13 | ValueToMint frontend.Variable 14 | } 15 | 16 | func (circuit *MintCircuit) Define(api frontend.API) error { 17 | // Initialize babyjub wrapper 18 | babyjub := babyjub.NewBjWrapper(api, tedwards.BN254) 19 | 20 | // Verify receiver's encrypted value is the mint amount 21 | CheckValue(api, babyjub, circuit.Receiver, circuit.ValueToMint) 22 | 23 | // Verify nullifier hash is not used 24 | CheckNullifierHash(api, circuit.Auditor, circuit.MintNullifier) 25 | 26 | // Verify receiver's encrypted summary includes the mint amount and is encrypted with the receiver's public key 27 | CheckPCTReceiver(api, babyjub, circuit.Receiver, circuit.ValueToMint) 28 | 29 | // Verify auditor's encrypted summary includes the mint amount and is encrypted with the auditor's public key 30 | CheckPCTAuditor(api, babyjub, circuit.Auditor, circuit.ValueToMint) 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /zk/pkg/circuits/registration_circuit.go: -------------------------------------------------------------------------------- 1 | package circuits 2 | 3 | import ( 4 | "github.com/ava-labs/EncryptedERC/pkg/babyjub" 5 | tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" 6 | "github.com/consensys/gnark/frontend" 7 | ) 8 | 9 | type RegistrationCircuit struct { 10 | Sender RegistrationSender 11 | } 12 | 13 | func (circuit *RegistrationCircuit) Define(api frontend.API) error { 14 | // Initialize babyjub wrapper 15 | babyjub := babyjub.NewBjWrapper(api, tedwards.BN254) 16 | 17 | // Verify that the sender's public key is well-formed 18 | CheckPublicKey(api, babyjub, circuit.Sender) 19 | 20 | // Verify that the sender's registration hash is well-formed 21 | CheckRegistrationHash(api, circuit.Sender) 22 | 23 | return nil 24 | } 25 | -------------------------------------------------------------------------------- /zk/pkg/circuits/structs.go: -------------------------------------------------------------------------------- 1 | package circuits 2 | 3 | import ( 4 | "github.com/consensys/gnark/frontend" 5 | "github.com/consensys/gnark/std/algebra/native/twistededwards" 6 | ) 7 | 8 | type Sender struct { 9 | PrivateKey frontend.Variable 10 | PublicKey PublicKey 11 | Balance frontend.Variable 12 | BalanceEGCT ElGamalCiphertext 13 | ValueEGCT ElGamalCiphertext 14 | } 15 | 16 | type WithdrawSender struct { 17 | PrivateKey frontend.Variable 18 | PublicKey PublicKey 19 | Balance frontend.Variable 20 | BalanceEGCT ElGamalCiphertext 21 | } 22 | 23 | type Receiver struct { 24 | PublicKey PublicKey 25 | ValueEGCT ElGamalCiphertext 26 | ValueRandom Randomness 27 | PCT PoseidonCiphertext 28 | } 29 | 30 | type RegistrationSender struct { 31 | PrivateKey frontend.Variable 32 | PublicKey PublicKey 33 | Address frontend.Variable `gnark:",public"` 34 | ChainID frontend.Variable `gnark:",public"` 35 | RegistrationHash frontend.Variable `gnark:",public"` 36 | } 37 | 38 | type Auditor struct { 39 | PublicKey PublicKey 40 | PCT PoseidonCiphertext 41 | } 42 | 43 | type MintNullifier struct { 44 | ChainID frontend.Variable `gnark:",public"` 45 | NullifierHash frontend.Variable `gnark:",public"` 46 | } 47 | 48 | type Randomness struct { 49 | R frontend.Variable 50 | } 51 | 52 | type PublicKey struct { 53 | P twistededwards.Point `gnark:",public"` 54 | } 55 | 56 | type PoseidonCiphertext struct { 57 | Ciphertext [4]frontend.Variable `gnark:",public"` 58 | AuthKey twistededwards.Point `gnark:",public"` 59 | Nonce frontend.Variable `gnark:",public"` 60 | Random frontend.Variable 61 | } 62 | 63 | type ElGamalCiphertext struct { 64 | C1 twistededwards.Point `gnark:",public"` 65 | C2 twistededwards.Point `gnark:",public"` 66 | } 67 | -------------------------------------------------------------------------------- /zk/pkg/circuits/transfer_circuit.go: -------------------------------------------------------------------------------- 1 | package circuits 2 | 3 | import ( 4 | "github.com/ava-labs/EncryptedERC/pkg/babyjub" 5 | tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" 6 | "github.com/consensys/gnark/frontend" 7 | ) 8 | 9 | type TransferCircuit struct { 10 | Sender Sender 11 | Receiver Receiver 12 | Auditor Auditor 13 | ValueToTransfer frontend.Variable 14 | } 15 | 16 | func (circuit *TransferCircuit) Define(api frontend.API) error { 17 | // Initialize babyjub wrapper 18 | babyjub := babyjub.NewBjWrapper(api, tedwards.BN254) 19 | 20 | // Verify the transfer amount is less than or equal to the sender's balance 21 | api.AssertIsLessOrEqual(circuit.ValueToTransfer, circuit.Sender.Balance) 22 | 23 | // Verify sender's public key is well-formed 24 | CheckPublicKey(api, babyjub, circuit.Sender) 25 | 26 | // Verify sender's encrypted balance is well-formed 27 | CheckBalance(api, babyjub, circuit.Sender) 28 | 29 | // Verify sender's encrypted value is the transfer amount 30 | CheckPositiveValue(api, babyjub, circuit.Sender, circuit.ValueToTransfer) 31 | 32 | // Verify receiver's encrypted value is the transfer amount 33 | CheckValue(api, babyjub, circuit.Receiver, circuit.ValueToTransfer) 34 | 35 | // Verify receiver's encrypted summary includes the transfer amount and is encrypted with the receiver's public key 36 | CheckPCTReceiver(api, babyjub, circuit.Receiver, circuit.ValueToTransfer) 37 | 38 | // Verify auditor's encrypted summary includes the transfer amount and is encrypted with the auditor's public key 39 | CheckPCTAuditor(api, babyjub, circuit.Auditor, circuit.ValueToTransfer) 40 | 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /zk/pkg/circuits/withdraw_circuit.go: -------------------------------------------------------------------------------- 1 | package circuits 2 | 3 | import ( 4 | "github.com/ava-labs/EncryptedERC/pkg/babyjub" 5 | tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards" 6 | "github.com/consensys/gnark/frontend" 7 | ) 8 | 9 | type WithdrawCircuit struct { 10 | Sender WithdrawSender 11 | Auditor Auditor 12 | ValueToBurn frontend.Variable `gnark:",public"` 13 | } 14 | 15 | func (circuit *WithdrawCircuit) Define(api frontend.API) error { 16 | // Initialize babyjub wrapper 17 | babyjub := babyjub.NewBjWrapper(api, tedwards.BN254) 18 | 19 | // Verify the transfer amount is less than or equal to the sender's balance 20 | api.AssertIsLessOrEqual(circuit.ValueToBurn, circuit.Sender.Balance) 21 | 22 | // Verify sender's public key is well-formed 23 | CheckPublicKey(api, babyjub, Sender{ 24 | PrivateKey: circuit.Sender.PrivateKey, 25 | PublicKey: circuit.Sender.PublicKey, 26 | }) 27 | 28 | // Verify sender's encrypted balance is well-formed 29 | CheckBalance(api, babyjub, Sender{ 30 | PrivateKey: circuit.Sender.PrivateKey, 31 | PublicKey: circuit.Sender.PublicKey, 32 | Balance: circuit.Sender.Balance, 33 | BalanceEGCT: circuit.Sender.BalanceEGCT, 34 | }) 35 | 36 | // Verify auditor's encrypted summary includes the burn amount and is encrypted with the auditor's public key 37 | CheckPCTAuditor(api, babyjub, circuit.Auditor, circuit.ValueToBurn) 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /zk/pkg/hardhat/helpers.go: -------------------------------------------------------------------------------- 1 | package hardhat 2 | 3 | type Inputs struct { 4 | PrivIns []string `json:"privateInputs"` 5 | PubIns []string `json:"publicInputs"` 6 | } 7 | -------------------------------------------------------------------------------- /zk/pkg/hardhat/mint.go: -------------------------------------------------------------------------------- 1 | package hardhat 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/ava-labs/EncryptedERC/pkg/circuits" 7 | "github.com/ava-labs/EncryptedERC/pkg/helpers" 8 | "github.com/ava-labs/EncryptedERC/pkg/utils" 9 | "github.com/consensys/gnark/backend/groth16" 10 | "github.com/consensys/gnark/frontend" 11 | ) 12 | 13 | func Mint(pp helpers.TestingParams) { 14 | inputString := pp.Input 15 | var inputs Inputs 16 | err := json.Unmarshal([]byte(inputString), &inputs) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | f := func() frontend.Circuit { return &circuits.MintCircuit{} } 22 | 23 | ccs, pk, vk, err := helpers.LoadCircuit(pp, f) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | witness, err := utils.GenerateWitness(inputs.PubIns, inputs.PrivIns) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | proof, err := groth16.Prove(ccs, pk, witness) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | a, b, c := utils.SetProof(proof) 39 | utils.WriteProof(pp.Output, &a, &b, &c) 40 | 41 | if pp.Extract { 42 | helpers.SaveCS(ccs, "MINT") 43 | helpers.SavePK(pk, "MINT") 44 | helpers.SaveVK(vk, "MINT") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /zk/pkg/hardhat/register.go: -------------------------------------------------------------------------------- 1 | package hardhat 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/ava-labs/EncryptedERC/pkg/circuits" 7 | "github.com/ava-labs/EncryptedERC/pkg/helpers" 8 | "github.com/ava-labs/EncryptedERC/pkg/utils" 9 | "github.com/consensys/gnark/backend/groth16" 10 | "github.com/consensys/gnark/frontend" 11 | ) 12 | 13 | func Register(pp helpers.TestingParams) { 14 | inputString := pp.Input 15 | var inputs Inputs 16 | err := json.Unmarshal([]byte(inputString), &inputs) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | f := func() frontend.Circuit { return &circuits.RegistrationCircuit{} } 22 | 23 | ccs, pk, vk, err := helpers.LoadCircuit(pp, f) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | witness, err := utils.GenerateWitness(inputs.PubIns, inputs.PrivIns) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | proof, err := groth16.Prove(ccs, pk, witness) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | a, b, c := utils.SetProof(proof) 39 | utils.WriteProof(pp.Output, &a, &b, &c) 40 | 41 | if pp.Extract { 42 | helpers.SaveCS(ccs, "REGISTER") 43 | helpers.SavePK(pk, "REGISTER") 44 | helpers.SaveVK(vk, "REGISTER") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /zk/pkg/hardhat/transfer.go: -------------------------------------------------------------------------------- 1 | package hardhat 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/ava-labs/EncryptedERC/pkg/circuits" 7 | "github.com/ava-labs/EncryptedERC/pkg/helpers" 8 | "github.com/ava-labs/EncryptedERC/pkg/utils" 9 | "github.com/consensys/gnark/backend/groth16" 10 | "github.com/consensys/gnark/frontend" 11 | ) 12 | 13 | func Transfer(pp helpers.TestingParams) { 14 | inputString := pp.Input 15 | var inputs Inputs 16 | err := json.Unmarshal([]byte(inputString), &inputs) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | f := func() frontend.Circuit { return &circuits.TransferCircuit{} } 22 | 23 | ccs, pk, vk, err := helpers.LoadCircuit(pp, f) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | witness, err := utils.GenerateWitness(inputs.PubIns, inputs.PrivIns) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | proof, err := groth16.Prove(ccs, pk, witness) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | a, b, c := utils.SetProof(proof) 39 | utils.WriteProof(pp.Output, &a, &b, &c) 40 | 41 | if pp.Extract { 42 | helpers.SaveCS(ccs, "TRANSFER") 43 | helpers.SavePK(pk, "TRANSFER") 44 | helpers.SaveVK(vk, "TRANSFER") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /zk/pkg/hardhat/withdraw.go: -------------------------------------------------------------------------------- 1 | package hardhat 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/ava-labs/EncryptedERC/pkg/circuits" 7 | "github.com/ava-labs/EncryptedERC/pkg/helpers" 8 | "github.com/ava-labs/EncryptedERC/pkg/utils" 9 | "github.com/consensys/gnark/backend/groth16" 10 | "github.com/consensys/gnark/frontend" 11 | ) 12 | 13 | func Withdraw(pp helpers.TestingParams) { 14 | inputString := pp.Input 15 | var inputs Inputs 16 | err := json.Unmarshal([]byte(inputString), &inputs) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | f := func() frontend.Circuit { return &circuits.WithdrawCircuit{} } 22 | 23 | ccs, pk, vk, err := helpers.LoadCircuit(pp, f) 24 | if err != nil { 25 | panic(err) 26 | } 27 | 28 | witness, err := utils.GenerateWitness(inputs.PubIns, inputs.PrivIns) 29 | if err != nil { 30 | panic(err) 31 | } 32 | 33 | proof, err := groth16.Prove(ccs, pk, witness) 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | a, b, c := utils.SetProof(proof) 39 | utils.WriteProof(pp.Output, &a, &b, &c) 40 | 41 | if pp.Extract { 42 | helpers.SaveCS(ccs, "WITHDRAW") 43 | helpers.SavePK(pk, "WITHDRAW") 44 | helpers.SaveVK(vk, "WITHDRAW") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /zk/pkg/helpers/helpers.go: -------------------------------------------------------------------------------- 1 | package helpers 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "os" 9 | 10 | "github.com/consensys/gnark-crypto/ecc" 11 | "github.com/consensys/gnark/backend/groth16" 12 | "github.com/consensys/gnark/constraint" 13 | "github.com/consensys/gnark/frontend" 14 | "github.com/consensys/gnark/frontend/cs/r1cs" 15 | ) 16 | 17 | type TestingParams struct { 18 | Input string 19 | Output string 20 | CsPath string 21 | PkPath string 22 | IsNew bool 23 | Extract bool 24 | } 25 | 26 | // function loads the contents of the circuit and the keys 27 | // if isNew, it compiles and generates the keys for the first time 28 | // otherwise, it reads the circuit and the keys from the given paths 29 | func LoadCircuit( 30 | params TestingParams, 31 | f func() frontend.Circuit, 32 | ) (constraint.ConstraintSystem, groth16.ProvingKey, groth16.VerifyingKey, error) { 33 | var err error 34 | var ccs constraint.ConstraintSystem 35 | var pk groth16.ProvingKey 36 | var vk groth16.VerifyingKey 37 | 38 | if params.IsNew { 39 | if ccs, err = frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, f()); err != nil { 40 | return nil, nil, nil, err 41 | } 42 | 43 | if pk, vk, err = groth16.Setup(ccs); err != nil { 44 | return nil, nil, nil, err 45 | } 46 | 47 | } else { 48 | if len(params.CsPath) == 0 || len(params.PkPath) == 0 { 49 | return nil, nil, nil, errors.New("r1cs and pk paths are required for existing circuit") 50 | } 51 | 52 | ccs, err = ReadCS(params.CsPath) 53 | if err != nil { 54 | return nil, nil, nil, err 55 | } 56 | pk, err = ReadPK(params.PkPath) 57 | if err != nil { 58 | return nil, nil, nil, err 59 | } 60 | } 61 | 62 | return ccs, pk, vk, nil 63 | } 64 | 65 | // reads the constraint system from the provided path 66 | func ReadCS(filename string) (constraint.ConstraintSystem, error) { 67 | ccs := groth16.NewCS(ecc.BN254) 68 | // Read the proving key 69 | csFile, err := os.ReadFile(filename) 70 | if err != nil { 71 | return ccs, err 72 | } 73 | 74 | pkCs := bytes.NewBuffer(csFile) 75 | _, err = ccs.ReadFrom(pkCs) 76 | if err != nil { 77 | panic(err) 78 | } 79 | 80 | return ccs, err 81 | } 82 | 83 | // reads the proving key from the provided path 84 | func ReadPK(filename string) (groth16.ProvingKey, error) { 85 | pk := groth16.NewProvingKey(ecc.BN254) 86 | 87 | // Read the verifying key 88 | pkFile, err := os.ReadFile(filename) 89 | if err != nil { 90 | fmt.Println("Error:", err) 91 | return pk, err 92 | } 93 | 94 | pkBuff := bytes.NewBuffer(pkFile) 95 | _, err = pk.ReadFrom(pkBuff) 96 | if err != nil { 97 | panic(err) 98 | } 99 | 100 | return pk, err 101 | } 102 | 103 | // saves proving key to the provided path 104 | func SavePK(pk groth16.ProvingKey, filename string) { 105 | var bufPK bytes.Buffer 106 | _, err := pk.WriteTo(&bufPK) 107 | if err != nil { 108 | panic(err) 109 | } 110 | 111 | file, err := os.Create(filename + ".pk") 112 | if err != nil { 113 | fmt.Println("Error:", err) 114 | return 115 | } 116 | defer file.Close() 117 | 118 | _, err = io.Copy(file, &bufPK) 119 | 120 | if err != nil { 121 | fmt.Println("Error:", err) 122 | return 123 | } 124 | } 125 | 126 | // saves constraint system to the provided path 127 | func SaveCS(cs constraint.ConstraintSystem, filename string) { 128 | // Open the output file for writing 129 | var bufCS bytes.Buffer 130 | _, err := cs.WriteTo(&bufCS) 131 | if err != nil { 132 | panic(err) 133 | } 134 | 135 | // Open the output file for writing 136 | file, err := os.Create(filename + ".r1cs") 137 | if err != nil { 138 | fmt.Println("Error:", err) 139 | return 140 | } 141 | defer file.Close() 142 | 143 | // Write the buffer contents to the file 144 | _, err = io.Copy(file, &bufCS) 145 | 146 | if err != nil { 147 | fmt.Println("Error:", err) 148 | return 149 | } 150 | } 151 | 152 | // saves verifying key to the provided path 153 | func SaveVK(vk groth16.VerifyingKey, filename string) { 154 | fileVK, err := os.Create(filename + ".sol") 155 | if err != nil { 156 | panic(err) 157 | } 158 | err = vk.ExportSolidity(fileVK) 159 | if err != nil { 160 | panic(err) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /zk/pkg/poseidon/poseidon_decryption.go: -------------------------------------------------------------------------------- 1 | package poseidon 2 | 3 | import "github.com/consensys/gnark/frontend" 4 | 5 | // implements poseidon decryption 6 | func poseidonDecrypt( 7 | api frontend.API, 8 | decryptedLength int, 9 | encryptionKey [2]frontend.Variable, 10 | nonce frontend.Variable, 11 | cipherText []frontend.Variable, 12 | ) []frontend.Variable { 13 | length := decryptedLength 14 | for decryptedLength%3 != 0 { 15 | decryptedLength += 1 16 | } 17 | 18 | out := make([]frontend.Variable, decryptedLength) 19 | 20 | two128 := frontend.Variable("340282366920938463463374607431768211456") 21 | api.AssertIsLessOrEqual(nonce, api.Sub(two128, frontend.Variable(1))) 22 | 23 | n := (decryptedLength + 1) / 3 24 | 25 | strategies := make([][]frontend.Variable, n+1) 26 | 27 | strategies[0] = PoseidonEx( 28 | api, 29 | []frontend.Variable{encryptionKey[0], encryptionKey[1], api.Add(nonce, api.Mul(length, two128))}, 30 | 0, 31 | 4, 32 | ) 33 | 34 | for i := 0; i < n; i++ { 35 | for j := 0; j < 3; j++ { 36 | out[i*3+j] = api.Sub(cipherText[i*3+j], strategies[i][j+1]) 37 | } 38 | 39 | strategiesInput := make([]frontend.Variable, 3) 40 | for j := 0; j < 3; j++ { 41 | strategiesInput[j] = cipherText[i*3+j] 42 | } 43 | 44 | strategies[i+1] = PoseidonEx( 45 | api, 46 | strategiesInput, 47 | strategies[i][0], 48 | 4, 49 | ) 50 | } 51 | 52 | remainder := length % 3 53 | if remainder != 0 { 54 | for i := length; i < decryptedLength; i++ { 55 | api.AssertIsEqual(out[i], 0) 56 | } 57 | } 58 | 59 | // Check the last ciphertext element 60 | api.AssertIsEqual(cipherText[decryptedLength], strategies[n][1]) 61 | 62 | return out[:length] 63 | } 64 | 65 | // implements poseidon decryption with 1 decrypted element 66 | func PoseidonDecryptSingle( 67 | api frontend.API, 68 | encryptionKey [2]frontend.Variable, 69 | nonce frontend.Variable, 70 | cipherText [4]frontend.Variable, 71 | ) []frontend.Variable { 72 | return poseidonDecrypt(api, 1, encryptionKey, nonce, cipherText[:]) 73 | } 74 | -------------------------------------------------------------------------------- /zk/pkg/utils/helpers.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "log" 7 | "math/big" 8 | "os" 9 | 10 | "github.com/consensys/gnark-crypto/ecc" 11 | "github.com/consensys/gnark/backend/groth16" 12 | "github.com/consensys/gnark/backend/witness" 13 | "github.com/iden3/go-iden3-crypto/utils" 14 | ) 15 | 16 | func NewBigArrayFromStrings(inputs []string) []*big.Int { 17 | bigInts := make([]*big.Int, len(inputs)) 18 | for i, input := range inputs { 19 | bigInts[i] = utils.NewIntFromString(input) 20 | } 21 | return bigInts 22 | } 23 | 24 | func NewStringArrayFromBigInts(inputs []*big.Int) []string { 25 | strings := make([]string, len(inputs)) 26 | for i, input := range inputs { 27 | strings[i] = input.String() 28 | } 29 | return strings 30 | } 31 | 32 | func Mapper[T any](s []T, f func(T, int) T) []T { 33 | result := make([]T, len(s)) 34 | for i, v := range s { 35 | result[i] = f(v, i) 36 | } 37 | return result 38 | } 39 | 40 | func Reduce[T any](s []T, f func(int, T, T) T, initValue T) T { 41 | acc := initValue 42 | for i, v := range s { 43 | acc = f(i, acc, v) 44 | } 45 | return acc 46 | } 47 | 48 | func GenerateWitness(publicIns, privateIns []string) (witness.Witness, error) { 49 | ww, _ := witness.New(ecc.BN254.ScalarField()) 50 | nbTotal := len(publicIns) + len(privateIns) 51 | values := make(chan any, nbTotal) 52 | go func() { 53 | for i := 0; i < len(publicIns); i++ { 54 | values <- publicIns[i] 55 | } 56 | for i := 0; i < len(privateIns); i++ { 57 | values <- privateIns[i] 58 | } 59 | close(values) 60 | }() 61 | 62 | err := ww.Fill(len(publicIns), len(privateIns), values) 63 | if err != nil { 64 | return nil, err 65 | } 66 | return ww, nil 67 | } 68 | 69 | // general helper function for writing the proof 70 | func WriteProof(output string, a *[2]string, b *[2][2]string, c *[2]string) { 71 | proof := map[string]interface{}{ 72 | "proof": []string{a[0], a[1], b[0][0], b[0][1], b[1][0], b[1][1], c[0], c[1]}, 73 | } 74 | 75 | proofJSON, err := json.Marshal(proof) 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | 80 | err = os.WriteFile(output, proofJSON, 0644) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | } 85 | 86 | // setProof function fills 'a', 'b', 'c' and public inputs for the generated proof 87 | func setProofABC(proofBytes []byte, a *[2]string, b *[2][2]string, c *[2]string) { 88 | const fpSize = 4 * 8 89 | 90 | a[0] = new(big.Int).SetBytes(proofBytes[fpSize*0 : fpSize*1]).String() 91 | a[1] = new(big.Int).SetBytes(proofBytes[fpSize*1 : fpSize*2]).String() 92 | b[0][0] = new(big.Int).SetBytes(proofBytes[fpSize*2 : fpSize*3]).String() 93 | b[0][1] = new(big.Int).SetBytes(proofBytes[fpSize*3 : fpSize*4]).String() 94 | b[1][0] = new(big.Int).SetBytes(proofBytes[fpSize*4 : fpSize*5]).String() 95 | b[1][1] = new(big.Int).SetBytes(proofBytes[fpSize*5 : fpSize*6]).String() 96 | c[0] = new(big.Int).SetBytes(proofBytes[fpSize*6 : fpSize*7]).String() 97 | c[1] = new(big.Int).SetBytes(proofBytes[fpSize*7 : fpSize*8]).String() 98 | } 99 | 100 | // setProof function fills 'a', 'b', 'c' and public inputs for the generated proof 101 | // and writes the proof to the output file 102 | func SetProof(proof groth16.Proof) (a [2]string, b [2][2]string, c [2]string) { 103 | var buf bytes.Buffer 104 | _, err := proof.WriteRawTo(&buf) 105 | if err != nil { 106 | panic(err) 107 | } 108 | proofBytes := buf.Bytes() 109 | 110 | setProofABC(proofBytes, &a, &b, &c) 111 | return a, b, c 112 | } 113 | --------------------------------------------------------------------------------