├── .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 |
--------------------------------------------------------------------------------