├── .env.example
├── .gitignore
├── .nvmrc
├── .vscode
└── settings.json
├── README.md
├── README
├── claim-01.png
├── claim-02.png
├── claim-03.png
├── claim-04.png
├── claim-05.png
├── claim-06.png
├── claim-07.png
├── create-schema-01.png
├── create-schema-02.png
├── create-schema-03.png
├── error-01.png
├── error-02.png
├── error-03.png
├── error-04.png
├── mint-01.png
├── mint-02.png
├── mint-03.png
├── mint-04.png
├── opensea.png
├── platform-test.png
└── toc.png
├── contracts
├── ERC20Verifier.sol
├── ERC721Verifier.sol
├── interfaces
│ ├── ICircuitValidator.sol
│ └── IZKPVerifier.sol
├── lib
│ └── GenesisUtils.sol
└── verifiers
│ └── ZKPVerifier.sol
├── hardhat.config.ts
├── htmlQRClaim
├── .gitignore
├── index.html
├── package.json
├── postcss.config.cjs
├── public
│ └── vite.svg
├── src
│ ├── App.tsx
│ ├── index.css
│ ├── main.tsx
│ └── vite-env.d.ts
├── tailwind.config.cjs
├── tsconfig.json
├── tsconfig.node.json
├── vite.config.ts
└── yarn.lock
├── package.json
├── proofRequest.ts
├── scripts
├── erc20Deploy.ts
├── erc20ZkpRequest.ts
├── erc721Deploy.ts
└── erc721ZkpRequest.ts
├── tsconfig.json
├── verify
├── ERC20Verifier
│ ├── BytesLib.sol
│ ├── Context.sol
│ ├── ERC20.sol
│ ├── ERC20Verifier.sol
│ ├── GenesisUtils.sol
│ ├── ICircuitValidator.sol
│ ├── IERC20.sol
│ ├── IERC20Metadata.sol
│ ├── IZKPVerifier.sol
│ ├── Ownable.sol
│ └── ZKPVerifier.sol
└── ERC721Verifier
│ ├── Address.sol
│ ├── BytesLib.sol
│ ├── Context.sol
│ ├── Counters.sol
│ ├── ERC165.sol
│ ├── ERC721.sol
│ ├── ERC721Verifier.sol
│ ├── GenesisUtils.sol
│ ├── ICircuitValidator.sol
│ ├── IERC165.sol
│ ├── IERC721.sol
│ ├── IERC721Metadata.sol
│ ├── IERC721Receiver.sol
│ ├── IZKPVerifier.sol
│ ├── Ownable.sol
│ ├── Strings.sol
│ └── ZKPVerifier.sol
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | ETHERSCAN_API_KEY=
2 | RPC_MUMBAI_URL=
3 | WALLET_PRIVATE_KEY=
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | coverage
4 | coverage.json
5 | typechain
6 | typechain-types
7 | .DS_Store
8 |
9 | #Hardhat files
10 | cache
11 | artifacts
12 |
13 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.17.0
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "editor.formatOnType": true,
4 | "editor.formatOnPaste": true
5 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PolygonID On Chain Verifications
2 |
3 | A basic tutorial on how to deploy a contract, and go through the proof process.
4 |
5 | ---
6 |
7 | ## TOC
8 |
9 | 
10 |
11 | ---
12 |
13 | ## Requirements
14 |
15 | - NVM Node v16.17.0
16 | - Yarn v1.22.19
17 |
18 | ---
19 |
20 | ## Concept
21 |
22 | There are 3 entities in SSI (Self-Sovereign Identify) for the "Trust Triangle" ([https://www.okta.com/identity-101/self-sovereign-identity/](https://www.okta.com/identity-101/self-sovereign-identity/)).
23 |
24 | 1 - Owner
25 |
26 | 2 - Issuer
27 |
28 | 3 - Verifier
29 |
30 | ---
31 |
32 | ## Deploy ERC20 Token
33 |
34 | ### Step 1 - Create Configuration Files & Install Dependencies
35 |
36 | ```bash
37 | cp .env.example .env;
38 |
39 | yarn;
40 | ```
41 |
42 | Fill in the following details to deploy the proper contract
43 |
44 | **File:** `./.env`
45 |
46 | ```bash
47 | ETHERSCAN_API_KEY=
48 | RPC_MUMBAI_URL=
49 | WALLET_PRIVATE_KEY=
50 | ```
51 |
52 | ### Step 2 - Deploy Contract
53 |
54 | ```bash
55 | npx hardhat run scripts/erc20Deploy.ts --network mumbai;
56 |
57 | # Expected output:
58 | # Compiled x Solidity files successfully
59 | # ERC20zkAirdrop deployed to 0xABc1...}
60 | ```
61 |
62 | ### Step 3 - Create Claim
63 |
64 | A - Sign up for [https://platform-test.polygonid.com](https://platform-test.polygonid.com).
65 |
66 | 
67 |
68 | B - Create Schema
69 |
70 | 
71 |
72 | 
73 |
74 | 
75 |
76 | C - Claim
77 |
78 | 
79 |
80 | 
81 |
82 | 
83 |
84 | 
85 |
86 | 
87 |
88 | 
89 |
90 | 
91 |
92 | ### Step 4 - Set ZKP Request
93 |
94 | Modify the following file
95 |
96 | **File:** `./scripts/erc20ZkpRequest.ts`
97 |
98 | ```ts
99 | // Change to copied schemaHas from claim in Step 2C
100 | const schemaHash = "9c2498080a90d43cada7fec79eeee8de";
101 | // Changed to deployed contract
102 | const ERC20VerifierAddress = "0x085523dF632FEaAE3Ae232E0EBc31FaC9956ddAb";
103 | const schemaEnd = fromLittleEndian(hexToBytes(schemaHash));
104 | const query = {
105 | schema: ethers.BigNumber.from(schemaEnd),
106 | slotIndex: 2,
107 | operator: 2,
108 | // Change to 10 or value that you want to use (eg. everything greater than 10)
109 | value: [10, ...new Array(63).fill(0).map(i => 0)],
110 | circuitId,
111 | };
112 | ```
113 |
114 | Set it on the contract:
115 |
116 | ```bash
117 | npx hardhat run --network mumbai scripts/erc20ZkpRequest.ts;
118 |
119 | # Expected output:
120 | # Request set at:
121 | # NOTE: May take a little bit to show up
122 | # https://mumbai.polygonscan.com/tx/0x60ac...
123 | ```
124 |
125 | ### Step 5 - QR Code Proof Request
126 |
127 | Modify the following script:
128 |
129 | **File:** `./htmlQRClaim/src/App.tsx`
130 |
131 | ```tsx
132 | // ...
133 |
134 | // Config
135 | // ========================================================
136 | // update with your contract address
137 | const DEPLOYED_CONTRACT_ADDRESS = "0xCfFEb70395C8E2A98d20Facf67122d638b18673c";
138 |
139 | /**
140 | *
141 | */
142 | const qrProofRequestJson = proofRequest;
143 | qrProofRequestJson.body.transaction_data.contract_address = DEPLOYED_CONTRACT_ADDRESS;
144 | qrProofRequestJson.body.scope[0].rules.query.req = {
145 | "Date": {
146 | // NOTE: this value needs to match the erc20ZkpRequest.ts L34 or erc721ZkpRequest.ts L34
147 | "$gt": 10
148 | }
149 | };
150 | // NOTE1: if you change this you need to resubmit the erc10|erc721ZKPRequest
151 | // NOTE2: type is case-sensitive
152 | // You can generate new schemas via https://platform-test.polygonid.com
153 | qrProofRequestJson.body.scope[0].rules.query.schema = {
154 | "url": "https://s3.eu-west-1.amazonaws.com/polygonid-schemas/96c80e89-3c04-4762-8f4a-c39373571506.json-ld",
155 | "type": "MyCustomSchema"
156 | };
157 | ```
158 |
159 | Run the local html QR code page
160 |
161 | ```bash
162 | cd htmlQRClaim;
163 | yarn dev;
164 |
165 | # Expected output:
166 | # VITE v3.2.0 ready in 238 ms
167 | #
168 | # ➜ Local: http://127.0.0.1:5173/
169 | # ➜ Network: use --host to expose
170 | ```
171 |
172 | ### Step 6 - Mint Airdrop With Proof
173 |
174 | 
175 |
176 | 
177 |
178 | 
179 |
180 | 
181 |
182 | ---
183 |
184 | ## Debugging Errors
185 |
186 | ### Wrong Claim Schema Has Been Used For Proof Generation
187 |
188 | 
189 |
190 | Double check that that the schema has been set correctly in the following places:
191 |
192 | **File:** `./scripts/erc20ZkpRequest.ts`
193 |
194 | ```ts
195 | // This should be copied from https://platform-test.polygonid.com
196 | const schemaHash = "f91912b9332bb22be08b4e10fe9eda9a";
197 | ```
198 |
199 | AND
200 |
201 | **Files:** `./htmlQRClaim/src/App.tsx`
202 |
203 | ```tsx
204 | // This should match the url and the type from https://platform-test.polygonid.com
205 | qrProofRequestJson.body.scope[0].rules.query.schema = {
206 | "url": "https://s3.eu-west-1.amazonaws.com/polygonid-schemas/96c80e89-3c04-4762-8f4a-c39373571506.json-ld",
207 | "type": "MyCustomSchema"
208 | };
209 | ```
210 |
211 | ### Wrong Query Operator Has Been Used For Proof Generation
212 |
213 | 
214 |
215 | Double check that the operator and the code for the operator is set correctly in the following places:
216 |
217 | **File:** `./scripts/erc20ZkpRequest.ts`
218 |
219 | ```ts
220 | const query = {
221 | schema: ethers.BigNumber.from(schemaEnd),
222 | slotIndex: 2,
223 | // see - https://0xpolygonid.github.io/tutorials/verifier/verification-library/zk-query-language/
224 | // 1 = equals
225 | // 2 = less-than
226 | // 3 = greater-than
227 | // 4 = in
228 | // 5 = not-in
229 | operator: 3,
230 | value: [10, ...new Array(63).fill(0).map(i => 0)],
231 | circuitId,
232 | };
233 | ```
234 |
235 | AND
236 |
237 | **Files:** `./htmlQRClaim/src/App.tsx`
238 |
239 | ```tsx
240 | qrProofRequestJson.body.scope[0].rules.query.req = {
241 | "customNumberAttribute": {
242 | // see - https://0xpolygonid.github.io/tutorials/verifier/verification-library/zk-query-language/
243 | // NOTE: this value needs to match the erc20ZkpRequest.ts L34 or erc721ZkpRequest.ts L34
244 | // $eq = 1
245 | // $lt = 2
246 | // $gt = 3
247 | // $in = 4
248 | // $nin = 5
249 | "$gt": 12
250 | }
251 | };
252 | ```
253 |
254 | ### Wrong Comparison Value Has Been Used For Proof Generation
255 |
256 | 
257 |
258 | Double check that the value used for the condition is set correctly in the following places:
259 |
260 | **File:** `./scripts/erc20ZkpRequest.ts`
261 |
262 | ```ts
263 | const query = {
264 | schema: ethers.BigNumber.from(schemaEnd),
265 | slotIndex: 2,
266 | operator: 3,
267 | // Make sure this value (ex: 10) matches the other value
268 | value: [10, ...new Array(63).fill(0).map(i => 0)],
269 | circuitId,
270 | };
271 | ```
272 |
273 | AND
274 |
275 | **Files:** `./htmlQRClaim/src/App.tsx`
276 |
277 | ```tsx
278 | qrProofRequestJson.body.scope[0].rules.query.req = {
279 | "customNumberAttribute": {
280 | // Make sure this value (ex: 10) matches the other value
281 | "$gt": 10
282 | }
283 | };
284 | ```
285 |
286 | ### Proof Can Not Be Submitted More Than Once
287 |
288 | 
289 |
290 | This is because you can only mint once with that specific proof.
291 |
292 | ---
293 |
294 | ## Deploy ERC721 Token
295 |
296 | ### Step 1 - Create Configuration Files & Install Dependencies For ERC721
297 |
298 | [See ERC20 - Step 1](#step-1---create-configuration-files--install-dependencies)
299 |
300 | ### Step 2 - Deploy Contract For ERC721
301 |
302 | ```bash
303 | npx hardhat run scripts/erc721Deploy.ts --network mumbai;
304 |
305 | # Expected output:
306 | # Compiled x Solidity files successfully
307 | # ERC721zkMint deployed to 0xABc1...}
308 | ```
309 |
310 | ### Step 3 - Create Claim For ERC721
311 |
312 | [See ERC20 - Step 3](#step-3---create-claim)
313 |
314 | ### Step 4 - Set ZKP Request For ERC721
315 |
316 | Modify the following file
317 |
318 | **File:** `./scripts/erc721ZkpRequest.ts`
319 |
320 | ```ts
321 | // Change to copied schemaHas from claim in Step 2C
322 | const schemaHash = "9c2498080a90d43cada7fec79eeee8de";
323 | // Changed to deployed contract
324 | const ERC721VerifierAddress = "0x085523dF632FEaAE3Ae232E0EBc31FaC9956ddAb";
325 | const schemaEnd = fromLittleEndian(hexToBytes(schemaHash));
326 | const query = {
327 | schema: ethers.BigNumber.from(schemaEnd),
328 | slotIndex: 2,
329 | operator: 2,
330 | // Change to 10 or value that you want to use (eg. everything greater than 10)
331 | value: [10, ...new Array(63).fill(0).map(i => 0)],
332 | circuitId,
333 | };
334 | ```
335 |
336 | Set it on the contract:
337 |
338 | ```bash
339 | npx hardhat run --network mumbai scripts/erc721ZkpRequest.ts;
340 |
341 | # Expected output:
342 | # Request set at:
343 | # NOTE: May take a little bit to show up
344 | # https://mumbai.polygonscan.com/tx/0x60ac...
345 | ```
346 |
347 | ### Step 5 - QR Code Proof Request For ERC721
348 |
349 | [See ERC20 - Step 5](#step-5---qr-code-proof-request)
350 |
351 | ### Step 6 - Mint NFT With Proof For ERC721
352 |
353 | [See ERC20 - Step 6](#step-6---mint-airdrop-with-proof)
354 |
355 | ### Step 7 - See On OpenSea
356 |
357 | Go to your [OpenSea Account](https://testnets.opensea.io/account), connect your wallet, and see the minted hex colour nft.
358 |
359 | 
360 |
361 |
--------------------------------------------------------------------------------
/README/claim-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-01.png
--------------------------------------------------------------------------------
/README/claim-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-02.png
--------------------------------------------------------------------------------
/README/claim-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-03.png
--------------------------------------------------------------------------------
/README/claim-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-04.png
--------------------------------------------------------------------------------
/README/claim-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-05.png
--------------------------------------------------------------------------------
/README/claim-06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-06.png
--------------------------------------------------------------------------------
/README/claim-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/claim-07.png
--------------------------------------------------------------------------------
/README/create-schema-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/create-schema-01.png
--------------------------------------------------------------------------------
/README/create-schema-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/create-schema-02.png
--------------------------------------------------------------------------------
/README/create-schema-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/create-schema-03.png
--------------------------------------------------------------------------------
/README/error-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/error-01.png
--------------------------------------------------------------------------------
/README/error-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/error-02.png
--------------------------------------------------------------------------------
/README/error-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/error-03.png
--------------------------------------------------------------------------------
/README/error-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/error-04.png
--------------------------------------------------------------------------------
/README/mint-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/mint-01.png
--------------------------------------------------------------------------------
/README/mint-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/mint-02.png
--------------------------------------------------------------------------------
/README/mint-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/mint-03.png
--------------------------------------------------------------------------------
/README/mint-04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/mint-04.png
--------------------------------------------------------------------------------
/README/opensea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/opensea.png
--------------------------------------------------------------------------------
/README/platform-test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/platform-test.png
--------------------------------------------------------------------------------
/README/toc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codingwithmanny/polygonid-on-chain-verification/211e82ce87f7092d6d8354342593c2c0e24fa9ba/README/toc.png
--------------------------------------------------------------------------------
/contracts/ERC20Verifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
7 | import "./lib/GenesisUtils.sol";
8 | import "./interfaces/ICircuitValidator.sol";
9 | import "./verifiers/ZKPVerifier.sol";
10 |
11 | // Main Contract
12 | // ========================================================
13 | contract ERC20Verifier is ERC20, ZKPVerifier {
14 | // Variables
15 | uint64 public constant TRANSFER_REQUEST_ID = 1;
16 | uint256 public TOKEN_AMOUNT_FOR_AIRDROP_PER_ID =
17 | 5 * 10**uint256(decimals());
18 | mapping(uint256 => address) public idToAddress;
19 | mapping(address => uint256) public addressToId;
20 |
21 | // Functions
22 | /**
23 | * @dev constructor
24 | */
25 | constructor(string memory name_, string memory symbol_)
26 | ERC20(name_, symbol_)
27 | {}
28 |
29 | /**
30 | * @dev _beforeProofSubmit
31 | */
32 | function _beforeProofSubmit(
33 | uint64, /* requestId */
34 | uint256[] memory inputs,
35 | ICircuitValidator validator
36 | ) internal view override {
37 | // check that challenge input of the proof is equal to the msg.sender
38 | address addr = GenesisUtils.int256ToAddress(
39 | inputs[validator.getChallengeInputIndex()]
40 | );
41 | require(
42 | _msgSender() == addr,
43 | "address in proof is not a sender address"
44 | );
45 | }
46 |
47 | /**
48 | * @dev _afterProofSubmit
49 | */
50 | function _afterProofSubmit(
51 | uint64 requestId,
52 | uint256[] memory inputs,
53 | ICircuitValidator validator
54 | ) internal override {
55 | require(
56 | requestId == TRANSFER_REQUEST_ID && addressToId[_msgSender()] == 0,
57 | "proof can not be submitted more than once"
58 | );
59 |
60 | uint256 id = inputs[validator.getChallengeInputIndex()];
61 | // execute the airdrop
62 | if (idToAddress[id] == address(0)) {
63 | super._mint(_msgSender(), TOKEN_AMOUNT_FOR_AIRDROP_PER_ID);
64 | addressToId[_msgSender()] = id;
65 | idToAddress[id] = _msgSender();
66 | }
67 | }
68 |
69 | /**
70 | * @dev _beforeTokenTransfer
71 | */
72 | function _beforeTokenTransfer(
73 | address, /* from */
74 | address to,
75 | uint256 /* amount */
76 | ) internal view override {
77 | require(
78 | proofs[to][TRANSFER_REQUEST_ID] == true,
79 | "only identities who provided proof are allowed to receive tokens"
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/contracts/ERC721Verifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
7 | import "@openzeppelin/contracts/utils/Counters.sol";
8 | import "./lib/GenesisUtils.sol";
9 | import "./interfaces/ICircuitValidator.sol";
10 | import "./verifiers/ZKPVerifier.sol";
11 |
12 | // Main Contract
13 | // ========================================================
14 | contract ERC721Verifier is ERC721, ZKPVerifier {
15 | // Variables
16 | uint64 public constant TRANSFER_REQUEST_ID = 1;
17 | string private erc721Name;
18 | string private erc721Symbol;
19 | mapping(uint256 => address) public idToAddress;
20 | mapping(address => uint256) public addressToId;
21 | using Counters for Counters.Counter;
22 | Counters.Counter private _tokenIdCounter;
23 | // Constants
24 | string internal constant TABLE =
25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26 |
27 | // Functions
28 | /**
29 | * @dev constructor
30 | */
31 | constructor(string memory name_, string memory symbol_)
32 | ERC721(name_, symbol_)
33 | {
34 | erc721Name = name_;
35 | erc721Symbol = symbol_;
36 | }
37 |
38 | /**
39 | * @dev _beforeProofSubmit
40 | */
41 | function _beforeProofSubmit(
42 | uint64, /* requestId */
43 | uint256[] memory inputs,
44 | ICircuitValidator validator
45 | ) internal view override {
46 | // check that challenge input of the proof is equal to the msg.sender
47 | address addr = GenesisUtils.int256ToAddress(
48 | inputs[validator.getChallengeInputIndex()]
49 | );
50 | require(
51 | _msgSender() == addr,
52 | "address in proof is not a sender address"
53 | );
54 | }
55 |
56 | /**
57 | * @dev _afterProofSubmit
58 | */
59 | function _afterProofSubmit(
60 | uint64 requestId,
61 | uint256[] memory inputs,
62 | ICircuitValidator validator
63 | ) internal override {
64 | require(
65 | requestId == TRANSFER_REQUEST_ID && addressToId[_msgSender()] == 0,
66 | "proof can not be submitted more than once"
67 | );
68 |
69 | uint256 id = inputs[validator.getChallengeInputIndex()];
70 | // execute the airdrop
71 | if (idToAddress[id] == address(0)) {
72 | uint256 tokenId = _tokenIdCounter.current();
73 | _tokenIdCounter.increment();
74 | _safeMint(_msgSender(), tokenId);
75 | addressToId[_msgSender()] = id;
76 | idToAddress[id] = _msgSender();
77 | }
78 | }
79 |
80 | /**
81 | * @dev _beforeTokenTransfer
82 | */
83 | function _beforeTokenTransfer(
84 | address, /* from */
85 | address to,
86 | uint256 /* amount */
87 | ) internal view override {
88 | require(
89 | proofs[to][TRANSFER_REQUEST_ID] == true,
90 | "only identities who provided proof are allowed to receive tokens"
91 | );
92 | }
93 |
94 | /**
95 | * Inspired by Java code
96 | * Converts a decimal value to a hex value without the #
97 | */
98 | function uintToHex(uint256 decimalValue)
99 | public
100 | pure
101 | returns (bytes memory)
102 | {
103 | uint256 remainder;
104 | bytes memory hexResult = "";
105 | string[16] memory hexDictionary = [
106 | "0",
107 | "1",
108 | "2",
109 | "3",
110 | "4",
111 | "5",
112 | "6",
113 | "7",
114 | "8",
115 | "9",
116 | "A",
117 | "B",
118 | "C",
119 | "D",
120 | "E",
121 | "F"
122 | ];
123 |
124 | while (decimalValue > 0) {
125 | remainder = decimalValue % 16;
126 | string memory hexValue = hexDictionary[remainder];
127 | hexResult = abi.encodePacked(hexValue, hexResult);
128 | decimalValue = decimalValue / 16;
129 | }
130 |
131 | // Account for missing leading zeros
132 | uint256 len = hexResult.length;
133 |
134 | if (len == 5) {
135 | hexResult = abi.encodePacked("0", hexResult);
136 | } else if (len == 4) {
137 | hexResult = abi.encodePacked("00", hexResult);
138 | } else if (len == 3) {
139 | hexResult = abi.encodePacked("000", hexResult);
140 | } else if (len == 4) {
141 | hexResult = abi.encodePacked("0000", hexResult);
142 | }
143 |
144 | return hexResult;
145 | }
146 |
147 | /**
148 | */
149 | function base64Encode(bytes memory data)
150 | internal
151 | pure
152 | returns (string memory)
153 | {
154 | if (data.length == 0) return "";
155 |
156 | // load the table into memory
157 | string memory table = TABLE;
158 |
159 | // multiply by 4/3 rounded up
160 | uint256 encodedLen = 4 * ((data.length + 2) / 3);
161 |
162 | // add some extra buffer at the end required for the writing
163 | string memory result = new string(encodedLen + 32);
164 |
165 | assembly {
166 | // set the actual output length
167 | mstore(result, encodedLen)
168 |
169 | // prepare the lookup table
170 | let tablePtr := add(table, 1)
171 |
172 | // input ptr
173 | let dataPtr := data
174 | let endPtr := add(dataPtr, mload(data))
175 |
176 | // result ptr, jump over length
177 | let resultPtr := add(result, 32)
178 |
179 | // run over the input, 3 bytes at a time
180 | for {
181 |
182 | } lt(dataPtr, endPtr) {
183 |
184 | } {
185 | dataPtr := add(dataPtr, 3)
186 |
187 | // read 3 bytes
188 | let input := mload(dataPtr)
189 |
190 | // write 4 characters
191 | mstore(
192 | resultPtr,
193 | shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F))))
194 | )
195 | resultPtr := add(resultPtr, 1)
196 | mstore(
197 | resultPtr,
198 | shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F))))
199 | )
200 | resultPtr := add(resultPtr, 1)
201 | mstore(
202 | resultPtr,
203 | shl(248, mload(add(tablePtr, and(shr(6, input), 0x3F))))
204 | )
205 | resultPtr := add(resultPtr, 1)
206 | mstore(
207 | resultPtr,
208 | shl(248, mload(add(tablePtr, and(input, 0x3F))))
209 | )
210 | resultPtr := add(resultPtr, 1)
211 | }
212 |
213 | // padding with '='
214 | switch mod(mload(data), 3)
215 | case 1 {
216 | mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
217 | }
218 | case 2 {
219 | mstore(sub(resultPtr, 1), shl(248, 0x3d))
220 | }
221 | }
222 |
223 | return result;
224 | }
225 |
226 | /**
227 | * Inspired by OraclizeAPI's implementation - MIT license
228 | * https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
229 | */
230 | function toString(uint256 value) internal pure returns (string memory) {
231 | if (value == 0) {
232 | return "0";
233 | }
234 | uint256 temp = value;
235 | uint256 digits;
236 | while (temp != 0) {
237 | digits++;
238 | temp /= 10;
239 | }
240 | bytes memory buffer = new bytes(digits);
241 | while (value != 0) {
242 | digits -= 1;
243 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
244 | value /= 10;
245 | }
246 | return string(buffer);
247 | }
248 |
249 | /**
250 | * Returns metadata associated to token
251 | */
252 | function tokenURI(uint256 tokenId)
253 | public
254 | view
255 | override
256 | returns (string memory)
257 | {
258 | // Validate if tokenId exists
259 | require(_exists(tokenId), "Token ID does not exist.");
260 |
261 | string memory hexValue = string(uintToHex(tokenId));
262 |
263 | string memory json = base64Encode(
264 | bytes(
265 | string(
266 | abi.encodePacked(
267 | '{"id": ',
268 | toString(tokenId),
269 | ", ",
270 | '"name": "#',
271 | hexValue,
272 | '", ',
273 | '"token_name": "',
274 | erc721Name,
275 | '", ',
276 | '"token_symbol": "',
277 | erc721Symbol,
278 | '", ',
279 | '"background_color": "FFFFFF", ',
280 | '"image": "data:image/svg+xml;base64,',
281 | base64Encode(
282 | bytes(
283 | string(
284 | abi.encodePacked(
285 | ''
288 | )
289 | )
290 | )
291 | ),
292 | '"}'
293 | )
294 | )
295 | )
296 | );
297 |
298 | return string(abi.encodePacked("data:application/json;base64,", json));
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/contracts/interfaces/ICircuitValidator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Interface
5 | // ========================================================
6 | interface ICircuitValidator {
7 | // Variables
8 | struct CircuitQuery {
9 | uint256 schema;
10 | uint256 slotIndex;
11 | uint256 operator;
12 | uint256[] value;
13 | string circuitId;
14 | }
15 |
16 | /**
17 | * @dev verify
18 | */
19 | function verify(
20 | uint256[] memory inputs,
21 | uint256[2] memory a,
22 | uint256[2][2] memory b,
23 | uint256[2] memory c,
24 | CircuitQuery memory query
25 | ) external view returns (bool r);
26 |
27 | /**
28 | * @dev getCircuitId
29 | */
30 | function getCircuitId() external pure returns (string memory id);
31 |
32 | /**
33 | * @dev getChallengeInputIndex
34 | */
35 | function getChallengeInputIndex() external pure returns (uint256 index);
36 |
37 | /**
38 | * @dev getUserIdInputIndex
39 | */
40 | function getUserIdInputIndex() external pure returns (uint256 index);
41 | }
42 |
--------------------------------------------------------------------------------
/contracts/interfaces/IZKPVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./ICircuitValidator.sol";
7 |
8 | // Interface
9 | // ========================================================
10 | interface IZKPVerifier {
11 | /**
12 | * @dev submitZKPResponse
13 | */
14 | function submitZKPResponse(
15 | uint64 requestId,
16 | uint256[] memory inputs,
17 | uint256[2] memory a,
18 | uint256[2][2] memory b,
19 | uint256[2] memory c
20 | ) external returns (bool);
21 |
22 | /**
23 | * @dev setZKPRequest
24 | */
25 | function setZKPRequest(
26 | uint64 requestId,
27 | ICircuitValidator validator,
28 | ICircuitValidator.CircuitQuery memory query
29 | ) external returns (bool);
30 |
31 | /**
32 | * @dev getZKPRequest
33 | */
34 | function getZKPRequest(uint64 requestId)
35 | external
36 | returns (ICircuitValidator.CircuitQuery memory);
37 | }
38 |
--------------------------------------------------------------------------------
/contracts/lib/GenesisUtils.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "solidity-bytes-utils/contracts/BytesLib.sol";
7 |
8 | // Library
9 | // ========================================================
10 | library GenesisUtils {
11 | /**
12 | * @dev int256ToBytes
13 | */
14 | function int256ToBytes(uint256 x) internal pure returns (bytes memory b) {
15 | b = new bytes(32);
16 | assembly {
17 | mstore(add(b, 32), x)
18 | }
19 | }
20 |
21 | /**
22 | * @dev reverse
23 | */
24 | function reverse(uint256 input) internal pure returns (uint256 v) {
25 | v = input;
26 |
27 | // swap bytes
28 | v =
29 | ((v &
30 | 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >>
31 | 8) |
32 | ((v &
33 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) <<
34 | 8);
35 |
36 | // swap 2-byte long pairs
37 | v =
38 | ((v &
39 | 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >>
40 | 16) |
41 | ((v &
42 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) <<
43 | 16);
44 |
45 | // swap 4-byte long pairs
46 | v =
47 | ((v &
48 | 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >>
49 | 32) |
50 | ((v &
51 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) <<
52 | 32);
53 |
54 | // swap 8-byte long pairs
55 | v =
56 | ((v &
57 | 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >>
58 | 64) |
59 | ((v &
60 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) <<
61 | 64);
62 |
63 | // swap 16-byte long pairs
64 | v = (v >> 128) | (v << 128);
65 | }
66 |
67 | /**
68 | * @dev sum
69 | */
70 | function sum(bytes memory array) internal pure returns (uint16 s) {
71 | require(array.length == 29, "Checksum requires 29 length array");
72 |
73 | for (uint256 i = 0; i < array.length; ++i) {
74 | s += uint16(uint8(array[i]));
75 | }
76 | }
77 |
78 | /**
79 | * @dev bytesToHexString
80 | */
81 | function bytesToHexString(bytes memory buffer)
82 | internal
83 | pure
84 | returns (string memory)
85 | {
86 | // Fixed buffer size for hexadecimal convertion
87 | bytes memory converted = new bytes(buffer.length * 2);
88 |
89 | bytes memory _base = "0123456789abcdef";
90 |
91 | for (uint256 i = 0; i < buffer.length; i++) {
92 | converted[i * 2] = _base[uint8(buffer[i]) / _base.length];
93 | converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length];
94 | }
95 |
96 | return string(abi.encodePacked("0x", converted));
97 | }
98 |
99 | /**
100 | * @dev compareStrings
101 | */
102 | function compareStrings(string memory a, string memory b)
103 | internal
104 | pure
105 | returns (bool)
106 | {
107 | return (keccak256(abi.encodePacked((a))) ==
108 | keccak256(abi.encodePacked((b))));
109 | }
110 |
111 | /**
112 | * @dev isGenesisState
113 | */
114 | function isGenesisState(uint256 id, uint256 idState)
115 | internal
116 | pure
117 | returns (bool)
118 | {
119 | uint256 userSwappedState = reverse(idState);
120 |
121 | bytes memory userStateB1 = int256ToBytes(userSwappedState);
122 |
123 | bytes memory cutState = BytesLib.slice(
124 | userStateB1,
125 | userStateB1.length - 27,
126 | 27
127 | );
128 |
129 | bytes memory typDefault = hex"0000";
130 |
131 | bytes memory beforeChecksum = BytesLib.concat(typDefault, cutState);
132 | require(
133 | beforeChecksum.length == 29,
134 | "Checksum requires 29 length array"
135 | );
136 |
137 | uint16 s = sum(beforeChecksum);
138 |
139 | bytes memory checkSumBytes = abi.encodePacked(s);
140 |
141 | bytes memory idBytes = BytesLib.concat(beforeChecksum, checkSumBytes);
142 | require(idBytes.length == 31, "idBytes requires 31 length array");
143 |
144 | return id == reverse(toUint256(idBytes));
145 | }
146 |
147 | /**
148 | * @dev toUint256
149 | */
150 | function toUint256(bytes memory _bytes)
151 | internal
152 | pure
153 | returns (uint256 value)
154 | {
155 | assembly {
156 | value := mload(add(_bytes, 0x20))
157 | }
158 | }
159 |
160 | /**
161 | * @dev bytesToAddress
162 | */
163 | function bytesToAddress(bytes memory bys)
164 | internal
165 | pure
166 | returns (address addr)
167 | {
168 | assembly {
169 | addr := mload(add(bys, 20))
170 | }
171 | }
172 |
173 | /**
174 | * @dev int256ToAddress
175 | */
176 | function int256ToAddress(uint256 input) internal pure returns (address) {
177 | return bytesToAddress(int256ToBytes(reverse(input)));
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/contracts/verifiers/ZKPVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "@openzeppelin/contracts/access/Ownable.sol";
7 | import "../interfaces/ICircuitValidator.sol";
8 | import "../interfaces/IZKPVerifier.sol";
9 |
10 | // Contract
11 | // ========================================================
12 | contract ZKPVerifier is IZKPVerifier, Ownable {
13 | // Variables
14 | // msg.sender-> ( requestID -> is proof given )
15 | mapping(address => mapping(uint64 => bool)) public proofs;
16 | mapping(uint64 => ICircuitValidator.CircuitQuery) public requestQueries;
17 | mapping(uint64 => ICircuitValidator) public requestValidators;
18 | uint64[] public supportedRequests;
19 |
20 | // Functions
21 | /**
22 | * @dev submitZKPResponse
23 | */
24 | function submitZKPResponse(
25 | uint64 requestId,
26 | uint256[] memory inputs,
27 | uint256[2] memory a,
28 | uint256[2][2] memory b,
29 | uint256[2] memory c
30 | ) external override returns (bool) {
31 | require(
32 | requestValidators[requestId] != ICircuitValidator(address(0)),
33 | "validator is not set for this request id"
34 | ); // validator exists
35 | require(
36 | requestQueries[requestId].schema != 0,
37 | "query is not set for this request id"
38 | ); // query exists
39 |
40 | _beforeProofSubmit(requestId, inputs, requestValidators[requestId]);
41 |
42 | require(
43 | requestValidators[requestId].verify(
44 | inputs,
45 | a,
46 | b,
47 | c,
48 | requestQueries[requestId]
49 | ),
50 | "proof response is not valid"
51 | );
52 |
53 | proofs[msg.sender][requestId] = true; // user provided a valid proof for request
54 |
55 | _afterProofSubmit(requestId, inputs, requestValidators[requestId]);
56 | return true;
57 | }
58 |
59 | /**
60 | * @dev getZKPRequest
61 | */
62 | function getZKPRequest(uint64 requestId)
63 | external
64 | view
65 | override
66 | returns (ICircuitValidator.CircuitQuery memory)
67 | {
68 | return requestQueries[requestId];
69 | }
70 |
71 | /**
72 | * @dev setZKPRequest
73 | */
74 | function setZKPRequest(
75 | uint64 requestId,
76 | ICircuitValidator validator,
77 | ICircuitValidator.CircuitQuery memory query
78 | ) external override onlyOwner returns (bool) {
79 | if (requestValidators[requestId] == ICircuitValidator(address(0x00))) {
80 | supportedRequests.push(requestId);
81 | }
82 | requestQueries[requestId].value = query.value;
83 | requestQueries[requestId].operator = query.operator;
84 | requestQueries[requestId].circuitId = query.circuitId;
85 | requestQueries[requestId].slotIndex = query.slotIndex;
86 | requestQueries[requestId].schema = query.schema;
87 |
88 | requestQueries[requestId].circuitId = query.circuitId;
89 |
90 | requestValidators[requestId] = validator;
91 | return true;
92 | }
93 |
94 | /**
95 | * @dev getSupportedRequests
96 | */
97 | function getSupportedRequests()
98 | external
99 | view
100 | returns (uint64[] memory arr)
101 | {
102 | return supportedRequests;
103 | }
104 |
105 | /**
106 | * @dev Hook that is called before any proof response submit
107 | */
108 | function _beforeProofSubmit(
109 | uint64 requestId,
110 | uint256[] memory inputs,
111 | ICircuitValidator validator
112 | ) internal virtual {}
113 |
114 | /**
115 | * @dev Hook that is called after any proof response submit
116 | */
117 | function _afterProofSubmit(
118 | uint64 requestId,
119 | uint256[] memory inputs,
120 | ICircuitValidator validator
121 | ) internal virtual {}
122 | }
123 |
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import { HardhatUserConfig } from "hardhat/config";
4 | import "@nomiclabs/hardhat-etherscan";
5 | import "@nomicfoundation/hardhat-toolbox";
6 | import dotenv from 'dotenv';
7 |
8 | // Config
9 | // ========================================================
10 | dotenv.config();
11 |
12 | // Main Configuration Settings
13 | // ========================================================
14 | const config: HardhatUserConfig = {
15 | solidity: {
16 | version: "0.8.17",
17 | settings: {
18 | optimizer: {
19 | enabled: true,
20 | runs: 200
21 | }
22 | }
23 | },
24 | networks: {
25 | mumbai: {
26 | url: `${process.env.RPC_MUMBAI_URL || ''}`,
27 | accounts: process.env.WALLET_PRIVATE_KEY
28 | ? [`0x${process.env.WALLET_PRIVATE_KEY}`]
29 | : [],
30 | allowUnlimitedContractSize: true
31 | }
32 | },
33 | etherscan: {
34 | apiKey: process.env.ETHERSCAN_API_KEY || undefined,
35 | },
36 | };
37 |
38 | // Exports
39 | // ========================================================
40 | export default config;
41 |
--------------------------------------------------------------------------------
/htmlQRClaim/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/htmlQRClaim/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | HTML QR Claim
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/htmlQRClaim/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "htmlqrclaim",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-qr-svg": "^2.4.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.0.22",
18 | "@types/react-dom": "^18.0.7",
19 | "@vitejs/plugin-react": "^2.2.0",
20 | "autoprefixer": "^10.4.12",
21 | "postcss": "^8.4.18",
22 | "tailwindcss": "^3.2.1",
23 | "typescript": "^4.6.4",
24 | "vite": "^3.2.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/htmlQRClaim/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/htmlQRClaim/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/htmlQRClaim/src/App.tsx:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import { QRCode } from 'react-qr-svg';
4 | import proofRequest from '../../proofRequest';
5 |
6 | // Config
7 | // ========================================================
8 | // update with your contract address
9 | const DEPLOYED_CONTRACT_ADDRESS = "0xb1e86C4c687B85520eF4fd2a0d14e81970a15aFB";
10 |
11 | /**
12 | *
13 | */
14 | let qrProofRequestJson: any = { ...proofRequest };
15 | qrProofRequestJson.body.transaction_data.contract_address = DEPLOYED_CONTRACT_ADDRESS;
16 | qrProofRequestJson.body.scope[0].rules.query.req = {
17 | // NOTE: this value needs to match the Attribute name in https://platform-test.polygonid.com
18 | "Date": {
19 | // NOTE: this value needs to match the erc20ZkpRequest.ts L34 or erc721ZkpRequest.ts L34
20 | // - $tl = operator 2 erc20ZkpRequest.ts L38
21 | // - 20020101 = value erc20ZkpRequest.ts L41
22 | "$lt": 20020101
23 | }
24 | };
25 | // NOTE1: if you change this you need to resubmit the erc10|erc721ZKPRequest
26 | // NOTE2: type is case-sensitive
27 | // You can generate new schemas via https://platform-test.polygonid.com
28 | qrProofRequestJson.body.scope[0].rules.query.schema = {
29 | "url": "https://s3.eu-west-1.amazonaws.com/polygonid-schemas/c79191fc-c84e-4203-bb72-4d354839cfb7.json-ld",
30 | "type": "KYCagecredential"
31 | };
32 |
33 | // Main Component
34 | // ========================================================
35 | const App = () => {
36 |
37 | return (
38 |
39 |
44 |
45 | )
46 | };
47 |
48 | // Exports
49 | // ========================================================
50 | export default App;
51 |
--------------------------------------------------------------------------------
/htmlQRClaim/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/htmlQRClaim/src/main.tsx:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import React from 'react';
4 | import ReactDOM from 'react-dom/client';
5 | import App from './App';
6 | import './index.css';
7 |
8 | // Main Render
9 | // ========================================================
10 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/htmlQRClaim/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/htmlQRClaim/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{js,ts,jsx,tsx}",
6 | ],
7 | theme: {
8 | extend: {},
9 | },
10 | plugins: [],
11 | }
12 |
--------------------------------------------------------------------------------
/htmlQRClaim/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": false,
7 | "skipLibCheck": true,
8 | "esModuleInterop": false,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"],
20 | "references": [{ "path": "./tsconfig.node.json" }]
21 | }
22 |
--------------------------------------------------------------------------------
/htmlQRClaim/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/htmlQRClaim/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite';
2 | import react from '@vitejs/plugin-react';
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()]
7 | })
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hardhat-project",
3 | "devDependencies": {
4 | "@ethersproject/abi": "^5.4.7",
5 | "@ethersproject/providers": "^5.4.7",
6 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0",
7 | "@nomicfoundation/hardhat-toolbox": "^2.0.0",
8 | "@nomiclabs/hardhat-ethers": "^2.0.0",
9 | "@typechain/ethers-v5": "^10.1.0",
10 | "@types/chai": "^4.2.0",
11 | "@types/mocha": "^9.1.0",
12 | "@types/node": ">=12.0.0",
13 | "ethers": "^5.4.7",
14 | "hardhat": "^2.11.1",
15 | "typechain": "^8.1.0"
16 | },
17 | "version": "0.0.0",
18 | "dependencies": {
19 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
20 | "@nomiclabs/hardhat-etherscan": "^3.0.0",
21 | "@openzeppelin/contracts": "^4.7.3",
22 | "@typechain/hardhat": "^6.1.2",
23 | "chai": "^4.2.0",
24 | "dotenv": "^16.0.3",
25 | "hardhat-gas-reporter": "^1.0.8",
26 | "solidity-bytes-utils": "^0.8.0",
27 | "solidity-coverage": "^0.8.0",
28 | "ts-node": ">=8.0.0",
29 | "typescript": ">=4.5.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/proofRequest.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | /**
4 | * This represents a template base that will be modified as needed in htmlQRClaim/src/App.tsx
5 | */
6 | const proofRequest = {
7 | // 1. UUID for the request
8 | // - can be anything UUID
9 | "id": "c811849d-6bfb-4d85-936e-3d9759c7f105",
10 | // 2. Content type used by the Polygon ID wallet
11 | // - needs to be constant / does not change
12 | "typ": "application/iden3comm-plain-json",
13 | // 3. ?
14 | "type": "https://iden3-communication.io/proofs/1.0/contract-invoke-request",
15 | // 4. Payload to send for proof request
16 | "body": {
17 | // Function to be executed from the contract for the zk response
18 | "transaction_data": {
19 | // - deployed contract address where it will call a specific function
20 | //
21 | "contract_address": "0xeD05AC777229866383bc0c2388472a21a0c1bE3c",
22 | // - hash of the function name from the ABI - b68967e2 = submitZKPResponse
23 | "method_id": "b68967e2",
24 | // - chain id of the network
25 | "chain_id": 80001,
26 | // - network name
27 | "network": "polygon-mumbai"
28 | },
29 | // Reason for the request
30 | // - Unknown if used or not
31 | "reason": "airdrop participation",
32 | // Scope of request and information required
33 | // - Currently only supports a single array request
34 | "scope": [
35 | {
36 | // - random integer id of the scope
37 | "id": 1,
38 | // - type of request currently supports `credentialAtomicQuerySig` and `credentialAtomicQueryMTP` (not currently used)
39 | "circuit_id": "credentialAtomicQuerySig",
40 | // - conditions of the request
41 | "rules": {
42 | // - only accepts query
43 | "query": {
44 | // - whitelist of polygon ID platform identifier
45 | "allowed_issuers": [
46 | "*"
47 | ],
48 | // - conditions to be met with zk-query-language - see https://0xpolygonid.github.io/tutorials/verifier/verification-library/zk-query-language/
49 | "req": {
50 | "Date": {
51 | // NOTE: this value needs to match the erc20ZkpRequest.ts L34 or erc721ZkpRequest.ts L34
52 | "$lt": 20020101
53 | }
54 | },
55 | // - schema of the proof and type, type is case-sensitive
56 | //
57 | "schema": {
58 | "url": "https://s3.eu-west-1.amazonaws.com/polygonid-schemas/c79191fc-c84e-4203-bb72-4d354839cfb7.json-ld",
59 | "type": "KYCagecredential"
60 | }
61 | }
62 | }
63 | }
64 | ]
65 | }
66 | };
67 |
68 | // Exports
69 | // ========================================================
70 | export default proofRequest;
--------------------------------------------------------------------------------
/scripts/erc20Deploy.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import { ethers } from "hardhat";
4 |
5 | // Deployment Script
6 | // ========================================================
7 | const main = async () => {
8 | // Replace these variables as needed
9 | const verifierContract = "ERC20Verifier";
10 | const verifierName = "ERC20zkAirdrop";
11 | const verifierSymbol = "zkERC20";
12 |
13 | // Deploy contract
14 | const ERC20Verifier = await ethers.getContractFactory(verifierContract);
15 | const erc20Verifier = await ERC20Verifier.deploy(
16 | verifierName,
17 | verifierSymbol
18 | );
19 | await erc20Verifier.deployed();
20 |
21 | // Output result
22 | console.log(`${verifierName} deployed to ${erc20Verifier.address}`);
23 | }
24 |
25 | // Init
26 | // ========================================================
27 | // We recommend this pattern to be able to use async/await everywhere
28 | // and properly handle errors.
29 | main().catch((error) => {
30 | console.error(error);
31 | process.exitCode = 1;
32 | });
33 |
--------------------------------------------------------------------------------
/scripts/erc20ZkpRequest.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import { ethers } from 'hardhat';
4 |
5 | // Deployment Script
6 | // ========================================================
7 | /**
8 | * @dev The goal of this is to set the zero-knowledge proof request on the contract so that went you submit the proof the contract will validate it
9 | */
10 | const main = async () => {
11 | // Main scope circuit identifier supported
12 | // - avoid changing as it's currently the only one supported
13 | const circuitId = "credentialAtomicQuerySig";
14 |
15 | // Main validator address on mumbai - https://mumbai.polygonscan.com/address/0xb1e86C4c687B85520eF4fd2a0d14e81970a15aFB#code
16 | // - do not change for testnet
17 | const validatorAddress = "0xb1e86C4c687B85520eF4fd2a0d14e81970a15aFB";
18 |
19 | // CHANGE THESE
20 | // Contract name when deployed
21 | const verifierContract = "ERC20Verifier";
22 | // Schema has provided by the issuer
23 | // - typically found in https://platform-test.polygonid.com/claiming/created-schemas
24 | const schemaHash = "9c2498080a90d43cada7fec79eeee8de"; // extracted from PID Platform
25 | // Deployed contract address
26 | const ERC20VerifierAddress = "0x085523dF632FEaAE3Ae232E0EBc31FaC9956ddAb";
27 | const schemaEnd = fromLittleEndian(hexToBytes(schemaHash));
28 | const query = {
29 | schema: ethers.BigNumber.from(schemaEnd),
30 | // slotIndex2 indicates the value stored as Attribute 1 inside the claim
31 | slotIndex: 2,
32 | // see - https://0xpolygonid.github.io/tutorials/verifier/verification-library/zk-query-language/
33 | // 1 = equals
34 | // 2 = less-than
35 | // 3 = greater-than
36 | // 4 = in
37 | // 5 = not-in
38 | operator: 2,
39 | // 20020101 refers to the numerical date we're using for our proof request
40 | // - see proofRequest.ts L489
41 | value: [20020101, ...new Array(63).fill(0).map(i => 0)], // the value must be 1 = true
42 | circuitId,
43 | };
44 |
45 | // Retrieve contract to interact with it
46 | const erc20Verifier = await ethers.getContractAt(verifierContract, ERC20VerifierAddress);
47 |
48 | // Set zkpRequest for contract
49 | try {
50 | // Use as a means to keep track in the contract for number of mints a person can perform from a specific wallet address
51 | const requestId = Number(await erc20Verifier.TRANSFER_REQUEST_ID());
52 | const tx = await erc20Verifier.setZKPRequest(
53 | requestId, // 1
54 | validatorAddress,
55 | query
56 | );
57 |
58 | tx.wait();
59 | console.log(`Request set at:\nNOTE: May take a little bit to show up\nhttps://mumbai.polygonscan.com/tx/${tx.hash}`);
60 | } catch (e) {
61 | console.error("Error: ", e);
62 | }
63 | };
64 |
65 | // Helper Functions
66 | // ========================================================
67 | /**
68 | *
69 | * @param hex
70 | * @returns array of bytes
71 | */
72 | const hexToBytes = (hex: string) => {
73 | for (var bytes = [], c = 0; c < hex.length; c += 2) {
74 | /**
75 | * @dev parseInt 16 = parsed as a hexadecimal number
76 | */
77 | bytes.push(parseInt(hex.substr(c, 2), 16));
78 | }
79 | return bytes;
80 | };
81 |
82 | /**
83 | * @dev Little-Endian: https://learnmeabitcoin.com/technical/little-endian
84 | * "Little-endian" refers to the order in which bytes of information can be processed by a computer.
85 | * @param bytes array of numbers for bytes
86 | * @returns number
87 | */
88 | const fromLittleEndian = (bytes: number[]) => {
89 | const n256 = BigInt(256);
90 | let result = BigInt(0);
91 | let base = BigInt(1);
92 | bytes.forEach((byte) => {
93 | result += base * BigInt(byte);
94 | base = base * n256;
95 | });
96 | return result;
97 | };
98 |
99 | // Init
100 | // ========================================================
101 | // We recommend this pattern to be able to use async/await everywhere
102 | // and properly handle errors.
103 | main().catch((error) => {
104 | console.error(error);
105 | process.exitCode = 1;
106 | });
107 |
--------------------------------------------------------------------------------
/scripts/erc721Deploy.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import { ethers } from "hardhat";
4 |
5 | // Deployment Script
6 | // ========================================================
7 | const main = async () => {
8 | // Replace these variables as needed
9 | const verifierContract = "ERC721Verifier";
10 | const verifierName = "ERC721zkMint";
11 | const verifierSymbol = "zkERC721";
12 |
13 | // Deploy contract
14 | const ERC721Verifier = await ethers.getContractFactory(verifierContract);
15 | const erc721Verifier = await ERC721Verifier.deploy(
16 | verifierName,
17 | verifierSymbol
18 | );
19 | await erc721Verifier.deployed();
20 |
21 | // Output result
22 | console.log(`${verifierName} deployed to ${erc721Verifier.address}`);
23 | }
24 |
25 | // Init
26 | // ========================================================
27 | // We recommend this pattern to be able to use async/await everywhere
28 | // and properly handle errors.
29 | main().catch((error) => {
30 | console.log(error?.message);
31 | console.error(error);
32 | process.exitCode = 1;
33 | });
34 |
--------------------------------------------------------------------------------
/scripts/erc721ZkpRequest.ts:
--------------------------------------------------------------------------------
1 | // Imports
2 | // ========================================================
3 | import { ethers } from 'hardhat';
4 |
5 | // Deployment Script
6 | // ========================================================
7 | /**
8 | * @dev The goal of this is to set the zero-knowledge proof request on the contract so that went you submit the proof the contract will validate it
9 | */
10 | const main = async () => {
11 | // Main scope circuit identifier supported
12 | // - avoid changing as it's currently the only one supported
13 | const circuitId = "credentialAtomicQuerySig";
14 |
15 | // Main validator address on mumbai - https://mumbai.polygonscan.com/address/0xb1e86C4c687B85520eF4fd2a0d14e81970a15aFB#code
16 | // - do not change for testnet
17 | const validatorAddress = "0xb1e86C4c687B85520eF4fd2a0d14e81970a15aFB";
18 |
19 | // CHANGE THESE
20 | // Contract name when deployed
21 | const verifierContract = "ERC721Verifier";
22 | // Schema has provided by the issuer
23 | // - typically found in https://platform-test.polygonid.com/claiming/created-schemas
24 | const schemaHash = "9c2498080a90d43cada7fec79eeee8de"; // extracted from PID Platform
25 | // Deployed contract address
26 | const ERC721VerifierAddress = "0xCfFEb70395C8E2A98d20Facf67122d638b18673c";
27 | const schemaEnd = fromLittleEndian(hexToBytes(schemaHash));
28 | const query = {
29 | schema: ethers.BigNumber.from(schemaEnd),
30 | slotIndex: 2, // slotIndex2 indicates the value stored as Attribute 1 inside the claim
31 | operator: 2,
32 | // 20020101 refers to the numerical date we're using for our proof request
33 | // - see proofRequest.ts L489
34 | value: [20020101, ...new Array(63).fill(0).map(i => 0)], // the value must be 1 = true
35 | circuitId,
36 | };
37 |
38 | // Retrieve contract to interact with it
39 | const erc721Verifier = await ethers.getContractAt(verifierContract, ERC721VerifierAddress);
40 |
41 | // Set zkpRequest for contract
42 | try {
43 | // Use as a means to keep track in the contract for number of mints a person can perform from a specific wallet address
44 | const requestId = Number(await erc721Verifier.TRANSFER_REQUEST_ID());
45 | const tx = await erc721Verifier.setZKPRequest(
46 | requestId, // 1
47 | validatorAddress,
48 | query
49 | );
50 |
51 | tx.wait();
52 | console.log(`Request set at:\nNOTE: May take a little bit to show up\nhttps://mumbai.polygonscan.com/tx/${tx.hash}`);
53 | } catch (e) {
54 | console.error("Error: ", e);
55 | }
56 | };
57 |
58 | // Helper Functions
59 | // ========================================================
60 | /**
61 | *
62 | * @param hex
63 | * @returns array of bytes
64 | */
65 | const hexToBytes = (hex: string) => {
66 | for (var bytes = [], c = 0; c < hex.length; c += 2) {
67 | /**
68 | * @dev parseInt 16 = parsed as a hexadecimal number
69 | */
70 | bytes.push(parseInt(hex.substr(c, 2), 16));
71 | }
72 | return bytes;
73 | };
74 |
75 | /**
76 | * @dev Little-Endian: https://learnmeabitcoin.com/technical/little-endian
77 | * "Little-endian" refers to the order in which bytes of information can be processed by a computer.
78 | * @param bytes array of numbers for bytes
79 | * @returns number
80 | */
81 | const fromLittleEndian = (bytes: number[]) => {
82 | const n256 = BigInt(256);
83 | let result = BigInt(0);
84 | let base = BigInt(1);
85 | bytes.forEach((byte) => {
86 | result += base * BigInt(byte);
87 | base = base * n256;
88 | });
89 | return result;
90 | };
91 |
92 | // Init
93 | // ========================================================
94 | // We recommend this pattern to be able to use async/await everywhere
95 | // and properly handle errors.
96 | main().catch((error) => {
97 | console.error(error);
98 | process.exitCode = 1;
99 | });
100 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "skipLibCheck": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/BytesLib.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Unlicense
2 | /*
3 | * @title Solidity Bytes Arrays Utils
4 | * @author Gonçalo Sá
5 | *
6 | * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
7 | * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
8 | */
9 | pragma solidity >=0.8.0 <0.9.0;
10 |
11 | library BytesLib {
12 | function concat(bytes memory _preBytes, bytes memory _postBytes)
13 | internal
14 | pure
15 | returns (bytes memory)
16 | {
17 | bytes memory tempBytes;
18 |
19 | assembly {
20 | // Get a location of some free memory and store it in tempBytes as
21 | // Solidity does for memory variables.
22 | tempBytes := mload(0x40)
23 |
24 | // Store the length of the first bytes array at the beginning of
25 | // the memory for tempBytes.
26 | let length := mload(_preBytes)
27 | mstore(tempBytes, length)
28 |
29 | // Maintain a memory counter for the current write location in the
30 | // temp bytes array by adding the 32 bytes for the array length to
31 | // the starting location.
32 | let mc := add(tempBytes, 0x20)
33 | // Stop copying when the memory counter reaches the length of the
34 | // first bytes array.
35 | let end := add(mc, length)
36 |
37 | for {
38 | // Initialize a copy counter to the start of the _preBytes data,
39 | // 32 bytes into its memory.
40 | let cc := add(_preBytes, 0x20)
41 | } lt(mc, end) {
42 | // Increase both counters by 32 bytes each iteration.
43 | mc := add(mc, 0x20)
44 | cc := add(cc, 0x20)
45 | } {
46 | // Write the _preBytes data into the tempBytes memory 32 bytes
47 | // at a time.
48 | mstore(mc, mload(cc))
49 | }
50 |
51 | // Add the length of _postBytes to the current length of tempBytes
52 | // and store it as the new length in the first 32 bytes of the
53 | // tempBytes memory.
54 | length := mload(_postBytes)
55 | mstore(tempBytes, add(length, mload(tempBytes)))
56 |
57 | // Move the memory counter back from a multiple of 0x20 to the
58 | // actual end of the _preBytes data.
59 | mc := end
60 | // Stop copying when the memory counter reaches the new combined
61 | // length of the arrays.
62 | end := add(mc, length)
63 |
64 | for {
65 | let cc := add(_postBytes, 0x20)
66 | } lt(mc, end) {
67 | mc := add(mc, 0x20)
68 | cc := add(cc, 0x20)
69 | } {
70 | mstore(mc, mload(cc))
71 | }
72 |
73 | // Update the free-memory pointer by padding our last write location
74 | // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
75 | // next 32 byte block, then round down to the nearest multiple of
76 | // 32. If the sum of the length of the two arrays is zero then add
77 | // one before rounding down to leave a blank 32 bytes (the length block with 0).
78 | mstore(
79 | 0x40,
80 | and(
81 | add(add(end, iszero(add(length, mload(_preBytes)))), 31),
82 | not(31) // Round down to the nearest 32 bytes.
83 | )
84 | )
85 | }
86 |
87 | return tempBytes;
88 | }
89 |
90 | function concatStorage(bytes storage _preBytes, bytes memory _postBytes)
91 | internal
92 | {
93 | assembly {
94 | // Read the first 32 bytes of _preBytes storage, which is the length
95 | // of the array. (We don't need to use the offset into the slot
96 | // because arrays use the entire slot.)
97 | let fslot := sload(_preBytes.slot)
98 | // Arrays of 31 bytes or less have an even value in their slot,
99 | // while longer arrays have an odd value. The actual length is
100 | // the slot divided by two for odd values, and the lowest order
101 | // byte divided by two for even values.
102 | // If the slot is even, bitwise and the slot with 255 and divide by
103 | // two to get the length. If the slot is odd, bitwise and the slot
104 | // with -1 and divide by two.
105 | let slength := div(
106 | and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
107 | 2
108 | )
109 | let mlength := mload(_postBytes)
110 | let newlength := add(slength, mlength)
111 | // slength can contain both the length and contents of the array
112 | // if length < 32 bytes so let's prepare for that
113 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
114 | switch add(lt(slength, 32), lt(newlength, 32))
115 | case 2 {
116 | // Since the new array still fits in the slot, we just need to
117 | // update the contents of the slot.
118 | // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
119 | sstore(
120 | _preBytes.slot,
121 | // all the modifications to the slot are inside this
122 | // next block
123 | add(
124 | // we can just add to the slot contents because the
125 | // bytes we want to change are the LSBs
126 | fslot,
127 | add(
128 | mul(
129 | div(
130 | // load the bytes from memory
131 | mload(add(_postBytes, 0x20)),
132 | // zero all bytes to the right
133 | exp(0x100, sub(32, mlength))
134 | ),
135 | // and now shift left the number of bytes to
136 | // leave space for the length in the slot
137 | exp(0x100, sub(32, newlength))
138 | ),
139 | // increase length by the double of the memory
140 | // bytes length
141 | mul(mlength, 2)
142 | )
143 | )
144 | )
145 | }
146 | case 1 {
147 | // The stored value fits in the slot, but the combined value
148 | // will exceed it.
149 | // get the keccak hash to get the contents of the array
150 | mstore(0x0, _preBytes.slot)
151 | let sc := add(keccak256(0x0, 0x20), div(slength, 32))
152 |
153 | // save new length
154 | sstore(_preBytes.slot, add(mul(newlength, 2), 1))
155 |
156 | // The contents of the _postBytes array start 32 bytes into
157 | // the structure. Our first read should obtain the `submod`
158 | // bytes that can fit into the unused space in the last word
159 | // of the stored array. To get this, we read 32 bytes starting
160 | // from `submod`, so the data we read overlaps with the array
161 | // contents by `submod` bytes. Masking the lowest-order
162 | // `submod` bytes allows us to add that value directly to the
163 | // stored value.
164 |
165 | let submod := sub(32, slength)
166 | let mc := add(_postBytes, submod)
167 | let end := add(_postBytes, mlength)
168 | let mask := sub(exp(0x100, submod), 1)
169 |
170 | sstore(
171 | sc,
172 | add(
173 | and(
174 | fslot,
175 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
176 | ),
177 | and(mload(mc), mask)
178 | )
179 | )
180 |
181 | for {
182 | mc := add(mc, 0x20)
183 | sc := add(sc, 1)
184 | } lt(mc, end) {
185 | sc := add(sc, 1)
186 | mc := add(mc, 0x20)
187 | } {
188 | sstore(sc, mload(mc))
189 | }
190 |
191 | mask := exp(0x100, sub(mc, end))
192 |
193 | sstore(sc, mul(div(mload(mc), mask), mask))
194 | }
195 | default {
196 | // get the keccak hash to get the contents of the array
197 | mstore(0x0, _preBytes.slot)
198 | // Start copying to the last used word of the stored array.
199 | let sc := add(keccak256(0x0, 0x20), div(slength, 32))
200 |
201 | // save new length
202 | sstore(_preBytes.slot, add(mul(newlength, 2), 1))
203 |
204 | // Copy over the first `submod` bytes of the new data as in
205 | // case 1 above.
206 | let slengthmod := mod(slength, 32)
207 | let mlengthmod := mod(mlength, 32)
208 | let submod := sub(32, slengthmod)
209 | let mc := add(_postBytes, submod)
210 | let end := add(_postBytes, mlength)
211 | let mask := sub(exp(0x100, submod), 1)
212 |
213 | sstore(sc, add(sload(sc), and(mload(mc), mask)))
214 |
215 | for {
216 | sc := add(sc, 1)
217 | mc := add(mc, 0x20)
218 | } lt(mc, end) {
219 | sc := add(sc, 1)
220 | mc := add(mc, 0x20)
221 | } {
222 | sstore(sc, mload(mc))
223 | }
224 |
225 | mask := exp(0x100, sub(mc, end))
226 |
227 | sstore(sc, mul(div(mload(mc), mask), mask))
228 | }
229 | }
230 | }
231 |
232 | function slice(
233 | bytes memory _bytes,
234 | uint256 _start,
235 | uint256 _length
236 | ) internal pure returns (bytes memory) {
237 | require(_length + 31 >= _length, "slice_overflow");
238 | require(_bytes.length >= _start + _length, "slice_outOfBounds");
239 |
240 | bytes memory tempBytes;
241 |
242 | assembly {
243 | switch iszero(_length)
244 | case 0 {
245 | // Get a location of some free memory and store it in tempBytes as
246 | // Solidity does for memory variables.
247 | tempBytes := mload(0x40)
248 |
249 | // The first word of the slice result is potentially a partial
250 | // word read from the original array. To read it, we calculate
251 | // the length of that partial word and start copying that many
252 | // bytes into the array. The first word we copy will start with
253 | // data we don't care about, but the last `lengthmod` bytes will
254 | // land at the beginning of the contents of the new array. When
255 | // we're done copying, we overwrite the full first word with
256 | // the actual length of the slice.
257 | let lengthmod := and(_length, 31)
258 |
259 | // The multiplication in the next line is necessary
260 | // because when slicing multiples of 32 bytes (lengthmod == 0)
261 | // the following copy loop was copying the origin's length
262 | // and then ending prematurely not copying everything it should.
263 | let mc := add(
264 | add(tempBytes, lengthmod),
265 | mul(0x20, iszero(lengthmod))
266 | )
267 | let end := add(mc, _length)
268 |
269 | for {
270 | // The multiplication in the next line has the same exact purpose
271 | // as the one above.
272 | let cc := add(
273 | add(
274 | add(_bytes, lengthmod),
275 | mul(0x20, iszero(lengthmod))
276 | ),
277 | _start
278 | )
279 | } lt(mc, end) {
280 | mc := add(mc, 0x20)
281 | cc := add(cc, 0x20)
282 | } {
283 | mstore(mc, mload(cc))
284 | }
285 |
286 | mstore(tempBytes, _length)
287 |
288 | //update free-memory pointer
289 | //allocating the array padded to 32 bytes like the compiler does now
290 | mstore(0x40, and(add(mc, 31), not(31)))
291 | }
292 | //if we want a zero-length slice let's just return a zero-length array
293 | default {
294 | tempBytes := mload(0x40)
295 | //zero out the 32 bytes slice we are about to return
296 | //we need to do it because Solidity does not garbage collect
297 | mstore(tempBytes, 0)
298 |
299 | mstore(0x40, add(tempBytes, 0x20))
300 | }
301 | }
302 |
303 | return tempBytes;
304 | }
305 |
306 | function toAddress(bytes memory _bytes, uint256 _start)
307 | internal
308 | pure
309 | returns (address)
310 | {
311 | require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
312 | address tempAddress;
313 |
314 | assembly {
315 | tempAddress := div(
316 | mload(add(add(_bytes, 0x20), _start)),
317 | 0x1000000000000000000000000
318 | )
319 | }
320 |
321 | return tempAddress;
322 | }
323 |
324 | function toUint8(bytes memory _bytes, uint256 _start)
325 | internal
326 | pure
327 | returns (uint8)
328 | {
329 | require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
330 | uint8 tempUint;
331 |
332 | assembly {
333 | tempUint := mload(add(add(_bytes, 0x1), _start))
334 | }
335 |
336 | return tempUint;
337 | }
338 |
339 | function toUint16(bytes memory _bytes, uint256 _start)
340 | internal
341 | pure
342 | returns (uint16)
343 | {
344 | require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
345 | uint16 tempUint;
346 |
347 | assembly {
348 | tempUint := mload(add(add(_bytes, 0x2), _start))
349 | }
350 |
351 | return tempUint;
352 | }
353 |
354 | function toUint32(bytes memory _bytes, uint256 _start)
355 | internal
356 | pure
357 | returns (uint32)
358 | {
359 | require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
360 | uint32 tempUint;
361 |
362 | assembly {
363 | tempUint := mload(add(add(_bytes, 0x4), _start))
364 | }
365 |
366 | return tempUint;
367 | }
368 |
369 | function toUint64(bytes memory _bytes, uint256 _start)
370 | internal
371 | pure
372 | returns (uint64)
373 | {
374 | require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
375 | uint64 tempUint;
376 |
377 | assembly {
378 | tempUint := mload(add(add(_bytes, 0x8), _start))
379 | }
380 |
381 | return tempUint;
382 | }
383 |
384 | function toUint96(bytes memory _bytes, uint256 _start)
385 | internal
386 | pure
387 | returns (uint96)
388 | {
389 | require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
390 | uint96 tempUint;
391 |
392 | assembly {
393 | tempUint := mload(add(add(_bytes, 0xc), _start))
394 | }
395 |
396 | return tempUint;
397 | }
398 |
399 | function toUint128(bytes memory _bytes, uint256 _start)
400 | internal
401 | pure
402 | returns (uint128)
403 | {
404 | require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
405 | uint128 tempUint;
406 |
407 | assembly {
408 | tempUint := mload(add(add(_bytes, 0x10), _start))
409 | }
410 |
411 | return tempUint;
412 | }
413 |
414 | function toUint256(bytes memory _bytes, uint256 _start)
415 | internal
416 | pure
417 | returns (uint256)
418 | {
419 | require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
420 | uint256 tempUint;
421 |
422 | assembly {
423 | tempUint := mload(add(add(_bytes, 0x20), _start))
424 | }
425 |
426 | return tempUint;
427 | }
428 |
429 | function toBytes32(bytes memory _bytes, uint256 _start)
430 | internal
431 | pure
432 | returns (bytes32)
433 | {
434 | require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
435 | bytes32 tempBytes32;
436 |
437 | assembly {
438 | tempBytes32 := mload(add(add(_bytes, 0x20), _start))
439 | }
440 |
441 | return tempBytes32;
442 | }
443 |
444 | function equal(bytes memory _preBytes, bytes memory _postBytes)
445 | internal
446 | pure
447 | returns (bool)
448 | {
449 | bool success = true;
450 |
451 | assembly {
452 | let length := mload(_preBytes)
453 |
454 | // if lengths don't match the arrays are not equal
455 | switch eq(length, mload(_postBytes))
456 | case 1 {
457 | // cb is a circuit breaker in the for loop since there's
458 | // no said feature for inline assembly loops
459 | // cb = 1 - don't breaker
460 | // cb = 0 - break
461 | let cb := 1
462 |
463 | let mc := add(_preBytes, 0x20)
464 | let end := add(mc, length)
465 |
466 | for {
467 | let cc := add(_postBytes, 0x20)
468 | // the next line is the loop condition:
469 | // while(uint256(mc < end) + cb == 2)
470 | } eq(add(lt(mc, end), cb), 2) {
471 | mc := add(mc, 0x20)
472 | cc := add(cc, 0x20)
473 | } {
474 | // if any of these checks fails then arrays are not equal
475 | if iszero(eq(mload(mc), mload(cc))) {
476 | // unsuccess:
477 | success := 0
478 | cb := 0
479 | }
480 | }
481 | }
482 | default {
483 | // unsuccess:
484 | success := 0
485 | }
486 | }
487 |
488 | return success;
489 | }
490 |
491 | function equalStorage(bytes storage _preBytes, bytes memory _postBytes)
492 | internal
493 | view
494 | returns (bool)
495 | {
496 | bool success = true;
497 |
498 | assembly {
499 | // we know _preBytes_offset is 0
500 | let fslot := sload(_preBytes.slot)
501 | // Decode the length of the stored array like in concatStorage().
502 | let slength := div(
503 | and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
504 | 2
505 | )
506 | let mlength := mload(_postBytes)
507 |
508 | // if lengths don't match the arrays are not equal
509 | switch eq(slength, mlength)
510 | case 1 {
511 | // slength can contain both the length and contents of the array
512 | // if length < 32 bytes so let's prepare for that
513 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
514 | if iszero(iszero(slength)) {
515 | switch lt(slength, 32)
516 | case 1 {
517 | // blank the last byte which is the length
518 | fslot := mul(div(fslot, 0x100), 0x100)
519 |
520 | if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
521 | // unsuccess:
522 | success := 0
523 | }
524 | }
525 | default {
526 | // cb is a circuit breaker in the for loop since there's
527 | // no said feature for inline assembly loops
528 | // cb = 1 - don't breaker
529 | // cb = 0 - break
530 | let cb := 1
531 |
532 | // get the keccak hash to get the contents of the array
533 | mstore(0x0, _preBytes.slot)
534 | let sc := keccak256(0x0, 0x20)
535 |
536 | let mc := add(_postBytes, 0x20)
537 | let end := add(mc, mlength)
538 |
539 | // the next line is the loop condition:
540 | // while(uint256(mc < end) + cb == 2)
541 | for {
542 |
543 | } eq(add(lt(mc, end), cb), 2) {
544 | sc := add(sc, 1)
545 | mc := add(mc, 0x20)
546 | } {
547 | if iszero(eq(sload(sc), mload(mc))) {
548 | // unsuccess:
549 | success := 0
550 | cb := 0
551 | }
552 | }
553 | }
554 | }
555 | }
556 | default {
557 | // unsuccess:
558 | success := 0
559 | }
560 | }
561 |
562 | return success;
563 | }
564 | }
565 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/Context.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @dev Provides information about the current execution context, including the
8 | * sender of the transaction and its data. While these are generally available
9 | * via msg.sender and msg.data, they should not be accessed in such a direct
10 | * manner, since when dealing with meta-transactions the account sending and
11 | * paying for execution may not be the actual sender (as far as an application
12 | * is concerned).
13 | *
14 | * This contract is only required for intermediate, library-like contracts.
15 | */
16 | abstract contract Context {
17 | function _msgSender() internal view virtual returns (address) {
18 | return msg.sender;
19 | }
20 |
21 | function _msgData() internal view virtual returns (bytes calldata) {
22 | return msg.data;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/ERC20.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/ERC20.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./IERC20.sol";
7 | import "./IERC20Metadata.sol";
8 | import "./Context.sol";
9 |
10 | /**
11 | * @dev Implementation of the {IERC20} interface.
12 | *
13 | * This implementation is agnostic to the way tokens are created. This means
14 | * that a supply mechanism has to be added in a derived contract using {_mint}.
15 | * For a generic mechanism see {ERC20PresetMinterPauser}.
16 | *
17 | * TIP: For a detailed writeup see our guide
18 | * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
19 | * to implement supply mechanisms].
20 | *
21 | * We have followed general OpenZeppelin Contracts guidelines: functions revert
22 | * instead returning `false` on failure. This behavior is nonetheless
23 | * conventional and does not conflict with the expectations of ERC20
24 | * applications.
25 | *
26 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
27 | * This allows applications to reconstruct the allowance for all accounts just
28 | * by listening to said events. Other implementations of the EIP may not emit
29 | * these events, as it isn't required by the specification.
30 | *
31 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
32 | * functions have been added to mitigate the well-known issues around setting
33 | * allowances. See {IERC20-approve}.
34 | */
35 | contract ERC20 is Context, IERC20, IERC20Metadata {
36 | mapping(address => uint256) private _balances;
37 |
38 | mapping(address => mapping(address => uint256)) private _allowances;
39 |
40 | uint256 private _totalSupply;
41 |
42 | string private _name;
43 | string private _symbol;
44 |
45 | /**
46 | * @dev Sets the values for {name} and {symbol}.
47 | *
48 | * The default value of {decimals} is 18. To select a different value for
49 | * {decimals} you should overload it.
50 | *
51 | * All two of these values are immutable: they can only be set once during
52 | * construction.
53 | */
54 | constructor(string memory name_, string memory symbol_) {
55 | _name = name_;
56 | _symbol = symbol_;
57 | }
58 |
59 | /**
60 | * @dev Returns the name of the token.
61 | */
62 | function name() public view virtual override returns (string memory) {
63 | return _name;
64 | }
65 |
66 | /**
67 | * @dev Returns the symbol of the token, usually a shorter version of the
68 | * name.
69 | */
70 | function symbol() public view virtual override returns (string memory) {
71 | return _symbol;
72 | }
73 |
74 | /**
75 | * @dev Returns the number of decimals used to get its user representation.
76 | * For example, if `decimals` equals `2`, a balance of `505` tokens should
77 | * be displayed to a user as `5.05` (`505 / 10 ** 2`).
78 | *
79 | * Tokens usually opt for a value of 18, imitating the relationship between
80 | * Ether and Wei. This is the value {ERC20} uses, unless this function is
81 | * overridden;
82 | *
83 | * NOTE: This information is only used for _display_ purposes: it in
84 | * no way affects any of the arithmetic of the contract, including
85 | * {IERC20-balanceOf} and {IERC20-transfer}.
86 | */
87 | function decimals() public view virtual override returns (uint8) {
88 | return 18;
89 | }
90 |
91 | /**
92 | * @dev See {IERC20-totalSupply}.
93 | */
94 | function totalSupply() public view virtual override returns (uint256) {
95 | return _totalSupply;
96 | }
97 |
98 | /**
99 | * @dev See {IERC20-balanceOf}.
100 | */
101 | function balanceOf(address account)
102 | public
103 | view
104 | virtual
105 | override
106 | returns (uint256)
107 | {
108 | return _balances[account];
109 | }
110 |
111 | /**
112 | * @dev See {IERC20-transfer}.
113 | *
114 | * Requirements:
115 | *
116 | * - `to` cannot be the zero address.
117 | * - the caller must have a balance of at least `amount`.
118 | */
119 | function transfer(address to, uint256 amount)
120 | public
121 | virtual
122 | override
123 | returns (bool)
124 | {
125 | address owner = _msgSender();
126 | _transfer(owner, to, amount);
127 | return true;
128 | }
129 |
130 | /**
131 | * @dev See {IERC20-allowance}.
132 | */
133 | function allowance(address owner, address spender)
134 | public
135 | view
136 | virtual
137 | override
138 | returns (uint256)
139 | {
140 | return _allowances[owner][spender];
141 | }
142 |
143 | /**
144 | * @dev See {IERC20-approve}.
145 | *
146 | * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
147 | * `transferFrom`. This is semantically equivalent to an infinite approval.
148 | *
149 | * Requirements:
150 | *
151 | * - `spender` cannot be the zero address.
152 | */
153 | function approve(address spender, uint256 amount)
154 | public
155 | virtual
156 | override
157 | returns (bool)
158 | {
159 | address owner = _msgSender();
160 | _approve(owner, spender, amount);
161 | return true;
162 | }
163 |
164 | /**
165 | * @dev See {IERC20-transferFrom}.
166 | *
167 | * Emits an {Approval} event indicating the updated allowance. This is not
168 | * required by the EIP. See the note at the beginning of {ERC20}.
169 | *
170 | * NOTE: Does not update the allowance if the current allowance
171 | * is the maximum `uint256`.
172 | *
173 | * Requirements:
174 | *
175 | * - `from` and `to` cannot be the zero address.
176 | * - `from` must have a balance of at least `amount`.
177 | * - the caller must have allowance for ``from``'s tokens of at least
178 | * `amount`.
179 | */
180 | function transferFrom(
181 | address from,
182 | address to,
183 | uint256 amount
184 | ) public virtual override returns (bool) {
185 | address spender = _msgSender();
186 | _spendAllowance(from, spender, amount);
187 | _transfer(from, to, amount);
188 | return true;
189 | }
190 |
191 | /**
192 | * @dev Atomically increases the allowance granted to `spender` by the caller.
193 | *
194 | * This is an alternative to {approve} that can be used as a mitigation for
195 | * problems described in {IERC20-approve}.
196 | *
197 | * Emits an {Approval} event indicating the updated allowance.
198 | *
199 | * Requirements:
200 | *
201 | * - `spender` cannot be the zero address.
202 | */
203 | function increaseAllowance(address spender, uint256 addedValue)
204 | public
205 | virtual
206 | returns (bool)
207 | {
208 | address owner = _msgSender();
209 | _approve(owner, spender, allowance(owner, spender) + addedValue);
210 | return true;
211 | }
212 |
213 | /**
214 | * @dev Atomically decreases the allowance granted to `spender` by the caller.
215 | *
216 | * This is an alternative to {approve} that can be used as a mitigation for
217 | * problems described in {IERC20-approve}.
218 | *
219 | * Emits an {Approval} event indicating the updated allowance.
220 | *
221 | * Requirements:
222 | *
223 | * - `spender` cannot be the zero address.
224 | * - `spender` must have allowance for the caller of at least
225 | * `subtractedValue`.
226 | */
227 | function decreaseAllowance(address spender, uint256 subtractedValue)
228 | public
229 | virtual
230 | returns (bool)
231 | {
232 | address owner = _msgSender();
233 | uint256 currentAllowance = allowance(owner, spender);
234 | require(
235 | currentAllowance >= subtractedValue,
236 | "ERC20: decreased allowance below zero"
237 | );
238 | unchecked {
239 | _approve(owner, spender, currentAllowance - subtractedValue);
240 | }
241 |
242 | return true;
243 | }
244 |
245 | /**
246 | * @dev Moves `amount` of tokens from `from` to `to`.
247 | *
248 | * This internal function is equivalent to {transfer}, and can be used to
249 | * e.g. implement automatic token fees, slashing mechanisms, etc.
250 | *
251 | * Emits a {Transfer} event.
252 | *
253 | * Requirements:
254 | *
255 | * - `from` cannot be the zero address.
256 | * - `to` cannot be the zero address.
257 | * - `from` must have a balance of at least `amount`.
258 | */
259 | function _transfer(
260 | address from,
261 | address to,
262 | uint256 amount
263 | ) internal virtual {
264 | require(from != address(0), "ERC20: transfer from the zero address");
265 | require(to != address(0), "ERC20: transfer to the zero address");
266 |
267 | _beforeTokenTransfer(from, to, amount);
268 |
269 | uint256 fromBalance = _balances[from];
270 | require(
271 | fromBalance >= amount,
272 | "ERC20: transfer amount exceeds balance"
273 | );
274 | unchecked {
275 | _balances[from] = fromBalance - amount;
276 | }
277 | _balances[to] += amount;
278 |
279 | emit Transfer(from, to, amount);
280 |
281 | _afterTokenTransfer(from, to, amount);
282 | }
283 |
284 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing
285 | * the total supply.
286 | *
287 | * Emits a {Transfer} event with `from` set to the zero address.
288 | *
289 | * Requirements:
290 | *
291 | * - `account` cannot be the zero address.
292 | */
293 | function _mint(address account, uint256 amount) internal virtual {
294 | require(account != address(0), "ERC20: mint to the zero address");
295 |
296 | _beforeTokenTransfer(address(0), account, amount);
297 |
298 | _totalSupply += amount;
299 | _balances[account] += amount;
300 | emit Transfer(address(0), account, amount);
301 |
302 | _afterTokenTransfer(address(0), account, amount);
303 | }
304 |
305 | /**
306 | * @dev Destroys `amount` tokens from `account`, reducing the
307 | * total supply.
308 | *
309 | * Emits a {Transfer} event with `to` set to the zero address.
310 | *
311 | * Requirements:
312 | *
313 | * - `account` cannot be the zero address.
314 | * - `account` must have at least `amount` tokens.
315 | */
316 | function _burn(address account, uint256 amount) internal virtual {
317 | require(account != address(0), "ERC20: burn from the zero address");
318 |
319 | _beforeTokenTransfer(account, address(0), amount);
320 |
321 | uint256 accountBalance = _balances[account];
322 | require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
323 | unchecked {
324 | _balances[account] = accountBalance - amount;
325 | }
326 | _totalSupply -= amount;
327 |
328 | emit Transfer(account, address(0), amount);
329 |
330 | _afterTokenTransfer(account, address(0), amount);
331 | }
332 |
333 | /**
334 | * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
335 | *
336 | * This internal function is equivalent to `approve`, and can be used to
337 | * e.g. set automatic allowances for certain subsystems, etc.
338 | *
339 | * Emits an {Approval} event.
340 | *
341 | * Requirements:
342 | *
343 | * - `owner` cannot be the zero address.
344 | * - `spender` cannot be the zero address.
345 | */
346 | function _approve(
347 | address owner,
348 | address spender,
349 | uint256 amount
350 | ) internal virtual {
351 | require(owner != address(0), "ERC20: approve from the zero address");
352 | require(spender != address(0), "ERC20: approve to the zero address");
353 |
354 | _allowances[owner][spender] = amount;
355 | emit Approval(owner, spender, amount);
356 | }
357 |
358 | /**
359 | * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
360 | *
361 | * Does not update the allowance amount in case of infinite allowance.
362 | * Revert if not enough allowance is available.
363 | *
364 | * Might emit an {Approval} event.
365 | */
366 | function _spendAllowance(
367 | address owner,
368 | address spender,
369 | uint256 amount
370 | ) internal virtual {
371 | uint256 currentAllowance = allowance(owner, spender);
372 | if (currentAllowance != type(uint256).max) {
373 | require(
374 | currentAllowance >= amount,
375 | "ERC20: insufficient allowance"
376 | );
377 | unchecked {
378 | _approve(owner, spender, currentAllowance - amount);
379 | }
380 | }
381 | }
382 |
383 | /**
384 | * @dev Hook that is called before any transfer of tokens. This includes
385 | * minting and burning.
386 | *
387 | * Calling conditions:
388 | *
389 | * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
390 | * will be transferred to `to`.
391 | * - when `from` is zero, `amount` tokens will be minted for `to`.
392 | * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
393 | * - `from` and `to` are never both zero.
394 | *
395 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
396 | */
397 | function _beforeTokenTransfer(
398 | address from,
399 | address to,
400 | uint256 amount
401 | ) internal virtual {}
402 |
403 | /**
404 | * @dev Hook that is called after any transfer of tokens. This includes
405 | * minting and burning.
406 | *
407 | * Calling conditions:
408 | *
409 | * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
410 | * has been transferred to `to`.
411 | * - when `from` is zero, `amount` tokens have been minted for `to`.
412 | * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
413 | * - `from` and `to` are never both zero.
414 | *
415 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
416 | */
417 | function _afterTokenTransfer(
418 | address from,
419 | address to,
420 | uint256 amount
421 | ) internal virtual {}
422 | }
423 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/ERC20Verifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./ERC20.sol";
7 | import "./GenesisUtils.sol";
8 | import "./ICircuitValidator.sol";
9 | import "./ZKPVerifier.sol";
10 |
11 | // Main Contract
12 | // ========================================================
13 | contract ERC20Verifier is ERC20, ZKPVerifier {
14 | // Variables
15 | uint64 public constant TRANSFER_REQUEST_ID = 1;
16 | uint256 public TOKEN_AMOUNT_FOR_AIRDROP_PER_ID =
17 | 5 * 10**uint256(decimals());
18 | mapping(uint256 => address) public idToAddress;
19 | mapping(address => uint256) public addressToId;
20 |
21 | // Functions
22 | /**
23 | * @dev constructor
24 | */
25 | constructor(string memory name_, string memory symbol_)
26 | ERC20(name_, symbol_)
27 | {}
28 |
29 | /**
30 | * @dev _beforeProofSubmit
31 | */
32 | function _beforeProofSubmit(
33 | uint64, /* requestId */
34 | uint256[] memory inputs,
35 | ICircuitValidator validator
36 | ) internal view override {
37 | // check that challenge input of the proof is equal to the msg.sender
38 | address addr = GenesisUtils.int256ToAddress(
39 | inputs[validator.getChallengeInputIndex()]
40 | );
41 | require(
42 | _msgSender() == addr,
43 | "address in proof is not a sender address"
44 | );
45 | }
46 |
47 | /**
48 | * @dev _afterProofSubmit
49 | */
50 | function _afterProofSubmit(
51 | uint64 requestId,
52 | uint256[] memory inputs,
53 | ICircuitValidator validator
54 | ) internal override {
55 | require(
56 | requestId == TRANSFER_REQUEST_ID && addressToId[_msgSender()] == 0,
57 | "proof can not be submitted more than once"
58 | );
59 |
60 | uint256 id = inputs[validator.getChallengeInputIndex()];
61 | // execute the airdrop
62 | if (idToAddress[id] == address(0)) {
63 | super._mint(_msgSender(), TOKEN_AMOUNT_FOR_AIRDROP_PER_ID);
64 | addressToId[_msgSender()] = id;
65 | idToAddress[id] = _msgSender();
66 | }
67 | }
68 |
69 | /**
70 | * @dev _beforeTokenTransfer
71 | */
72 | function _beforeTokenTransfer(
73 | address, /* from */
74 | address to,
75 | uint256 /* amount */
76 | ) internal view override {
77 | require(
78 | proofs[to][TRANSFER_REQUEST_ID] == true,
79 | "only identities who provided proof are allowed to receive tokens"
80 | );
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/GenesisUtils.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./BytesLib.sol";
7 |
8 | // Library
9 | // ========================================================
10 | library GenesisUtils {
11 | /**
12 | * @dev int256ToBytes
13 | */
14 | function int256ToBytes(uint256 x) internal pure returns (bytes memory b) {
15 | b = new bytes(32);
16 | assembly {
17 | mstore(add(b, 32), x)
18 | }
19 | }
20 |
21 | /**
22 | * @dev reverse
23 | */
24 | function reverse(uint256 input) internal pure returns (uint256 v) {
25 | v = input;
26 |
27 | // swap bytes
28 | v =
29 | ((v &
30 | 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >>
31 | 8) |
32 | ((v &
33 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) <<
34 | 8);
35 |
36 | // swap 2-byte long pairs
37 | v =
38 | ((v &
39 | 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >>
40 | 16) |
41 | ((v &
42 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) <<
43 | 16);
44 |
45 | // swap 4-byte long pairs
46 | v =
47 | ((v &
48 | 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >>
49 | 32) |
50 | ((v &
51 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) <<
52 | 32);
53 |
54 | // swap 8-byte long pairs
55 | v =
56 | ((v &
57 | 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >>
58 | 64) |
59 | ((v &
60 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) <<
61 | 64);
62 |
63 | // swap 16-byte long pairs
64 | v = (v >> 128) | (v << 128);
65 | }
66 |
67 | /**
68 | * @dev sum
69 | */
70 | function sum(bytes memory array) internal pure returns (uint16 s) {
71 | require(array.length == 29, "Checksum requires 29 length array");
72 |
73 | for (uint256 i = 0; i < array.length; ++i) {
74 | s += uint16(uint8(array[i]));
75 | }
76 | }
77 |
78 | /**
79 | * @dev bytesToHexString
80 | */
81 | function bytesToHexString(bytes memory buffer)
82 | internal
83 | pure
84 | returns (string memory)
85 | {
86 | // Fixed buffer size for hexadecimal convertion
87 | bytes memory converted = new bytes(buffer.length * 2);
88 |
89 | bytes memory _base = "0123456789abcdef";
90 |
91 | for (uint256 i = 0; i < buffer.length; i++) {
92 | converted[i * 2] = _base[uint8(buffer[i]) / _base.length];
93 | converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length];
94 | }
95 |
96 | return string(abi.encodePacked("0x", converted));
97 | }
98 |
99 | /**
100 | * @dev compareStrings
101 | */
102 | function compareStrings(string memory a, string memory b)
103 | internal
104 | pure
105 | returns (bool)
106 | {
107 | return (keccak256(abi.encodePacked((a))) ==
108 | keccak256(abi.encodePacked((b))));
109 | }
110 |
111 | /**
112 | * @dev isGenesisState
113 | */
114 | function isGenesisState(uint256 id, uint256 idState)
115 | internal
116 | pure
117 | returns (bool)
118 | {
119 | uint256 userSwappedState = reverse(idState);
120 |
121 | bytes memory userStateB1 = int256ToBytes(userSwappedState);
122 |
123 | bytes memory cutState = BytesLib.slice(
124 | userStateB1,
125 | userStateB1.length - 27,
126 | 27
127 | );
128 |
129 | bytes memory typDefault = hex"0000";
130 |
131 | bytes memory beforeChecksum = BytesLib.concat(typDefault, cutState);
132 | require(
133 | beforeChecksum.length == 29,
134 | "Checksum requires 29 length array"
135 | );
136 |
137 | uint16 s = sum(beforeChecksum);
138 |
139 | bytes memory checkSumBytes = abi.encodePacked(s);
140 |
141 | bytes memory idBytes = BytesLib.concat(beforeChecksum, checkSumBytes);
142 | require(idBytes.length == 31, "idBytes requires 31 length array");
143 |
144 | return id == reverse(toUint256(idBytes));
145 | }
146 |
147 | /**
148 | * @dev toUint256
149 | */
150 | function toUint256(bytes memory _bytes)
151 | internal
152 | pure
153 | returns (uint256 value)
154 | {
155 | assembly {
156 | value := mload(add(_bytes, 0x20))
157 | }
158 | }
159 |
160 | /**
161 | * @dev bytesToAddress
162 | */
163 | function bytesToAddress(bytes memory bys)
164 | internal
165 | pure
166 | returns (address addr)
167 | {
168 | assembly {
169 | addr := mload(add(bys, 20))
170 | }
171 | }
172 |
173 | /**
174 | * @dev int256ToAddress
175 | */
176 | function int256ToAddress(uint256 input) internal pure returns (address) {
177 | return bytesToAddress(int256ToBytes(reverse(input)));
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/ICircuitValidator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Interface
5 | // ========================================================
6 | interface ICircuitValidator {
7 | // Variables
8 | struct CircuitQuery {
9 | uint256 schema;
10 | uint256 slotIndex;
11 | uint256 operator;
12 | uint256[] value;
13 | string circuitId;
14 | }
15 |
16 | /**
17 | * @dev verify
18 | */
19 | function verify(
20 | uint256[] memory inputs,
21 | uint256[2] memory a,
22 | uint256[2][2] memory b,
23 | uint256[2] memory c,
24 | CircuitQuery memory query
25 | ) external view returns (bool r);
26 |
27 | /**
28 | * @dev getCircuitId
29 | */
30 | function getCircuitId() external pure returns (string memory id);
31 |
32 | /**
33 | * @dev getChallengeInputIndex
34 | */
35 | function getChallengeInputIndex() external pure returns (uint256 index);
36 |
37 | /**
38 | * @dev getUserIdInputIndex
39 | */
40 | function getUserIdInputIndex() external pure returns (uint256 index);
41 | }
42 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/IERC20.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @dev Interface of the ERC20 standard as defined in the EIP.
8 | */
9 | interface IERC20 {
10 | /**
11 | * @dev Emitted when `value` tokens are moved from one account (`from`) to
12 | * another (`to`).
13 | *
14 | * Note that `value` may be zero.
15 | */
16 | event Transfer(address indexed from, address indexed to, uint256 value);
17 |
18 | /**
19 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by
20 | * a call to {approve}. `value` is the new allowance.
21 | */
22 | event Approval(
23 | address indexed owner,
24 | address indexed spender,
25 | uint256 value
26 | );
27 |
28 | /**
29 | * @dev Returns the amount of tokens in existence.
30 | */
31 | function totalSupply() external view returns (uint256);
32 |
33 | /**
34 | * @dev Returns the amount of tokens owned by `account`.
35 | */
36 | function balanceOf(address account) external view returns (uint256);
37 |
38 | /**
39 | * @dev Moves `amount` tokens from the caller's account to `to`.
40 | *
41 | * Returns a boolean value indicating whether the operation succeeded.
42 | *
43 | * Emits a {Transfer} event.
44 | */
45 | function transfer(address to, uint256 amount) external returns (bool);
46 |
47 | /**
48 | * @dev Returns the remaining number of tokens that `spender` will be
49 | * allowed to spend on behalf of `owner` through {transferFrom}. This is
50 | * zero by default.
51 | *
52 | * This value changes when {approve} or {transferFrom} are called.
53 | */
54 | function allowance(address owner, address spender)
55 | external
56 | view
57 | returns (uint256);
58 |
59 | /**
60 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
61 | *
62 | * Returns a boolean value indicating whether the operation succeeded.
63 | *
64 | * IMPORTANT: Beware that changing an allowance with this method brings the risk
65 | * that someone may use both the old and the new allowance by unfortunate
66 | * transaction ordering. One possible solution to mitigate this race
67 | * condition is to first reduce the spender's allowance to 0 and set the
68 | * desired value afterwards:
69 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
70 | *
71 | * Emits an {Approval} event.
72 | */
73 | function approve(address spender, uint256 amount) external returns (bool);
74 |
75 | /**
76 | * @dev Moves `amount` tokens from `from` to `to` using the
77 | * allowance mechanism. `amount` is then deducted from the caller's
78 | * allowance.
79 | *
80 | * Returns a boolean value indicating whether the operation succeeded.
81 | *
82 | * Emits a {Transfer} event.
83 | */
84 | function transferFrom(
85 | address from,
86 | address to,
87 | uint256 amount
88 | ) external returns (bool);
89 | }
90 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/IERC20Metadata.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./IERC20.sol";
7 |
8 | /**
9 | * @dev Interface for the optional metadata functions from the ERC20 standard.
10 | *
11 | * _Available since v4.1._
12 | */
13 | interface IERC20Metadata is IERC20 {
14 | /**
15 | * @dev Returns the name of the token.
16 | */
17 | function name() external view returns (string memory);
18 |
19 | /**
20 | * @dev Returns the symbol of the token.
21 | */
22 | function symbol() external view returns (string memory);
23 |
24 | /**
25 | * @dev Returns the decimals places of the token.
26 | */
27 | function decimals() external view returns (uint8);
28 | }
29 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/IZKPVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./ICircuitValidator.sol";
7 |
8 | // Interface
9 | // ========================================================
10 | interface IZKPVerifier {
11 | /**
12 | * @dev submitZKPResponse
13 | */
14 | function submitZKPResponse(
15 | uint64 requestId,
16 | uint256[] memory inputs,
17 | uint256[2] memory a,
18 | uint256[2][2] memory b,
19 | uint256[2] memory c
20 | ) external returns (bool);
21 |
22 | /**
23 | * @dev setZKPRequest
24 | */
25 | function setZKPRequest(
26 | uint64 requestId,
27 | ICircuitValidator validator,
28 | ICircuitValidator.CircuitQuery memory query
29 | ) external returns (bool);
30 |
31 | /**
32 | * @dev getZKPRequest
33 | */
34 | function getZKPRequest(uint64 requestId)
35 | external
36 | returns (ICircuitValidator.CircuitQuery memory);
37 | }
38 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/Ownable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./Context.sol";
7 |
8 | /**
9 | * @dev Contract module which provides a basic access control mechanism, where
10 | * there is an account (an owner) that can be granted exclusive access to
11 | * specific functions.
12 | *
13 | * By default, the owner account will be the one that deploys the contract. This
14 | * can later be changed with {transferOwnership}.
15 | *
16 | * This module is used through inheritance. It will make available the modifier
17 | * `onlyOwner`, which can be applied to your functions to restrict their use to
18 | * the owner.
19 | */
20 | abstract contract Ownable is Context {
21 | address private _owner;
22 |
23 | event OwnershipTransferred(
24 | address indexed previousOwner,
25 | address indexed newOwner
26 | );
27 |
28 | /**
29 | * @dev Initializes the contract setting the deployer as the initial owner.
30 | */
31 | constructor() {
32 | _transferOwnership(_msgSender());
33 | }
34 |
35 | /**
36 | * @dev Throws if called by any account other than the owner.
37 | */
38 | modifier onlyOwner() {
39 | _checkOwner();
40 | _;
41 | }
42 |
43 | /**
44 | * @dev Returns the address of the current owner.
45 | */
46 | function owner() public view virtual returns (address) {
47 | return _owner;
48 | }
49 |
50 | /**
51 | * @dev Throws if the sender is not the owner.
52 | */
53 | function _checkOwner() internal view virtual {
54 | require(owner() == _msgSender(), "Ownable: caller is not the owner");
55 | }
56 |
57 | /**
58 | * @dev Leaves the contract without owner. It will not be possible to call
59 | * `onlyOwner` functions anymore. Can only be called by the current owner.
60 | *
61 | * NOTE: Renouncing ownership will leave the contract without an owner,
62 | * thereby removing any functionality that is only available to the owner.
63 | */
64 | function renounceOwnership() public virtual onlyOwner {
65 | _transferOwnership(address(0));
66 | }
67 |
68 | /**
69 | * @dev Transfers ownership of the contract to a new account (`newOwner`).
70 | * Can only be called by the current owner.
71 | */
72 | function transferOwnership(address newOwner) public virtual onlyOwner {
73 | require(
74 | newOwner != address(0),
75 | "Ownable: new owner is the zero address"
76 | );
77 | _transferOwnership(newOwner);
78 | }
79 |
80 | /**
81 | * @dev Transfers ownership of the contract to a new account (`newOwner`).
82 | * Internal function without access restriction.
83 | */
84 | function _transferOwnership(address newOwner) internal virtual {
85 | address oldOwner = _owner;
86 | _owner = newOwner;
87 | emit OwnershipTransferred(oldOwner, newOwner);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/verify/ERC20Verifier/ZKPVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./Ownable.sol";
7 | import "./ICircuitValidator.sol";
8 | import "./IZKPVerifier.sol";
9 |
10 | // Contract
11 | // ========================================================
12 | contract ZKPVerifier is IZKPVerifier, Ownable {
13 | // Variables
14 | // msg.sender-> ( requestID -> is proof given )
15 | mapping(address => mapping(uint64 => bool)) public proofs;
16 | mapping(uint64 => ICircuitValidator.CircuitQuery) public requestQueries;
17 | mapping(uint64 => ICircuitValidator) public requestValidators;
18 | uint64[] public supportedRequests;
19 |
20 | // Functions
21 | /**
22 | * @dev submitZKPResponse
23 | */
24 | function submitZKPResponse(
25 | uint64 requestId,
26 | uint256[] memory inputs,
27 | uint256[2] memory a,
28 | uint256[2][2] memory b,
29 | uint256[2] memory c
30 | ) external override returns (bool) {
31 | require(
32 | requestValidators[requestId] != ICircuitValidator(address(0)),
33 | "validator is not set for this request id"
34 | ); // validator exists
35 | require(
36 | requestQueries[requestId].schema != 0,
37 | "query is not set for this request id"
38 | ); // query exists
39 |
40 | _beforeProofSubmit(requestId, inputs, requestValidators[requestId]);
41 |
42 | require(
43 | requestValidators[requestId].verify(
44 | inputs,
45 | a,
46 | b,
47 | c,
48 | requestQueries[requestId]
49 | ),
50 | "proof response is not valid"
51 | );
52 |
53 | proofs[msg.sender][requestId] = true; // user provided a valid proof for request
54 |
55 | _afterProofSubmit(requestId, inputs, requestValidators[requestId]);
56 | return true;
57 | }
58 |
59 | /**
60 | * @dev getZKPRequest
61 | */
62 | function getZKPRequest(uint64 requestId)
63 | external
64 | view
65 | override
66 | returns (ICircuitValidator.CircuitQuery memory)
67 | {
68 | return requestQueries[requestId];
69 | }
70 |
71 | /**
72 | * @dev setZKPRequest
73 | */
74 | function setZKPRequest(
75 | uint64 requestId,
76 | ICircuitValidator validator,
77 | ICircuitValidator.CircuitQuery memory query
78 | ) external override onlyOwner returns (bool) {
79 | if (requestValidators[requestId] == ICircuitValidator(address(0x00))) {
80 | supportedRequests.push(requestId);
81 | }
82 | requestQueries[requestId].value = query.value;
83 | requestQueries[requestId].operator = query.operator;
84 | requestQueries[requestId].circuitId = query.circuitId;
85 | requestQueries[requestId].slotIndex = query.slotIndex;
86 | requestQueries[requestId].schema = query.schema;
87 |
88 | requestQueries[requestId].circuitId = query.circuitId;
89 |
90 | requestValidators[requestId] = validator;
91 | return true;
92 | }
93 |
94 | /**
95 | * @dev getSupportedRequests
96 | */
97 | function getSupportedRequests()
98 | external
99 | view
100 | returns (uint64[] memory arr)
101 | {
102 | return supportedRequests;
103 | }
104 |
105 | /**
106 | * @dev Hook that is called before any proof response submit
107 | */
108 | function _beforeProofSubmit(
109 | uint64 requestId,
110 | uint256[] memory inputs,
111 | ICircuitValidator validator
112 | ) internal virtual {}
113 |
114 | /**
115 | * @dev Hook that is called after any proof response submit
116 | */
117 | function _afterProofSubmit(
118 | uint64 requestId,
119 | uint256[] memory inputs,
120 | ICircuitValidator validator
121 | ) internal virtual {}
122 | }
123 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/Address.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)
3 |
4 | pragma solidity ^0.8.1;
5 |
6 | /**
7 | * @dev Collection of functions related to the address type
8 | */
9 | library Address {
10 | /**
11 | * @dev Returns true if `account` is a contract.
12 | *
13 | * [IMPORTANT]
14 | * ====
15 | * It is unsafe to assume that an address for which this function returns
16 | * false is an externally-owned account (EOA) and not a contract.
17 | *
18 | * Among others, `isContract` will return false for the following
19 | * types of addresses:
20 | *
21 | * - an externally-owned account
22 | * - a contract in construction
23 | * - an address where a contract will be created
24 | * - an address where a contract lived, but was destroyed
25 | * ====
26 | *
27 | * [IMPORTANT]
28 | * ====
29 | * You shouldn't rely on `isContract` to protect against flash loan attacks!
30 | *
31 | * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
32 | * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
33 | * constructor.
34 | * ====
35 | */
36 | function isContract(address account) internal view returns (bool) {
37 | // This method relies on extcodesize/address.code.length, which returns 0
38 | // for contracts in construction, since the code is only stored at the end
39 | // of the constructor execution.
40 |
41 | return account.code.length > 0;
42 | }
43 |
44 | /**
45 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
46 | * `recipient`, forwarding all available gas and reverting on errors.
47 | *
48 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
49 | * of certain opcodes, possibly making contracts go over the 2300 gas limit
50 | * imposed by `transfer`, making them unable to receive funds via
51 | * `transfer`. {sendValue} removes this limitation.
52 | *
53 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
54 | *
55 | * IMPORTANT: because control is transferred to `recipient`, care must be
56 | * taken to not create reentrancy vulnerabilities. Consider using
57 | * {ReentrancyGuard} or the
58 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
59 | */
60 | function sendValue(address payable recipient, uint256 amount) internal {
61 | require(
62 | address(this).balance >= amount,
63 | "Address: insufficient balance"
64 | );
65 |
66 | (bool success, ) = recipient.call{value: amount}("");
67 | require(
68 | success,
69 | "Address: unable to send value, recipient may have reverted"
70 | );
71 | }
72 |
73 | /**
74 | * @dev Performs a Solidity function call using a low level `call`. A
75 | * plain `call` is an unsafe replacement for a function call: use this
76 | * function instead.
77 | *
78 | * If `target` reverts with a revert reason, it is bubbled up by this
79 | * function (like regular Solidity function calls).
80 | *
81 | * Returns the raw returned data. To convert to the expected return value,
82 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
83 | *
84 | * Requirements:
85 | *
86 | * - `target` must be a contract.
87 | * - calling `target` with `data` must not revert.
88 | *
89 | * _Available since v3.1._
90 | */
91 | function functionCall(address target, bytes memory data)
92 | internal
93 | returns (bytes memory)
94 | {
95 | return functionCall(target, data, "Address: low-level call failed");
96 | }
97 |
98 | /**
99 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
100 | * `errorMessage` as a fallback revert reason when `target` reverts.
101 | *
102 | * _Available since v3.1._
103 | */
104 | function functionCall(
105 | address target,
106 | bytes memory data,
107 | string memory errorMessage
108 | ) internal returns (bytes memory) {
109 | return functionCallWithValue(target, data, 0, errorMessage);
110 | }
111 |
112 | /**
113 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
114 | * but also transferring `value` wei to `target`.
115 | *
116 | * Requirements:
117 | *
118 | * - the calling contract must have an ETH balance of at least `value`.
119 | * - the called Solidity function must be `payable`.
120 | *
121 | * _Available since v3.1._
122 | */
123 | function functionCallWithValue(
124 | address target,
125 | bytes memory data,
126 | uint256 value
127 | ) internal returns (bytes memory) {
128 | return
129 | functionCallWithValue(
130 | target,
131 | data,
132 | value,
133 | "Address: low-level call with value failed"
134 | );
135 | }
136 |
137 | /**
138 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
139 | * with `errorMessage` as a fallback revert reason when `target` reverts.
140 | *
141 | * _Available since v3.1._
142 | */
143 | function functionCallWithValue(
144 | address target,
145 | bytes memory data,
146 | uint256 value,
147 | string memory errorMessage
148 | ) internal returns (bytes memory) {
149 | require(
150 | address(this).balance >= value,
151 | "Address: insufficient balance for call"
152 | );
153 | require(isContract(target), "Address: call to non-contract");
154 |
155 | (bool success, bytes memory returndata) = target.call{value: value}(
156 | data
157 | );
158 | return verifyCallResult(success, returndata, errorMessage);
159 | }
160 |
161 | /**
162 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
163 | * but performing a static call.
164 | *
165 | * _Available since v3.3._
166 | */
167 | function functionStaticCall(address target, bytes memory data)
168 | internal
169 | view
170 | returns (bytes memory)
171 | {
172 | return
173 | functionStaticCall(
174 | target,
175 | data,
176 | "Address: low-level static call failed"
177 | );
178 | }
179 |
180 | /**
181 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
182 | * but performing a static call.
183 | *
184 | * _Available since v3.3._
185 | */
186 | function functionStaticCall(
187 | address target,
188 | bytes memory data,
189 | string memory errorMessage
190 | ) internal view returns (bytes memory) {
191 | require(isContract(target), "Address: static call to non-contract");
192 |
193 | (bool success, bytes memory returndata) = target.staticcall(data);
194 | return verifyCallResult(success, returndata, errorMessage);
195 | }
196 |
197 | /**
198 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
199 | * but performing a delegate call.
200 | *
201 | * _Available since v3.4._
202 | */
203 | function functionDelegateCall(address target, bytes memory data)
204 | internal
205 | returns (bytes memory)
206 | {
207 | return
208 | functionDelegateCall(
209 | target,
210 | data,
211 | "Address: low-level delegate call failed"
212 | );
213 | }
214 |
215 | /**
216 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
217 | * but performing a delegate call.
218 | *
219 | * _Available since v3.4._
220 | */
221 | function functionDelegateCall(
222 | address target,
223 | bytes memory data,
224 | string memory errorMessage
225 | ) internal returns (bytes memory) {
226 | require(isContract(target), "Address: delegate call to non-contract");
227 |
228 | (bool success, bytes memory returndata) = target.delegatecall(data);
229 | return verifyCallResult(success, returndata, errorMessage);
230 | }
231 |
232 | /**
233 | * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
234 | * revert reason using the provided one.
235 | *
236 | * _Available since v4.3._
237 | */
238 | function verifyCallResult(
239 | bool success,
240 | bytes memory returndata,
241 | string memory errorMessage
242 | ) internal pure returns (bytes memory) {
243 | if (success) {
244 | return returndata;
245 | } else {
246 | // Look for revert reason and bubble it up if present
247 | if (returndata.length > 0) {
248 | // The easiest way to bubble the revert reason is using memory via assembly
249 | /// @solidity memory-safe-assembly
250 | assembly {
251 | let returndata_size := mload(returndata)
252 | revert(add(32, returndata), returndata_size)
253 | }
254 | } else {
255 | revert(errorMessage);
256 | }
257 | }
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/BytesLib.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: Unlicense
2 | /*
3 | * @title Solidity Bytes Arrays Utils
4 | * @author Gonçalo Sá
5 | *
6 | * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
7 | * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
8 | */
9 | pragma solidity >=0.8.0 <0.9.0;
10 |
11 | library BytesLib {
12 | function concat(bytes memory _preBytes, bytes memory _postBytes)
13 | internal
14 | pure
15 | returns (bytes memory)
16 | {
17 | bytes memory tempBytes;
18 |
19 | assembly {
20 | // Get a location of some free memory and store it in tempBytes as
21 | // Solidity does for memory variables.
22 | tempBytes := mload(0x40)
23 |
24 | // Store the length of the first bytes array at the beginning of
25 | // the memory for tempBytes.
26 | let length := mload(_preBytes)
27 | mstore(tempBytes, length)
28 |
29 | // Maintain a memory counter for the current write location in the
30 | // temp bytes array by adding the 32 bytes for the array length to
31 | // the starting location.
32 | let mc := add(tempBytes, 0x20)
33 | // Stop copying when the memory counter reaches the length of the
34 | // first bytes array.
35 | let end := add(mc, length)
36 |
37 | for {
38 | // Initialize a copy counter to the start of the _preBytes data,
39 | // 32 bytes into its memory.
40 | let cc := add(_preBytes, 0x20)
41 | } lt(mc, end) {
42 | // Increase both counters by 32 bytes each iteration.
43 | mc := add(mc, 0x20)
44 | cc := add(cc, 0x20)
45 | } {
46 | // Write the _preBytes data into the tempBytes memory 32 bytes
47 | // at a time.
48 | mstore(mc, mload(cc))
49 | }
50 |
51 | // Add the length of _postBytes to the current length of tempBytes
52 | // and store it as the new length in the first 32 bytes of the
53 | // tempBytes memory.
54 | length := mload(_postBytes)
55 | mstore(tempBytes, add(length, mload(tempBytes)))
56 |
57 | // Move the memory counter back from a multiple of 0x20 to the
58 | // actual end of the _preBytes data.
59 | mc := end
60 | // Stop copying when the memory counter reaches the new combined
61 | // length of the arrays.
62 | end := add(mc, length)
63 |
64 | for {
65 | let cc := add(_postBytes, 0x20)
66 | } lt(mc, end) {
67 | mc := add(mc, 0x20)
68 | cc := add(cc, 0x20)
69 | } {
70 | mstore(mc, mload(cc))
71 | }
72 |
73 | // Update the free-memory pointer by padding our last write location
74 | // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
75 | // next 32 byte block, then round down to the nearest multiple of
76 | // 32. If the sum of the length of the two arrays is zero then add
77 | // one before rounding down to leave a blank 32 bytes (the length block with 0).
78 | mstore(
79 | 0x40,
80 | and(
81 | add(add(end, iszero(add(length, mload(_preBytes)))), 31),
82 | not(31) // Round down to the nearest 32 bytes.
83 | )
84 | )
85 | }
86 |
87 | return tempBytes;
88 | }
89 |
90 | function concatStorage(bytes storage _preBytes, bytes memory _postBytes)
91 | internal
92 | {
93 | assembly {
94 | // Read the first 32 bytes of _preBytes storage, which is the length
95 | // of the array. (We don't need to use the offset into the slot
96 | // because arrays use the entire slot.)
97 | let fslot := sload(_preBytes.slot)
98 | // Arrays of 31 bytes or less have an even value in their slot,
99 | // while longer arrays have an odd value. The actual length is
100 | // the slot divided by two for odd values, and the lowest order
101 | // byte divided by two for even values.
102 | // If the slot is even, bitwise and the slot with 255 and divide by
103 | // two to get the length. If the slot is odd, bitwise and the slot
104 | // with -1 and divide by two.
105 | let slength := div(
106 | and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
107 | 2
108 | )
109 | let mlength := mload(_postBytes)
110 | let newlength := add(slength, mlength)
111 | // slength can contain both the length and contents of the array
112 | // if length < 32 bytes so let's prepare for that
113 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
114 | switch add(lt(slength, 32), lt(newlength, 32))
115 | case 2 {
116 | // Since the new array still fits in the slot, we just need to
117 | // update the contents of the slot.
118 | // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
119 | sstore(
120 | _preBytes.slot,
121 | // all the modifications to the slot are inside this
122 | // next block
123 | add(
124 | // we can just add to the slot contents because the
125 | // bytes we want to change are the LSBs
126 | fslot,
127 | add(
128 | mul(
129 | div(
130 | // load the bytes from memory
131 | mload(add(_postBytes, 0x20)),
132 | // zero all bytes to the right
133 | exp(0x100, sub(32, mlength))
134 | ),
135 | // and now shift left the number of bytes to
136 | // leave space for the length in the slot
137 | exp(0x100, sub(32, newlength))
138 | ),
139 | // increase length by the double of the memory
140 | // bytes length
141 | mul(mlength, 2)
142 | )
143 | )
144 | )
145 | }
146 | case 1 {
147 | // The stored value fits in the slot, but the combined value
148 | // will exceed it.
149 | // get the keccak hash to get the contents of the array
150 | mstore(0x0, _preBytes.slot)
151 | let sc := add(keccak256(0x0, 0x20), div(slength, 32))
152 |
153 | // save new length
154 | sstore(_preBytes.slot, add(mul(newlength, 2), 1))
155 |
156 | // The contents of the _postBytes array start 32 bytes into
157 | // the structure. Our first read should obtain the `submod`
158 | // bytes that can fit into the unused space in the last word
159 | // of the stored array. To get this, we read 32 bytes starting
160 | // from `submod`, so the data we read overlaps with the array
161 | // contents by `submod` bytes. Masking the lowest-order
162 | // `submod` bytes allows us to add that value directly to the
163 | // stored value.
164 |
165 | let submod := sub(32, slength)
166 | let mc := add(_postBytes, submod)
167 | let end := add(_postBytes, mlength)
168 | let mask := sub(exp(0x100, submod), 1)
169 |
170 | sstore(
171 | sc,
172 | add(
173 | and(
174 | fslot,
175 | 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
176 | ),
177 | and(mload(mc), mask)
178 | )
179 | )
180 |
181 | for {
182 | mc := add(mc, 0x20)
183 | sc := add(sc, 1)
184 | } lt(mc, end) {
185 | sc := add(sc, 1)
186 | mc := add(mc, 0x20)
187 | } {
188 | sstore(sc, mload(mc))
189 | }
190 |
191 | mask := exp(0x100, sub(mc, end))
192 |
193 | sstore(sc, mul(div(mload(mc), mask), mask))
194 | }
195 | default {
196 | // get the keccak hash to get the contents of the array
197 | mstore(0x0, _preBytes.slot)
198 | // Start copying to the last used word of the stored array.
199 | let sc := add(keccak256(0x0, 0x20), div(slength, 32))
200 |
201 | // save new length
202 | sstore(_preBytes.slot, add(mul(newlength, 2), 1))
203 |
204 | // Copy over the first `submod` bytes of the new data as in
205 | // case 1 above.
206 | let slengthmod := mod(slength, 32)
207 | let mlengthmod := mod(mlength, 32)
208 | let submod := sub(32, slengthmod)
209 | let mc := add(_postBytes, submod)
210 | let end := add(_postBytes, mlength)
211 | let mask := sub(exp(0x100, submod), 1)
212 |
213 | sstore(sc, add(sload(sc), and(mload(mc), mask)))
214 |
215 | for {
216 | sc := add(sc, 1)
217 | mc := add(mc, 0x20)
218 | } lt(mc, end) {
219 | sc := add(sc, 1)
220 | mc := add(mc, 0x20)
221 | } {
222 | sstore(sc, mload(mc))
223 | }
224 |
225 | mask := exp(0x100, sub(mc, end))
226 |
227 | sstore(sc, mul(div(mload(mc), mask), mask))
228 | }
229 | }
230 | }
231 |
232 | function slice(
233 | bytes memory _bytes,
234 | uint256 _start,
235 | uint256 _length
236 | ) internal pure returns (bytes memory) {
237 | require(_length + 31 >= _length, "slice_overflow");
238 | require(_bytes.length >= _start + _length, "slice_outOfBounds");
239 |
240 | bytes memory tempBytes;
241 |
242 | assembly {
243 | switch iszero(_length)
244 | case 0 {
245 | // Get a location of some free memory and store it in tempBytes as
246 | // Solidity does for memory variables.
247 | tempBytes := mload(0x40)
248 |
249 | // The first word of the slice result is potentially a partial
250 | // word read from the original array. To read it, we calculate
251 | // the length of that partial word and start copying that many
252 | // bytes into the array. The first word we copy will start with
253 | // data we don't care about, but the last `lengthmod` bytes will
254 | // land at the beginning of the contents of the new array. When
255 | // we're done copying, we overwrite the full first word with
256 | // the actual length of the slice.
257 | let lengthmod := and(_length, 31)
258 |
259 | // The multiplication in the next line is necessary
260 | // because when slicing multiples of 32 bytes (lengthmod == 0)
261 | // the following copy loop was copying the origin's length
262 | // and then ending prematurely not copying everything it should.
263 | let mc := add(
264 | add(tempBytes, lengthmod),
265 | mul(0x20, iszero(lengthmod))
266 | )
267 | let end := add(mc, _length)
268 |
269 | for {
270 | // The multiplication in the next line has the same exact purpose
271 | // as the one above.
272 | let cc := add(
273 | add(
274 | add(_bytes, lengthmod),
275 | mul(0x20, iszero(lengthmod))
276 | ),
277 | _start
278 | )
279 | } lt(mc, end) {
280 | mc := add(mc, 0x20)
281 | cc := add(cc, 0x20)
282 | } {
283 | mstore(mc, mload(cc))
284 | }
285 |
286 | mstore(tempBytes, _length)
287 |
288 | //update free-memory pointer
289 | //allocating the array padded to 32 bytes like the compiler does now
290 | mstore(0x40, and(add(mc, 31), not(31)))
291 | }
292 | //if we want a zero-length slice let's just return a zero-length array
293 | default {
294 | tempBytes := mload(0x40)
295 | //zero out the 32 bytes slice we are about to return
296 | //we need to do it because Solidity does not garbage collect
297 | mstore(tempBytes, 0)
298 |
299 | mstore(0x40, add(tempBytes, 0x20))
300 | }
301 | }
302 |
303 | return tempBytes;
304 | }
305 |
306 | function toAddress(bytes memory _bytes, uint256 _start)
307 | internal
308 | pure
309 | returns (address)
310 | {
311 | require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
312 | address tempAddress;
313 |
314 | assembly {
315 | tempAddress := div(
316 | mload(add(add(_bytes, 0x20), _start)),
317 | 0x1000000000000000000000000
318 | )
319 | }
320 |
321 | return tempAddress;
322 | }
323 |
324 | function toUint8(bytes memory _bytes, uint256 _start)
325 | internal
326 | pure
327 | returns (uint8)
328 | {
329 | require(_bytes.length >= _start + 1, "toUint8_outOfBounds");
330 | uint8 tempUint;
331 |
332 | assembly {
333 | tempUint := mload(add(add(_bytes, 0x1), _start))
334 | }
335 |
336 | return tempUint;
337 | }
338 |
339 | function toUint16(bytes memory _bytes, uint256 _start)
340 | internal
341 | pure
342 | returns (uint16)
343 | {
344 | require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
345 | uint16 tempUint;
346 |
347 | assembly {
348 | tempUint := mload(add(add(_bytes, 0x2), _start))
349 | }
350 |
351 | return tempUint;
352 | }
353 |
354 | function toUint32(bytes memory _bytes, uint256 _start)
355 | internal
356 | pure
357 | returns (uint32)
358 | {
359 | require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
360 | uint32 tempUint;
361 |
362 | assembly {
363 | tempUint := mload(add(add(_bytes, 0x4), _start))
364 | }
365 |
366 | return tempUint;
367 | }
368 |
369 | function toUint64(bytes memory _bytes, uint256 _start)
370 | internal
371 | pure
372 | returns (uint64)
373 | {
374 | require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
375 | uint64 tempUint;
376 |
377 | assembly {
378 | tempUint := mload(add(add(_bytes, 0x8), _start))
379 | }
380 |
381 | return tempUint;
382 | }
383 |
384 | function toUint96(bytes memory _bytes, uint256 _start)
385 | internal
386 | pure
387 | returns (uint96)
388 | {
389 | require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
390 | uint96 tempUint;
391 |
392 | assembly {
393 | tempUint := mload(add(add(_bytes, 0xc), _start))
394 | }
395 |
396 | return tempUint;
397 | }
398 |
399 | function toUint128(bytes memory _bytes, uint256 _start)
400 | internal
401 | pure
402 | returns (uint128)
403 | {
404 | require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
405 | uint128 tempUint;
406 |
407 | assembly {
408 | tempUint := mload(add(add(_bytes, 0x10), _start))
409 | }
410 |
411 | return tempUint;
412 | }
413 |
414 | function toUint256(bytes memory _bytes, uint256 _start)
415 | internal
416 | pure
417 | returns (uint256)
418 | {
419 | require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
420 | uint256 tempUint;
421 |
422 | assembly {
423 | tempUint := mload(add(add(_bytes, 0x20), _start))
424 | }
425 |
426 | return tempUint;
427 | }
428 |
429 | function toBytes32(bytes memory _bytes, uint256 _start)
430 | internal
431 | pure
432 | returns (bytes32)
433 | {
434 | require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
435 | bytes32 tempBytes32;
436 |
437 | assembly {
438 | tempBytes32 := mload(add(add(_bytes, 0x20), _start))
439 | }
440 |
441 | return tempBytes32;
442 | }
443 |
444 | function equal(bytes memory _preBytes, bytes memory _postBytes)
445 | internal
446 | pure
447 | returns (bool)
448 | {
449 | bool success = true;
450 |
451 | assembly {
452 | let length := mload(_preBytes)
453 |
454 | // if lengths don't match the arrays are not equal
455 | switch eq(length, mload(_postBytes))
456 | case 1 {
457 | // cb is a circuit breaker in the for loop since there's
458 | // no said feature for inline assembly loops
459 | // cb = 1 - don't breaker
460 | // cb = 0 - break
461 | let cb := 1
462 |
463 | let mc := add(_preBytes, 0x20)
464 | let end := add(mc, length)
465 |
466 | for {
467 | let cc := add(_postBytes, 0x20)
468 | // the next line is the loop condition:
469 | // while(uint256(mc < end) + cb == 2)
470 | } eq(add(lt(mc, end), cb), 2) {
471 | mc := add(mc, 0x20)
472 | cc := add(cc, 0x20)
473 | } {
474 | // if any of these checks fails then arrays are not equal
475 | if iszero(eq(mload(mc), mload(cc))) {
476 | // unsuccess:
477 | success := 0
478 | cb := 0
479 | }
480 | }
481 | }
482 | default {
483 | // unsuccess:
484 | success := 0
485 | }
486 | }
487 |
488 | return success;
489 | }
490 |
491 | function equalStorage(bytes storage _preBytes, bytes memory _postBytes)
492 | internal
493 | view
494 | returns (bool)
495 | {
496 | bool success = true;
497 |
498 | assembly {
499 | // we know _preBytes_offset is 0
500 | let fslot := sload(_preBytes.slot)
501 | // Decode the length of the stored array like in concatStorage().
502 | let slength := div(
503 | and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)),
504 | 2
505 | )
506 | let mlength := mload(_postBytes)
507 |
508 | // if lengths don't match the arrays are not equal
509 | switch eq(slength, mlength)
510 | case 1 {
511 | // slength can contain both the length and contents of the array
512 | // if length < 32 bytes so let's prepare for that
513 | // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
514 | if iszero(iszero(slength)) {
515 | switch lt(slength, 32)
516 | case 1 {
517 | // blank the last byte which is the length
518 | fslot := mul(div(fslot, 0x100), 0x100)
519 |
520 | if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
521 | // unsuccess:
522 | success := 0
523 | }
524 | }
525 | default {
526 | // cb is a circuit breaker in the for loop since there's
527 | // no said feature for inline assembly loops
528 | // cb = 1 - don't breaker
529 | // cb = 0 - break
530 | let cb := 1
531 |
532 | // get the keccak hash to get the contents of the array
533 | mstore(0x0, _preBytes.slot)
534 | let sc := keccak256(0x0, 0x20)
535 |
536 | let mc := add(_postBytes, 0x20)
537 | let end := add(mc, mlength)
538 |
539 | // the next line is the loop condition:
540 | // while(uint256(mc < end) + cb == 2)
541 | for {
542 |
543 | } eq(add(lt(mc, end), cb), 2) {
544 | sc := add(sc, 1)
545 | mc := add(mc, 0x20)
546 | } {
547 | if iszero(eq(sload(sc), mload(mc))) {
548 | // unsuccess:
549 | success := 0
550 | cb := 0
551 | }
552 | }
553 | }
554 | }
555 | }
556 | default {
557 | // unsuccess:
558 | success := 0
559 | }
560 | }
561 |
562 | return success;
563 | }
564 | }
565 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/Context.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @dev Provides information about the current execution context, including the
8 | * sender of the transaction and its data. While these are generally available
9 | * via msg.sender and msg.data, they should not be accessed in such a direct
10 | * manner, since when dealing with meta-transactions the account sending and
11 | * paying for execution may not be the actual sender (as far as an application
12 | * is concerned).
13 | *
14 | * This contract is only required for intermediate, library-like contracts.
15 | */
16 | abstract contract Context {
17 | function _msgSender() internal view virtual returns (address) {
18 | return msg.sender;
19 | }
20 |
21 | function _msgData() internal view virtual returns (bytes calldata) {
22 | return msg.data;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/Counters.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @title Counters
8 | * @author Matt Condon (@shrugs)
9 | * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number
10 | * of elements in a mapping, issuing ERC721 ids, or counting request ids.
11 | *
12 | * Include with `using Counters for Counters.Counter;`
13 | */
14 | library Counters {
15 | struct Counter {
16 | // This variable should never be directly accessed by users of the library: interactions must be restricted to
17 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
18 | // this feature: see https://github.com/ethereum/solidity/issues/4637
19 | uint256 _value; // default: 0
20 | }
21 |
22 | function current(Counter storage counter) internal view returns (uint256) {
23 | return counter._value;
24 | }
25 |
26 | function increment(Counter storage counter) internal {
27 | unchecked {
28 | counter._value += 1;
29 | }
30 | }
31 |
32 | function decrement(Counter storage counter) internal {
33 | uint256 value = counter._value;
34 | require(value > 0, "Counter: decrement overflow");
35 | unchecked {
36 | counter._value = value - 1;
37 | }
38 | }
39 |
40 | function reset(Counter storage counter) internal {
41 | counter._value = 0;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/ERC165.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./IERC165.sol";
7 |
8 | /**
9 | * @dev Implementation of the {IERC165} interface.
10 | *
11 | * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
12 | * for the additional interface id that will be supported. For example:
13 | *
14 | * ```solidity
15 | * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
16 | * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
17 | * }
18 | * ```
19 | *
20 | * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
21 | */
22 | abstract contract ERC165 is IERC165 {
23 | /**
24 | * @dev See {IERC165-supportsInterface}.
25 | */
26 | function supportsInterface(bytes4 interfaceId)
27 | public
28 | view
29 | virtual
30 | override
31 | returns (bool)
32 | {
33 | return interfaceId == type(IERC165).interfaceId;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/ERC721.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/ERC721.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./IERC721.sol";
7 | import "./IERC721Receiver.sol";
8 | import "./IERC721Metadata.sol";
9 | import "./Address.sol";
10 | import "./Context.sol";
11 | import "./Strings.sol";
12 | import "./ERC165.sol";
13 |
14 | /**
15 | * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
16 | * the Metadata extension, but not including the Enumerable extension, which is available separately as
17 | * {ERC721Enumerable}.
18 | */
19 | contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
20 | using Address for address;
21 | using Strings for uint256;
22 |
23 | // Token name
24 | string private _name;
25 |
26 | // Token symbol
27 | string private _symbol;
28 |
29 | // Mapping from token ID to owner address
30 | mapping(uint256 => address) private _owners;
31 |
32 | // Mapping owner address to token count
33 | mapping(address => uint256) private _balances;
34 |
35 | // Mapping from token ID to approved address
36 | mapping(uint256 => address) private _tokenApprovals;
37 |
38 | // Mapping from owner to operator approvals
39 | mapping(address => mapping(address => bool)) private _operatorApprovals;
40 |
41 | /**
42 | * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
43 | */
44 | constructor(string memory name_, string memory symbol_) {
45 | _name = name_;
46 | _symbol = symbol_;
47 | }
48 |
49 | /**
50 | * @dev See {IERC165-supportsInterface}.
51 | */
52 | function supportsInterface(bytes4 interfaceId)
53 | public
54 | view
55 | virtual
56 | override(ERC165, IERC165)
57 | returns (bool)
58 | {
59 | return
60 | interfaceId == type(IERC721).interfaceId ||
61 | interfaceId == type(IERC721Metadata).interfaceId ||
62 | super.supportsInterface(interfaceId);
63 | }
64 |
65 | /**
66 | * @dev See {IERC721-balanceOf}.
67 | */
68 | function balanceOf(address owner)
69 | public
70 | view
71 | virtual
72 | override
73 | returns (uint256)
74 | {
75 | require(
76 | owner != address(0),
77 | "ERC721: address zero is not a valid owner"
78 | );
79 | return _balances[owner];
80 | }
81 |
82 | /**
83 | * @dev See {IERC721-ownerOf}.
84 | */
85 | function ownerOf(uint256 tokenId)
86 | public
87 | view
88 | virtual
89 | override
90 | returns (address)
91 | {
92 | address owner = _owners[tokenId];
93 | require(owner != address(0), "ERC721: invalid token ID");
94 | return owner;
95 | }
96 |
97 | /**
98 | * @dev See {IERC721Metadata-name}.
99 | */
100 | function name() public view virtual override returns (string memory) {
101 | return _name;
102 | }
103 |
104 | /**
105 | * @dev See {IERC721Metadata-symbol}.
106 | */
107 | function symbol() public view virtual override returns (string memory) {
108 | return _symbol;
109 | }
110 |
111 | /**
112 | * @dev See {IERC721Metadata-tokenURI}.
113 | */
114 | function tokenURI(uint256 tokenId)
115 | public
116 | view
117 | virtual
118 | override
119 | returns (string memory)
120 | {
121 | _requireMinted(tokenId);
122 |
123 | string memory baseURI = _baseURI();
124 | return
125 | bytes(baseURI).length > 0
126 | ? string(abi.encodePacked(baseURI, tokenId.toString()))
127 | : "";
128 | }
129 |
130 | /**
131 | * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
132 | * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
133 | * by default, can be overridden in child contracts.
134 | */
135 | function _baseURI() internal view virtual returns (string memory) {
136 | return "";
137 | }
138 |
139 | /**
140 | * @dev See {IERC721-approve}.
141 | */
142 | function approve(address to, uint256 tokenId) public virtual override {
143 | address owner = ERC721.ownerOf(tokenId);
144 | require(to != owner, "ERC721: approval to current owner");
145 |
146 | require(
147 | _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
148 | "ERC721: approve caller is not token owner nor approved for all"
149 | );
150 |
151 | _approve(to, tokenId);
152 | }
153 |
154 | /**
155 | * @dev See {IERC721-getApproved}.
156 | */
157 | function getApproved(uint256 tokenId)
158 | public
159 | view
160 | virtual
161 | override
162 | returns (address)
163 | {
164 | _requireMinted(tokenId);
165 |
166 | return _tokenApprovals[tokenId];
167 | }
168 |
169 | /**
170 | * @dev See {IERC721-setApprovalForAll}.
171 | */
172 | function setApprovalForAll(address operator, bool approved)
173 | public
174 | virtual
175 | override
176 | {
177 | _setApprovalForAll(_msgSender(), operator, approved);
178 | }
179 |
180 | /**
181 | * @dev See {IERC721-isApprovedForAll}.
182 | */
183 | function isApprovedForAll(address owner, address operator)
184 | public
185 | view
186 | virtual
187 | override
188 | returns (bool)
189 | {
190 | return _operatorApprovals[owner][operator];
191 | }
192 |
193 | /**
194 | * @dev See {IERC721-transferFrom}.
195 | */
196 | function transferFrom(
197 | address from,
198 | address to,
199 | uint256 tokenId
200 | ) public virtual override {
201 | //solhint-disable-next-line max-line-length
202 | require(
203 | _isApprovedOrOwner(_msgSender(), tokenId),
204 | "ERC721: caller is not token owner nor approved"
205 | );
206 |
207 | _transfer(from, to, tokenId);
208 | }
209 |
210 | /**
211 | * @dev See {IERC721-safeTransferFrom}.
212 | */
213 | function safeTransferFrom(
214 | address from,
215 | address to,
216 | uint256 tokenId
217 | ) public virtual override {
218 | safeTransferFrom(from, to, tokenId, "");
219 | }
220 |
221 | /**
222 | * @dev See {IERC721-safeTransferFrom}.
223 | */
224 | function safeTransferFrom(
225 | address from,
226 | address to,
227 | uint256 tokenId,
228 | bytes memory data
229 | ) public virtual override {
230 | require(
231 | _isApprovedOrOwner(_msgSender(), tokenId),
232 | "ERC721: caller is not token owner nor approved"
233 | );
234 | _safeTransfer(from, to, tokenId, data);
235 | }
236 |
237 | /**
238 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
239 | * are aware of the ERC721 protocol to prevent tokens from being forever locked.
240 | *
241 | * `data` is additional data, it has no specified format and it is sent in call to `to`.
242 | *
243 | * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
244 | * implement alternative mechanisms to perform token transfer, such as signature-based.
245 | *
246 | * Requirements:
247 | *
248 | * - `from` cannot be the zero address.
249 | * - `to` cannot be the zero address.
250 | * - `tokenId` token must exist and be owned by `from`.
251 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
252 | *
253 | * Emits a {Transfer} event.
254 | */
255 | function _safeTransfer(
256 | address from,
257 | address to,
258 | uint256 tokenId,
259 | bytes memory data
260 | ) internal virtual {
261 | _transfer(from, to, tokenId);
262 | require(
263 | _checkOnERC721Received(from, to, tokenId, data),
264 | "ERC721: transfer to non ERC721Receiver implementer"
265 | );
266 | }
267 |
268 | /**
269 | * @dev Returns whether `tokenId` exists.
270 | *
271 | * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
272 | *
273 | * Tokens start existing when they are minted (`_mint`),
274 | * and stop existing when they are burned (`_burn`).
275 | */
276 | function _exists(uint256 tokenId) internal view virtual returns (bool) {
277 | return _owners[tokenId] != address(0);
278 | }
279 |
280 | /**
281 | * @dev Returns whether `spender` is allowed to manage `tokenId`.
282 | *
283 | * Requirements:
284 | *
285 | * - `tokenId` must exist.
286 | */
287 | function _isApprovedOrOwner(address spender, uint256 tokenId)
288 | internal
289 | view
290 | virtual
291 | returns (bool)
292 | {
293 | address owner = ERC721.ownerOf(tokenId);
294 | return (spender == owner ||
295 | isApprovedForAll(owner, spender) ||
296 | getApproved(tokenId) == spender);
297 | }
298 |
299 | /**
300 | * @dev Safely mints `tokenId` and transfers it to `to`.
301 | *
302 | * Requirements:
303 | *
304 | * - `tokenId` must not exist.
305 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
306 | *
307 | * Emits a {Transfer} event.
308 | */
309 | function _safeMint(address to, uint256 tokenId) internal virtual {
310 | _safeMint(to, tokenId, "");
311 | }
312 |
313 | /**
314 | * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
315 | * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
316 | */
317 | function _safeMint(
318 | address to,
319 | uint256 tokenId,
320 | bytes memory data
321 | ) internal virtual {
322 | _mint(to, tokenId);
323 | require(
324 | _checkOnERC721Received(address(0), to, tokenId, data),
325 | "ERC721: transfer to non ERC721Receiver implementer"
326 | );
327 | }
328 |
329 | /**
330 | * @dev Mints `tokenId` and transfers it to `to`.
331 | *
332 | * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
333 | *
334 | * Requirements:
335 | *
336 | * - `tokenId` must not exist.
337 | * - `to` cannot be the zero address.
338 | *
339 | * Emits a {Transfer} event.
340 | */
341 | function _mint(address to, uint256 tokenId) internal virtual {
342 | require(to != address(0), "ERC721: mint to the zero address");
343 | require(!_exists(tokenId), "ERC721: token already minted");
344 |
345 | _beforeTokenTransfer(address(0), to, tokenId);
346 |
347 | _balances[to] += 1;
348 | _owners[tokenId] = to;
349 |
350 | emit Transfer(address(0), to, tokenId);
351 |
352 | _afterTokenTransfer(address(0), to, tokenId);
353 | }
354 |
355 | /**
356 | * @dev Destroys `tokenId`.
357 | * The approval is cleared when the token is burned.
358 | *
359 | * Requirements:
360 | *
361 | * - `tokenId` must exist.
362 | *
363 | * Emits a {Transfer} event.
364 | */
365 | function _burn(uint256 tokenId) internal virtual {
366 | address owner = ERC721.ownerOf(tokenId);
367 |
368 | _beforeTokenTransfer(owner, address(0), tokenId);
369 |
370 | // Clear approvals
371 | _approve(address(0), tokenId);
372 |
373 | _balances[owner] -= 1;
374 | delete _owners[tokenId];
375 |
376 | emit Transfer(owner, address(0), tokenId);
377 |
378 | _afterTokenTransfer(owner, address(0), tokenId);
379 | }
380 |
381 | /**
382 | * @dev Transfers `tokenId` from `from` to `to`.
383 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
384 | *
385 | * Requirements:
386 | *
387 | * - `to` cannot be the zero address.
388 | * - `tokenId` token must be owned by `from`.
389 | *
390 | * Emits a {Transfer} event.
391 | */
392 | function _transfer(
393 | address from,
394 | address to,
395 | uint256 tokenId
396 | ) internal virtual {
397 | require(
398 | ERC721.ownerOf(tokenId) == from,
399 | "ERC721: transfer from incorrect owner"
400 | );
401 | require(to != address(0), "ERC721: transfer to the zero address");
402 |
403 | _beforeTokenTransfer(from, to, tokenId);
404 |
405 | // Clear approvals from the previous owner
406 | _approve(address(0), tokenId);
407 |
408 | _balances[from] -= 1;
409 | _balances[to] += 1;
410 | _owners[tokenId] = to;
411 |
412 | emit Transfer(from, to, tokenId);
413 |
414 | _afterTokenTransfer(from, to, tokenId);
415 | }
416 |
417 | /**
418 | * @dev Approve `to` to operate on `tokenId`
419 | *
420 | * Emits an {Approval} event.
421 | */
422 | function _approve(address to, uint256 tokenId) internal virtual {
423 | _tokenApprovals[tokenId] = to;
424 | emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
425 | }
426 |
427 | /**
428 | * @dev Approve `operator` to operate on all of `owner` tokens
429 | *
430 | * Emits an {ApprovalForAll} event.
431 | */
432 | function _setApprovalForAll(
433 | address owner,
434 | address operator,
435 | bool approved
436 | ) internal virtual {
437 | require(owner != operator, "ERC721: approve to caller");
438 | _operatorApprovals[owner][operator] = approved;
439 | emit ApprovalForAll(owner, operator, approved);
440 | }
441 |
442 | /**
443 | * @dev Reverts if the `tokenId` has not been minted yet.
444 | */
445 | function _requireMinted(uint256 tokenId) internal view virtual {
446 | require(_exists(tokenId), "ERC721: invalid token ID");
447 | }
448 |
449 | /**
450 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
451 | * The call is not executed if the target address is not a contract.
452 | *
453 | * @param from address representing the previous owner of the given token ID
454 | * @param to target address that will receive the tokens
455 | * @param tokenId uint256 ID of the token to be transferred
456 | * @param data bytes optional data to send along with the call
457 | * @return bool whether the call correctly returned the expected magic value
458 | */
459 | function _checkOnERC721Received(
460 | address from,
461 | address to,
462 | uint256 tokenId,
463 | bytes memory data
464 | ) private returns (bool) {
465 | if (to.isContract()) {
466 | try
467 | IERC721Receiver(to).onERC721Received(
468 | _msgSender(),
469 | from,
470 | tokenId,
471 | data
472 | )
473 | returns (bytes4 retval) {
474 | return retval == IERC721Receiver.onERC721Received.selector;
475 | } catch (bytes memory reason) {
476 | if (reason.length == 0) {
477 | revert(
478 | "ERC721: transfer to non ERC721Receiver implementer"
479 | );
480 | } else {
481 | /// @solidity memory-safe-assembly
482 | assembly {
483 | revert(add(32, reason), mload(reason))
484 | }
485 | }
486 | }
487 | } else {
488 | return true;
489 | }
490 | }
491 |
492 | /**
493 | * @dev Hook that is called before any token transfer. This includes minting
494 | * and burning.
495 | *
496 | * Calling conditions:
497 | *
498 | * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
499 | * transferred to `to`.
500 | * - When `from` is zero, `tokenId` will be minted for `to`.
501 | * - When `to` is zero, ``from``'s `tokenId` will be burned.
502 | * - `from` and `to` are never both zero.
503 | *
504 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
505 | */
506 | function _beforeTokenTransfer(
507 | address from,
508 | address to,
509 | uint256 tokenId
510 | ) internal virtual {}
511 |
512 | /**
513 | * @dev Hook that is called after any transfer of tokens. This includes
514 | * minting and burning.
515 | *
516 | * Calling conditions:
517 | *
518 | * - when `from` and `to` are both non-zero.
519 | * - `from` and `to` are never both zero.
520 | *
521 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
522 | */
523 | function _afterTokenTransfer(
524 | address from,
525 | address to,
526 | uint256 tokenId
527 | ) internal virtual {}
528 | }
529 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/ERC721Verifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./ERC721.sol";
7 | import "./Counters.sol";
8 | import "./GenesisUtils.sol";
9 | import "./ICircuitValidator.sol";
10 | import "./ZKPVerifier.sol";
11 |
12 | // Main Contract
13 | // ========================================================
14 | contract ERC721Verifier is ERC721, ZKPVerifier {
15 | // Variables
16 | uint64 public constant TRANSFER_REQUEST_ID = 1;
17 | string private erc721Name;
18 | string private erc721Symbol;
19 | mapping(uint256 => address) public idToAddress;
20 | mapping(address => uint256) public addressToId;
21 | using Counters for Counters.Counter;
22 | Counters.Counter private _tokenIdCounter;
23 | // Constants
24 | string internal constant TABLE =
25 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26 |
27 | // Functions
28 | /**
29 | * @dev constructor
30 | */
31 | constructor(string memory name_, string memory symbol_)
32 | ERC721(name_, symbol_)
33 | {
34 | erc721Name = name_;
35 | erc721Symbol = symbol_;
36 | }
37 |
38 | /**
39 | * @dev _beforeProofSubmit
40 | */
41 | function _beforeProofSubmit(
42 | uint64, /* requestId */
43 | uint256[] memory inputs,
44 | ICircuitValidator validator
45 | ) internal view override {
46 | // check that challenge input of the proof is equal to the msg.sender
47 | address addr = GenesisUtils.int256ToAddress(
48 | inputs[validator.getChallengeInputIndex()]
49 | );
50 | require(
51 | _msgSender() == addr,
52 | "address in proof is not a sender address"
53 | );
54 | }
55 |
56 | /**
57 | * @dev _afterProofSubmit
58 | */
59 | function _afterProofSubmit(
60 | uint64 requestId,
61 | uint256[] memory inputs,
62 | ICircuitValidator validator
63 | ) internal override {
64 | require(
65 | requestId == TRANSFER_REQUEST_ID && addressToId[_msgSender()] == 0,
66 | "proof can not be submitted more than once"
67 | );
68 |
69 | uint256 id = inputs[validator.getChallengeInputIndex()];
70 | // execute the airdrop
71 | if (idToAddress[id] == address(0)) {
72 | uint256 tokenId = _tokenIdCounter.current();
73 | _tokenIdCounter.increment();
74 | _safeMint(_msgSender(), tokenId);
75 | addressToId[_msgSender()] = id;
76 | idToAddress[id] = _msgSender();
77 | }
78 | }
79 |
80 | /**
81 | * @dev _beforeTokenTransfer
82 | */
83 | function _beforeTokenTransfer(
84 | address, /* from */
85 | address to,
86 | uint256 /* amount */
87 | ) internal view override {
88 | require(
89 | proofs[to][TRANSFER_REQUEST_ID] == true,
90 | "only identities who provided proof are allowed to receive tokens"
91 | );
92 | }
93 |
94 | /**
95 | * Inspired by Java code
96 | * Converts a decimal value to a hex value without the #
97 | */
98 | function uintToHex(uint256 decimalValue)
99 | public
100 | pure
101 | returns (bytes memory)
102 | {
103 | uint256 remainder;
104 | bytes memory hexResult = "";
105 | string[16] memory hexDictionary = [
106 | "0",
107 | "1",
108 | "2",
109 | "3",
110 | "4",
111 | "5",
112 | "6",
113 | "7",
114 | "8",
115 | "9",
116 | "A",
117 | "B",
118 | "C",
119 | "D",
120 | "E",
121 | "F"
122 | ];
123 |
124 | while (decimalValue > 0) {
125 | remainder = decimalValue % 16;
126 | string memory hexValue = hexDictionary[remainder];
127 | hexResult = abi.encodePacked(hexValue, hexResult);
128 | decimalValue = decimalValue / 16;
129 | }
130 |
131 | // Account for missing leading zeros
132 | uint256 len = hexResult.length;
133 |
134 | if (len == 5) {
135 | hexResult = abi.encodePacked("0", hexResult);
136 | } else if (len == 4) {
137 | hexResult = abi.encodePacked("00", hexResult);
138 | } else if (len == 3) {
139 | hexResult = abi.encodePacked("000", hexResult);
140 | } else if (len == 4) {
141 | hexResult = abi.encodePacked("0000", hexResult);
142 | }
143 |
144 | return hexResult;
145 | }
146 |
147 | /**
148 | */
149 | function base64Encode(bytes memory data)
150 | internal
151 | pure
152 | returns (string memory)
153 | {
154 | if (data.length == 0) return "";
155 |
156 | // load the table into memory
157 | string memory table = TABLE;
158 |
159 | // multiply by 4/3 rounded up
160 | uint256 encodedLen = 4 * ((data.length + 2) / 3);
161 |
162 | // add some extra buffer at the end required for the writing
163 | string memory result = new string(encodedLen + 32);
164 |
165 | assembly {
166 | // set the actual output length
167 | mstore(result, encodedLen)
168 |
169 | // prepare the lookup table
170 | let tablePtr := add(table, 1)
171 |
172 | // input ptr
173 | let dataPtr := data
174 | let endPtr := add(dataPtr, mload(data))
175 |
176 | // result ptr, jump over length
177 | let resultPtr := add(result, 32)
178 |
179 | // run over the input, 3 bytes at a time
180 | for {
181 |
182 | } lt(dataPtr, endPtr) {
183 |
184 | } {
185 | dataPtr := add(dataPtr, 3)
186 |
187 | // read 3 bytes
188 | let input := mload(dataPtr)
189 |
190 | // write 4 characters
191 | mstore(
192 | resultPtr,
193 | shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F))))
194 | )
195 | resultPtr := add(resultPtr, 1)
196 | mstore(
197 | resultPtr,
198 | shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F))))
199 | )
200 | resultPtr := add(resultPtr, 1)
201 | mstore(
202 | resultPtr,
203 | shl(248, mload(add(tablePtr, and(shr(6, input), 0x3F))))
204 | )
205 | resultPtr := add(resultPtr, 1)
206 | mstore(
207 | resultPtr,
208 | shl(248, mload(add(tablePtr, and(input, 0x3F))))
209 | )
210 | resultPtr := add(resultPtr, 1)
211 | }
212 |
213 | // padding with '='
214 | switch mod(mload(data), 3)
215 | case 1 {
216 | mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
217 | }
218 | case 2 {
219 | mstore(sub(resultPtr, 1), shl(248, 0x3d))
220 | }
221 | }
222 |
223 | return result;
224 | }
225 |
226 | /**
227 | * Inspired by OraclizeAPI's implementation - MIT license
228 | * https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
229 | */
230 | function toString(uint256 value) internal pure returns (string memory) {
231 | if (value == 0) {
232 | return "0";
233 | }
234 | uint256 temp = value;
235 | uint256 digits;
236 | while (temp != 0) {
237 | digits++;
238 | temp /= 10;
239 | }
240 | bytes memory buffer = new bytes(digits);
241 | while (value != 0) {
242 | digits -= 1;
243 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
244 | value /= 10;
245 | }
246 | return string(buffer);
247 | }
248 |
249 | /**
250 | * Returns metadata associated to token
251 | */
252 | function tokenURI(uint256 tokenId)
253 | public
254 | view
255 | override
256 | returns (string memory)
257 | {
258 | // Validate if tokenId exists
259 | require(_exists(tokenId), "Token ID does not exist.");
260 |
261 | string memory hexValue = string(uintToHex(tokenId));
262 |
263 | string memory json = base64Encode(
264 | bytes(
265 | string(
266 | abi.encodePacked(
267 | '{"id": ',
268 | toString(tokenId),
269 | ", ",
270 | '"name": "#',
271 | hexValue,
272 | '", ',
273 | '"token_name": "',
274 | erc721Name,
275 | '", ',
276 | '"token_symbol": "',
277 | erc721Symbol,
278 | '", ',
279 | '"background_color": "FFFFFF", ',
280 | '"image": "data:image/svg+xml;base64,',
281 | base64Encode(
282 | bytes(
283 | string(
284 | abi.encodePacked(
285 | ''
288 | )
289 | )
290 | )
291 | ),
292 | '"}'
293 | )
294 | )
295 | )
296 | );
297 |
298 | return string(abi.encodePacked("data:application/json;base64,", json));
299 | }
300 | }
301 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/GenesisUtils.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-3.0
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./BytesLib.sol";
7 |
8 | // Library
9 | // ========================================================
10 | library GenesisUtils {
11 | /**
12 | * @dev int256ToBytes
13 | */
14 | function int256ToBytes(uint256 x) internal pure returns (bytes memory b) {
15 | b = new bytes(32);
16 | assembly {
17 | mstore(add(b, 32), x)
18 | }
19 | }
20 |
21 | /**
22 | * @dev reverse
23 | */
24 | function reverse(uint256 input) internal pure returns (uint256 v) {
25 | v = input;
26 |
27 | // swap bytes
28 | v =
29 | ((v &
30 | 0xFF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00) >>
31 | 8) |
32 | ((v &
33 | 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) <<
34 | 8);
35 |
36 | // swap 2-byte long pairs
37 | v =
38 | ((v &
39 | 0xFFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000) >>
40 | 16) |
41 | ((v &
42 | 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) <<
43 | 16);
44 |
45 | // swap 4-byte long pairs
46 | v =
47 | ((v &
48 | 0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000) >>
49 | 32) |
50 | ((v &
51 | 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) <<
52 | 32);
53 |
54 | // swap 8-byte long pairs
55 | v =
56 | ((v &
57 | 0xFFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF0000000000000000) >>
58 | 64) |
59 | ((v &
60 | 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) <<
61 | 64);
62 |
63 | // swap 16-byte long pairs
64 | v = (v >> 128) | (v << 128);
65 | }
66 |
67 | /**
68 | * @dev sum
69 | */
70 | function sum(bytes memory array) internal pure returns (uint16 s) {
71 | require(array.length == 29, "Checksum requires 29 length array");
72 |
73 | for (uint256 i = 0; i < array.length; ++i) {
74 | s += uint16(uint8(array[i]));
75 | }
76 | }
77 |
78 | /**
79 | * @dev bytesToHexString
80 | */
81 | function bytesToHexString(bytes memory buffer)
82 | internal
83 | pure
84 | returns (string memory)
85 | {
86 | // Fixed buffer size for hexadecimal convertion
87 | bytes memory converted = new bytes(buffer.length * 2);
88 |
89 | bytes memory _base = "0123456789abcdef";
90 |
91 | for (uint256 i = 0; i < buffer.length; i++) {
92 | converted[i * 2] = _base[uint8(buffer[i]) / _base.length];
93 | converted[i * 2 + 1] = _base[uint8(buffer[i]) % _base.length];
94 | }
95 |
96 | return string(abi.encodePacked("0x", converted));
97 | }
98 |
99 | /**
100 | * @dev compareStrings
101 | */
102 | function compareStrings(string memory a, string memory b)
103 | internal
104 | pure
105 | returns (bool)
106 | {
107 | return (keccak256(abi.encodePacked((a))) ==
108 | keccak256(abi.encodePacked((b))));
109 | }
110 |
111 | /**
112 | * @dev isGenesisState
113 | */
114 | function isGenesisState(uint256 id, uint256 idState)
115 | internal
116 | pure
117 | returns (bool)
118 | {
119 | uint256 userSwappedState = reverse(idState);
120 |
121 | bytes memory userStateB1 = int256ToBytes(userSwappedState);
122 |
123 | bytes memory cutState = BytesLib.slice(
124 | userStateB1,
125 | userStateB1.length - 27,
126 | 27
127 | );
128 |
129 | bytes memory typDefault = hex"0000";
130 |
131 | bytes memory beforeChecksum = BytesLib.concat(typDefault, cutState);
132 | require(
133 | beforeChecksum.length == 29,
134 | "Checksum requires 29 length array"
135 | );
136 |
137 | uint16 s = sum(beforeChecksum);
138 |
139 | bytes memory checkSumBytes = abi.encodePacked(s);
140 |
141 | bytes memory idBytes = BytesLib.concat(beforeChecksum, checkSumBytes);
142 | require(idBytes.length == 31, "idBytes requires 31 length array");
143 |
144 | return id == reverse(toUint256(idBytes));
145 | }
146 |
147 | /**
148 | * @dev toUint256
149 | */
150 | function toUint256(bytes memory _bytes)
151 | internal
152 | pure
153 | returns (uint256 value)
154 | {
155 | assembly {
156 | value := mload(add(_bytes, 0x20))
157 | }
158 | }
159 |
160 | /**
161 | * @dev bytesToAddress
162 | */
163 | function bytesToAddress(bytes memory bys)
164 | internal
165 | pure
166 | returns (address addr)
167 | {
168 | assembly {
169 | addr := mload(add(bys, 20))
170 | }
171 | }
172 |
173 | /**
174 | * @dev int256ToAddress
175 | */
176 | function int256ToAddress(uint256 input) internal pure returns (address) {
177 | return bytesToAddress(int256ToBytes(reverse(input)));
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/ICircuitValidator.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Interface
5 | // ========================================================
6 | interface ICircuitValidator {
7 | // Variables
8 | struct CircuitQuery {
9 | uint256 schema;
10 | uint256 slotIndex;
11 | uint256 operator;
12 | uint256[] value;
13 | string circuitId;
14 | }
15 |
16 | /**
17 | * @dev verify
18 | */
19 | function verify(
20 | uint256[] memory inputs,
21 | uint256[2] memory a,
22 | uint256[2][2] memory b,
23 | uint256[2] memory c,
24 | CircuitQuery memory query
25 | ) external view returns (bool r);
26 |
27 | /**
28 | * @dev getCircuitId
29 | */
30 | function getCircuitId() external pure returns (string memory id);
31 |
32 | /**
33 | * @dev getChallengeInputIndex
34 | */
35 | function getChallengeInputIndex() external pure returns (uint256 index);
36 |
37 | /**
38 | * @dev getUserIdInputIndex
39 | */
40 | function getUserIdInputIndex() external pure returns (uint256 index);
41 | }
42 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/IERC165.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @dev Interface of the ERC165 standard, as defined in the
8 | * https://eips.ethereum.org/EIPS/eip-165[EIP].
9 | *
10 | * Implementers can declare support of contract interfaces, which can then be
11 | * queried by others ({ERC165Checker}).
12 | *
13 | * For an implementation, see {ERC165}.
14 | */
15 | interface IERC165 {
16 | /**
17 | * @dev Returns true if this contract implements the interface defined by
18 | * `interfaceId`. See the corresponding
19 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
20 | * to learn more about how these ids are created.
21 | *
22 | * This function call must use less than 30 000 gas.
23 | */
24 | function supportsInterface(bytes4 interfaceId) external view returns (bool);
25 | }
26 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/IERC721.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./IERC165.sol";
7 |
8 | /**
9 | * @dev Required interface of an ERC721 compliant contract.
10 | */
11 | interface IERC721 is IERC165 {
12 | /**
13 | * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
14 | */
15 | event Transfer(
16 | address indexed from,
17 | address indexed to,
18 | uint256 indexed tokenId
19 | );
20 |
21 | /**
22 | * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
23 | */
24 | event Approval(
25 | address indexed owner,
26 | address indexed approved,
27 | uint256 indexed tokenId
28 | );
29 |
30 | /**
31 | * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
32 | */
33 | event ApprovalForAll(
34 | address indexed owner,
35 | address indexed operator,
36 | bool approved
37 | );
38 |
39 | /**
40 | * @dev Returns the number of tokens in ``owner``'s account.
41 | */
42 | function balanceOf(address owner) external view returns (uint256 balance);
43 |
44 | /**
45 | * @dev Returns the owner of the `tokenId` token.
46 | *
47 | * Requirements:
48 | *
49 | * - `tokenId` must exist.
50 | */
51 | function ownerOf(uint256 tokenId) external view returns (address owner);
52 |
53 | /**
54 | * @dev Safely transfers `tokenId` token from `from` to `to`.
55 | *
56 | * Requirements:
57 | *
58 | * - `from` cannot be the zero address.
59 | * - `to` cannot be the zero address.
60 | * - `tokenId` token must exist and be owned by `from`.
61 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
62 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
63 | *
64 | * Emits a {Transfer} event.
65 | */
66 | function safeTransferFrom(
67 | address from,
68 | address to,
69 | uint256 tokenId,
70 | bytes calldata data
71 | ) external;
72 |
73 | /**
74 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
75 | * are aware of the ERC721 protocol to prevent tokens from being forever locked.
76 | *
77 | * Requirements:
78 | *
79 | * - `from` cannot be the zero address.
80 | * - `to` cannot be the zero address.
81 | * - `tokenId` token must exist and be owned by `from`.
82 | * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
83 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
84 | *
85 | * Emits a {Transfer} event.
86 | */
87 | function safeTransferFrom(
88 | address from,
89 | address to,
90 | uint256 tokenId
91 | ) external;
92 |
93 | /**
94 | * @dev Transfers `tokenId` token from `from` to `to`.
95 | *
96 | * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
97 | *
98 | * Requirements:
99 | *
100 | * - `from` cannot be the zero address.
101 | * - `to` cannot be the zero address.
102 | * - `tokenId` token must be owned by `from`.
103 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
104 | *
105 | * Emits a {Transfer} event.
106 | */
107 | function transferFrom(
108 | address from,
109 | address to,
110 | uint256 tokenId
111 | ) external;
112 |
113 | /**
114 | * @dev Gives permission to `to` to transfer `tokenId` token to another account.
115 | * The approval is cleared when the token is transferred.
116 | *
117 | * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
118 | *
119 | * Requirements:
120 | *
121 | * - The caller must own the token or be an approved operator.
122 | * - `tokenId` must exist.
123 | *
124 | * Emits an {Approval} event.
125 | */
126 | function approve(address to, uint256 tokenId) external;
127 |
128 | /**
129 | * @dev Approve or remove `operator` as an operator for the caller.
130 | * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
131 | *
132 | * Requirements:
133 | *
134 | * - The `operator` cannot be the caller.
135 | *
136 | * Emits an {ApprovalForAll} event.
137 | */
138 | function setApprovalForAll(address operator, bool _approved) external;
139 |
140 | /**
141 | * @dev Returns the account approved for `tokenId` token.
142 | *
143 | * Requirements:
144 | *
145 | * - `tokenId` must exist.
146 | */
147 | function getApproved(uint256 tokenId)
148 | external
149 | view
150 | returns (address operator);
151 |
152 | /**
153 | * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
154 | *
155 | * See {setApprovalForAll}
156 | */
157 | function isApprovedForAll(address owner, address operator)
158 | external
159 | view
160 | returns (bool);
161 | }
162 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/IERC721Metadata.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./IERC721.sol";
7 |
8 | /**
9 | * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
10 | * @dev See https://eips.ethereum.org/EIPS/eip-721
11 | */
12 | interface IERC721Metadata is IERC721 {
13 | /**
14 | * @dev Returns the token collection name.
15 | */
16 | function name() external view returns (string memory);
17 |
18 | /**
19 | * @dev Returns the token collection symbol.
20 | */
21 | function symbol() external view returns (string memory);
22 |
23 | /**
24 | * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
25 | */
26 | function tokenURI(uint256 tokenId) external view returns (string memory);
27 | }
28 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/IERC721Receiver.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @title ERC721 token receiver interface
8 | * @dev Interface for any contract that wants to support safeTransfers
9 | * from ERC721 asset contracts.
10 | */
11 | interface IERC721Receiver {
12 | /**
13 | * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
14 | * by `operator` from `from`, this function is called.
15 | *
16 | * It must return its Solidity selector to confirm the token transfer.
17 | * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
18 | *
19 | * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
20 | */
21 | function onERC721Received(
22 | address operator,
23 | address from,
24 | uint256 tokenId,
25 | bytes calldata data
26 | ) external returns (bytes4);
27 | }
28 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/IZKPVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./ICircuitValidator.sol";
7 |
8 | // Interface
9 | // ========================================================
10 | interface IZKPVerifier {
11 | /**
12 | * @dev submitZKPResponse
13 | */
14 | function submitZKPResponse(
15 | uint64 requestId,
16 | uint256[] memory inputs,
17 | uint256[2] memory a,
18 | uint256[2][2] memory b,
19 | uint256[2] memory c
20 | ) external returns (bool);
21 |
22 | /**
23 | * @dev setZKPRequest
24 | */
25 | function setZKPRequest(
26 | uint64 requestId,
27 | ICircuitValidator validator,
28 | ICircuitValidator.CircuitQuery memory query
29 | ) external returns (bool);
30 |
31 | /**
32 | * @dev getZKPRequest
33 | */
34 | function getZKPRequest(uint64 requestId)
35 | external
36 | returns (ICircuitValidator.CircuitQuery memory);
37 | }
38 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/Ownable.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | import "./Context.sol";
7 |
8 | /**
9 | * @dev Contract module which provides a basic access control mechanism, where
10 | * there is an account (an owner) that can be granted exclusive access to
11 | * specific functions.
12 | *
13 | * By default, the owner account will be the one that deploys the contract. This
14 | * can later be changed with {transferOwnership}.
15 | *
16 | * This module is used through inheritance. It will make available the modifier
17 | * `onlyOwner`, which can be applied to your functions to restrict their use to
18 | * the owner.
19 | */
20 | abstract contract Ownable is Context {
21 | address private _owner;
22 |
23 | event OwnershipTransferred(
24 | address indexed previousOwner,
25 | address indexed newOwner
26 | );
27 |
28 | /**
29 | * @dev Initializes the contract setting the deployer as the initial owner.
30 | */
31 | constructor() {
32 | _transferOwnership(_msgSender());
33 | }
34 |
35 | /**
36 | * @dev Throws if called by any account other than the owner.
37 | */
38 | modifier onlyOwner() {
39 | _checkOwner();
40 | _;
41 | }
42 |
43 | /**
44 | * @dev Returns the address of the current owner.
45 | */
46 | function owner() public view virtual returns (address) {
47 | return _owner;
48 | }
49 |
50 | /**
51 | * @dev Throws if the sender is not the owner.
52 | */
53 | function _checkOwner() internal view virtual {
54 | require(owner() == _msgSender(), "Ownable: caller is not the owner");
55 | }
56 |
57 | /**
58 | * @dev Leaves the contract without owner. It will not be possible to call
59 | * `onlyOwner` functions anymore. Can only be called by the current owner.
60 | *
61 | * NOTE: Renouncing ownership will leave the contract without an owner,
62 | * thereby removing any functionality that is only available to the owner.
63 | */
64 | function renounceOwnership() public virtual onlyOwner {
65 | _transferOwnership(address(0));
66 | }
67 |
68 | /**
69 | * @dev Transfers ownership of the contract to a new account (`newOwner`).
70 | * Can only be called by the current owner.
71 | */
72 | function transferOwnership(address newOwner) public virtual onlyOwner {
73 | require(
74 | newOwner != address(0),
75 | "Ownable: new owner is the zero address"
76 | );
77 | _transferOwnership(newOwner);
78 | }
79 |
80 | /**
81 | * @dev Transfers ownership of the contract to a new account (`newOwner`).
82 | * Internal function without access restriction.
83 | */
84 | function _transferOwnership(address newOwner) internal virtual {
85 | address oldOwner = _owner;
86 | _owner = newOwner;
87 | emit OwnershipTransferred(oldOwner, newOwner);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/Strings.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | // OpenZeppelin Contracts (last updated v4.7.0) (utils/Strings.sol)
3 |
4 | pragma solidity ^0.8.0;
5 |
6 | /**
7 | * @dev String operations.
8 | */
9 | library Strings {
10 | bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
11 | uint8 private constant _ADDRESS_LENGTH = 20;
12 |
13 | /**
14 | * @dev Converts a `uint256` to its ASCII `string` decimal representation.
15 | */
16 | function toString(uint256 value) internal pure returns (string memory) {
17 | // Inspired by OraclizeAPI's implementation - MIT licence
18 | // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
19 |
20 | if (value == 0) {
21 | return "0";
22 | }
23 | uint256 temp = value;
24 | uint256 digits;
25 | while (temp != 0) {
26 | digits++;
27 | temp /= 10;
28 | }
29 | bytes memory buffer = new bytes(digits);
30 | while (value != 0) {
31 | digits -= 1;
32 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
33 | value /= 10;
34 | }
35 | return string(buffer);
36 | }
37 |
38 | /**
39 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
40 | */
41 | function toHexString(uint256 value) internal pure returns (string memory) {
42 | if (value == 0) {
43 | return "0x00";
44 | }
45 | uint256 temp = value;
46 | uint256 length = 0;
47 | while (temp != 0) {
48 | length++;
49 | temp >>= 8;
50 | }
51 | return toHexString(value, length);
52 | }
53 |
54 | /**
55 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
56 | */
57 | function toHexString(uint256 value, uint256 length)
58 | internal
59 | pure
60 | returns (string memory)
61 | {
62 | bytes memory buffer = new bytes(2 * length + 2);
63 | buffer[0] = "0";
64 | buffer[1] = "x";
65 | for (uint256 i = 2 * length + 1; i > 1; --i) {
66 | buffer[i] = _HEX_SYMBOLS[value & 0xf];
67 | value >>= 4;
68 | }
69 | require(value == 0, "Strings: hex length insufficient");
70 | return string(buffer);
71 | }
72 |
73 | /**
74 | * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
75 | */
76 | function toHexString(address addr) internal pure returns (string memory) {
77 | return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/verify/ERC721Verifier/ZKPVerifier.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | // Imports
5 | // ========================================================
6 | import "./Ownable.sol";
7 | import "./ICircuitValidator.sol";
8 | import "./IZKPVerifier.sol";
9 |
10 | // Contract
11 | // ========================================================
12 | contract ZKPVerifier is IZKPVerifier, Ownable {
13 | // Variables
14 | // msg.sender-> ( requestID -> is proof given )
15 | mapping(address => mapping(uint64 => bool)) public proofs;
16 | mapping(uint64 => ICircuitValidator.CircuitQuery) public requestQueries;
17 | mapping(uint64 => ICircuitValidator) public requestValidators;
18 | uint64[] public supportedRequests;
19 |
20 | // Functions
21 | /**
22 | * @dev submitZKPResponse
23 | */
24 | function submitZKPResponse(
25 | uint64 requestId,
26 | uint256[] memory inputs,
27 | uint256[2] memory a,
28 | uint256[2][2] memory b,
29 | uint256[2] memory c
30 | ) external override returns (bool) {
31 | require(
32 | requestValidators[requestId] != ICircuitValidator(address(0)),
33 | "validator is not set for this request id"
34 | ); // validator exists
35 | require(
36 | requestQueries[requestId].schema != 0,
37 | "query is not set for this request id"
38 | ); // query exists
39 |
40 | _beforeProofSubmit(requestId, inputs, requestValidators[requestId]);
41 |
42 | require(
43 | requestValidators[requestId].verify(
44 | inputs,
45 | a,
46 | b,
47 | c,
48 | requestQueries[requestId]
49 | ),
50 | "proof response is not valid"
51 | );
52 |
53 | proofs[msg.sender][requestId] = true; // user provided a valid proof for request
54 |
55 | _afterProofSubmit(requestId, inputs, requestValidators[requestId]);
56 | return true;
57 | }
58 |
59 | /**
60 | * @dev getZKPRequest
61 | */
62 | function getZKPRequest(uint64 requestId)
63 | external
64 | view
65 | override
66 | returns (ICircuitValidator.CircuitQuery memory)
67 | {
68 | return requestQueries[requestId];
69 | }
70 |
71 | /**
72 | * @dev setZKPRequest
73 | */
74 | function setZKPRequest(
75 | uint64 requestId,
76 | ICircuitValidator validator,
77 | ICircuitValidator.CircuitQuery memory query
78 | ) external override onlyOwner returns (bool) {
79 | if (requestValidators[requestId] == ICircuitValidator(address(0x00))) {
80 | supportedRequests.push(requestId);
81 | }
82 | requestQueries[requestId].value = query.value;
83 | requestQueries[requestId].operator = query.operator;
84 | requestQueries[requestId].circuitId = query.circuitId;
85 | requestQueries[requestId].slotIndex = query.slotIndex;
86 | requestQueries[requestId].schema = query.schema;
87 |
88 | requestQueries[requestId].circuitId = query.circuitId;
89 |
90 | requestValidators[requestId] = validator;
91 | return true;
92 | }
93 |
94 | /**
95 | * @dev getSupportedRequests
96 | */
97 | function getSupportedRequests()
98 | external
99 | view
100 | returns (uint64[] memory arr)
101 | {
102 | return supportedRequests;
103 | }
104 |
105 | /**
106 | * @dev Hook that is called before any proof response submit
107 | */
108 | function _beforeProofSubmit(
109 | uint64 requestId,
110 | uint256[] memory inputs,
111 | ICircuitValidator validator
112 | ) internal virtual {}
113 |
114 | /**
115 | * @dev Hook that is called after any proof response submit
116 | */
117 | function _afterProofSubmit(
118 | uint64 requestId,
119 | uint256[] memory inputs,
120 | ICircuitValidator validator
121 | ) internal virtual {}
122 | }
123 |
--------------------------------------------------------------------------------