├── .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 | ![Auto Generated](README/toc.png) 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 | ![Platform Test Polygon ID](README/platform-test.png) 67 | 68 | B - Create Schema 69 | 70 | ![Create Schema](README/create-schema-01.png) 71 | 72 | ![Configure Schema](README/create-schema-02.png) 73 | 74 | ![Offer Claim](README/create-schema-03.png) 75 | 76 | C - Claim 77 | 78 | ![Claim](README/claim-01.png) 79 | 80 | ![Claim Link](README/claim-02.png) 81 | 82 | ![Polygon ID App](README/claim-03.png) 83 | 84 | ![Connect Polygon ID Wallet](README/claim-04.png) 85 | 86 | ![Re-Scan For Claim](README/claim-05.png) 87 | 88 | ![Accept Claim](README/claim-06.png) 89 | 90 | ![View Claim](README/claim-07.png) 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 | ![Mint](README/mint-01.png) 175 | 176 | ![Generate Proof](README/mint-02.png) 177 | 178 | ![Authenticate](README/mint-03.png) 179 | 180 | ![Mint](README/mint-04.png) 181 | 182 | --- 183 | 184 | ## Debugging Errors 185 | 186 | ### Wrong Claim Schema Has Been Used For Proof Generation 187 | 188 | ![Wrong Claim Schema Has Been Used For Proof Generation](README/error-01.png) 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 | ![Wrong Query Operator Has Been Used For Proof Generation](README/error-02.png) 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 | ![Wrong Comparison Value Has Been Used For Proof Generation](README/error-03.png) 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 | ![Proof Can Not Be Submitted More Than Once](README/error-04.png) 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 | ![OpenSea Minted NFT](README/opensea.png) 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 | --------------------------------------------------------------------------------