From de64eeea6e1ae1ef52351fd6036318555ae3ee45 Mon Sep 17 00:00:00 2001 From: architect-dev Date: Wed, 25 Mar 2026 17:00:01 +0100 Subject: [PATCH 1/2] Update packages, use cofhesdk --- packages/hardhat/hardhat.config.ts | 2 +- packages/hardhat/package.json | 6 +- yarn.lock | 290 ++++++++++++++++++++++++----- 3 files changed, 246 insertions(+), 52 deletions(-) diff --git a/packages/hardhat/hardhat.config.ts b/packages/hardhat/hardhat.config.ts index fa25d01..1c90472 100644 --- a/packages/hardhat/hardhat.config.ts +++ b/packages/hardhat/hardhat.config.ts @@ -13,7 +13,7 @@ import "hardhat-deploy-ethers"; import { task } from "hardhat/config"; import generateTsAbis from "./scripts/generateTsAbis"; -import "cofhe-hardhat-plugin"; +import "@cofhe/hardhat-plugin"; import "./tasks/hh-prepare-wallet"; diff --git a/packages/hardhat/package.json b/packages/hardhat/package.json index d6e663a..74a61b0 100644 --- a/packages/hardhat/package.json +++ b/packages/hardhat/package.json @@ -32,10 +32,11 @@ "qrcode": "~1.5.4" }, "devDependencies": { + "@cofhe/hardhat-plugin": "0.4.0", + "@cofhe/sdk": "0.4.0", "@ethersproject/abi": "~5.7.0", "@ethersproject/providers": "~5.7.2", - "@fhenixprotocol/cofhe-contracts": "0.0.13", - "@fhenixprotocol/cofhe-mock-contracts": "^0.2.1-alpha.0", + "@fhenixprotocol/cofhe-contracts": "0.1.3", "@nomicfoundation/hardhat-chai-matchers": "~2.0.7", "@nomicfoundation/hardhat-ethers": "~3.0.8", "@nomicfoundation/hardhat-ignition": "^0.15.11", @@ -61,6 +62,7 @@ "eslint-config-prettier": "~8.10.0", "eslint-plugin-prettier": "~5.2.1", "ethers": "~6.13.2", + "fhenix-confidential-contracts": "0.2.1", "hardhat": "~2.22.10", "hardhat-deploy": "~0.12.4", "hardhat-deploy-ethers": "~0.4.2", diff --git a/yarn.lock b/yarn.lock index 6abc1d5..fce82d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -234,6 +234,73 @@ __metadata: languageName: node linkType: hard +"@cofhe/hardhat-plugin@npm:0.4.0": + version: 0.4.0 + resolution: "@cofhe/hardhat-plugin@npm:0.4.0" + dependencies: + "@cofhe/mock-contracts": 0.4.0 + "@cofhe/sdk": 0.4.0 + chai: ^4.2.0 + chalk: ^4.1.2 + fast-glob: ^3.3.2 + viem: ^2.38.6 + peerDependencies: + "@fhenixprotocol/cofhe-contracts": ">=0.1.0" + "@nomicfoundation/hardhat-ethers": ^3.0.0 + "@openzeppelin/contracts": ^5.0.0 + ethers: ^5.0.0 || ^6.0.0 + hardhat: ^2.0.0 + peerDependenciesMeta: + "@fhenixprotocol/cofhe-contracts": + optional: false + "@openzeppelin/contracts": + optional: false + checksum: 679d47335b88c306ba3c03d632703a6f74c392c2e394d4df82885719cc03c91c24765579978b26b90b35de3e74ce7bd10bfe1be78aacb2545c88eba61ae4b1fb + languageName: node + linkType: hard + +"@cofhe/mock-contracts@npm:0.4.0": + version: 0.4.0 + resolution: "@cofhe/mock-contracts@npm:0.4.0" + dependencies: + "@fhenixprotocol/cofhe-contracts": 0.1.0 + "@openzeppelin/contracts": ^5.0.0 + "@openzeppelin/contracts-upgradeable": ^5.0.0 + checksum: cc4b2cc5716b419cacd4e58472ee677002085ea6317ac238a8f2dd49a0ccef249f8fdbfbadeb12cd29ca6d33ce20fdcce530f11b7992bf686472a89ba44b373d + languageName: node + linkType: hard + +"@cofhe/sdk@npm:0.4.0": + version: 0.4.0 + resolution: "@cofhe/sdk@npm:0.4.0" + dependencies: + iframe-shared-storage: ^1.0.34 + immer: ^10.1.1 + node-tfhe: 0.11.1 + tfhe: 0.11.1 + tweetnacl: ^1.0.3 + viem: ^2.38.6 + zod: ^4.0.0 + zustand: ^5.0.1 + peerDependencies: + "@nomicfoundation/hardhat-ethers": ^3.0.0 + "@wagmi/core": ^2.0.0 + ethers: ^5.0.0 || ^6.0.0 + hardhat: ^2.0.0 + viem: ^2.38.6 + peerDependenciesMeta: + "@nomicfoundation/hardhat-ethers": + optional: true + "@wagmi/core": + optional: true + ethers: + optional: true + hardhat: + optional: true + checksum: dd826e03af6d26d8f25ed0a91c3cc0dd3d5674fea152b9192c5ce83c820cee75cf06019b22c63c416d0c2aaaaf6f6c787efd26acbb85a6549dae8025134be76b + languageName: node + linkType: hard + "@coinbase/wallet-sdk@npm:4.3.0": version: 4.3.0 resolution: "@coinbase/wallet-sdk@npm:4.3.0" @@ -1399,7 +1466,25 @@ __metadata: languageName: node linkType: hard -"@fhenixprotocol/cofhe-mock-contracts@npm:^0.2.1-alpha.0, @fhenixprotocol/cofhe-mock-contracts@npm:^0.2.1-alpha.4": +"@fhenixprotocol/cofhe-contracts@npm:0.1.0": + version: 0.1.0 + resolution: "@fhenixprotocol/cofhe-contracts@npm:0.1.0" + dependencies: + "@openzeppelin/contracts": ^5.0.0 + checksum: 8de9ec1e67f1df8d89943d73a0c3c02ddffc26907c25a5ffa6c5fe8c916e2a010e3f775a326f5923e81ba40b54bff19f849f6cbdbef81628ac7bbed0cda0fb3b + languageName: node + linkType: hard + +"@fhenixprotocol/cofhe-contracts@npm:0.1.3": + version: 0.1.3 + resolution: "@fhenixprotocol/cofhe-contracts@npm:0.1.3" + dependencies: + "@openzeppelin/contracts": ^5.0.0 + checksum: 37d4879cbc12c1fe65b73bf44a8a1c7f75fa0e4b29af89a5dd42689a742a7c8c6c6f95b5be7f6e0137b73c1558125651f19d6a138e0dbede676abbcf956cc401 + languageName: node + linkType: hard + +"@fhenixprotocol/cofhe-mock-contracts@npm:^0.2.1-alpha.4": version: 0.2.1-alpha.4 resolution: "@fhenixprotocol/cofhe-mock-contracts@npm:0.2.1-alpha.4" dependencies: @@ -2685,10 +2770,10 @@ __metadata: languageName: node linkType: hard -"@next/env@npm:15.2.2": - version: 15.2.2 - resolution: "@next/env@npm:15.2.2" - checksum: da032cecdb04583ae6aa0679a912d5e6b36220bc33cf0933b8b350b91decc1001e1aa7baf26d9ac1e89033eefdc5936c634f485b05517facec1737d02f6099e2 +"@next/env@npm:15.2.8": + version: 15.2.8 + resolution: "@next/env@npm:15.2.8" + checksum: df7606a0cca0b47b1157ef2e791c892f1fd6b9f715624e5aef48a3ca1de57d09cb3c821cc13c888a9b20ba8b17c5fb5742bb5ac6e0e7cd95f1e162fe33d9e59c languageName: node linkType: hard @@ -2701,58 +2786,58 @@ __metadata: languageName: node linkType: hard -"@next/swc-darwin-arm64@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-darwin-arm64@npm:15.2.2" +"@next/swc-darwin-arm64@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-darwin-arm64@npm:15.2.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@next/swc-darwin-x64@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-darwin-x64@npm:15.2.2" +"@next/swc-darwin-x64@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-darwin-x64@npm:15.2.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@next/swc-linux-arm64-gnu@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-linux-arm64-gnu@npm:15.2.2" +"@next/swc-linux-arm64-gnu@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-linux-arm64-gnu@npm:15.2.5" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-arm64-musl@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-linux-arm64-musl@npm:15.2.2" +"@next/swc-linux-arm64-musl@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-linux-arm64-musl@npm:15.2.5" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@next/swc-linux-x64-gnu@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-linux-x64-gnu@npm:15.2.2" +"@next/swc-linux-x64-gnu@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-linux-x64-gnu@npm:15.2.5" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@next/swc-linux-x64-musl@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-linux-x64-musl@npm:15.2.2" +"@next/swc-linux-x64-musl@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-linux-x64-musl@npm:15.2.5" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@next/swc-win32-arm64-msvc@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-win32-arm64-msvc@npm:15.2.2" +"@next/swc-win32-arm64-msvc@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-win32-arm64-msvc@npm:15.2.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@next/swc-win32-x64-msvc@npm:15.2.2": - version: 15.2.2 - resolution: "@next/swc-win32-x64-msvc@npm:15.2.2" +"@next/swc-win32-x64-msvc@npm:15.2.5": + version: 15.2.5 + resolution: "@next/swc-win32-x64-msvc@npm:15.2.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -4510,10 +4595,11 @@ __metadata: version: 0.0.0-use.local resolution: "@se-2/hardhat@workspace:packages/hardhat" dependencies: + "@cofhe/hardhat-plugin": 0.4.0 + "@cofhe/sdk": 0.4.0 "@ethersproject/abi": ~5.7.0 "@ethersproject/providers": ~5.7.2 - "@fhenixprotocol/cofhe-contracts": 0.0.13 - "@fhenixprotocol/cofhe-mock-contracts": ^0.2.1-alpha.0 + "@fhenixprotocol/cofhe-contracts": 0.1.3 "@inquirer/password": ^4.0.2 "@nomicfoundation/hardhat-chai-matchers": ~2.0.7 "@nomicfoundation/hardhat-ethers": ~3.0.8 @@ -4545,6 +4631,7 @@ __metadata: eslint-config-prettier: ~8.10.0 eslint-plugin-prettier: ~5.2.1 ethers: ~6.13.2 + fhenix-confidential-contracts: 0.2.1 hardhat: ~2.22.10 hardhat-deploy: ~0.12.4 hardhat-deploy-ethers: ~0.4.2 @@ -4601,7 +4688,7 @@ __metadata: kubo-rpc-client: ~5.0.2 lucide-react: ^0.477.0 motion: ^12.5.0 - next: 15.2.2 + next: 15.2.8 next-nprogress-bar: ~2.3.13 next-themes: ~0.3.0 postcss: ~8.4.45 @@ -6961,6 +7048,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:1.2.3, abitype@npm:^1.2.3": + version: 1.2.3 + resolution: "abitype@npm:1.2.3" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3.22.0 || ^4.0.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: b5b5620f8e55a6dd7ae829630c0ded02b30f589f0f8f5ca931cdfcf6d7daa8154e30e3fe3593b3f6c4872a955ac55d447ccc2f801fd6a6aa698bdad966e3fe2e + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -8047,7 +8149,7 @@ __metadata: languageName: node linkType: hard -"chai@npm:~4.5.0": +"chai@npm:^4.2.0, chai@npm:~4.5.0": version: 4.5.0 resolution: "chai@npm:4.5.0" dependencies: @@ -10718,6 +10820,19 @@ __metadata: languageName: node linkType: hard +"fhenix-confidential-contracts@npm:0.2.1": + version: 0.2.1 + resolution: "fhenix-confidential-contracts@npm:0.2.1" + dependencies: + "@openzeppelin/contracts": ^5.2.0 + "@typechain/ethers-v6": ~0.5.1 + dotenv: ~16.4.5 + envfile: ~7.1.0 + qrcode: ~1.5.4 + checksum: 2997104f0ab63bc5af5e299039ee45833cca0c8f8e28498a5c820e103c2c2a3ab03bfbce1c839f4318d4dfb7c5f9e4044268713ff6fecf2e53f811ded55b657d + languageName: node + linkType: hard + "figures@npm:^3.2.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -11918,7 +12033,7 @@ __metadata: languageName: node linkType: hard -"idb-keyval@npm:^6.2.1": +"idb-keyval@npm:^6.2.1, idb-keyval@npm:^6.2.2": version: 6.2.2 resolution: "idb-keyval@npm:6.2.2" checksum: 5ac99363d346dd849fda82057301b95c8a79d4d8f5435f84fb921c6f5f471d6a2bdf89599bbe075ea3466612d0a58b787a1698ef5e8f1fbb9a5d629be96523fa @@ -11932,6 +12047,16 @@ __metadata: languageName: node linkType: hard +"iframe-shared-storage@npm:^1.0.34": + version: 1.0.34 + resolution: "iframe-shared-storage@npm:1.0.34" + dependencies: + idb-keyval: ^6.2.2 + postmsg-rpc: ^2.4.0 + checksum: 4d01e05aa957f98eec0c9e14a1daa701a5dc94d9c99f058ccb344ce80e17fb56da438307cfcd55f1c5f7eda74c525bc0b1a84a6082415edf88d39e44f36e108c + languageName: node + linkType: hard + "ignore-walk@npm:^7.0.0": version: 7.0.0 resolution: "ignore-walk@npm:7.0.0" @@ -14334,7 +14459,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.11, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": +"nanoid@npm:^3.3.11, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8": version: 3.3.11 resolution: "nanoid@npm:3.3.11" bin: @@ -14425,19 +14550,19 @@ __metadata: languageName: node linkType: hard -"next@npm:15.2.2": - version: 15.2.2 - resolution: "next@npm:15.2.2" - dependencies: - "@next/env": 15.2.2 - "@next/swc-darwin-arm64": 15.2.2 - "@next/swc-darwin-x64": 15.2.2 - "@next/swc-linux-arm64-gnu": 15.2.2 - "@next/swc-linux-arm64-musl": 15.2.2 - "@next/swc-linux-x64-gnu": 15.2.2 - "@next/swc-linux-x64-musl": 15.2.2 - "@next/swc-win32-arm64-msvc": 15.2.2 - "@next/swc-win32-x64-msvc": 15.2.2 +"next@npm:15.2.8": + version: 15.2.8 + resolution: "next@npm:15.2.8" + dependencies: + "@next/env": 15.2.8 + "@next/swc-darwin-arm64": 15.2.5 + "@next/swc-darwin-x64": 15.2.5 + "@next/swc-linux-arm64-gnu": 15.2.5 + "@next/swc-linux-arm64-musl": 15.2.5 + "@next/swc-linux-x64-gnu": 15.2.5 + "@next/swc-linux-x64-musl": 15.2.5 + "@next/swc-win32-arm64-msvc": 15.2.5 + "@next/swc-win32-x64-msvc": 15.2.5 "@swc/counter": 0.1.3 "@swc/helpers": 0.5.15 busboy: 1.6.0 @@ -14482,7 +14607,7 @@ __metadata: optional: true bin: next: dist/bin/next - checksum: f7db89f62511d24682e255250530d018c808d7911a868fc0a8cd4ed87b6cea7f609c7d9e5869616b64351eef3da4c3ef45349e70e910d6dec3ff4d4faf604c04 + checksum: 8d60e137d7e425615fbf639a05f7268c99016b89237bebc20805fb63da18692fd2534ef5ccdda790ddf0c1fd12c5a32850eb87feb6f322b0f5432a83867972ca languageName: node linkType: hard @@ -15136,6 +15261,27 @@ __metadata: languageName: node linkType: hard +"ox@npm:0.14.7": + version: 0.14.7 + resolution: "ox@npm:0.14.7" + dependencies: + "@adraffy/ens-normalize": ^1.11.0 + "@noble/ciphers": ^1.3.0 + "@noble/curves": 1.9.1 + "@noble/hashes": ^1.8.0 + "@scure/bip32": ^1.7.0 + "@scure/bip39": ^1.6.0 + abitype: ^1.2.3 + eventemitter3: 5.0.1 + peerDependencies: + typescript: ">=5.4.0" + peerDependenciesMeta: + typescript: + optional: true + checksum: 072768caa20419f6fba739aa28cca3896789b37e59e33d811d7a265a6c1044b4a03ff6abd82613f3da0e3965c517994b4841afa36da2c1bbf545fcf16680e885 + languageName: node + linkType: hard + "ox@npm:0.6.7": version: 0.6.7 resolution: "ox@npm:0.6.7" @@ -15696,6 +15842,15 @@ __metadata: languageName: node linkType: hard +"postmsg-rpc@npm:^2.4.0": + version: 2.4.0 + resolution: "postmsg-rpc@npm:2.4.0" + dependencies: + shortid: ^2.2.8 + checksum: aa397b3b47bf4ae09543843de531873cc08ef1a38dcf5a01ce3a28b7eb459271b795b99dea3eb97c9966de985279c6fda3e114801459774518a0bef3db74049e + languageName: node + linkType: hard + "preact@npm:^10.16.0, preact@npm:^10.24.2": version: 10.27.2 resolution: "preact@npm:10.27.2" @@ -17026,6 +17181,15 @@ __metadata: languageName: node linkType: hard +"shortid@npm:^2.2.8": + version: 2.2.17 + resolution: "shortid@npm:2.2.17" + dependencies: + nanoid: ^3.3.8 + checksum: e661408a6f7fb86a9947178984e739bdc99478745ffb8c8632b7b98c2a8d03d97c33aab6dcb9de73b8a64827a253266d407c362b6ac8b904b12014e8e428f9cd + languageName: node + linkType: hard + "side-channel-list@npm:^1.0.0": version: 1.0.0 resolution: "side-channel-list@npm:1.0.0" @@ -19082,6 +19246,27 @@ __metadata: languageName: node linkType: hard +"viem@npm:^2.38.6": + version: 2.47.6 + resolution: "viem@npm:2.47.6" + dependencies: + "@noble/curves": 1.9.1 + "@noble/hashes": 1.8.0 + "@scure/bip32": 1.7.0 + "@scure/bip39": 1.6.0 + abitype: 1.2.3 + isows: 1.0.7 + ox: 0.14.7 + ws: 8.18.3 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 3aefa1edc74598affb715f2b2c325db0fad2330e193a94fa7ff0293acd4b24b8a9ea3d082d78dc40c763e051b07853320074a69b1e72621ac339ccb1995ac92b + languageName: node + linkType: hard + "wagmi@npm:2.14.11": version: 2.14.11 resolution: "wagmi@npm:2.14.11" @@ -19706,6 +19891,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^4.0.0": + version: 4.3.6 + resolution: "zod@npm:4.3.6" + checksum: 19cec761b46bae4b6e7e861ea740f3f248e50a6671825afc8a5758e27b35d6f20ccde9942422fd5cf6f8b697f18bd05ef8bb33f5f2db112ab25cc628de2fae47 + languageName: node + linkType: hard + "zustand@npm:4.4.1": version: 4.4.1 resolution: "zustand@npm:4.4.1" From b7c36898ce350e8e32d97a2ac8ec623435c58d90 Mon Sep 17 00:00:00 2001 From: architect-dev Date: Wed, 25 Mar 2026 17:00:13 +0100 Subject: [PATCH 2/2] Replace FHERC20 with ERC7984 --- .../hardhat/contracts/ConfidentialClaim.sol | 93 - .../hardhat/contracts/ConfidentialERC20.sol | 136 +- .../hardhat/contracts/ConfidentialETH.sol | 119 +- packages/hardhat/contracts/FHEContract.sol | 37 - packages/hardhat/contracts/FHERC20.sol | 458 ---- .../hardhat/contracts/FHERC20Upgradeable.sol | 510 ---- packages/hardhat/contracts/FUSD.sol | 234 -- packages/hardhat/contracts/RedactCore.sol | 111 +- .../contracts/hardhat/ERC20_Harness.sol | 21 + .../contracts/hardhat/WETH_Harness.sol | 24 + .../hardhat/contracts/interfaces/IFHERC20.sol | 211 -- .../contracts/interfaces/IFHERC20Errors.sol | 51 - .../hardhat/contracts/interfaces/IWETH.sol | 11 - .../hardhat/contracts/test/ERC20_Harness.sol | 40 - .../contracts/test/FHERC20_Harness.sol | 26 - .../contracts/test/MockFherc20Vault.sol | 22 - packages/hardhat/gasReporterOutput.json | 2212 +++++++++++++++++ .../hardhat/test/ConfidentialERC20.test.ts | 260 +- packages/hardhat/test/ConfidentialETH.test.ts | 276 +- packages/hardhat/test/FHEContract.test.ts | 96 - packages/hardhat/test/FHERC20.test.ts | 555 ----- packages/hardhat/test/RedactCore.test.ts | 196 +- packages/hardhat/test/utils.ts | 128 +- 23 files changed, 2683 insertions(+), 3144 deletions(-) delete mode 100644 packages/hardhat/contracts/ConfidentialClaim.sol delete mode 100644 packages/hardhat/contracts/FHEContract.sol delete mode 100644 packages/hardhat/contracts/FHERC20.sol delete mode 100644 packages/hardhat/contracts/FHERC20Upgradeable.sol delete mode 100644 packages/hardhat/contracts/FUSD.sol create mode 100644 packages/hardhat/contracts/hardhat/ERC20_Harness.sol create mode 100644 packages/hardhat/contracts/hardhat/WETH_Harness.sol delete mode 100644 packages/hardhat/contracts/interfaces/IFHERC20.sol delete mode 100644 packages/hardhat/contracts/interfaces/IFHERC20Errors.sol delete mode 100644 packages/hardhat/contracts/interfaces/IWETH.sol delete mode 100644 packages/hardhat/contracts/test/ERC20_Harness.sol delete mode 100644 packages/hardhat/contracts/test/FHERC20_Harness.sol delete mode 100644 packages/hardhat/contracts/test/MockFherc20Vault.sol create mode 100644 packages/hardhat/gasReporterOutput.json delete mode 100644 packages/hardhat/test/FHEContract.test.ts delete mode 100644 packages/hardhat/test/FHERC20.test.ts diff --git a/packages/hardhat/contracts/ConfidentialClaim.sol b/packages/hardhat/contracts/ConfidentialClaim.sol deleted file mode 100644 index 215c56e..0000000 --- a/packages/hardhat/contracts/ConfidentialClaim.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { IERC20, IERC20Metadata, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IFHERC20, FHERC20 } from "./FHERC20.sol"; -import { euint128, FHE } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; -import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -abstract contract ConfidentialClaim { - using EnumerableSet for EnumerableSet.UintSet; - - struct Claim { - uint256 ctHash; - uint128 requestedAmount; - uint128 decryptedAmount; - bool decrypted; - address to; - bool claimed; - } - - mapping(uint256 ctHash => Claim) private _claims; - mapping(address => EnumerableSet.UintSet) private _userClaims; - - error ClaimNotFound(); - error AlreadyClaimed(); - - function _createClaim(address to, uint128 value, euint128 claimable) internal { - _claims[euint128.unwrap(claimable)] = Claim({ - ctHash: euint128.unwrap(claimable), - requestedAmount: value, - decryptedAmount: 0, - decrypted: false, - to: to, - claimed: false - }); - _userClaims[to].add(euint128.unwrap(claimable)); - } - - function _handleClaim(uint256 ctHash) internal returns (Claim memory claim) { - claim = _claims[ctHash]; - - // Check that the claimable amount exists and has not been claimed yet - if (claim.to == address(0)) revert ClaimNotFound(); - if (claim.claimed) revert AlreadyClaimed(); - - // Get the decrypted amount (reverts if the amount is not decrypted yet) - uint128 amount = SafeCast.toUint128(FHE.getDecryptResult(ctHash)); - - // Update the claim - claim.decryptedAmount = amount; - claim.decrypted = true; - claim.claimed = true; - - // Update the claim in storage - _claims[ctHash] = claim; - - // Remove the claimable amount from the user's claimable set - _userClaims[claim.to].remove(ctHash); - } - - function _handleClaimAll() internal returns (Claim[] memory claims) { - claims = new Claim[](_userClaims[msg.sender].length()); - - uint256[] memory ctHashes = _userClaims[msg.sender].values(); - for (uint256 i = 0; i < ctHashes.length; i++) { - claims[i] = _handleClaim(ctHashes[i]); - } - } - - function getClaim(uint256 ctHash) public view returns (Claim memory) { - Claim memory _claim = _claims[ctHash]; - (uint256 amount, bool decrypted) = FHE.getDecryptResultSafe(ctHash); - _claim.decryptedAmount = SafeCast.toUint128(amount); - _claim.decrypted = decrypted; - return _claim; - } - - function getUserClaims(address user) public view returns (Claim[] memory) { - uint256[] memory ctHashes = _userClaims[user].values(); - Claim[] memory userClaims = new Claim[](ctHashes.length); - for (uint256 i = 0; i < ctHashes.length; i++) { - userClaims[i] = _claims[ctHashes[i]]; - (uint256 amount, bool decrypted) = FHE.getDecryptResultSafe(ctHashes[i]); - userClaims[i].decryptedAmount = SafeCast.toUint128(amount); - userClaims[i].decrypted = decrypted; - } - return userClaims; - } -} diff --git a/packages/hardhat/contracts/ConfidentialERC20.sol b/packages/hardhat/contracts/ConfidentialERC20.sol index c405dd2..d80c48a 100644 --- a/packages/hardhat/contracts/ConfidentialERC20.sol +++ b/packages/hardhat/contracts/ConfidentialERC20.sol @@ -1,118 +1,50 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - pragma solidity ^0.8.25; -import { IERC20, IERC20Metadata, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { IFHERC20, FHERC20 } from "./FHERC20.sol"; -import { euint128, FHE } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; -import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import { ConfidentialClaim } from "./ConfidentialClaim.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -contract ConfidentialERC20 is FHERC20, Ownable, ConfidentialClaim { - using EnumerableSet for EnumerableSet.UintSet; - using SafeERC20 for IERC20; - - IERC20 private immutable _erc20; - string private _symbol; - - event EncryptedERC20(address indexed from, address indexed to, uint128 value); - event DecryptedERC20(address indexed from, address indexed to, uint128 value); - event ClaimedDecryptedERC20(address indexed from, address indexed to, uint128 value); - event SymbolUpdated(string symbol); - - /** - * @dev The erc20 token couldn't be wrapped. - */ - error FHERC20InvalidErc20(address token); - - /** - * @dev The recipient is the zero address. - */ - error InvalidRecipient(); +import { ERC7984 } from "fhenix-confidential-contracts/contracts/ERC7984/ERC7984.sol"; +import { ERC7984ERC20Wrapper } from "fhenix-confidential-contracts/contracts/ERC7984/extensions/ERC7984ERC20Wrapper.sol"; +import { IERC7984 } from "fhenix-confidential-contracts/contracts/interfaces/IERC7984.sol"; + +/** + * @dev Confidential ERC-20 wrapper deployed by {RedactCore}. + * + * Wraps a standard ERC-20 token into a confidential {ERC7984} token. + * Name is auto-generated as `"ERC7984 Confidential "` and + * symbol as `"e"`. + */ +contract ConfidentialERC20 is ERC7984ERC20Wrapper, Ownable { + error InvalidUnderlying(address token); constructor( - IERC20 erc20_, - string memory symbolOverride_ + IERC20 erc20_ ) - Ownable(msg.sender) - FHERC20( - string.concat("Confidential ", IERC20Metadata(address(erc20_)).name()), - bytes(symbolOverride_).length == 0 - ? string.concat("e", IERC20Metadata(address(erc20_)).symbol()) - : symbolOverride_, - IERC20Metadata(address(erc20_)).decimals() + ERC7984( + string.concat("ERC7984 Confidential ", IERC20Metadata(address(erc20_)).name()), + string.concat("e", IERC20Metadata(address(erc20_)).symbol()), + _cappedDecimals(address(erc20_)), + "" ) + ERC7984ERC20Wrapper(erc20_) + Ownable(msg.sender) { - try IFHERC20(address(erc20_)).isFherc20() returns (bool isFherc20) { - if (isFherc20) { - revert FHERC20InvalidErc20(address(erc20_)); - } - } catch { - // Not an FHERC20, continue - } - - _erc20 = erc20_; - - _symbol = bytes(symbolOverride_).length == 0 - ? string.concat("e", IERC20Metadata(address(erc20_)).symbol()) - : symbolOverride_; + if (_isERC7984(address(erc20_))) revert InvalidUnderlying(address(erc20_)); } - function symbol() public view override returns (string memory) { - return _symbol; + function _cappedDecimals(address token) private view returns (uint8) { + (bool ok, bytes memory data) = token.staticcall(abi.encodeCall(IERC20Metadata.decimals, ())); + uint8 d = (ok && data.length == 32) ? abi.decode(data, (uint8)) : 18; + return d > 6 ? 6 : d; } - function updateSymbol(string memory updatedSymbol) public onlyOwner { - _symbol = updatedSymbol; - emit SymbolUpdated(updatedSymbol); - } - - /** - * @dev Returns the address of the erc20 ERC-20 token that is being encrypted wrapped. - */ - function erc20() public view returns (IERC20) { - return _erc20; - } - - function encrypt(address to, uint128 value) public { - if (to == address(0)) to = msg.sender; - _erc20.safeTransferFrom(msg.sender, address(this), value); - _mint(to, value); - emit EncryptedERC20(msg.sender, to, value); - } - - function decrypt(address to, uint128 value) public { - if (to == address(0)) to = msg.sender; - euint128 burned = _burn(msg.sender, value); - FHE.decrypt(burned); - _createClaim(to, value, burned); - emit DecryptedERC20(msg.sender, to, value); - } - - /** - * @notice Claim a decrypted amount of the underlying ERC20 - * @param ctHash The ctHash of the burned amount - */ - function claimDecrypted(uint256 ctHash) public { - Claim memory claim = _handleClaim(ctHash); - - // Send the ERC20 to the recipient - _erc20.safeTransfer(claim.to, claim.decryptedAmount); - emit ClaimedDecryptedERC20(msg.sender, claim.to, claim.decryptedAmount); - } - - /** - * @notice Claim all decrypted amounts of the underlying ERC20 - */ - function claimAllDecrypted() public { - Claim[] memory claims = _handleClaimAll(); - - for (uint256 i = 0; i < claims.length; i++) { - _erc20.safeTransfer(claims[i].to, claims[i].decryptedAmount); - emit ClaimedDecryptedERC20(msg.sender, claims[i].to, claims[i].decryptedAmount); + function _isERC7984(address token) private view returns (bool) { + try IERC165(token).supportsInterface(type(IERC7984).interfaceId) returns (bool supported) { + return supported; + } catch { + return false; } } } diff --git a/packages/hardhat/contracts/ConfidentialETH.sol b/packages/hardhat/contracts/ConfidentialETH.sol index 71e11ca..62248ac 100644 --- a/packages/hardhat/contracts/ConfidentialETH.sol +++ b/packages/hardhat/contracts/ConfidentialETH.sol @@ -1,102 +1,31 @@ // SPDX-License-Identifier: MIT - pragma solidity ^0.8.25; -import { IERC20, IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { FHERC20 } from "./FHERC20.sol"; -import { IWETH } from "./interfaces/IWETH.sol"; -import { euint128, FHE } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; -import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import { ConfidentialClaim } from "./ConfidentialClaim.sol"; -import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -contract ConfidentialETH is FHERC20, Ownable, ConfidentialClaim { - using EnumerableSet for EnumerableSet.UintSet; - using SafeERC20 for IERC20; - using SafeERC20 for IWETH; - - IWETH public wETH; - +import { ERC7984 } from "fhenix-confidential-contracts/contracts/ERC7984/ERC7984.sol"; +import { + ERC7984NativeWrapper, + IWETH +} from "fhenix-confidential-contracts/contracts/ERC7984/extensions/ERC7984NativeWrapper.sol"; + +/** + * @dev Confidential native-token wrapper deployed externally and registered with {RedactCore}. + * + * Wraps native ETH (or any WETH-compatible asset) into a confidential {ERC7984} token. + * Name is fixed as `"ERC7984 Confidential Ether"` and symbol as `"eETH"`. + */ +contract ConfidentialETH is ERC7984NativeWrapper, Ownable { constructor( - IWETH wETH_ - ) Ownable(msg.sender) FHERC20("Confidential Wrapped ETHER", "eETH", IERC20Metadata(address(wETH_)).decimals()) { - wETH = wETH_; - } - - /** - * @dev Returns the address of the erc20 ERC-20 token that is being encrypted wrapped. - */ - function erc20() public view returns (IERC20) { - return wETH; - } - - receive() external payable {} - - fallback() external payable {} - - event EncryptedWETH(address indexed from, address indexed to, uint128 value); - event EncryptedETH(address indexed from, address indexed to, uint256 value); - event DecryptedETH(address indexed from, address indexed to, uint128 value); - event ClaimedDecryptedETH(address indexed from, address indexed to, uint128 value); - - /** - * @dev The ETH transfer failed. - */ - error ETHTransferFailed(); - - /** - * @dev The recipient is the zero address. - */ - error InvalidRecipient(); - - function encryptWETH(address to, uint128 value) public { - if (to == address(0)) to = msg.sender; - wETH.safeTransferFrom(msg.sender, address(this), value); - wETH.withdraw(value); - _mint(to, value); - emit EncryptedWETH(msg.sender, to, value); - } - - function encryptETH(address to) public payable { - if (to == address(0)) to = msg.sender; - _mint(to, SafeCast.toUint128(msg.value)); - emit EncryptedETH(msg.sender, to, msg.value); - } - - function decrypt(address to, uint128 value) public { - if (to == address(0)) to = msg.sender; - euint128 burned = _burn(msg.sender, value); - FHE.decrypt(burned); - _createClaim(to, value, burned); - emit DecryptedETH(msg.sender, to, value); - } - - /** - * @notice Claim a decrypted amount of ETH - * @param ctHash The ctHash of the burned amount - */ - function claimDecrypted(uint256 ctHash) public { - Claim memory claim = _handleClaim(ctHash); - - // Send the ETH to the recipient - (bool sent, ) = claim.to.call{ value: claim.decryptedAmount }(""); - if (!sent) revert ETHTransferFailed(); - - emit ClaimedDecryptedETH(msg.sender, claim.to, claim.decryptedAmount); - } - - /** - * @notice Claim all decrypted amounts of ETH - */ - function claimAllDecrypted() public { - Claim[] memory claims = _handleClaimAll(); - - for (uint256 i = 0; i < claims.length; i++) { - (bool sent, ) = claims[i].to.call{ value: claims[i].decryptedAmount }(""); - if (!sent) revert ETHTransferFailed(); - emit ClaimedDecryptedETH(msg.sender, claims[i].to, claims[i].decryptedAmount); - } + IWETH weth_ + ) + ERC7984("ERC7984 Confidential Ether", "eETH", _cappedDecimals(address(weth_)), "") + ERC7984NativeWrapper(weth_) + Ownable(msg.sender) + {} + + function _cappedDecimals(address token) private view returns (uint8) { + uint8 d = IERC20Metadata(token).decimals(); + return d > 6 ? 6 : d; } } diff --git a/packages/hardhat/contracts/FHEContract.sol b/packages/hardhat/contracts/FHEContract.sol deleted file mode 100644 index 19da0e0..0000000 --- a/packages/hardhat/contracts/FHEContract.sol +++ /dev/null @@ -1,37 +0,0 @@ -//SPDX-License-Identifier: MIT -pragma solidity >=0.8.0 <0.9.0; - -// Import FHE.sol library -import "@fhenixprotocol/cofhe-contracts/FHE.sol"; - -// Useful for debugging. Remove when deploying to a live network. -import "hardhat/console.sol"; - -contract FHEContract { - euint32 public val; - - constructor() { - val = FHE.asEuint32(0); - } - - function setVal(InEuint32 memory _inVal) public { - val = FHE.asEuint32(_inVal); - FHE.allowSender(val); - FHE.allowThis(val); - } - - function valOp(uint8 op, InEuint32 memory _operand) public { - euint32 operand = FHE.asEuint32(_operand); - if (op == 0) { - val = FHE.add(val, operand); - } else if (op == 1) { - val = FHE.sub(val, operand); - } else if (op == 2) { - val = FHE.mul(val, operand); - } else if (op == 3) { - val = FHE.div(val, operand); - } - FHE.allowSender(val); - FHE.allowThis(val); - } -} diff --git a/packages/hardhat/contracts/FHERC20.sol b/packages/hardhat/contracts/FHERC20.sol deleted file mode 100644 index 6cf3e8c..0000000 --- a/packages/hardhat/contracts/FHERC20.sol +++ /dev/null @@ -1,458 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; -import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import { Context } from "@openzeppelin/contracts/utils/Context.sol"; -import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; -import { Nonces } from "@openzeppelin/contracts/utils/Nonces.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { IFHERC20 } from "./interfaces/IFHERC20.sol"; -import { IFHERC20Errors } from "./interfaces/IFHERC20Errors.sol"; -import { FHE, euint128, InEuint128, Utils } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * - * TIP: For a detailed writeup see our guide - * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * The default value of {decimals} is 18. To change this, you should override - * this function so it returns a different value. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC-20 - * applications. - * - * Note: This FHERC20 does not include FHE operations, and is intended to decouple the - * frontend work from the active CoFHE (FHE Coprocessor) work during development and auditing. - */ -abstract contract FHERC20 is IFHERC20, IFHERC20Errors, Context, EIP712, Nonces { - // NOTE: `indicatedBalances` are intended to indicate movement and change - // of an encrypted FHERC20 balance, without exposing any encrypted data. - // - // !! WARNING !! These indicated balances MUST NOT be used in any FHERC20 logic, only - // the encrypted balance should be used. - // - // `indicatedBalance` is implemented to make FHERC20s maximally backwards - // compatible with existing ERC20 expectations. - // - // `indicatedBalance` is internally represented by a number between 0 and 99999. - // When viewed in a wallet, it is transformed into a decimal value with 4 digits - // of precision (0.0000 to 0.9999). These same increments are used as the - // value in any emitted events. If the user has not interacted with this FHERC20 - // their indicated amount will be 0. Their first interaction will set the amount to - // the midpoint (0.5000), and each subsequent interaction will shift that value by - // the increment (0.0001). This gives room for up to 5000 interactions in either - // direction, which is sufficient for >99.99% of user use cases. - // - // These `indicatedBalance` changes will show up in: - // - transactions and block scanners (0xAAA -> 0xBBB - 0.0001 eETH) - // - wallets and portfolios (eETH - 0.5538) - // - // `indicatedBalance` is included in the FHERC20 standard as a stop-gap - // to indicate change when the real encrypted change is not yet implemented - // in infrastructure like wallets and etherscans. - mapping(address account => uint16) internal _indicatedBalances; - mapping(address account => euint128) private _encBalances; - - uint16 internal _indicatedTotalSupply; - euint128 private _encTotalSupply; - - string private _name; - string private _symbol; - uint8 private _decimals; - uint256 private _indicatorTick; - - // EIP712 Permit - - bytes32 private constant PERMIT_TYPEHASH = - keccak256("Permit(address owner,address spender,uint256 value_hash,uint256 nonce,uint256 deadline)"); - - /** - * @dev Sets the values for {name} and {symbol}. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - constructor(string memory name_, string memory symbol_, uint8 decimals_) EIP712(name_, "1") { - _name = name_; - _symbol = symbol_; - _decimals = decimals_; - - _indicatorTick = decimals_ <= 4 ? 1 : 10 ** (decimals_ - 4); - } - - /** - * @dev Returns true if the token is a FHERC20. - */ - function isFherc20() public view virtual returns (bool) { - return true; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual returns (string memory) { - return _name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual returns (string memory) { - return _symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the default value returned by this function, unless - * it's overridden. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual returns (uint8) { - return _decimals; - } - - /** - * @dev See {IERC20-totalSupply}. - * - * Returns the indicated total supply. - */ - function totalSupply() public view virtual returns (uint256) { - return _indicatedTotalSupply * _indicatorTick; - } - - /** - * @dev Returns the encrypted total supply. - */ - function encTotalSupply() public view virtual returns (euint128) { - return _encTotalSupply; - } - - /** - * @dev Returns an flag indicating that the public balances returned by - * `balanceOf` is an indication of the underlying encrypted balance. - * The value returned is between 0.0000 and 0.9999, and - * acts as a counter of tokens transfers and changes. - * - * Receiving tokens increments this indicator by +0.0001. - * Sending tokens decrements the indicator by -0.0001. - */ - function balanceOfIsIndicator() public view virtual returns (bool) { - return true; - } - - /** - * @dev Returns the true size of the indicator tick - */ - function indicatorTick() public view returns (uint256) { - return _indicatorTick; - } - - /** - * @dev Returns an indicator of the underlying encrypted balance. - * The value returned is [0](no interaction) / [0.0001 - 0.9999](indicated) - * Indicator acts as a counter of tokens transfers and changes. - * - * Receiving tokens increments this indicator by +0.0001. - * Sending tokens decrements the indicator by -0.0001. - * - * Returned in the decimal expectation of the token. - */ - function balanceOf(address account) public view virtual returns (uint256) { - return _indicatedBalances[account] * _indicatorTick; - } - - /** - * @dev See {IERC20-balanceOf}. - * - * Returns the euint128 representing the account's true balance (encrypted) - */ - function encBalanceOf(address account) public view virtual returns (euint128) { - return _encBalances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20 - */ - function transfer(address, uint256) public pure returns (bool) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-transfer}. - * - * Intended to be used as a EOA call with an encrypted input `InEuint128 inValue`. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `value`. - * - `inValue` must be a `InEuint128` to preserve confidentiality. - */ - function encTransfer(address to, InEuint128 memory inValue) public virtual returns (euint128 transferred) { - return encTransfer(to, FHE.asEuint128(inValue)); - } - - /** - * @dev See {IERC20-transfer}. - * - * Intended to be used as part of a contract call. - * Ensure that `value` is allowed to be used by using `FHE.allow` with this contracts address. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `value`. - * - `value` must be a `euint128` to preserve confidentiality. - */ - function encTransfer(address to, euint128 value) public virtual returns (euint128 transferred) { - address owner = _msgSender(); - transferred = _transfer(owner, to, value); - } - - /** - * @dev See {IERC20-allowance}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20. - * Allowances have been removed from FHERC20s to prevent encrypted balance leakage. - * Allowances have been replaced with an EIP712 permit for each `encTransferFrom`. - */ - function allowance(address, address) external pure returns (uint256) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-approve}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20. - * Allowances have been removed from FHERC20s to prevent encrypted balance leakage. - * Allowances have been replaced with an EIP712 permit for each `encTransferFrom`. - */ - function approve(address, uint256) external pure returns (bool) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-transferFrom}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20 - */ - function transferFrom(address, address, uint256) public pure returns (bool) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `value`. - * - the caller must have allowance for ``from``'s tokens of at least - * `value`. - */ - function encTransferFrom( - address from, - address to, - InEuint128 memory inValue, - FHERC20_EIP712_Permit calldata permit - ) public virtual returns (euint128 transferred) { - if (block.timestamp > permit.deadline) revert ERC2612ExpiredSignature(permit.deadline); - - if (from != permit.owner) revert FHERC20EncTransferFromOwnerMismatch(from, permit.owner); - if (msg.sender != permit.spender) revert FHERC20EncTransferFromSpenderMismatch(msg.sender, permit.spender); - - if (inValue.ctHash != permit.value_hash) - revert FHERC20EncTransferFromValueHashMismatch(inValue.ctHash, permit.value_hash); - - bytes32 structHash = keccak256( - abi.encode( - PERMIT_TYPEHASH, - permit.owner, - permit.spender, - permit.value_hash, - _useNonce(permit.owner), - permit.deadline - ) - ); - - bytes32 hash = _hashTypedDataV4(structHash); - - address signer = ECDSA.recover(hash, permit.v, permit.r, permit.s); - if (signer != permit.owner) { - revert ERC2612InvalidSigner(signer, permit.owner); - } - - euint128 value = FHE.asEuint128(inValue); - - transferred = _transfer(from, to, value); - } - - /** - * @dev Moves a `value` amount of tokens from `from` to `to`. - * - * This internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * NOTE: This function is not virtual, {_update} should be overridden instead. - */ - function _transfer(address from, address to, euint128 value) internal returns (euint128 transferred) { - if (from == address(0)) { - revert ERC20InvalidSender(address(0)); - } - if (to == address(0)) { - revert ERC20InvalidReceiver(address(0)); - } - transferred = _update(from, to, value); - } - - /* - * @dev Increments a user's balance indicator by 0.0001 - */ - function _incrementIndicator(uint16 current) internal pure returns (uint16) { - if (current == 0 || current == 9999) return 5001; - return current + 1; - } - - /* - * @dev Decrements a user's balance indicator by 0.0001 - */ - function _decrementIndicator(uint16 value) internal pure returns (uint16) { - if (value == 0 || value == 1) return 4999; - return value - 1; - } - - /** - * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` - * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding - * this function. - * - * Emits a {Transfer} event and an {EncTransfer} event which includes the encrypted value. - */ - function _update(address from, address to, euint128 value) internal virtual returns (euint128 transferred) { - // If `value` is greater than the user's encBalance, it is replaced with 0 - // The transaction will succeed, but the amount transferred may be 0 - // Both `from` and `to` will have their `encBalance` updated in either case to preserve confidentiality - // - // NOTE: If the function is `_mint`, `from` is the zero address, and does not have an `encBalance` to - // compare against, so this check is skipped. - if (from != address(0)) { - transferred = FHE.select(value.lte(_encBalances[from]), value, FHE.asEuint128(0)); - } else { - transferred = value; - } - - if (from == address(0)) { - _indicatedTotalSupply = _incrementIndicator(_indicatedTotalSupply); - _encTotalSupply = FHE.add(_encTotalSupply, transferred); - } else { - _encBalances[from] = FHE.sub(_encBalances[from], transferred); - _indicatedBalances[from] = _decrementIndicator(_indicatedBalances[from]); - } - - if (to == address(0)) { - _indicatedTotalSupply = _decrementIndicator(_indicatedTotalSupply); - _encTotalSupply = FHE.sub(_encTotalSupply, transferred); - } else { - _encBalances[to] = FHE.add(_encBalances[to], transferred); - _indicatedBalances[to] = _incrementIndicator(_indicatedBalances[to]); - } - - // Update CoFHE Access Control List (ACL) to allow decrypting / sealing of the new balances - if (euint128.unwrap(_encBalances[from]) != 0) { - FHE.allowThis(_encBalances[from]); - FHE.allow(_encBalances[from], from); - FHE.allow(transferred, from); - } - if (euint128.unwrap(_encBalances[to]) != 0) { - FHE.allowThis(_encBalances[to]); - FHE.allow(_encBalances[to], to); - FHE.allow(transferred, to); - } - - // Allow the caller to decrypt the transferred amount - FHE.allow(transferred, msg.sender); - - // Allow the total supply to be decrypted by anyone - FHE.allowGlobal(_encTotalSupply); - - emit Transfer(from, to, _indicatorTick); - emit EncTransfer(from, to, euint128.unwrap(transferred)); - } - - /** - * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). - * Relies on the `_update` mechanism - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * NOTE: This function is not virtual, {_update} should be overridden instead. - */ - function _mint(address account, uint128 value) internal returns (euint128 transferred) { - if (account == address(0)) { - revert ERC20InvalidReceiver(address(0)); - } - transferred = _update(address(0), account, FHE.asEuint128(value)); - } - - /** - * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. - * Relies on the `_update` mechanism. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * NOTE: This function is not virtual, {_update} should be overridden instead - */ - function _burn(address account, uint128 value) internal returns (euint128 transferred) { - if (account == address(0)) { - revert ERC20InvalidSender(address(0)); - } - transferred = _update(account, address(0), FHE.asEuint128(value)); - } - - // EIP712 Permit - - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases ``owner``'s nonce by one. This - * prevents a signature from being used multiple times. - */ - function nonces(address owner) public view override(IFHERC20, Nonces) returns (uint256) { - return super.nonces(owner); - } - - /** - * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view virtual returns (bytes32) { - return _domainSeparatorV4(); - } - - // FHERC20 - - function resetIndicatedBalance() external { - _indicatedBalances[msg.sender] = 0; - } -} diff --git a/packages/hardhat/contracts/FHERC20Upgradeable.sol b/packages/hardhat/contracts/FHERC20Upgradeable.sol deleted file mode 100644 index e40e979..0000000 --- a/packages/hardhat/contracts/FHERC20Upgradeable.sol +++ /dev/null @@ -1,510 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { IFHERC20 } from "./interfaces/IFHERC20.sol"; -import { IFHERC20Errors } from "./interfaces/IFHERC20Errors.sol"; -import { UUPSUpgradeable, Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import { ContextUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; -import { EIP712Upgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol"; -import { NoncesUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/NoncesUpgradeable.sol"; -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; -import { FHE, euint128, InEuint128, Utils } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * - * TIP: For a detailed writeup see our guide - * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * The default value of {decimals} is 18. To change this, you should override - * this function so it returns a different value. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC-20 - * applications. - * - * Note: This FHERC20 does not include FHE operations, and is intended to decouple the - * frontend work from the active CoFHE (FHE Coprocessor) work during development and auditing. - */ -abstract contract FHERC20Upgradeable is - IFHERC20, - IFHERC20Errors, - Initializable, - ContextUpgradeable, - EIP712Upgradeable, - NoncesUpgradeable, - UUPSUpgradeable -{ - struct FHERC20Storage { - // NOTE: `indicatedBalances` are intended to indicate movement and change - // of an encrypted FHERC20 balance, without exposing any encrypted data. - // - // !! WARNING !! These indicated balances MUST NOT be used in any FHERC20 logic, only - // the encrypted balance should be used. - // - // `indicatedBalance` is implemented to make FHERC20s maximally backwards - // compatible with existing ERC20 expectations. - // - // `indicatedBalance` is internally represented by a number between 0 and 99999. - // When viewed in a wallet, it is transformed into a decimal value with 4 digits - // of precision (0.0000 to 0.9999). These same increments are used as the - // value in any emitted events. If the user has not interacted with this FHERC20 - // their indicated amount will be 0. Their first interaction will set the amount to - // the midpoint (0.5000), and each subsequent interaction will shift that value by - // the increment (0.0001). This gives room for up to 5000 interactions in either - // direction, which is sufficient for >99.99% of user use cases. - // - // These `indicatedBalance` changes will show up in: - // - transactions and block scanners (0xAAA -> 0xBBB - 0.0001 eETH) - // - wallets and portfolios (eETH - 0.5538) - // - // `indicatedBalance` is included in the FHERC20 standard as a stop-gap - // to indicate change when the real encrypted change is not yet implemented - // in infrastructure like wallets and etherscans. - mapping(address account => uint16) _indicatedBalances; - mapping(address account => euint128) _encBalances; - uint16 _indicatedTotalSupply; - euint128 _encTotalSupply; - string _name; - string _symbol; - uint8 _decimals; - uint256 _indicatorTick; - } - - // bytes32 private constant FHERC20StorageLocation = - // keccak256( - // abi.encode(uint256(keccak256("fhenix.storage.FHERC20")) - 1) - // ) & ~bytes32(uint256(0xff)); - bytes32 private constant FHERC20StorageLocation = - 0xbc8c60a4c847b9f9ad87177ea86cc5fcc3776c8621f569416d2140307d537200; - - function _getFHERC20Storage() private pure returns (FHERC20Storage storage $) { - assembly { - $.slot := FHERC20StorageLocation - } - } - - // EIP712 Permit - - bytes32 private constant PERMIT_TYPEHASH = - keccak256("Permit(address owner,address spender,uint256 value_hash,uint256 nonce,uint256 deadline)"); - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - /** - * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by - * {upgradeTo} and {upgradeToAndCall}. - * - * Implement this to add upgrade authorization mechanisms. - */ - function _authorizeUpgrade(address newImplementation) internal virtual override { - // Add your authorization logic here - // For example, you might want to add: - // require(msg.sender == owner, "Only owner can upgrade"); - // NOTE: Must be implemented by the FHERC20Upgradeable implementation - } - - /** - * @dev Sets the values for {name} and {symbol}. - * - * All two of these values are immutable: they can only be set once during - * construction. - */ - function __FHERC20_init(string memory name_, string memory symbol_, uint8 decimals_) internal onlyInitializing { - __EIP712_init(name_, "1"); - __FHERC20_init_unchained(name_, symbol_, decimals_); - } - - function __FHERC20_init_unchained( - string memory name_, - string memory symbol_, - uint8 decimals_ - ) internal onlyInitializing { - FHERC20Storage storage $ = _getFHERC20Storage(); - $._name = name_; - $._symbol = symbol_; - $._decimals = decimals_; - $._indicatorTick = decimals_ <= 4 ? 1 : 10 ** (decimals_ - 4); - } - - /** - * @dev Returns true if the token is a FHERC20. - */ - function isFherc20() public view virtual returns (bool) { - return true; - } - - /** - * @dev Returns the name of the token. - */ - function name() public view virtual returns (string memory) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._name; - } - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual returns (string memory) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._symbol; - } - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the default value returned by this function, unless - * it's overridden. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual returns (uint8) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._decimals; - } - - /** - * @dev See {IERC20-totalSupply}. - * - * Returns the indicated total supply of the token. - */ - function totalSupply() public view virtual returns (uint256) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._indicatedTotalSupply * $._indicatorTick; - } - - /** - * @dev Returns the true total supply of the token. - */ - function encTotalSupply() public view virtual returns (euint128) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._encTotalSupply; - } - - /** - * @dev Returns an flag indicating that the public balances returned by - * `balanceOf` is an indication of the underlying encrypted balance. - * The value returned is between 0.0000 and 0.9999, and - * acts as a counter of tokens transfers and changes. - * - * Receiving tokens increments this indicator by +0.0001. - * Sending tokens decrements the indicator by -0.0001. - */ - function balanceOfIsIndicator() public view virtual returns (bool) { - return true; - } - - /** - * @dev Returns the true size of the indicator tick - */ - function indicatorTick() public view returns (uint256) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._indicatorTick; - } - - /** - * @dev See {IFHERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual returns (uint256) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._indicatedBalances[account] * $._indicatorTick; - } - - /** - * @dev See {IERC20-balanceOf}. - * - * Returns the euint128 representing the account's true balance (encrypted) - */ - function encBalanceOf(address account) public view virtual returns (euint128) { - FHERC20Storage storage $ = _getFHERC20Storage(); - return $._encBalances[account]; - } - - /** - * @dev See {IERC20-transfer}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20 - */ - function transfer(address, uint256) public pure returns (bool) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-transfer}. - * - * Intended to be used as a EOA call with an encrypted input `InEuint128 inValue`. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `value`. - * - `inValue` must be a `InEuint128` to preserve confidentiality. - */ - function encTransfer(address to, InEuint128 memory inValue) public virtual returns (euint128 transferred) { - return encTransfer(to, FHE.asEuint128(inValue)); - } - - /** - * @dev See {IERC20-transfer}. - * - * Intended to be used as part of a contract call. - * Ensure that `value` is allowed to be used by using `FHE.allow` with this contracts address. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `value`. - * - `value` must be a `euint128` to preserve confidentiality. - */ - function encTransfer(address to, euint128 value) public virtual returns (euint128 transferred) { - address owner = _msgSender(); - transferred = _transfer(owner, to, value); - } - - /** - * @dev See {IERC20-allowance}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20. - * Allowances have been removed from FHERC20s to prevent encrypted balance leakage. - * Allowances have been replaced with an EIP712 permit for each `encTransferFrom`. - */ - function allowance(address, address) external pure returns (uint256) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-approve}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20. - * Allowances have been removed from FHERC20s to prevent encrypted balance leakage. - * Allowances have been replaced with an EIP712 permit for each `encTransferFrom`. - */ - function approve(address, uint256) external pure returns (bool) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-transferFrom}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20 - */ - function transferFrom(address, address, uint256) public pure returns (bool) { - revert FHERC20IncompatibleFunction(); - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `value`. - * - the caller must have allowance for ``from``'s tokens of at least - * `value`. - */ - function encTransferFrom( - address from, - address to, - InEuint128 memory inValue, - FHERC20_EIP712_Permit calldata permit - ) public virtual returns (euint128 transferred) { - if (block.timestamp > permit.deadline) revert ERC2612ExpiredSignature(permit.deadline); - - if (from != permit.owner) revert FHERC20EncTransferFromOwnerMismatch(from, permit.owner); - if (msg.sender != permit.spender) revert FHERC20EncTransferFromSpenderMismatch(to, permit.spender); - - if (inValue.ctHash != permit.value_hash) - revert FHERC20EncTransferFromValueHashMismatch(inValue.ctHash, permit.value_hash); - - bytes32 structHash = keccak256( - abi.encode( - PERMIT_TYPEHASH, - permit.owner, - permit.spender, - permit.value_hash, - _useNonce(permit.owner), - permit.deadline - ) - ); - - bytes32 hash = _hashTypedDataV4(structHash); - - address signer = ECDSA.recover(hash, permit.v, permit.r, permit.s); - if (signer != permit.owner) { - revert ERC2612InvalidSigner(signer, permit.owner); - } - - euint128 value = FHE.asEuint128(inValue); - - transferred = _transfer(from, to, value); - } - - /** - * @dev Moves a `value` amount of tokens from `from` to `to`. - * - * This internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event. - * - * NOTE: This function is not virtual, {_update} should be overridden instead. - */ - function _transfer(address from, address to, euint128 value) internal returns (euint128 transferred) { - if (from == address(0)) { - revert ERC20InvalidSender(address(0)); - } - if (to == address(0)) { - revert ERC20InvalidReceiver(address(0)); - } - transferred = _update(from, to, value); - } - - /* - * @dev Increments a user's balance indicator by 0.0001 - */ - function _incrementIndicator(uint16 current) internal pure returns (uint16) { - if (current == 0 || current == 9999) return 5001; - return current + 1; - } - - /* - * @dev Decrements a user's balance indicator by 0.0001 - */ - function _decrementIndicator(uint16 value) internal pure returns (uint16) { - if (value == 0 || value == 1) return 4999; - return value - 1; - } - - /** - * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` - * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding - * this function. - * - * The `cleartextValue` input is used only for totalSupply, and is included when updated is called - * by the `_mint` and `_burn` functions, else it is 0. - * - * Emits a {Transfer} event. - */ - function _update(address from, address to, euint128 value) internal virtual returns (euint128 transferred) { - FHERC20Storage storage $ = _getFHERC20Storage(); - - // If `value` is greater than the user's encBalance, it is replaced with 0 - // The transaction will succeed, but the amount transferred may be 0 - // Both `from` and `to` will have their `encBalance` updated in either case to preserve confidentiality - // - // NOTE: If the function is `_mint`, `from` is the zero address, and does not have an `encBalance` to - // compare against, so this check is skipped. - if (from != address(0)) { - transferred = FHE.select(value.lte($._encBalances[from]), value, FHE.asEuint128(0)); - } else { - transferred = value; - } - - if (from == address(0)) { - $._indicatedTotalSupply = _incrementIndicator($._indicatedTotalSupply); - $._encTotalSupply = FHE.add($._encTotalSupply, transferred); - } else { - $._encBalances[from] = FHE.sub($._encBalances[from], transferred); - $._indicatedBalances[from] = _decrementIndicator($._indicatedBalances[from]); - } - - if (to == address(0)) { - $._indicatedTotalSupply = _decrementIndicator($._indicatedTotalSupply); - $._encTotalSupply = FHE.sub($._encTotalSupply, transferred); - } else { - $._encBalances[to] = FHE.add($._encBalances[to], transferred); - $._indicatedBalances[to] = _incrementIndicator($._indicatedBalances[to]); - } - - // Update CoFHE Access Control List (ACL) to allow decrypting / sealing of the new balances - if (euint128.unwrap($._encBalances[from]) != 0) { - FHE.allowThis($._encBalances[from]); - FHE.allow($._encBalances[from], from); - FHE.allow(transferred, from); - } - if (euint128.unwrap($._encBalances[to]) != 0) { - FHE.allowThis($._encBalances[to]); - FHE.allow($._encBalances[to], to); - FHE.allow(transferred, to); - } - - // Allow the caller to decrypt the transferred amount - FHE.allow(transferred, msg.sender); - - // Allow the total supply to be decrypted by anyone - FHE.allowGlobal($._encTotalSupply); - - emit Transfer(from, to, $._indicatorTick); - emit EncTransfer(from, to, euint128.unwrap(transferred)); - } - - /** - * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). - * Relies on the `_update` mechanism - * - * Emits a {Transfer} event with `from` set to the zero address. - * - * NOTE: This function is not virtual, {_update} should be overridden instead. - */ - function _mint(address account, uint128 value) internal returns (euint128 transferred) { - if (account == address(0)) { - revert ERC20InvalidReceiver(address(0)); - } - transferred = _update(address(0), account, FHE.asEuint128(value)); - } - - /** - * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. - * Relies on the `_update` mechanism. - * - * Emits a {Transfer} event with `to` set to the zero address. - * - * NOTE: This function is not virtual, {_update} should be overridden instead - */ - function _burn(address account, uint128 value) internal returns (euint128 transferred) { - if (account == address(0)) { - revert ERC20InvalidSender(address(0)); - } - transferred = _update(account, address(0), FHE.asEuint128(value)); - } - - // EIP712 Permit - - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases ``owner``'s nonce by one. This - * prevents a signature from being used multiple times. - */ - function nonces(address owner) public view override(IFHERC20, NoncesUpgradeable) returns (uint256) { - return super.nonces(owner); - } - - /** - * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view virtual returns (bytes32) { - return _domainSeparatorV4(); - } - - // FHERC20 - - function resetIndicatedBalance() external { - FHERC20Storage storage $ = _getFHERC20Storage(); - $._indicatedBalances[msg.sender] = 0; - } -} diff --git a/packages/hardhat/contracts/FUSD.sol b/packages/hardhat/contracts/FUSD.sol deleted file mode 100644 index 93f7d5a..0000000 --- a/packages/hardhat/contracts/FUSD.sol +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import "./FHERC20Upgradeable.sol"; -import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; -import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -interface IFUSDVault { - function redeem(address to, uint128 amount) external; -} - -contract FUSD is FHERC20Upgradeable, AccessControlUpgradeable { - using EnumerableSet for EnumerableSet.UintSet; - - error CallerNotMinter(address caller); - error CallerNotAdmin(address caller); - error InvalidFUSDVault(); - error InvalidRecipient(); - error ClaimNotFound(); - error AlreadyClaimed(); - - event MintedFUSD( - address indexed caller, - address indexed to, - uint128 amount - ); - event RedeemedFUSD( - address indexed caller, - address indexed to, - uint128 amount - ); - event ClaimedRedeemedFUSD( - address indexed caller, - address indexed to, - uint128 amount - ); - event FUSDVaultUpdated( - address indexed caller, - address indexed newFUSDVault - ); - - struct Claim { - uint256 ctHash; - uint128 requestedAmount; - uint128 decryptedAmount; - bool decrypted; - address to; - bool claimed; - } - - struct FUSDStorage { - // The address of the vault that holds the underlying stablecoin assets - // Also handles minting and redeeming of FUSD - address fusdVault; - // Mapping of ctHash to claim - mapping(uint256 ctHash => Claim) claims; - // Mapping of user to a set of ctHashes (each representing a claim) - mapping(address => EnumerableSet.UintSet) userClaims; - } - - bytes32 private constant MINTER_ROLE = keccak256("MINTER_ROLE"); - - // bytes32 private constant FUSDStorageLocation = - // keccak256( - // abi.encode(uint256(keccak256("fhenix.storage.FUSD")) - 1) - // ) & ~bytes32(uint256(0xff)); - bytes32 private constant FUSDStorageLocation = - 0xe50f9a2817db0d2f949d224eb43b71bea5efba5616570a0f831be3f359f46000; - - function _getFUSDStorage() private pure returns (FUSDStorage storage $) { - assembly { - $.slot := FUSDStorageLocation - } - } - - function initialize(address fusdVault_) public initializer { - __FHERC20_init("FHE US Dollar", "FUSD", 6); - - FUSDStorage storage $ = _getFUSDStorage(); - - _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - - $.fusdVault = fusdVault_; - _grantRole(MINTER_ROLE, fusdVault_); - } - - /** - * @dev Modifier that reverts if the caller is not an admin - */ - modifier onlyAdmin() { - if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) - revert CallerNotAdmin(msg.sender); - _; - } - - /** - * @dev Modifier that reverts if the caller is not a minter - */ - modifier onlyMinter() { - if (!hasRole(MINTER_ROLE, msg.sender)) - revert CallerNotMinter(msg.sender); - _; - } - - /** - * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by - * {upgradeTo} and {upgradeToAndCall}. - * - * Implement this to add upgrade authorization mechanisms. - */ - function _authorizeUpgrade( - address newImplementation - ) internal override onlyAdmin {} - - // VAULT FUNCTIONS - - function mint(address to, uint128 amount) external onlyMinter { - if (to == address(0)) revert InvalidRecipient(); - - _mint(to, amount); - - emit MintedFUSD(msg.sender, to, amount); - } - - function redeem(address to, uint128 amount) external { - if (to == address(0)) revert InvalidRecipient(); - - euint128 burned = _burn(msg.sender, amount); - FHE.decrypt(burned); - _createClaim(to, amount, burned); - - emit RedeemedFUSD(msg.sender, to, amount); - } - - function claimRedeemed(uint256 ctHash) external { - FUSDStorage storage $ = _getFUSDStorage(); - Claim memory claim = _handleClaim(ctHash); - - // Call the vault with the redeemed amount - IFUSDVault($.fusdVault).redeem(claim.to, claim.decryptedAmount); - - emit ClaimedRedeemedFUSD(msg.sender, claim.to, claim.decryptedAmount); - } - - // ADMIN FUNCTIONS - - function updateFUSDVault(address fusdVault_) external onlyAdmin { - if (fusdVault_ == address(0)) revert InvalidFUSDVault(); - - FUSDStorage storage $ = _getFUSDStorage(); - - // Revoke the old minter role - _revokeRole(MINTER_ROLE, $.fusdVault); - - // Update the vault address and grant the new minter role - $.fusdVault = fusdVault_; - _grantRole(MINTER_ROLE, fusdVault_); - - emit FUSDVaultUpdated(msg.sender, fusdVault_); - } - - // CLAIM FUNCTIONS - - function _createClaim( - address to, - uint128 value, - euint128 claimable - ) internal { - FUSDStorage storage $ = _getFUSDStorage(); - - $.claims[euint128.unwrap(claimable)] = Claim({ - ctHash: euint128.unwrap(claimable), - requestedAmount: value, - decryptedAmount: 0, - decrypted: false, - to: to, - claimed: false - }); - - $.userClaims[to].add(euint128.unwrap(claimable)); - } - - function _handleClaim( - uint256 ctHash - ) internal returns (Claim memory claim) { - FUSDStorage storage $ = _getFUSDStorage(); - claim = $.claims[ctHash]; - - // Check that the claimable amount exists and has not been claimed yet - if (claim.to == address(0)) revert ClaimNotFound(); - if (claim.claimed) revert AlreadyClaimed(); - - // Get the decrypted amount (reverts if the amount is not decrypted yet) - uint128 amount = SafeCast.toUint128(FHE.getDecryptResult(ctHash)); - - // Update the claim - claim.decryptedAmount = amount; - claim.decrypted = true; - claim.claimed = true; - - // Update the claim in storage - $.claims[ctHash] = claim; - - // Remove the claimable amount from the user's claimable set - $.userClaims[claim.to].remove(ctHash); - } - - function getClaim(uint256 ctHash) public view returns (Claim memory) { - FUSDStorage storage $ = _getFUSDStorage(); - Claim memory _claim = $.claims[ctHash]; - - (uint256 amount, bool decrypted) = FHE.getDecryptResultSafe(ctHash); - - _claim.decryptedAmount = SafeCast.toUint128(amount); - _claim.decrypted = decrypted; - - return _claim; - } - - function getUserClaims(address user) public view returns (Claim[] memory) { - FUSDStorage storage $ = _getFUSDStorage(); - uint256[] memory ctHashes = $.userClaims[user].values(); - - Claim[] memory userClaims = new Claim[](ctHashes.length); - for (uint256 i = 0; i < ctHashes.length; i++) { - userClaims[i] = $.claims[ctHashes[i]]; - } - - return userClaims; - } -} diff --git a/packages/hardhat/contracts/RedactCore.sol b/packages/hardhat/contracts/RedactCore.sol index 96a9e62..f87552d 100644 --- a/packages/hardhat/contracts/RedactCore.sol +++ b/packages/hardhat/contracts/RedactCore.sol @@ -1,94 +1,83 @@ // SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - pragma solidity ^0.8.25; -import { IERC20, IERC20Metadata, ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; -import { Ownable, Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; import { ConfidentialERC20 } from "./ConfidentialERC20.sol"; import { ConfidentialETH } from "./ConfidentialETH.sol"; -import { IWETH } from "./interfaces/IWETH.sol"; - +import { IWETH } from "fhenix-confidential-contracts/contracts/interfaces/IWETH.sol"; + +/** + * @dev Core registry and factory for the Redact protocol. + * + * Manages a mapping of ERC-20 tokens to their confidential {ConfidentialERC20} wrappers. + * A pre-deployed {ConfidentialETH} instance is registered at construction time for the + * chain's native token (via WETH). + * + * Anyone may call {deployConfidentialERC20} to permissionlessly create a confidential wrapper + * for a supported ERC-20. Each deployed wrapper is owned by this contract. + */ contract RedactCore is Ownable2Step { using EnumerableMap for EnumerableMap.AddressToAddressMap; - EnumerableMap.AddressToAddressMap private _fherc20Map; + EnumerableMap.AddressToAddressMap private _confidentialERC20Map; - // Confidential ETH :: ETH / wETH deposited into Redacted are routed to eETH IWETH public immutable wETH; ConfidentialETH public immutable eETH; - // Stablecoins :: deposited stablecoins are routed to FUSD - mapping(address erc20 => bool isStablecoin) public _stablecoins; + event ConfidentialERC20Deployed(address indexed erc20, address indexed confidentialERC20); + + error AlreadyDeployed(); + error InvalidWETH(); + error InvalideETH(); constructor(IWETH wETH_, ConfidentialETH eETH_) Ownable(msg.sender) { - if (address(wETH_) == address(0)) revert Invalid_WETH(); - if (address(eETH_) == address(0)) revert Invalid_eETH(); + if (address(wETH_) == address(0)) revert InvalidWETH(); + if (address(eETH_) == address(0)) revert InvalideETH(); wETH = wETH_; eETH = eETH_; - _fherc20Map.set(address(wETH), address(eETH)); + _confidentialERC20Map.set(address(wETH), address(eETH)); } - event Fherc20Deployed(address indexed erc20, address indexed fherc20); - event StablecoinUpdated(address indexed erc20, bool isStablecoin); - event Fherc20SymbolUpdated(address indexed fherc20, string symbol); - - error Invalid_AlreadyDeployed(); - error Invalid_Stablecoin(); - error Invalid_WETH(); - error Invalid_eETH(); + /** + * @dev Deploys a new {ConfidentialERC20} wrapper for `erc20` and registers it. + * Reverts if a wrapper already exists or if `erc20` is WETH (use {eETH} instead). + */ + function deployConfidentialERC20(IERC20 erc20) public returns (ConfidentialERC20) { + if (address(erc20) == address(wETH)) revert InvalidWETH(); + if (_confidentialERC20Map.contains(address(erc20))) revert AlreadyDeployed(); - function updateStablecoin(address stablecoin, bool isStablecoin) public onlyOwner { - _stablecoins[stablecoin] = isStablecoin; - emit StablecoinUpdated(stablecoin, isStablecoin); - } - - function getFherc20(address erc20) public view returns (address) { - (bool exists, address fherc20) = _fherc20Map.tryGet(erc20); - if (!exists) return address(0); - return fherc20; - } + ConfidentialERC20 confidentialERC20 = new ConfidentialERC20(erc20); + _confidentialERC20Map.set(address(erc20), address(confidentialERC20)); - function getIsStablecoin(address erc20) public view returns (bool) { - return _stablecoins[erc20]; + emit ConfidentialERC20Deployed(address(erc20), address(confidentialERC20)); + return confidentialERC20; } - function getIsWETH(address erc20) public view returns (bool) { - return erc20 == address(wETH); - } - - function updateFherc20Symbol(ConfidentialERC20 fherc20, string memory updatedSymbol) public onlyOwner { - fherc20.updateSymbol(updatedSymbol); - emit Fherc20SymbolUpdated(address(fherc20), updatedSymbol); - } - - function deployFherc20(IERC20 erc20) public { - if (_fherc20Map.contains(address(erc20))) revert Invalid_AlreadyDeployed(); - - if (_stablecoins[address(erc20)]) revert Invalid_Stablecoin(); - if (address(erc20) == address(wETH)) revert Invalid_WETH(); - - ConfidentialERC20 fherc20 = new ConfidentialERC20(erc20, ""); - _fherc20Map.set(address(erc20), address(fherc20)); - - emit Fherc20Deployed(address(erc20), address(fherc20)); + /** + * @dev Returns the confidential wrapper address for `erc20`, or `address(0)` if none exists. + */ + function getConfidentialERC20(address erc20) public view returns (address) { + (bool exists, address confidential) = _confidentialERC20Map.tryGet(erc20); + return exists ? confidential : address(0); } struct MappedERC20 { address erc20; - address fherc20; + address confidentialERC20; } - function getDeployedFherc20s() public view returns (MappedERC20[] memory mappedFherc20s) { - mappedFherc20s = new MappedERC20[](_fherc20Map.length()); - - address _mapErc20; - address _mapFherc20; + /** + * @dev Returns all registered ERC-20 → ConfidentialERC20 pairs. + */ + function getDeployedConfidentialERC20s() public view returns (MappedERC20[] memory mapped) { + uint256 len = _confidentialERC20Map.length(); + mapped = new MappedERC20[](len); - for (uint256 i = 0; i < _fherc20Map.length(); i++) { - (_mapErc20, _mapFherc20) = _fherc20Map.at(i); - mappedFherc20s[i] = MappedERC20(_mapErc20, _mapFherc20); + for (uint256 i = 0; i < len; i++) { + (address erc20, address confidential) = _confidentialERC20Map.at(i); + mapped[i] = MappedERC20(erc20, confidential); } } } diff --git a/packages/hardhat/contracts/hardhat/ERC20_Harness.sol b/packages/hardhat/contracts/hardhat/ERC20_Harness.sol new file mode 100644 index 0000000..a5a64b3 --- /dev/null +++ b/packages/hardhat/contracts/hardhat/ERC20_Harness.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/// @dev Minimal ERC-20 test harness with configurable decimals and a public mint function. +contract ERC20_Harness is ERC20 { + uint8 private _decimals; + + constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { + _decimals = decimals_; + } + + function decimals() public view override returns (uint8) { + return _decimals; + } + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } +} diff --git a/packages/hardhat/contracts/hardhat/WETH_Harness.sol b/packages/hardhat/contracts/hardhat/WETH_Harness.sol new file mode 100644 index 0000000..5ceb950 --- /dev/null +++ b/packages/hardhat/contracts/hardhat/WETH_Harness.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { IWETH } from "fhenix-confidential-contracts/contracts/interfaces/IWETH.sol"; + +/// @dev Minimal WETH test harness implementing the IWETH interface. +contract WETH_Harness is ERC20, IWETH { + constructor() ERC20("Wrapped Ether", "WETH") {} + + function deposit() external payable override { + _mint(msg.sender, msg.value); + } + + function withdraw(uint256 amount) external override { + _burn(msg.sender, amount); + (bool ok, ) = msg.sender.call{ value: amount }(""); + require(ok, "WETH_Harness: ETH transfer failed"); + } + + receive() external payable { + _mint(msg.sender, msg.value); + } +} diff --git a/packages/hardhat/contracts/interfaces/IFHERC20.sol b/packages/hardhat/contracts/interfaces/IFHERC20.sol deleted file mode 100644 index e9c160c..0000000 --- a/packages/hardhat/contracts/interfaces/IFHERC20.sol +++ /dev/null @@ -1,211 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import { euint128, InEuint128 } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * - * TIP: For a detailed writeup see our guide - * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * The default value of {decimals} is 18. To change this, you should override - * this function so it returns a different value. - * - * We have followed general OpenZeppelin Contracts guidelines: functions revert - * instead returning `false` on failure. This behavior is nonetheless - * conventional and does not conflict with the expectations of ERC-20 - * applications. - * - * Note: This FHERC20 does not include FHE operations, and is intended to decouple the - * frontend work from the active CoFHE (FHE Coprocessor) work during development and auditing. - */ -interface IFHERC20 is IERC20, IERC20Metadata { - /** - * @dev EIP712 Permit reusable struct - */ - struct FHERC20_EIP712_Permit { - address owner; - address spender; - uint256 value_hash; - uint256 deadline; - uint8 v; - bytes32 r; - bytes32 s; - } - - /** - * @dev Emitted when `value_hash` encrypted tokens are moved from one account (`from`) to - * another (`to`). - * - * Note that `value` may be zero. - */ - event EncTransfer(address indexed from, address indexed to, uint256 value_hash); - - /** - * @dev Returns true if the token is a FHERC20. - */ - function isFherc20() external view returns (bool); - - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); - - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() external view returns (string memory); - - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the default value returned by this function, unless - * it's overridden. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() external view returns (uint8); - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() external view returns (uint256); - - /** - * @dev Returns an flag indicating that the external balances returned by - * `balanceOf` is an indication of the underlying encrypted balance. - * The value returned is between 0.0000 and 0.9999, and - * acts as a counter of tokens transfers and changes. - * - * Receiving tokens increments this indicator by +0.0001. - * Sending tokens decrements the indicator by -0.0001. - */ - function balanceOfIsIndicator() external view returns (bool); - - /** - * @dev Returns the true size of the indicator tick - */ - function indicatorTick() external view returns (uint256); - - /** - * @dev Returns an indicator of the underlying encrypted balance. - * The value returned is [0](no interaction) / [0.0001 - 0.9999](indicated) - * Indicator acts as a counter of tokens transfers and changes. - * - * Receiving tokens increments this indicator by +0.0001. - * Sending tokens decrements the indicator by -0.0001. - * - * Returned in the decimal expectation of the token. - */ - function balanceOf(address account) external view returns (uint256); - - /** - * @dev See {IERC20-balanceOf}. - * - * Returns the euint128 representing the account's true balance (encrypted) - */ - function encBalanceOf(address account) external view returns (euint128); - - /** - * @dev See {IERC20-transfer}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20 - */ - function transfer(address, uint256) external pure returns (bool); - - /** - * @dev See {IERC20-transfer}. - * - * Intended to be used as a EOA call with an encrypted input `InEuint128 inValue`. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `value`. - * - `inValue` must be a `InEuint128` to preserve confidentiality. - */ - function encTransfer(address to, InEuint128 memory inValue) external returns (euint128 transferred); - - /** - * @dev See {IERC20-transfer}. - * - * Intended to be used as part of a contract call. - * Ensure that `value` is allowed to be used by using `FHE.allow` with this contracts address. - * - * Requirements: - * - * - `to` cannot be the zero address. - * - the caller must have a balance of at least `value`. - * - `value` must be a `euint128` to preserve confidentiality. - */ - function encTransfer(address to, euint128 value) external returns (euint128 transferred); - - /** - * @dev See {IERC20-allowance}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20. - * Allowances have been removed from FHERC20s to prevent encrypted balance leakage. - * Allowances have been replaced with an EIP712 permit for each `encTransferFrom`. - */ - function allowance(address, address) external pure returns (uint256); - - /** - * @dev See {IERC20-approve}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20. - * Allowances have been removed from FHERC20s to prevent encrypted balance leakage. - * Allowances have been replaced with an EIP712 permit for each `encTransferFrom`. - */ - function approve(address, uint256) external pure returns (bool); - - /** - * @dev See {IERC20-transferFrom}. - * Always reverts to prevent FHERC20 from being unintentionally treated as an ERC20 - */ - function transferFrom(address, address, uint256) external pure returns (bool); - - /** - * @dev See {IERC20-transferFrom}. - * - * Requirements: - * - * - `from` and `to` cannot be the zero address. - * - `from` must have a balance of at least `value`. - * - the caller must have allowance for ``from``'s tokens of at least - * `value`. - */ - function encTransferFrom( - address from, - address to, - InEuint128 memory inValue, - FHERC20_EIP712_Permit calldata permit - ) external returns (euint128 transferred); - - // EIP712 Permit - - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases ``owner``'s nonce by one. This - * prevents a signature from being used multiple times. - */ - function nonces(address owner) external view returns (uint256); - /** - * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view returns (bytes32); -} diff --git a/packages/hardhat/contracts/interfaces/IFHERC20Errors.sol b/packages/hardhat/contracts/interfaces/IFHERC20Errors.sol deleted file mode 100644 index 90ddb40..0000000 --- a/packages/hardhat/contracts/interfaces/IFHERC20Errors.sol +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol) -pragma solidity ^0.8.25; - -import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; - -/** - * @dev Standard FHERC-20 Errors - * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. - */ -interface IFHERC20Errors is IERC20Errors { - /** - * @dev Indicates an incompatible function being called. - * Prevents unintentional treatment of an FHERC20 as a cleartext ERC20 - */ - error FHERC20IncompatibleFunction(); - - /** - * @dev encTransferFrom `from` and `permit.owner` don't match - * @param from encTransferFrom param. - * @param permitOwner token owner included in FHERC20_EIP712_Permit struct. - */ - error FHERC20EncTransferFromOwnerMismatch(address from, address permitOwner); - - /** - * @dev encTransferFrom `to` and `permit.spender` don't match - * @param to encTransferFrom param. - * @param permitSpender token receiver included in FHERC20_EIP712_Permit struct. - */ - error FHERC20EncTransferFromSpenderMismatch(address to, address permitSpender); - - /** - * @dev encTransferFrom `value` greater than `permit.value_hash` dont match (permit doesn't match InEuint128) - * @param inValueHash encTransferFrom param inValue.ctHash. - * @param permitValueHash token amount hash included in FHERC20_EIP712_Permit struct. - */ - error FHERC20EncTransferFromValueHashMismatch(uint256 inValueHash, uint256 permitValueHash); - - /** - * @dev Permit deadline has expired. - * @param deadline Expired deadline of the FHERC20_EIP712_Permit. - */ - error ERC2612ExpiredSignature(uint256 deadline); - - /** - * @dev Mismatched signature. - * @param signer ECDSA recovered signer of the FHERC20_EIP712_Permit. - * @param owner Owner passed in as part of the FHERC20_EIP712_Permit struct. - */ - error ERC2612InvalidSigner(address signer, address owner); -} diff --git a/packages/hardhat/contracts/interfaces/IWETH.sol b/packages/hardhat/contracts/interfaces/IWETH.sol deleted file mode 100644 index b7e4efa..0000000 --- a/packages/hardhat/contracts/interfaces/IWETH.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; - -interface IWETH is IERC20 { - function withdraw(uint256 amount) external; - function deposit() external payable; -} diff --git a/packages/hardhat/contracts/test/ERC20_Harness.sol b/packages/hardhat/contracts/test/ERC20_Harness.sol deleted file mode 100644 index 7e8be4b..0000000 --- a/packages/hardhat/contracts/test/ERC20_Harness.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { IWETH } from "../interfaces/IWETH.sol"; - -contract ERC20_Harness is ERC20 { - uint8 _decimals; - - constructor(string memory name_, string memory symbol_, uint8 decimals_) ERC20(name_, symbol_) { - _decimals = decimals_; - } - - function decimals() public view override returns (uint8) { - return _decimals; - } - - function mint(address account, uint256 value) public { - _mint(account, value); - } - - function burn(address account, uint256 value) public { - _burn(account, value); - } -} - -contract WETH_Harness is ERC20_Harness, IWETH { - constructor() ERC20_Harness("Wrapped ETH", "wETH", 18) {} - - function withdraw(uint256 amount) public { - _burn(msg.sender, amount); - payable(msg.sender).transfer(amount); - } - - function deposit() public payable { - _mint(msg.sender, msg.value); - } -} diff --git a/packages/hardhat/contracts/test/FHERC20_Harness.sol b/packages/hardhat/contracts/test/FHERC20_Harness.sol deleted file mode 100644 index 2aa112d..0000000 --- a/packages/hardhat/contracts/test/FHERC20_Harness.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { FHERC20 } from "../FHERC20.sol"; - -contract FHERC20_Harness is FHERC20 { - constructor(string memory name_, string memory symbol_, uint8 decimals_) FHERC20(name_, symbol_, decimals_) {} - - function mint(address account, uint128 value) public { - _mint(account, value); - } - - function burn(address account, uint128 value) public { - _burn(account, value); - } - - function setUserIndicatedBalance(address account, uint16 value) public { - _indicatedBalances[account] = value; - } - - function setTotalIndicatedSupply(uint16 value) public { - _indicatedTotalSupply = value; - } -} diff --git a/packages/hardhat/contracts/test/MockFherc20Vault.sol b/packages/hardhat/contracts/test/MockFherc20Vault.sol deleted file mode 100644 index f988b8f..0000000 --- a/packages/hardhat/contracts/test/MockFherc20Vault.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/ERC20.sol) - -pragma solidity ^0.8.25; - -import { ConfidentialERC20 } from "../ConfidentialERC20.sol"; -import { ConfidentialETH } from "../ConfidentialETH.sol"; -import { IWETH } from "../interfaces/IWETH.sol"; -import { InEuint128 } from "@fhenixprotocol/cofhe-contracts/FHE.sol"; -import { IFHERC20 } from "../interfaces/IFHERC20.sol"; - -contract MockFherc20Vault { - IFHERC20 public fherc20; - - constructor(address fherc20_) { - fherc20 = IFHERC20(fherc20_); - } - - function deposit(InEuint128 memory inValue, IFHERC20.FHERC20_EIP712_Permit calldata permit) public { - fherc20.encTransferFrom(msg.sender, address(this), inValue, permit); - } -} diff --git a/packages/hardhat/gasReporterOutput.json b/packages/hardhat/gasReporterOutput.json new file mode 100644 index 0000000..813f863 --- /dev/null +++ b/packages/hardhat/gasReporterOutput.json @@ -0,0 +1,2212 @@ +{ + "namespace": "HardhatGasReporter", + "toolchain": "hardhat", + "version": "2.2.3", + "options": { + "currency": "USD", + "currencyDisplayPrecision": 2, + "darkMode": false, + "enabled": true, + "excludeContracts": [], + "excludeAutoGeneratedGetters": false, + "forceTerminalOutput": false, + "includeBytecodeInJSON": false, + "includeIntrinsicGas": true, + "L1": "ethereum", + "noColors": false, + "offline": false, + "opStackBaseFeeScalar": 0, + "opStackBlobBaseFeeScalar": 0, + "outputJSON": false, + "outputJSONFile": "./gasReporterOutput.json", + "reportFormat": "terminal", + "reportPureAndViewMethods": false, + "rst": false, + "rstTitle": "", + "suppressTerminalOutput": false, + "showMethodSig": false, + "showUncalledMethods": false, + "blockGasLimit": 30000000, + "solcInfo": { + "version": "0.8.25", + "optimizer": true, + "runs": 200, + "viaIR": false + } + }, + "data": { + "methods": { + "ConfidentialERC20_54095227": { + "key": "54095227", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "confidentialTotalSupply", + "fnSig": "confidentialTotalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_dd62ed3e": { + "key": "dd62ed3e", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "allowance", + "fnSig": "allowance(address,address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_095ea7b3": { + "key": "095ea7b3", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "approve", + "fnSig": "approve(address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_70a08231": { + "key": "70a08231", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "balanceOf", + "fnSig": "balanceOf(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_a415f269": { + "key": "a415f269", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "balanceOfIsIndicator", + "fnSig": "balanceOfIsIndicator()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_cdc75a80": { + "key": "cdc75a80", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "claimUnshielded", + "fnSig": "claimUnshielded(bytes32,uint64,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_435c885f": { + "key": "435c885f", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "claimUnshieldedBatch", + "fnSig": "claimUnshieldedBatch(bytes32[],uint64[],bytes[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_344ff101": { + "key": "344ff101", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "confidentialBalanceOf", + "fnSig": "confidentialBalanceOf(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_5bebed7e": { + "key": "5bebed7e", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransfer", + "fnSig": "confidentialTransfer(address,bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_a794ee95": { + "key": "a794ee95", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransfer", + "fnSig": "confidentialTransfer(address,(uint256,uint8,uint8,bytes))", + "intrinsicGas": [ + 23772 + ], + "callData": [ + 0 + ], + "gasData": [ + 1193805 + ], + "numberOfCalls": 1, + "executionGasAverage": 1193805, + "calldataGasAverage": 0, + "min": 1193805, + "max": 1193805 + }, + "ConfidentialERC20_537d3c50": { + "key": "537d3c50", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransferAndCall", + "fnSig": "confidentialTransferAndCall(address,bytes32,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_d174999a": { + "key": "d174999a", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransferAndCall", + "fnSig": "confidentialTransferAndCall(address,(uint256,uint8,uint8,bytes),bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_7edb0e7d": { + "key": "7edb0e7d", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransferFrom", + "fnSig": "confidentialTransferFrom(address,address,(uint256,uint8,uint8,bytes))", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_eb3155b5": { + "key": "eb3155b5", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransferFrom", + "fnSig": "confidentialTransferFrom(address,address,bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_c7b8a75e": { + "key": "c7b8a75e", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransferFromAndCall", + "fnSig": "confidentialTransferFromAndCall(address,address,bytes32,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_fca114d6": { + "key": "fca114d6", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "confidentialTransferFromAndCall", + "fnSig": "confidentialTransferFromAndCall(address,address,(uint256,uint8,uint8,bytes),bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_e8a3d485": { + "key": "e8a3d485", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "contractURI", + "fnSig": "contractURI()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_313ce567": { + "key": "313ce567", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "decimals", + "fnSig": "decimals()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_b1af281e": { + "key": "b1af281e", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "discloseEncryptedAmount", + "fnSig": "discloseEncryptedAmount(bytes32,uint64,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_c9100bcb": { + "key": "c9100bcb", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "getClaim", + "fnSig": "getClaim(bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_7bcb4a64": { + "key": "7bcb4a64", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "getUserClaims", + "fnSig": "getUserClaims(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_490690f3": { + "key": "490690f3", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "indicatorTick", + "fnSig": "indicatorTick()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_f89d30b1": { + "key": "f89d30b1", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "inferredTotalSupply", + "fnSig": "inferredTotalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_b6363cf2": { + "key": "b6363cf2", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "isOperator", + "fnSig": "isOperator(address,address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_2ab4d052": { + "key": "2ab4d052", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "maxTotalSupply", + "fnSig": "maxTotalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_06fdde03": { + "key": "06fdde03", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "name", + "fnSig": "name()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_88a7ca5c": { + "key": "88a7ca5c", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "onTransferReceived", + "fnSig": "onTransferReceived(address,address,uint256,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_8da5cb5b": { + "key": "8da5cb5b", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "owner", + "fnSig": "owner()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_2c4e722e": { + "key": "2c4e722e", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "rate", + "fnSig": "rate()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_715018a6": { + "key": "715018a6", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "renounceOwnership", + "fnSig": "renounceOwnership()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_d8061806": { + "key": "d8061806", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "requestDiscloseEncryptedAmount", + "fnSig": "requestDiscloseEncryptedAmount(bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_b4b20190": { + "key": "b4b20190", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "resetIndicatedBalance", + "fnSig": "resetIndicatedBalance()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_d4febb96": { + "key": "d4febb96", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "setOperator", + "fnSig": "setOperator(address,uint48)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_8f214a33": { + "key": "8f214a33", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "shield", + "fnSig": "shield(address,uint256)", + "intrinsicGas": [ + 21596, + 21596, + 21596 + ], + "callData": [ + 0, + 0, + 0 + ], + "gasData": [ + 729418, + 729418, + 948318 + ], + "numberOfCalls": 3, + "executionGasAverage": 802385, + "calldataGasAverage": 0, + "min": 729418, + "max": 948318 + }, + "ConfidentialERC20_01ffc9a7": { + "key": "01ffc9a7", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "supportsInterface", + "fnSig": "supportsInterface(bytes4)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_95d89b41": { + "key": "95d89b41", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "symbol", + "fnSig": "symbol()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_18160ddd": { + "key": "18160ddd", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "totalSupply", + "fnSig": "totalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_a9059cbb": { + "key": "a9059cbb", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "transfer", + "fnSig": "transfer(address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_23b872dd": { + "key": "23b872dd", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "transferFrom", + "fnSig": "transferFrom(address,address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_f2fde38b": { + "key": "f2fde38b", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "transferOwnership", + "fnSig": "transferOwnership(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_6f307dc3": { + "key": "6f307dc3", + "isCall": true, + "contract": "ConfidentialERC20", + "method": "underlying", + "fnSig": "underlying()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialERC20_4ccac778": { + "key": "4ccac778", + "isCall": false, + "contract": "ConfidentialERC20", + "method": "unshield", + "fnSig": "unshield(address,address,uint64)", + "intrinsicGas": [ + 21952, + 21952 + ], + "callData": [ + 0, + 0 + ], + "gasData": [ + 1292090, + 1292090 + ], + "numberOfCalls": 2, + "executionGasAverage": 1292090, + "calldataGasAverage": 0, + "min": 1292090, + "max": 1292090 + }, + "ConfidentialETH_54095227": { + "key": "54095227", + "isCall": true, + "contract": "ConfidentialETH", + "method": "confidentialTotalSupply", + "fnSig": "confidentialTotalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_dd62ed3e": { + "key": "dd62ed3e", + "isCall": true, + "contract": "ConfidentialETH", + "method": "allowance", + "fnSig": "allowance(address,address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_095ea7b3": { + "key": "095ea7b3", + "isCall": true, + "contract": "ConfidentialETH", + "method": "approve", + "fnSig": "approve(address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_70a08231": { + "key": "70a08231", + "isCall": true, + "contract": "ConfidentialETH", + "method": "balanceOf", + "fnSig": "balanceOf(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_a415f269": { + "key": "a415f269", + "isCall": true, + "contract": "ConfidentialETH", + "method": "balanceOfIsIndicator", + "fnSig": "balanceOfIsIndicator()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_cdc75a80": { + "key": "cdc75a80", + "isCall": false, + "contract": "ConfidentialETH", + "method": "claimUnshielded", + "fnSig": "claimUnshielded(bytes32,uint64,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_435c885f": { + "key": "435c885f", + "isCall": false, + "contract": "ConfidentialETH", + "method": "claimUnshieldedBatch", + "fnSig": "claimUnshieldedBatch(bytes32[],uint64[],bytes[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_344ff101": { + "key": "344ff101", + "isCall": true, + "contract": "ConfidentialETH", + "method": "confidentialBalanceOf", + "fnSig": "confidentialBalanceOf(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_5bebed7e": { + "key": "5bebed7e", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransfer", + "fnSig": "confidentialTransfer(address,bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_a794ee95": { + "key": "a794ee95", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransfer", + "fnSig": "confidentialTransfer(address,(uint256,uint8,uint8,bytes))", + "intrinsicGas": [ + 23772 + ], + "callData": [ + 0 + ], + "gasData": [ + 1194995 + ], + "numberOfCalls": 1, + "executionGasAverage": 1194995, + "calldataGasAverage": 0, + "min": 1194995, + "max": 1194995 + }, + "ConfidentialETH_537d3c50": { + "key": "537d3c50", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransferAndCall", + "fnSig": "confidentialTransferAndCall(address,bytes32,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_d174999a": { + "key": "d174999a", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransferAndCall", + "fnSig": "confidentialTransferAndCall(address,(uint256,uint8,uint8,bytes),bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_7edb0e7d": { + "key": "7edb0e7d", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransferFrom", + "fnSig": "confidentialTransferFrom(address,address,(uint256,uint8,uint8,bytes))", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_eb3155b5": { + "key": "eb3155b5", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransferFrom", + "fnSig": "confidentialTransferFrom(address,address,bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_c7b8a75e": { + "key": "c7b8a75e", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransferFromAndCall", + "fnSig": "confidentialTransferFromAndCall(address,address,bytes32,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_fca114d6": { + "key": "fca114d6", + "isCall": false, + "contract": "ConfidentialETH", + "method": "confidentialTransferFromAndCall", + "fnSig": "confidentialTransferFromAndCall(address,address,(uint256,uint8,uint8,bytes),bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_e8a3d485": { + "key": "e8a3d485", + "isCall": true, + "contract": "ConfidentialETH", + "method": "contractURI", + "fnSig": "contractURI()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_313ce567": { + "key": "313ce567", + "isCall": true, + "contract": "ConfidentialETH", + "method": "decimals", + "fnSig": "decimals()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_b1af281e": { + "key": "b1af281e", + "isCall": false, + "contract": "ConfidentialETH", + "method": "discloseEncryptedAmount", + "fnSig": "discloseEncryptedAmount(bytes32,uint64,bytes)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_c9100bcb": { + "key": "c9100bcb", + "isCall": true, + "contract": "ConfidentialETH", + "method": "getClaim", + "fnSig": "getClaim(bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_7bcb4a64": { + "key": "7bcb4a64", + "isCall": true, + "contract": "ConfidentialETH", + "method": "getUserClaims", + "fnSig": "getUserClaims(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_490690f3": { + "key": "490690f3", + "isCall": true, + "contract": "ConfidentialETH", + "method": "indicatorTick", + "fnSig": "indicatorTick()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_f89d30b1": { + "key": "f89d30b1", + "isCall": true, + "contract": "ConfidentialETH", + "method": "inferredTotalSupply", + "fnSig": "inferredTotalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_b6363cf2": { + "key": "b6363cf2", + "isCall": true, + "contract": "ConfidentialETH", + "method": "isOperator", + "fnSig": "isOperator(address,address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_2ab4d052": { + "key": "2ab4d052", + "isCall": true, + "contract": "ConfidentialETH", + "method": "maxTotalSupply", + "fnSig": "maxTotalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_06fdde03": { + "key": "06fdde03", + "isCall": true, + "contract": "ConfidentialETH", + "method": "name", + "fnSig": "name()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_8da5cb5b": { + "key": "8da5cb5b", + "isCall": true, + "contract": "ConfidentialETH", + "method": "owner", + "fnSig": "owner()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_2c4e722e": { + "key": "2c4e722e", + "isCall": true, + "contract": "ConfidentialETH", + "method": "rate", + "fnSig": "rate()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_715018a6": { + "key": "715018a6", + "isCall": false, + "contract": "ConfidentialETH", + "method": "renounceOwnership", + "fnSig": "renounceOwnership()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_d8061806": { + "key": "d8061806", + "isCall": false, + "contract": "ConfidentialETH", + "method": "requestDiscloseEncryptedAmount", + "fnSig": "requestDiscloseEncryptedAmount(bytes32)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_b4b20190": { + "key": "b4b20190", + "isCall": false, + "contract": "ConfidentialETH", + "method": "resetIndicatedBalance", + "fnSig": "resetIndicatedBalance()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_d4febb96": { + "key": "d4febb96", + "isCall": false, + "contract": "ConfidentialETH", + "method": "setOperator", + "fnSig": "setOperator(address,uint48)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_759ded8c": { + "key": "759ded8c", + "isCall": false, + "contract": "ConfidentialETH", + "method": "shieldNative", + "fnSig": "shieldNative(address)", + "intrinsicGas": [ + 21432, + 21432, + 21432, + 21432, + 21432, + 21432, + 21432 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "gasData": [ + 701260, + 701260, + 701260, + 860460, + 860460, + 862524, + 862524 + ], + "numberOfCalls": 7, + "executionGasAverage": 792821, + "calldataGasAverage": 0, + "min": 701260, + "max": 862524 + }, + "ConfidentialETH_49ea576e": { + "key": "49ea576e", + "isCall": false, + "contract": "ConfidentialETH", + "method": "shieldWrappedNative", + "fnSig": "shieldWrappedNative(address,uint256)", + "intrinsicGas": [ + 21632 + ], + "callData": [ + 0 + ], + "gasData": [ + 720878 + ], + "numberOfCalls": 1, + "executionGasAverage": 720878, + "calldataGasAverage": 0, + "min": 720878, + "max": 720878 + }, + "ConfidentialETH_01ffc9a7": { + "key": "01ffc9a7", + "isCall": true, + "contract": "ConfidentialETH", + "method": "supportsInterface", + "fnSig": "supportsInterface(bytes4)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_95d89b41": { + "key": "95d89b41", + "isCall": true, + "contract": "ConfidentialETH", + "method": "symbol", + "fnSig": "symbol()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_18160ddd": { + "key": "18160ddd", + "isCall": true, + "contract": "ConfidentialETH", + "method": "totalSupply", + "fnSig": "totalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_a9059cbb": { + "key": "a9059cbb", + "isCall": true, + "contract": "ConfidentialETH", + "method": "transfer", + "fnSig": "transfer(address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_23b872dd": { + "key": "23b872dd", + "isCall": true, + "contract": "ConfidentialETH", + "method": "transferFrom", + "fnSig": "transferFrom(address,address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_f2fde38b": { + "key": "f2fde38b", + "isCall": false, + "contract": "ConfidentialETH", + "method": "transferOwnership", + "fnSig": "transferOwnership(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ConfidentialETH_4ccac778": { + "key": "4ccac778", + "isCall": false, + "contract": "ConfidentialETH", + "method": "unshield", + "fnSig": "unshield(address,address,uint64)", + "intrinsicGas": [ + 21952, + 21952 + ], + "callData": [ + 0, + 0 + ], + "gasData": [ + 993608, + 993608 + ], + "numberOfCalls": 2, + "executionGasAverage": 993608, + "calldataGasAverage": 0, + "min": 993608, + "max": 993608 + }, + "ConfidentialETH_3fc8cef3": { + "key": "3fc8cef3", + "isCall": true, + "contract": "ConfidentialETH", + "method": "weth", + "fnSig": "weth()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_79ba5097": { + "key": "79ba5097", + "isCall": false, + "contract": "RedactCore", + "method": "acceptOwnership", + "fnSig": "acceptOwnership()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_70858f38": { + "key": "70858f38", + "isCall": false, + "contract": "RedactCore", + "method": "deployConfidentialERC20", + "fnSig": "deployConfidentialERC20(address)", + "intrinsicGas": [ + 21420, + 21420, + 21432, + 21432, + 21432, + 21432, + 21432, + 21432, + 21432 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "gasData": [ + 2963444, + 2963444, + 2963456, + 2963456, + 2963456, + 2963456, + 2963456, + 2963456, + 2986111 + ], + "numberOfCalls": 9, + "executionGasAverage": 2965971, + "calldataGasAverage": 0, + "min": 2963444, + "max": 2986111 + }, + "RedactCore_0de371e2": { + "key": "0de371e2", + "isCall": true, + "contract": "RedactCore", + "method": "eETH", + "fnSig": "eETH()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_ca4f7cc4": { + "key": "ca4f7cc4", + "isCall": true, + "contract": "RedactCore", + "method": "getConfidentialERC20", + "fnSig": "getConfidentialERC20(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_7a1fb1cd": { + "key": "7a1fb1cd", + "isCall": true, + "contract": "RedactCore", + "method": "getDeployedConfidentialERC20s", + "fnSig": "getDeployedConfidentialERC20s()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_8da5cb5b": { + "key": "8da5cb5b", + "isCall": true, + "contract": "RedactCore", + "method": "owner", + "fnSig": "owner()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_e30c3978": { + "key": "e30c3978", + "isCall": true, + "contract": "RedactCore", + "method": "pendingOwner", + "fnSig": "pendingOwner()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_715018a6": { + "key": "715018a6", + "isCall": false, + "contract": "RedactCore", + "method": "renounceOwnership", + "fnSig": "renounceOwnership()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_f2fde38b": { + "key": "f2fde38b", + "isCall": false, + "contract": "RedactCore", + "method": "transferOwnership", + "fnSig": "transferOwnership(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "RedactCore_f2428621": { + "key": "f2428621", + "isCall": true, + "contract": "RedactCore", + "method": "wETH", + "fnSig": "wETH()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_dd62ed3e": { + "key": "dd62ed3e", + "isCall": true, + "contract": "ERC20_Harness", + "method": "allowance", + "fnSig": "allowance(address,address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_095ea7b3": { + "key": "095ea7b3", + "isCall": false, + "contract": "ERC20_Harness", + "method": "approve", + "fnSig": "approve(address,uint256)", + "intrinsicGas": [ + 21584, + 21584, + 21596 + ], + "callData": [ + 0, + 0, + 0 + ], + "gasData": [ + 46319, + 46319, + 46331 + ], + "numberOfCalls": 3, + "executionGasAverage": 46323, + "calldataGasAverage": 0, + "min": 46319, + "max": 46331 + }, + "ERC20_Harness_70a08231": { + "key": "70a08231", + "isCall": true, + "contract": "ERC20_Harness", + "method": "balanceOf", + "fnSig": "balanceOf(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_313ce567": { + "key": "313ce567", + "isCall": true, + "contract": "ERC20_Harness", + "method": "decimals", + "fnSig": "decimals()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_40c10f19": { + "key": "40c10f19", + "isCall": false, + "contract": "ERC20_Harness", + "method": "mint", + "fnSig": "mint(address,uint256)", + "intrinsicGas": [ + 21596, + 21596, + 21596 + ], + "callData": [ + 0, + 0, + 0 + ], + "gasData": [ + 68380, + 68380, + 68380 + ], + "numberOfCalls": 3, + "executionGasAverage": 68380, + "calldataGasAverage": 0, + "min": 68380, + "max": 68380 + }, + "ERC20_Harness_06fdde03": { + "key": "06fdde03", + "isCall": true, + "contract": "ERC20_Harness", + "method": "name", + "fnSig": "name()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_95d89b41": { + "key": "95d89b41", + "isCall": true, + "contract": "ERC20_Harness", + "method": "symbol", + "fnSig": "symbol()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_18160ddd": { + "key": "18160ddd", + "isCall": true, + "contract": "ERC20_Harness", + "method": "totalSupply", + "fnSig": "totalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_a9059cbb": { + "key": "a9059cbb", + "isCall": false, + "contract": "ERC20_Harness", + "method": "transfer", + "fnSig": "transfer(address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "ERC20_Harness_23b872dd": { + "key": "23b872dd", + "isCall": false, + "contract": "ERC20_Harness", + "method": "transferFrom", + "fnSig": "transferFrom(address,address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_252dba42": { + "key": "252dba42", + "isCall": false, + "contract": "Multicall3", + "method": "aggregate", + "fnSig": "aggregate((address,bytes)[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_82ad56cb": { + "key": "82ad56cb", + "isCall": false, + "contract": "Multicall3", + "method": "aggregate3", + "fnSig": "aggregate3((address,bool,bytes)[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_174dea71": { + "key": "174dea71", + "isCall": false, + "contract": "Multicall3", + "method": "aggregate3Value", + "fnSig": "aggregate3Value((address,bool,uint256,bytes)[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_c3077fa9": { + "key": "c3077fa9", + "isCall": false, + "contract": "Multicall3", + "method": "blockAndAggregate", + "fnSig": "blockAndAggregate((address,bytes)[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_3e64a696": { + "key": "3e64a696", + "isCall": true, + "contract": "Multicall3", + "method": "getBasefee", + "fnSig": "getBasefee()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_ee82ac5e": { + "key": "ee82ac5e", + "isCall": true, + "contract": "Multicall3", + "method": "getBlockHash", + "fnSig": "getBlockHash(uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_42cbb15c": { + "key": "42cbb15c", + "isCall": true, + "contract": "Multicall3", + "method": "getBlockNumber", + "fnSig": "getBlockNumber()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_3408e470": { + "key": "3408e470", + "isCall": true, + "contract": "Multicall3", + "method": "getChainId", + "fnSig": "getChainId()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_a8b0574e": { + "key": "a8b0574e", + "isCall": true, + "contract": "Multicall3", + "method": "getCurrentBlockCoinbase", + "fnSig": "getCurrentBlockCoinbase()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_72425d9d": { + "key": "72425d9d", + "isCall": true, + "contract": "Multicall3", + "method": "getCurrentBlockDifficulty", + "fnSig": "getCurrentBlockDifficulty()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_86d516e8": { + "key": "86d516e8", + "isCall": true, + "contract": "Multicall3", + "method": "getCurrentBlockGasLimit", + "fnSig": "getCurrentBlockGasLimit()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_0f28c97d": { + "key": "0f28c97d", + "isCall": true, + "contract": "Multicall3", + "method": "getCurrentBlockTimestamp", + "fnSig": "getCurrentBlockTimestamp()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_4d2301cc": { + "key": "4d2301cc", + "isCall": true, + "contract": "Multicall3", + "method": "getEthBalance", + "fnSig": "getEthBalance(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_27e86d6e": { + "key": "27e86d6e", + "isCall": true, + "contract": "Multicall3", + "method": "getLastBlockHash", + "fnSig": "getLastBlockHash()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_bce38bd7": { + "key": "bce38bd7", + "isCall": false, + "contract": "Multicall3", + "method": "tryAggregate", + "fnSig": "tryAggregate(bool,(address,bytes)[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "Multicall3_399542e9": { + "key": "399542e9", + "isCall": false, + "contract": "Multicall3", + "method": "tryBlockAndAggregate", + "fnSig": "tryBlockAndAggregate(bool,(address,bytes)[])", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_dd62ed3e": { + "key": "dd62ed3e", + "isCall": true, + "contract": "WETH_Harness", + "method": "allowance", + "fnSig": "allowance(address,address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_095ea7b3": { + "key": "095ea7b3", + "isCall": false, + "contract": "WETH_Harness", + "method": "approve", + "fnSig": "approve(address,uint256)", + "intrinsicGas": [ + 21632 + ], + "callData": [ + 0 + ], + "gasData": [ + 46367 + ], + "numberOfCalls": 1, + "executionGasAverage": 46367, + "calldataGasAverage": 0, + "min": 46367, + "max": 46367 + }, + "WETH_Harness_70a08231": { + "key": "70a08231", + "isCall": true, + "contract": "WETH_Harness", + "method": "balanceOf", + "fnSig": "balanceOf(address)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_313ce567": { + "key": "313ce567", + "isCall": true, + "contract": "WETH_Harness", + "method": "decimals", + "fnSig": "decimals()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_d0e30db0": { + "key": "d0e30db0", + "isCall": false, + "contract": "WETH_Harness", + "method": "deposit", + "fnSig": "deposit()", + "intrinsicGas": [ + 21064 + ], + "callData": [ + 0 + ], + "gasData": [ + 67728 + ], + "numberOfCalls": 1, + "executionGasAverage": 67728, + "calldataGasAverage": 0, + "min": 67728, + "max": 67728 + }, + "WETH_Harness_06fdde03": { + "key": "06fdde03", + "isCall": true, + "contract": "WETH_Harness", + "method": "name", + "fnSig": "name()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_95d89b41": { + "key": "95d89b41", + "isCall": true, + "contract": "WETH_Harness", + "method": "symbol", + "fnSig": "symbol()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_18160ddd": { + "key": "18160ddd", + "isCall": true, + "contract": "WETH_Harness", + "method": "totalSupply", + "fnSig": "totalSupply()", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_a9059cbb": { + "key": "a9059cbb", + "isCall": false, + "contract": "WETH_Harness", + "method": "transfer", + "fnSig": "transfer(address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_23b872dd": { + "key": "23b872dd", + "isCall": false, + "contract": "WETH_Harness", + "method": "transferFrom", + "fnSig": "transferFrom(address,address,uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + }, + "WETH_Harness_2e1a7d4d": { + "key": "2e1a7d4d", + "isCall": false, + "contract": "WETH_Harness", + "method": "withdraw", + "fnSig": "withdraw(uint256)", + "intrinsicGas": [], + "callData": [], + "gasData": [], + "numberOfCalls": 0 + } + }, + "deployments": [ + { + "name": "../fhenix-confidential-contracts/contracts/interfaces/IWETH.sol:IWETH", + "gasData": [], + "callData": [] + }, + { + "name": "Arrays", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEaddress", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEbool", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEuint128", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEuint16", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEuint32", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEuint64", + "gasData": [], + "callData": [] + }, + { + "name": "BindingsEuint8", + "gasData": [], + "callData": [] + }, + { + "name": "Common", + "gasData": [], + "callData": [] + }, + { + "name": "Comparators", + "gasData": [], + "callData": [] + }, + { + "name": "ConfidentialERC20", + "gasData": [ + 3132262, + 3132687, + 3132687, + 3132687, + 3132687, + 3132687, + 3132687, + 3132687, + 3132687, + 3132687, + 3133102 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "executionGasAverage": 3132686, + "calldataGasAverage": 0, + "min": 3132262, + "max": 3133102, + "percent": 10.4 + }, + { + "name": "ConfidentialETH", + "gasData": [ + 3261529, + 3261529, + 3261529, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541, + 3261541 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "executionGasAverage": 3261540, + "calldataGasAverage": 0, + "min": 3261529, + "max": 3261541, + "percent": 10.9 + }, + { + "name": "Context", + "gasData": [], + "callData": [] + }, + { + "name": "EnumerableMap", + "gasData": [], + "callData": [] + }, + { + "name": "EnumerableSet", + "gasData": [], + "callData": [] + }, + { + "name": "ERC165", + "gasData": [], + "callData": [] + }, + { + "name": "ERC20", + "gasData": [], + "callData": [] + }, + { + "name": "ERC20_Harness", + "gasData": [ + 536971, + 536983, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537043, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079, + 537079 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "executionGasAverage": 537053, + "calldataGasAverage": 0, + "min": 536971, + "max": 537079, + "percent": 1.8 + }, + { + "name": "ERC7984", + "gasData": [], + "callData": [] + }, + { + "name": "ERC7984ERC20Wrapper", + "gasData": [], + "callData": [] + }, + { + "name": "ERC7984NativeWrapper", + "gasData": [], + "callData": [] + }, + { + "name": "ERC7984Utils", + "gasData": [], + "callData": [] + }, + { + "name": "ERC7984WrapperClaimHelper", + "gasData": [], + "callData": [] + }, + { + "name": "FHE", + "gasData": [], + "callData": [] + }, + { + "name": "FHESafeMath", + "gasData": [], + "callData": [] + }, + { + "name": "IERC1155Errors", + "gasData": [], + "callData": [] + }, + { + "name": "IERC1363", + "gasData": [], + "callData": [] + }, + { + "name": "IERC1363Receiver", + "gasData": [], + "callData": [] + }, + { + "name": "IERC165", + "gasData": [], + "callData": [] + }, + { + "name": "IERC20", + "gasData": [], + "callData": [] + }, + { + "name": "IERC20Errors", + "gasData": [], + "callData": [] + }, + { + "name": "IERC20Metadata", + "gasData": [], + "callData": [] + }, + { + "name": "IERC721Errors", + "gasData": [], + "callData": [] + }, + { + "name": "IERC7984", + "gasData": [], + "callData": [] + }, + { + "name": "IERC7984ERC20Wrapper", + "gasData": [], + "callData": [] + }, + { + "name": "IERC7984NativeWrapper", + "gasData": [], + "callData": [] + }, + { + "name": "IERC7984Receiver", + "gasData": [], + "callData": [] + }, + { + "name": "Impl", + "gasData": [], + "callData": [] + }, + { + "name": "interfaces/IWETH.sol:IWETH", + "gasData": [], + "callData": [] + }, + { + "name": "ITaskManager", + "gasData": [], + "callData": [] + }, + { + "name": "Math", + "gasData": [], + "callData": [] + }, + { + "name": "Multicall3", + "gasData": [], + "callData": [] + }, + { + "name": "Ownable", + "gasData": [], + "callData": [] + }, + { + "name": "Ownable2Step", + "gasData": [], + "callData": [] + }, + { + "name": "Panic", + "gasData": [], + "callData": [] + }, + { + "name": "RedactCore", + "gasData": [ + 4023402, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414, + 4023414 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "executionGasAverage": 4023413, + "calldataGasAverage": 0, + "min": 4023402, + "max": 4023414, + "percent": 13.4 + }, + { + "name": "SafeCast", + "gasData": [], + "callData": [] + }, + { + "name": "SafeERC20", + "gasData": [], + "callData": [] + }, + { + "name": "SignedMath", + "gasData": [], + "callData": [] + }, + { + "name": "SlotDerivation", + "gasData": [], + "callData": [] + }, + { + "name": "StorageSlot", + "gasData": [], + "callData": [] + }, + { + "name": "Strings", + "gasData": [], + "callData": [] + }, + { + "name": "Utils", + "gasData": [], + "callData": [] + }, + { + "name": "WETH_Harness", + "gasData": [ + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417, + 592417 + ], + "callData": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "executionGasAverage": 592417, + "calldataGasAverage": 0, + "min": 592417, + "max": 592417, + "percent": 2 + } + ] + } +} \ No newline at end of file diff --git a/packages/hardhat/test/ConfidentialERC20.test.ts b/packages/hardhat/test/ConfidentialERC20.test.ts index a2127fa..f48de51 100644 --- a/packages/hardhat/test/ConfidentialERC20.test.ts +++ b/packages/hardhat/test/ConfidentialERC20.test.ts @@ -3,223 +3,149 @@ import hre, { ethers } from "hardhat"; import { ConfidentialERC20, ERC20_Harness } from "../typechain-types"; import { expectERC20BalancesChange, - expectFHERC20BalancesChange, + expectERC7984BalancesChange, prepExpectERC20BalancesChange, - ticksToIndicated, + prepExpectERC7984BalancesChange, } from "./utils"; -import { prepExpectFHERC20BalancesChange } from "./utils"; +import { Encryptable } from "@cofhe/sdk"; describe("ConfidentialERC20", function () { - // We define a fixture to reuse the same setup in every test. - const deployContracts = async () => { - // Deploy wBTC - const wBTCFactory = await ethers.getContractFactory("ERC20_Harness"); - const wBTC = (await wBTCFactory.deploy("Wrapped BTC", "wBTC", 8)) as ERC20_Harness; - await wBTC.waitForDeployment(); + async function deployFixture() { + const [owner, bob, alice] = await ethers.getSigners(); - // Deploy eBTC - const eBTCFactory = await ethers.getContractFactory("ConfidentialERC20"); - const eBTC = (await eBTCFactory.deploy(wBTC, "eBTC")) as ConfidentialERC20; - await eBTC.waitForDeployment(); + const usdcFactory = await ethers.getContractFactory("ERC20_Harness"); + const usdc = (await usdcFactory.deploy("USD Coin", "USDC", 6)) as ERC20_Harness; - return { wBTC, eBTC }; - }; + const eUSDCFactory = await ethers.getContractFactory("ConfidentialERC20"); + const eUSDC = (await eUSDCFactory.deploy(usdc.target)) as ConfidentialERC20; - async function setupFixture() { - const [owner, bob, alice, eve] = await ethers.getSigners(); - const { wBTC, eBTC } = await deployContracts(); + const bobClient = await hre.cofhe.createClientWithBatteries(bob); + const aliceClient = await hre.cofhe.createClientWithBatteries(alice); - await hre.cofhe.initializeWithHardhatSigner(owner); - - return { owner, bob, alice, eve, wBTC, eBTC }; + return { owner, bob, alice, bobClient, aliceClient, usdc, eUSDC }; } describe("initialization", function () { - it("Should be constructed correctly", async function () { - const { wBTC, eBTC } = await setupFixture(); - - expect(await eBTC.name()).to.equal("Confidential Wrapped BTC", "ConfidentialERC20 name correct"); - expect(await eBTC.symbol()).to.equal("eBTC", "ConfidentialERC20 symbol correct"); - expect(await eBTC.decimals()).to.equal(8, "ConfidentialERC20 decimals correct"); - expect(await eBTC.erc20()).to.equal(wBTC.target, "ConfidentialERC20 underlying ERC20 correct"); - expect(await eBTC.isFherc20()).to.equal(true, "ConfidentialERC20 isFherc20 correct"); - }); + it("should generate correct name and symbol", async function () { + const { eUSDC } = await deployFixture(); - it("Should handle symbol correctly", async function () { - // Deploy TEST - const TESTFactory = await ethers.getContractFactory("ERC20_Harness"); - const TEST = (await TESTFactory.deploy("Test Token", "TEST", 18)) as ERC20_Harness; - await TEST.waitForDeployment(); - - // Deploy eTEST - const eTESTFactory = await ethers.getContractFactory("ConfidentialERC20"); - const eTEST = (await eTESTFactory.deploy(TEST, "eTEST")) as ConfidentialERC20; - await eTEST.waitForDeployment(); + expect(await eUSDC.name()).to.equal("ERC7984 Confidential USD Coin"); + expect(await eUSDC.symbol()).to.equal("eUSDC"); + }); - expect(await eTEST.name()).to.equal("Confidential Test Token", "eTEST name correct"); - expect(await eTEST.symbol()).to.equal("eTEST", "eTEST symbol correct"); - expect(await eTEST.decimals()).to.equal(await TEST.decimals(), "eTEST decimals correct"); - expect(await eTEST.erc20()).to.equal(TEST.target, "eTEST underlying ERC20 correct"); + it("should set deployer as owner", async function () { + const { eUSDC, owner } = await deployFixture(); - await eTEST.updateSymbol("encTEST"); - expect(await eTEST.symbol()).to.equal("encTEST", "eTEST symbol updated correct"); + expect(await eUSDC.owner()).to.equal(owner.address); }); - it("Should revert if underlying token is not ERC20", async function () { - const { eBTC } = await setupFixture(); + it("should have correct decimals (capped at 6)", async function () { + const { eUSDC } = await deployFixture(); - // Deploy eeBTC - const eeBTCFactory = await ethers.getContractFactory("ConfidentialERC20"); - await expect(eeBTCFactory.deploy(eBTC, "eeBTC")).to.be.revertedWithCustomError(eBTC, "FHERC20InvalidErc20"); + expect(await eUSDC.decimals()).to.equal(6); }); - }); - - describe("encrypt balance (ERC20 -> FHERC20)", function () { - it("Should succeed", async function () { - const { eBTC, bob, wBTC } = await setupFixture(); - expect(await eBTC.totalSupply()).to.equal(0, "Total indicated supply init 0"); - expect(await eBTC.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); + it("should cap decimals for 18-decimal token", async function () { + const factory18 = await ethers.getContractFactory("ERC20_Harness"); + const dai = (await factory18.deploy("Dai", "DAI", 18)) as ERC20_Harness; - const mintValue = BigInt(10e8); - const transferValue = BigInt(1e8); + const eDaiFactory = await ethers.getContractFactory("ConfidentialERC20"); + const eDai = (await eDaiFactory.deploy(dai.target)) as ConfidentialERC20; - // Mint wBTC - await wBTC.mint(bob, mintValue); - await wBTC.connect(bob).approve(eBTC.target, mintValue); + expect(await eDai.decimals()).to.equal(6); + expect(await eDai.rate()).to.equal(10n ** 12n); + expect(await eDai.name()).to.equal("ERC7984 Confidential Dai"); + expect(await eDai.symbol()).to.equal("eDAI"); + }); - // 1st TX, indicated + 5001, true + 1e8 + it("should use native decimals for ≤6-decimal token", async function () { + const factory2 = await ethers.getContractFactory("ERC20_Harness"); + const token = (await factory2.deploy("Test", "TST", 2)) as ERC20_Harness; - await prepExpectERC20BalancesChange(wBTC, bob.address); - await prepExpectFHERC20BalancesChange(eBTC, bob.address); + const eFactory = await ethers.getContractFactory("ConfidentialERC20"); + const eToken = (await eFactory.deploy(token.target)) as ConfidentialERC20; - await expect(eBTC.connect(bob).encrypt(bob, transferValue)).to.emit(eBTC, "Transfer"); + expect(await eToken.decimals()).to.equal(2); + expect(await eToken.rate()).to.equal(1n); + }); - await expectERC20BalancesChange(wBTC, bob.address, -1n * transferValue); - await expectFHERC20BalancesChange(eBTC, bob.address, await ticksToIndicated(eBTC, 5001n), transferValue); + it("should have empty contractURI", async function () { + const { eUSDC } = await deployFixture(); - expect(await eBTC.totalSupply()).to.equal( - await ticksToIndicated(eBTC, 5001n), - "Total indicated supply increases", - ); - await hre.cofhe.mocks.expectPlaintext(await eBTC.encTotalSupply(), transferValue); + expect(await eUSDC.contractURI()).to.equal(""); + }); - // 2nd TX, indicated + 1, true + 1e8 + it("should report correct underlying", async function () { + const { eUSDC, usdc } = await deployFixture(); - await prepExpectERC20BalancesChange(wBTC, bob.address); - await prepExpectFHERC20BalancesChange(eBTC, bob.address); + expect(await eUSDC.underlying()).to.equal(usdc.target); + }); - await expect(eBTC.connect(bob).encrypt(bob, transferValue)).to.emit(eBTC, "Transfer"); + it("should reject wrapping another ERC7984 token", async function () { + const { eUSDC } = await deployFixture(); - await expectERC20BalancesChange(wBTC, bob.address, -1n * transferValue); - await expectFHERC20BalancesChange(eBTC, bob.address, await ticksToIndicated(eBTC, 1n), transferValue); + const factory = await ethers.getContractFactory("ConfidentialERC20"); + await expect(factory.deploy(eUSDC.target)).to.be.revertedWithCustomError(factory, "InvalidUnderlying"); }); }); - describe("decrypt & claim balance (FHERC20 -> ERC20)", function () { - it("Should succeed", async function () { - const { eBTC, bob, wBTC } = await setupFixture(); - - expect(await eBTC.totalSupply()).to.equal(0, "Total supply init 0"); - expect(await eBTC.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); - - const mintValue = BigInt(10e8); - const transferValue = BigInt(1e8); - - // Mint and encrypt wBTC - await wBTC.mint(bob, mintValue); - await wBTC.connect(bob).approve(eBTC.target, mintValue); - await eBTC.connect(bob).encrypt(bob, mintValue); + describe("shield", function () { + it("should shield tokens", async function () { + const { bob, usdc, eUSDC } = await deployFixture(); - // TX + const shieldAmount = 1_000_000n; // 1 USDC (6 decimals, rate=1) + await usdc.mint(bob.address, shieldAmount); + await usdc.connect(bob).approve(eUSDC.target, shieldAmount); - await prepExpectERC20BalancesChange(wBTC, bob.address); - await prepExpectFHERC20BalancesChange(eBTC, bob.address); + await prepExpectERC20BalancesChange(usdc, bob.address); + await prepExpectERC7984BalancesChange(eUSDC, bob.address); - await expect(eBTC.connect(bob).decrypt(bob, transferValue)).to.emit(eBTC, "Transfer"); + await eUSDC.connect(bob).shield(bob.address, shieldAmount); - // -- expect only **FHERC20** balance to change - await expectERC20BalancesChange(wBTC, bob.address, 0n); - await expectFHERC20BalancesChange( - eBTC, - bob.address, - -1n * (await ticksToIndicated(eBTC, 1n)), - -1n * transferValue, - ); - - // Decrypt inserts a claimable amount into the user's claimable set - - let claims = await eBTC.getUserClaims(bob.address); - expect(claims.length).to.equal(1, "Bob has 1 claimable amount"); - - const claimableCtHash = claims[0].ctHash; - let claim = await eBTC.getClaim(claimableCtHash); - expect(claim.claimed).to.equal(false, "Claimable amount not claimed"); - await hre.cofhe.mocks.expectPlaintext(claimableCtHash, transferValue); - - // Hardhat time travel 11 seconds - await hre.network.provider.send("evm_increaseTime", [11]); - await hre.network.provider.send("evm_mine"); - - // Claim Decrypted + await expectERC20BalancesChange(usdc, bob.address, -shieldAmount); + await expectERC7984BalancesChange(eUSDC, bob.address, shieldAmount); + }); + }); - await prepExpectERC20BalancesChange(wBTC, bob.address); - await prepExpectFHERC20BalancesChange(eBTC, bob.address); + describe("unshield", function () { + it("should unshield tokens and create claim", async function () { + const { bob, alice, usdc, eUSDC } = await deployFixture(); - await eBTC.connect(bob).claimDecrypted(claimableCtHash); + const amount = 1_000_000n; + await usdc.mint(bob.address, amount); + await usdc.connect(bob).approve(eUSDC.target, amount); + await eUSDC.connect(bob).shield(bob.address, amount); - // -- expect only **ERC20** balance to change - await expectERC20BalancesChange(wBTC, bob.address, 1n * transferValue); - await expectFHERC20BalancesChange(eBTC, bob.address, 0n, 0n); + const unshieldAmount = 500_000n; - // Claimable amount is now claimed - claim = await eBTC.getClaim(claimableCtHash); - expect(claim.claimed).to.equal(true, "Claimable amount claimed"); + await prepExpectERC7984BalancesChange(eUSDC, bob.address); - // User has no claimable amounts left - claims = await eBTC.getUserClaims(bob.address); - expect(claims.length).to.equal(0, "Bob has no claimable amounts"); + const tx = await eUSDC.connect(bob).unshield(bob.address, alice.address, unshieldAmount); + await expect(tx).to.emit(eUSDC, "Unshielded"); - // Total indicated supply decreases - expect(await eBTC.totalSupply()).to.equal( - await ticksToIndicated(eBTC, 5000n), - "Total indicated supply decreases", - ); - await hre.cofhe.mocks.expectPlaintext(await eBTC.encTotalSupply(), mintValue - transferValue); + await expectERC7984BalancesChange(eUSDC, bob.address, -unshieldAmount); }); - it("Should claim all decrypted amounts", async function () { - const { eBTC, bob, wBTC } = await setupFixture(); - - expect(await eBTC.totalSupply()).to.equal(0, "Total supply init 0"); - expect(await eBTC.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); - - const mintValue = BigInt(10e8); - const transferValue = BigInt(1e8); - - // Mint and encrypt wBTC - await wBTC.mint(bob, mintValue); - await wBTC.connect(bob).approve(eBTC.target, mintValue); - await eBTC.connect(bob).encrypt(bob.address, mintValue); - - // Multiple decryptions + }); - await eBTC.connect(bob).decrypt(bob.address, transferValue); - await eBTC.connect(bob).decrypt(bob.address, transferValue); + describe("confidentialTransfer", function () { + it("should transfer between accounts", async function () { + const { bob, alice, usdc, eUSDC, bobClient } = await deployFixture(); - // Hardhat time travel 11 seconds - await hre.network.provider.send("evm_increaseTime", [11]); - await hre.network.provider.send("evm_mine"); + const amount = 1_000_000n; + await usdc.mint(bob.address, amount); + await usdc.connect(bob).approve(eUSDC.target, amount); + await eUSDC.connect(bob).shield(bob.address, amount); - prepExpectERC20BalancesChange(wBTC, bob.address); + const transferAmount = 300_000n; + const [enc] = await bobClient.encryptInputs([Encryptable.uint64(transferAmount)]).execute(); - // Claim all decrypted amounts - await eBTC.connect(bob).claimAllDecrypted(); + await prepExpectERC7984BalancesChange(eUSDC, bob.address); + await prepExpectERC7984BalancesChange(eUSDC, alice.address); - await expectERC20BalancesChange(wBTC, bob.address, 2n * transferValue); + await eUSDC.connect(bob)["confidentialTransfer(address,(uint256,uint8,uint8,bytes))"](alice.address, enc); - // Expect all decrypted amounts to be claimed - const claims = await eBTC.getUserClaims(bob.address); - expect(claims.length).to.equal(0, "Bob has no claimable amounts"); + await expectERC7984BalancesChange(eUSDC, bob.address, -transferAmount); + await expectERC7984BalancesChange(eUSDC, alice.address, transferAmount); }); }); }); diff --git a/packages/hardhat/test/ConfidentialETH.test.ts b/packages/hardhat/test/ConfidentialETH.test.ts index 203c497..5065ce9 100644 --- a/packages/hardhat/test/ConfidentialETH.test.ts +++ b/packages/hardhat/test/ConfidentialETH.test.ts @@ -1,235 +1,161 @@ import { expect } from "chai"; import hre, { ethers } from "hardhat"; -import { ConfidentialETH, WETH_Harness } from "../typechain-types"; -import { - expectERC20BalancesChange, - expectFHERC20BalancesChange, - prepExpectERC20BalancesChange, - ticksToIndicated, -} from "./utils"; -import { prepExpectFHERC20BalancesChange } from "./utils"; +import { ConfidentialETH, WETH_Harness } from "../../typechain-types"; +import { expectERC7984BalancesChange, prepExpectERC7984BalancesChange } from "../utils"; +import { Encryptable } from "@cofhe/sdk"; describe("ConfidentialETH", function () { - // We define a fixture to reuse the same setup in every test. - const deployContracts = async () => { - // Deploy wETH + async function deployFixture() { + const [owner, bob, alice] = await ethers.getSigners(); + const wETHFactory = await ethers.getContractFactory("WETH_Harness"); const wETH = (await wETHFactory.deploy()) as WETH_Harness; - await wETH.waitForDeployment(); - // Deploy eETH const eETHFactory = await ethers.getContractFactory("ConfidentialETH"); - const eETH = (await eETHFactory.deploy(wETH)) as ConfidentialETH; - await eETH.waitForDeployment(); - - return { wETH, eETH }; - }; + const eETH = (await eETHFactory.deploy(wETH.target)) as ConfidentialETH; - async function setupFixture() { - const [owner, bob, alice, eve] = await ethers.getSigners(); - const { wETH, eETH } = await deployContracts(); + const bobClient = await hre.cofhe.createClientWithBatteries(bob); + const aliceClient = await hre.cofhe.createClientWithBatteries(alice); - await hre.cofhe.initializeWithHardhatSigner(owner); - - return { owner, bob, alice, eve, wETH, eETH }; + return { owner, bob, alice, bobClient, aliceClient, wETH, eETH }; } + // wETH has 18 decimals → rate = 1e12, confidential decimals = 6 + const conversionRate = 1_000_000_000_000n; + describe("initialization", function () { - it("Should be constructed correctly", async function () { - const { wETH, eETH } = await setupFixture(); - - expect(await eETH.name()).to.equal("Confidential Wrapped ETHER", "ConfidentialETH name correct"); - expect(await eETH.symbol()).to.equal("eETH", "ConfidentialETH symbol correct"); - expect(await eETH.decimals()).to.equal(18, "ConfidentialETH decimals correct"); - expect(await eETH.wETH()).to.equal(wETH.target, "ConfidentialETH underlying wETH correct"); - expect(await eETH.isFherc20()).to.equal(true, "ConfidentialETH isFherc20 correct"); - }); - }); + it("should have correct name and symbol", async function () { + const { eETH } = await deployFixture(); - describe("encrypt balance (ERC20 -> FHERC20)", function () { - it("Should succeed with wETH", async function () { - const { eETH, bob, wETH } = await setupFixture(); + expect(await eETH.name()).to.equal("ERC7984 Confidential Ether"); + expect(await eETH.symbol()).to.equal("eETH"); + }); - expect(await eETH.totalSupply()).to.equal(0, "Total indicated supply init 0"); - expect(await eETH.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); + it("should set deployer as owner", async function () { + const { eETH, owner } = await deployFixture(); - const mintValue = BigInt(10e18); + expect(await eETH.owner()).to.equal(owner.address); + }); - // Mint wETH - await wETH.connect(bob).deposit({ value: mintValue }); + it("should have correct decimals (6)", async function () { + const { eETH } = await deployFixture(); - // Approve eETH - await wETH.connect(bob).approve(eETH.target, mintValue); + expect(await eETH.decimals()).to.equal(6); + }); - const transferValue = BigInt(1e18); + it("should have correct rate (1e12)", async function () { + const { eETH } = await deployFixture(); - // 1st TX, indicated + 5001, true + 1e18 + expect(await eETH.rate()).to.equal(conversionRate); + }); - await prepExpectERC20BalancesChange(wETH, bob.address); - await prepExpectFHERC20BalancesChange(eETH, bob.address); + it("should report correct weth address", async function () { + const { eETH, wETH } = await deployFixture(); - await expect(eETH.connect(bob).encryptWETH(bob, transferValue)).to.emit(eETH, "Transfer"); + expect(await eETH.weth()).to.equal(wETH.target); + }); - await expectERC20BalancesChange(wETH, bob.address, -1n * transferValue); - await expectFHERC20BalancesChange(eETH, bob.address, await ticksToIndicated(eETH, 5001n), transferValue); + it("should have empty contractURI", async function () { + const { eETH } = await deployFixture(); - expect(await eETH.totalSupply()).to.equal( - await ticksToIndicated(eETH, 5001n), - "Total indicated supply increases", - ); - await hre.cofhe.mocks.expectPlaintext(await eETH.encTotalSupply(), transferValue); + expect(await eETH.contractURI()).to.equal(""); }); - it("Should succeed with ETH", async function () { - const { eETH, bob, wETH } = await setupFixture(); + }); - expect(await eETH.totalSupply()).to.equal(0, "Total indicated supply init 0"); - expect(await eETH.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); + describe("shieldNative", function () { + it("should shield native ETH", async function () { + const { bob, eETH } = await deployFixture(); - const mintValue = BigInt(10e18); + const shieldValue = ethers.parseEther("1"); // 1 ETH = 1e18 wei + const expectedConfidential = shieldValue / conversionRate; // 1e6 - // Mint wETH - await wETH.connect(bob).deposit({ value: mintValue }); + await prepExpectERC7984BalancesChange(eETH, bob.address); - // Approve eETH - await wETH.connect(bob).approve(eETH.target, mintValue); + await eETH.connect(bob).shieldNative(bob.address, { value: shieldValue }); - const transferValue = BigInt(1e18); + await expectERC7984BalancesChange(eETH, bob.address, expectedConfidential); + }); - // 1st TX, indicated + 5001, true + 1e18 + it("should refund dust below conversion rate", async function () { + const { bob, eETH } = await deployFixture(); - const bobEthInit = await ethers.provider.getBalance(bob.address); - await prepExpectFHERC20BalancesChange(eETH, bob.address); + const dust = 500n; + const shieldValue = conversionRate + dust; // 1e12 + 500 - const tx = await eETH.connect(bob).encryptETH(bob, { value: transferValue }); + const balBefore = await ethers.provider.getBalance(bob.address); + const tx = await eETH.connect(bob).shieldNative(bob.address, { value: shieldValue }); const receipt = await tx.wait(); - const gasCost = receipt ? receipt.gasUsed * tx.gasPrice : 0n; + const gasCost = receipt!.gasUsed * receipt!.gasPrice; + const balAfter = await ethers.provider.getBalance(bob.address); - const bobEthFinal = await ethers.provider.getBalance(bob.address); - expect(bobEthFinal).to.equal(bobEthInit - transferValue - gasCost, "Bob's ETH balance decreased"); + // Should have spent exactly 1e12 (+ gas), dust refunded + const spent = balBefore - balAfter - gasCost; + expect(spent).to.equal(conversionRate); + }); - await expectFHERC20BalancesChange(eETH, bob.address, await ticksToIndicated(eETH, 5001n), transferValue); + it("should emit ShieldedNative event", async function () { + const { bob, eETH } = await deployFixture(); - expect(await eETH.totalSupply()).to.equal( - await ticksToIndicated(eETH, 5001n), - "Total indicated supply increases", - ); - await hre.cofhe.mocks.expectPlaintext(await eETH.encTotalSupply(), transferValue); + const shieldValue = ethers.parseEther("2"); + await expect(eETH.connect(bob).shieldNative(bob.address, { value: shieldValue })) + .to.emit(eETH, "ShieldedNative") + .withArgs(bob.address, bob.address, shieldValue); }); }); - describe("decrypt & claim", function () { - it("Should succeed", async function () { - const { eETH, bob, wETH } = await setupFixture(); + describe("shieldWrappedNative", function () { + it("should shield via WETH", async function () { + const { bob, wETH, eETH } = await deployFixture(); - expect(await eETH.totalSupply()).to.equal(0, "Total supply init 0"); - expect(await eETH.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); + const wrapAmount = ethers.parseEther("1"); + await wETH.connect(bob).deposit({ value: wrapAmount }); + await wETH.connect(bob).approve(eETH.target, wrapAmount); - const mintValue = BigInt(10e18); - const transferValue = BigInt(1e18); + await prepExpectERC7984BalancesChange(eETH, bob.address); - // Mint and encrypt wETH - await wETH.connect(bob).deposit({ value: mintValue }); - await wETH.connect(bob).approve(eETH.target, mintValue); - await eETH.connect(bob).encryptWETH(bob, mintValue); + await eETH.connect(bob).shieldWrappedNative(bob.address, wrapAmount); - // TX - - await prepExpectERC20BalancesChange(wETH, bob.address); - await prepExpectFHERC20BalancesChange(eETH, bob.address); - - await expect(eETH.connect(bob).decrypt(bob, transferValue)).to.emit(eETH, "Transfer"); - - // -- expect only **FHERC20** balance to change - await expectERC20BalancesChange(wETH, bob.address, 0n); - await expectFHERC20BalancesChange( - eETH, - bob.address, - -1n * (await ticksToIndicated(eETH, 1n)), - -1n * transferValue, - ); - - // Decrypt inserts a claimable amount into the user's claimable set + const expected = wrapAmount / conversionRate; + await expectERC7984BalancesChange(eETH, bob.address, expected); + }); + }); - let claims = await eETH.getUserClaims(bob.address); - expect(claims.length).to.equal(1, "Bob has 1 claimable amount"); + describe("unshield", function () { + it("should burn confidential tokens and create claim", async function () { + const { bob, alice, eETH } = await deployFixture(); - const claimableCtHash = claims[0].ctHash; - let claim = await eETH.getClaim(claimableCtHash); - expect(claim.claimed).to.equal(false, "Claimable amount not claimed"); - await hre.cofhe.mocks.expectPlaintext(claimableCtHash, transferValue); + const shieldValue = ethers.parseEther("1"); + await eETH.connect(bob).shieldNative(bob.address, { value: shieldValue }); - // Hardhat time travel 11 seconds - await hre.network.provider.send("evm_increaseTime", [11]); - await hre.network.provider.send("evm_mine"); + const confidentialAmount = shieldValue / conversionRate; + const unshieldAmount = confidentialAmount / 2n; - // Claim Decrypted + await prepExpectERC7984BalancesChange(eETH, bob.address); - const bobEthInit = await ethers.provider.getBalance(bob.address); - await prepExpectFHERC20BalancesChange(eETH, bob.address); + const tx = await eETH.connect(bob).unshield(bob.address, alice.address, unshieldAmount); + await expect(tx).to.emit(eETH, "Unshielded"); - const tx = await eETH.connect(bob).claimDecrypted(claimableCtHash); - const receipt = await tx.wait(); - const gasCost = receipt ? receipt.gasUsed * tx.gasPrice : 0n; - - // -- expect only **ERC20** balance to change - const bobEthFinal = await ethers.provider.getBalance(bob.address); - expect(bobEthFinal).to.equal(bobEthInit + transferValue - gasCost, "Bob's ETH balance increased"); - await expectFHERC20BalancesChange(eETH, bob.address, 0n, 0n); - - // Claimable amount is now claimed - claim = await eETH.getClaim(claimableCtHash); - expect(claim.claimed).to.equal(true, "Claimable amount claimed"); - - // User has no claimable amounts left - claims = await eETH.getUserClaims(bob.address); - expect(claims.length).to.equal(0, "Bob has no claimable amounts"); - - // Total indicated supply decreases - expect(await eETH.totalSupply()).to.equal( - await ticksToIndicated(eETH, 5000n), - "Total indicated supply decreases", - ); - await hre.cofhe.mocks.expectPlaintext(await eETH.encTotalSupply(), mintValue - transferValue); + await expectERC7984BalancesChange(eETH, bob.address, -unshieldAmount); }); - it("Should claim all decrypted amounts", async function () { - const { eETH, bob, wETH } = await setupFixture(); - - expect(await eETH.totalSupply()).to.equal(0, "Total supply init 0"); - expect(await eETH.encTotalSupply()).to.equal(0, "Total supply not initialized (hash is 0)"); - - const mintValue = BigInt(10e8); - const transferValue = BigInt(1e8); - - // Mint and encrypt wETH - await wETH.connect(bob).deposit({ value: mintValue }); - await wETH.connect(bob).approve(eETH.target, mintValue); - await eETH.connect(bob).encryptWETH(bob.address, mintValue); - - // Multiple decryptions + }); - await eETH.connect(bob).decrypt(bob.address, transferValue); - await eETH.connect(bob).decrypt(bob.address, transferValue); + describe("confidentialTransfer", function () { + it("should transfer between accounts", async function () { + const { bob, alice, eETH, bobClient } = await deployFixture(); - // Hardhat time travel 11 seconds - await hre.network.provider.send("evm_increaseTime", [11]); - await hre.network.provider.send("evm_mine"); + const shieldValue = ethers.parseEther("1"); + await eETH.connect(bob).shieldNative(bob.address, { value: shieldValue }); - const ethBalanceInit = await ethers.provider.getBalance(bob.address); + const transferAmount = 300_000n; + const [enc] = await bobClient.encryptInputs([Encryptable.uint64(transferAmount)]).execute(); - // Claim all decrypted amounts - const tx = await eETH.connect(bob).claimAllDecrypted(); - const receipt = await tx.wait(); - const gasCost = receipt ? receipt.gasUsed * tx.gasPrice : 0n; + await prepExpectERC7984BalancesChange(eETH, bob.address); + await prepExpectERC7984BalancesChange(eETH, alice.address); - const ethBalanceFinal = await ethers.provider.getBalance(bob.address); - expect(ethBalanceFinal).to.equal( - ethBalanceInit + 2n * transferValue - gasCost, - "Bob's ETH balance increased (with gas cost deducted)", - ); + await eETH.connect(bob)["confidentialTransfer(address,(uint256,uint8,uint8,bytes))"](alice.address, enc); - // Expect all decrypted amounts to be claimed - const claims = await eETH.getUserClaims(bob.address); - expect(claims.length).to.equal(0, "Bob has no claimable amounts"); + await expectERC7984BalancesChange(eETH, bob.address, -transferAmount); + await expectERC7984BalancesChange(eETH, alice.address, transferAmount); }); }); }); diff --git a/packages/hardhat/test/FHEContract.test.ts b/packages/hardhat/test/FHEContract.test.ts deleted file mode 100644 index a5eb4b4..0000000 --- a/packages/hardhat/test/FHEContract.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { expect } from "chai"; -import hre, { ethers } from "hardhat"; -import { FHEContract } from "../typechain-types"; -import { cofhejs, Encryptable, FheTypes } from "cofhejs/node"; -import { nullLogState } from "./utils"; - -describe("FHEContract", function () { - // We define a fixture to reuse the same setup in every test. - - let fheContract: FHEContract; - before(async () => { - const [owner] = await ethers.getSigners(); - const fheContractFactory = await ethers.getContractFactory("FHEContract"); - fheContract = (await fheContractFactory.deploy()) as FHEContract; - await fheContract.waitForDeployment(); - }); - - describe("Deployment", function () { - it("Should have the right value on deploy", async function () { - const val = await fheContract.val(); - await hre.cofhe.mocks.expectPlaintext(val, 0n); - }); - - it("Should allow setting and reading a value", async function () { - const [owner] = await ethers.getSigners(); - await hre.cofhe.initializeWithHardhatSigner(owner); - - // Select target value - const targetValue = 10n; - - // Encrypt target value, and pass it to the contract - const encryptedInputResult = await cofhejs.encrypt([Encryptable.uint32(targetValue)] as const); - const [inputValue] = await hre.cofhe.expectResultSuccess(encryptedInputResult); - await fheContract.setVal(inputValue); - - // Fetch the encrypted value ctHash from the contract - const valCtHash = await fheContract.val(); - - // Unseal the value - const unsealResult = await cofhejs.unseal(valCtHash, FheTypes.Uint32); - const valUnsealed = await hre.cofhe.expectResultSuccess(unsealResult); - - // Compare the unsealed value to the target value - expect(valUnsealed).to.equal(targetValue); - }); - - it("Should allow performing operations on the value", async function () { - const [owner] = await ethers.getSigners(); - await hre.cofhe.initializeWithHardhatSigner(owner); - - const targetValue = 10n; - const operandValue = 2n; - - // Encrypt target value, and pass it to the contract - const encryptedInputResult = await cofhejs.encrypt([ - Encryptable.uint32(targetValue), - Encryptable.uint32(operandValue), - ] as const); - const [inputValue, operandInputValue] = await hre.cofhe.expectResultSuccess(encryptedInputResult); - await fheContract.setVal(inputValue); - - // Use the mocks to peek at the value - let valCtHash = await fheContract.val(); - await hre.cofhe.mocks.expectPlaintext(valCtHash, targetValue); - - // Perform addition on the value - await fheContract.valOp(0, operandInputValue); - valCtHash = await fheContract.val(); - await hre.cofhe.mocks.expectPlaintext(valCtHash, targetValue + 2n); - - // Reset the value - await fheContract.setVal(inputValue); - - // Perform subtraction on the value - await fheContract.valOp(1, operandInputValue); - valCtHash = await fheContract.val(); - await hre.cofhe.mocks.expectPlaintext(valCtHash, targetValue - 2n); - - // Reset the value - await fheContract.setVal(inputValue); - - // Perform multiplication on the value - await fheContract.valOp(2, operandInputValue); - valCtHash = await fheContract.val(); - await hre.cofhe.mocks.expectPlaintext(valCtHash, targetValue * 2n); - - // Reset the value - await fheContract.setVal(inputValue); - - // Perform division on the value - await fheContract.valOp(3, operandInputValue); - valCtHash = await fheContract.val(); - await hre.cofhe.mocks.expectPlaintext(valCtHash, targetValue / 2n); - }); - }); -}); diff --git a/packages/hardhat/test/FHERC20.test.ts b/packages/hardhat/test/FHERC20.test.ts deleted file mode 100644 index 2d6aca9..0000000 --- a/packages/hardhat/test/FHERC20.test.ts +++ /dev/null @@ -1,555 +0,0 @@ -import { expect } from "chai"; -import hre, { ethers } from "hardhat"; -import { FHERC20_Harness } from "../typechain-types"; -import { cofhejs, Encryptable } from "cofhejs/node"; -import { - expectFHERC20BalancesChange, - prepExpectFHERC20BalancesChange, - ticksToIndicated, - tick, - generateTransferFromPermit, - nullLogState, -} from "./utils"; -import { ZeroAddress } from "ethers"; - -describe("FHERC20", function () { - // We define a fixture to reuse the same setup in every test. - const deployContracts = async () => { - // Deploy wBTC - const XFHEFactory = await ethers.getContractFactory("FHERC20_Harness"); - const XFHE = (await XFHEFactory.deploy("Unknown FHERC20", "XFHE", 18)) as FHERC20_Harness; - await XFHE.waitForDeployment(); - - return { XFHE }; - }; - - async function setupFixture() { - const [owner, bob, alice, eve] = await ethers.getSigners(); - const { XFHE } = await deployContracts(); - - await hre.cofhe.initializeWithHardhatSigner(owner); - - return { owner, bob, alice, eve, XFHE }; - } - - describe("initialization", function () { - it("Should be constructed correctly", async function () { - const { XFHE } = await setupFixture(); - - expect(await XFHE.name()).to.equal("Unknown FHERC20"); - expect(await XFHE.symbol()).to.equal("XFHE"); - expect(await XFHE.decimals()).to.equal(18); - expect(await XFHE.balanceOfIsIndicator()).to.equal(true); - expect(await XFHE.indicatorTick()).to.equal(10 ** (18 - 4)); - expect(await XFHE.isFherc20()).to.equal(true); - }); - }); - - describe("indicated balances", function () { - it("indicated balances should wrap around", async function () { - const { bob, XFHE } = await setupFixture(); - - const mintValue = ethers.parseEther("10"); - const burnValue = ethers.parseEther("1"); - - // Balance 9999 -> wraparound -> 5001 - await XFHE.setUserIndicatedBalance(bob, 9999); - await XFHE.mint(bob, mintValue); - expect(await XFHE.balanceOf(bob)).to.equal(await ticksToIndicated(XFHE, 5001n)); - - // Balance 1 -> wraparound -> 4999 - await XFHE.setUserIndicatedBalance(bob, 1); - await XFHE.burn(bob, burnValue); - expect(await XFHE.balanceOf(bob)).to.equal(await ticksToIndicated(XFHE, 4999n)); - - // Total supply 9999 -> wraparound -> 5001 - await XFHE.setTotalIndicatedSupply(9999); - await XFHE.mint(bob, mintValue); - expect(await XFHE.totalSupply()).to.equal(await ticksToIndicated(XFHE, 5001n)); - - // Total supply 1 -> wraparound -> 4999 - await XFHE.setTotalIndicatedSupply(1); - await XFHE.burn(bob, burnValue); - expect(await XFHE.totalSupply()).to.equal(await ticksToIndicated(XFHE, 4999n)); - }); - }); - - describe("mint", function () { - it("should mint", async function () { - const { bob, XFHE } = await setupFixture(); - - expect(await XFHE.totalSupply()).to.equal(0); - expect(await ticksToIndicated(XFHE, 0n)).to.equal(0n); - expect(await XFHE.encTotalSupply()).to.equal(0n, "Total supply not initialized (hash is 0)"); - - // 1st TX, indicated + 5001, true + 1e18 - - const value = ethers.parseEther("1"); - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - - await expect(XFHE.mint(bob.address, value)) - .to.emit(XFHE, "Transfer") - .withArgs(ZeroAddress, bob.address, await tick(XFHE)); - - await expectFHERC20BalancesChange(XFHE, bob.address, await ticksToIndicated(XFHE, 5001n), value); - - expect(await XFHE.totalSupply()).to.equal( - await ticksToIndicated(XFHE, 5001n), - "Total indicated supply increases", - ); - await hre.cofhe.mocks.expectPlaintext(await XFHE.encTotalSupply(), value); - - // 2nd TX, indicated + 1, true + 1e18 - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - - await hre.cofhe.mocks.withLogs("XFHE.mint()", async () => { - await expect(XFHE.mint(bob.address, value)) - .to.emit(XFHE, "Transfer") - .withArgs(ZeroAddress, bob.address, await tick(XFHE)); - }); - - await expectFHERC20BalancesChange(XFHE, bob.address, await tick(XFHE), value); - }); - it("Should revert if minting to the zero address", async function () { - const { XFHE } = await setupFixture(); - - await expect(XFHE.mint(ZeroAddress, ethers.parseEther("1"))).to.be.revertedWithCustomError( - XFHE, - "ERC20InvalidReceiver", - ); - }); - }); - - describe("burn", function () { - it("should burn", async function () { - const { XFHE, bob } = await setupFixture(); - - const mintValue = ethers.parseEther("10"); - const burnValue = ethers.parseEther("1"); - - await XFHE.mint(bob, mintValue); - - // Burn TX - - expect(await XFHE.totalSupply()).to.equal( - await ticksToIndicated(XFHE, 5001n), - "Total indicated supply is 0.5001", - ); - await hre.cofhe.mocks.expectPlaintext(await XFHE.encTotalSupply(), mintValue); - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - - await expect(XFHE.burn(bob, burnValue)) - .to.emit(XFHE, "Transfer") - .withArgs(bob.address, ZeroAddress, await tick(XFHE)); - - await expectFHERC20BalancesChange(XFHE, bob.address, -1n * (await ticksToIndicated(XFHE, 1n)), -1n * burnValue); - await hre.cofhe.mocks.expectPlaintext(await XFHE.encTotalSupply(), mintValue - burnValue); - - expect(await XFHE.totalSupply()).to.equal( - await ticksToIndicated(XFHE, 5000n), - "Total indicated supply reduced to .5000", - ); - }); - it("Should revert if burning from the zero address", async function () { - const { XFHE } = await setupFixture(); - const burnValue = ethers.parseEther("1"); - await expect(XFHE.burn(ZeroAddress, burnValue)).to.be.revertedWithCustomError(XFHE, "ERC20InvalidSender"); - }); - }); - - describe("ERC20 legacy functions", function () { - it("Should revert on legacy ERC20.transfer()", async function () { - const { XFHE, bob, alice } = await setupFixture(); - - const transferValue = ethers.parseEther("1"); - await XFHE.mint(bob, transferValue); - await XFHE.mint(alice, transferValue); - - // Transfer - - await expect(XFHE.connect(bob).transfer(alice, transferValue)).to.be.revertedWithCustomError( - XFHE, - "FHERC20IncompatibleFunction", - ); - }); - - it("Should revert on legacy ERC20.transferFrom()", async function () { - const { XFHE, bob, alice } = await setupFixture(); - - const transferValue = ethers.parseEther("1"); - await XFHE.mint(bob, transferValue); - await XFHE.mint(alice, transferValue); - - // TransferFrom - - await expect(XFHE.connect(bob).transferFrom(alice, bob, transferValue)).to.be.revertedWithCustomError( - XFHE, - "FHERC20IncompatibleFunction", - ); - }); - - it("Should revert on legacy ERC20.approve()", async function () { - const { XFHE, bob, alice } = await setupFixture(); - - const approveValue = ethers.parseEther("1"); - - await XFHE.mint(bob, approveValue); - - // Approve - - await expect(XFHE.connect(bob).approve(alice, approveValue)).to.be.revertedWithCustomError( - XFHE, - "FHERC20IncompatibleFunction", - ); - }); - - it("Should revert on legacy ERC20.allowance()", async function () { - const { XFHE, bob, alice } = await setupFixture(); - - const allowanceValue = ethers.parseEther("1"); - await XFHE.mint(bob, allowanceValue); - - // Allowance - - await expect(XFHE.allowance(bob, alice)).to.be.revertedWithCustomError(XFHE, "FHERC20IncompatibleFunction"); - }); - }); - - describe("encTransfer", function () { - it("Should transfer from bob to alice", async function () { - const { XFHE, bob, alice } = await setupFixture(); - - const mintValue = ethers.parseEther("10"); - - await XFHE.mint(bob, mintValue); - await XFHE.mint(alice, mintValue); - - // Initialize bob in cofhejs - await hre.cofhe.expectResultSuccess(await hre.cofhe.initializeWithHardhatSigner(bob)); - - // Encrypt transfer value - const transferValueRaw = ethers.parseEther("1"); - const encTransferResult = await cofhejs.encrypt([Encryptable.uint128(transferValueRaw)] as const); - const [encTransferInput] = await hre.cofhe.expectResultSuccess(encTransferResult); - - // encTransfer - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - await prepExpectFHERC20BalancesChange(XFHE, alice.address); - - await expect( - XFHE.connect(bob)["encTransfer(address,(uint256,uint8,uint8,bytes))"](alice.address, encTransferInput), - ) - .to.emit(XFHE, "Transfer") - .withArgs(bob.address, alice.address, await tick(XFHE)); - - await expectFHERC20BalancesChange( - XFHE, - bob.address, - -1n * (await ticksToIndicated(XFHE, 1n)), - -1n * transferValueRaw, - ); - await expectFHERC20BalancesChange( - XFHE, - alice.address, - 1n * (await ticksToIndicated(XFHE, 1n)), - 1n * transferValueRaw, - ); - }); - - it("Should revert on transfer to 0 address", async function () { - const { XFHE, bob } = await setupFixture(); - - // Encrypt transfer value - const transferValueRaw = ethers.parseEther("1"); - const encTransferResult = await cofhejs.encrypt([Encryptable.uint128(transferValueRaw)] as const); - const [encTransferInput] = await hre.cofhe.expectResultSuccess(encTransferResult); - - // encTransfer (reverts) - await expect( - XFHE.connect(bob)["encTransfer(address,(uint256,uint8,uint8,bytes))"](ZeroAddress, encTransferInput), - ).to.be.revertedWithCustomError(XFHE, "ERC20InvalidReceiver"); - }); - }); - - describe("encTransferFrom", function () { - const setupEncTransferFromFixture = async () => { - const { XFHE, bob, alice, eve } = await setupFixture(); - - const mintValue = ethers.parseEther("10"); - await XFHE.mint(bob, mintValue); - await XFHE.mint(alice, mintValue); - - // Encrypt transfer value - const transferValue = ethers.parseEther("1"); - const encTransferResult = await cofhejs.encrypt([Encryptable.uint128(transferValue)] as const); - const [encTransferInput] = await hre.cofhe.expectResultSuccess(encTransferResult); - - return { XFHE, bob, alice, eve, encTransferInput, transferValue }; - }; - - it("Should transfer from bob to alice", async function () { - const { XFHE, bob, alice, encTransferInput, transferValue } = await setupEncTransferFromFixture(); - - // Generate encTransferFrom permit - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: alice.address, - valueHash: encTransferInput.ctHash, - }); - - // Success - Bob -> Alice - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - await prepExpectFHERC20BalancesChange(XFHE, alice.address); - - await expect(XFHE.connect(alice).encTransferFrom(bob.address, alice.address, encTransferInput, permit)) - .to.emit(XFHE, "Transfer") - .withArgs(bob.address, alice.address, await tick(XFHE)); - - await expectFHERC20BalancesChange( - XFHE, - bob.address, - -1n * (await ticksToIndicated(XFHE, 1n)), - -1n * transferValue, - ); - await expectFHERC20BalancesChange( - XFHE, - alice.address, - 1n * (await ticksToIndicated(XFHE, 1n)), - 1n * transferValue, - ); - }); - - it("Should transfer from bob to alice (eve spender)", async function () { - const { XFHE, bob, alice, eve, encTransferInput, transferValue } = await setupEncTransferFromFixture(); - - // Generate encTransferFrom permit - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: eve.address, - valueHash: encTransferInput.ctHash, - }); - - // Success - Bob -> Alice - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - await prepExpectFHERC20BalancesChange(XFHE, alice.address); - - await expect(XFHE.connect(eve).encTransferFrom(bob.address, alice.address, encTransferInput, permit)) - .to.emit(XFHE, "Transfer") - .withArgs(bob.address, alice.address, await tick(XFHE)); - - await expectFHERC20BalancesChange( - XFHE, - bob.address, - -1n * (await ticksToIndicated(XFHE, 1n)), - -1n * transferValue, - ); - await expectFHERC20BalancesChange( - XFHE, - alice.address, - 1n * (await ticksToIndicated(XFHE, 1n)), - 1n * transferValue, - ); - }); - - it("Should transfer from bob to MockFherc20Vault", async function () { - const { XFHE, bob, alice, eve, encTransferInput, transferValue } = await setupEncTransferFromFixture(); - - const vaultFactory = await ethers.getContractFactory("MockFherc20Vault"); - const Vault = await vaultFactory.deploy(XFHE.target); - await Vault.waitForDeployment(); - const vaultAddress = await Vault.getAddress(); - - // Mint to vault (initialize indicator) - await XFHE.mint(vaultAddress, await ethers.parseEther("1")); - - // Generate encTransferFrom permit - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: vaultAddress, - valueHash: encTransferInput.ctHash, - }); - - // Success - Bob -> Alice - - await prepExpectFHERC20BalancesChange(XFHE, bob.address); - await prepExpectFHERC20BalancesChange(XFHE, vaultAddress); - - await expect(Vault.connect(bob).deposit(encTransferInput, permit)) - .to.emit(XFHE, "Transfer") - .withArgs(bob.address, vaultAddress, await tick(XFHE)); - - await expectFHERC20BalancesChange( - XFHE, - bob.address, - -1n * (await ticksToIndicated(XFHE, 1n)), - -1n * transferValue, - ); - await expectFHERC20BalancesChange( - XFHE, - vaultAddress, - 1n * (await ticksToIndicated(XFHE, 1n)), - 1n * transferValue, - ); - }); - - it("Should revert if invalid receiver", async function () { - const { XFHE, bob, alice, encTransferInput } = await setupEncTransferFromFixture(); - - // Generate encTransferFrom permit - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: alice.address, - valueHash: encTransferInput.ctHash, - }); - - await expect( - XFHE.connect(alice).encTransferFrom(bob.address, ZeroAddress, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "ERC20InvalidReceiver"); - }); - - it("Should revert if permit expired", async function () { - const { XFHE, bob, alice, encTransferInput } = await setupEncTransferFromFixture(); - - // Deadline passed - ERC2612ExpiredSignature - - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: alice.address, - valueHash: encTransferInput.ctHash, - nonce: await XFHE.nonces(bob), - deadline: 0n, - }); - - // Hardhat time travel 1 second - await hre.network.provider.send("evm_increaseTime", [1]); - await hre.network.provider.send("evm_mine"); - - // Get hardhat timestamp - const latestBlock = await ethers.provider.getBlock("latest"); - const timestamp = latestBlock?.timestamp; - expect(timestamp).to.be.greaterThan(permit.deadline); - - // Expect revert - await expect( - XFHE.connect(alice).encTransferFrom(bob.address, alice.address, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "ERC2612ExpiredSignature"); - }); - - it("Should revert on owner mismatch", async function () { - const { XFHE, bob, alice, encTransferInput } = await setupEncTransferFromFixture(); - - // FHERC20EncTransferFromOwnerMismatch bob -> alice - - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: alice.address, - spender: alice.address, - valueHash: encTransferInput.ctHash, - }); - - // Expect revert - - await expect( - XFHE.connect(bob).encTransferFrom(bob.address, alice.address, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "FHERC20EncTransferFromOwnerMismatch"); - }); - - it("Should revert on spender mismatch", async function () { - const { XFHE, bob, alice, eve, encTransferInput } = await setupEncTransferFromFixture(); - - // FHERC20EncTransferFromSpenderMismatch - - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: eve.address, - valueHash: encTransferInput.ctHash, - }); - - // Expect revert - - await expect( - XFHE.connect(alice).encTransferFrom(bob.address, alice.address, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "FHERC20EncTransferFromSpenderMismatch"); - }); - - it("Should revert on value_hash mismatch", async function () { - const { XFHE, bob, alice, encTransferInput } = await setupEncTransferFromFixture(); - - // FHERC20EncTransferFromValueHashMismatch - - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: alice.address, - valueHash: encTransferInput.ctHash + 1n, - }); - - // Expect revert - - await expect( - XFHE.connect(alice).encTransferFrom(bob.address, alice.address, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "FHERC20EncTransferFromValueHashMismatch"); - }); - - it("Should revert on signer not owner", async function () { - const { XFHE, bob, alice, encTransferInput } = await setupEncTransferFromFixture(); - - // Signer != owner - ERC2612InvalidSigner - - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: alice, - owner: bob.address, - spender: alice.address, - valueHash: encTransferInput.ctHash, - }); - - // Expect revert - - await expect( - XFHE.connect(alice).encTransferFrom(bob.address, alice.address, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "ERC2612InvalidSigner"); - }); - - it("Should revert on invalid nonce", async function () { - const { XFHE, bob, alice, encTransferInput } = await setupEncTransferFromFixture(); - - // Invalid nonce - ERC2612InvalidSigner - - const permit = await generateTransferFromPermit({ - token: XFHE, - signer: bob, - owner: bob.address, - spender: alice.address, - valueHash: encTransferInput.ctHash, - nonce: (await XFHE.nonces(bob)) + 1n, - }); - - // Expect revert - - await expect( - XFHE.connect(alice).encTransferFrom(bob.address, alice.address, encTransferInput, permit), - ).to.be.revertedWithCustomError(XFHE, "ERC2612InvalidSigner"); - }); - }); -}); diff --git a/packages/hardhat/test/RedactCore.test.ts b/packages/hardhat/test/RedactCore.test.ts index f42174b..9e29819 100644 --- a/packages/hardhat/test/RedactCore.test.ts +++ b/packages/hardhat/test/RedactCore.test.ts @@ -1,142 +1,160 @@ import { expect } from "chai"; import { ethers } from "hardhat"; -import { ConfidentialETH, ERC20_Harness, WETH_Harness } from "../typechain-types"; -import { RedactCore } from "../typechain-types"; +import { + RedactCore, + ConfidentialETH, + ConfidentialERC20, + WETH_Harness, + ERC20_Harness, +} from "../../typechain-types"; +import { ZeroAddress } from "ethers"; describe("RedactCore", function () { - // We define a fixture to reuse the same setup in every test. - const deployContracts = async () => { - // Deploy WETH + async function deployFixture() { + const [owner, alice] = await ethers.getSigners(); + const wETHFactory = await ethers.getContractFactory("WETH_Harness"); const wETH = (await wETHFactory.deploy()) as WETH_Harness; - await wETH.waitForDeployment(); - // Deploy eETH const eETHFactory = await ethers.getContractFactory("ConfidentialETH"); const eETH = (await eETHFactory.deploy(wETH.target)) as ConfidentialETH; - await eETH.waitForDeployment(); - // Deploy USDC - const usdcFactory = await ethers.getContractFactory("ERC20_Harness"); - const usdc = (await usdcFactory.deploy("USD Coin", "USDC", 18)) as ERC20_Harness; - await usdc.waitForDeployment(); + const coreFactory = await ethers.getContractFactory("RedactCore"); + const core = (await coreFactory.deploy(wETH.target, eETH.target)) as RedactCore; - // Deploy wBTC - const wbtcFactory = await ethers.getContractFactory("ERC20_Harness"); - const wBTC = (await wbtcFactory.deploy("Wrapped Bitcoin", "wBTC", 18)) as ERC20_Harness; - await wBTC.waitForDeployment(); + const usdcFactory = await ethers.getContractFactory("ERC20_Harness"); + const usdc = (await usdcFactory.deploy("USD Coin", "USDC", 6)) as ERC20_Harness; + const wbtc = (await usdcFactory.deploy("Wrapped BTC", "WBTC", 8)) as ERC20_Harness; - // Deploy RedactCore - const redactCoreFactory = await ethers.getContractFactory("RedactCore"); - const redactCore = (await redactCoreFactory.deploy(wETH.target, eETH.target)) as RedactCore; - await redactCore.waitForDeployment(); + return { owner, alice, wETH, eETH, core, usdc, wbtc }; + } - return { wETH, eETH, usdc, wBTC, redactCore }; - }; + describe("constructor", function () { + it("should store wETH and eETH", async function () { + const { core, wETH, eETH } = await deployFixture(); - async function setupFixture() { - const [owner, bob, alice] = await ethers.getSigners(); - const contracts = await deployContracts(); + expect(await core.wETH()).to.equal(wETH.target); + expect(await core.eETH()).to.equal(eETH.target); + }); - await contracts.redactCore.updateStablecoin(contracts.usdc.target, true); + it("should register wETH → eETH in the map", async function () { + const { core, wETH, eETH } = await deployFixture(); - return { owner, bob, alice, ...contracts }; - } + expect(await core.getConfidentialERC20(wETH.target as string)).to.equal(eETH.target); + }); - describe("initialization", function () { - it("Should be constructed correctly", async function () { - const { redactCore, wETH, eETH } = await setupFixture(); + it("should set deployer as owner", async function () { + const { core, owner } = await deployFixture(); - expect(await redactCore.wETH()).to.equal(wETH.target); - expect(await redactCore.eETH()).to.equal(eETH.target); - expect(await redactCore.getIsWETH(wETH.target)).to.equal(true); + expect(await core.owner()).to.equal(owner.address); }); - it("Should be initialized with stablecoins", async function () { - const { redactCore, usdc } = await setupFixture(); + it("should revert with zero-address wETH", async function () { + const { eETH } = await deployFixture(); + const factory = await ethers.getContractFactory("RedactCore"); - expect(await redactCore.getIsStablecoin(usdc.target)).to.equal(true); + await expect(factory.deploy(ZeroAddress, eETH.target)).to.be.revertedWithCustomError( + factory, + "InvalidWETH", + ); }); - it("Should be able to update stablecoins", async function () { - const { redactCore, usdc } = await setupFixture(); - await redactCore.updateStablecoin(usdc.target, false); - expect(await redactCore.getIsStablecoin(usdc.target)).to.equal(false); + it("should revert with zero-address eETH", async function () { + const { wETH } = await deployFixture(); + const factory = await ethers.getContractFactory("RedactCore"); + + await expect(factory.deploy(wETH.target, ZeroAddress)).to.be.revertedWithCustomError( + factory, + "InvalideETH", + ); }); }); - describe("deploy FHERC20", function () { - it("Should be able to deploy FHERC20", async function () { - const { redactCore, wBTC } = await setupFixture(); + describe("deployConfidentialERC20", function () { + it("should deploy a ConfidentialERC20 wrapper", async function () { + const { core, usdc } = await deployFixture(); + + const tx = await core.deployConfidentialERC20(usdc.target); + const addr = await core.getConfidentialERC20(usdc.target as string); - await expect(redactCore.deployFherc20(wBTC.target)).to.emit(redactCore, "Fherc20Deployed"); + expect(addr).to.not.equal(ZeroAddress); + await expect(tx).to.emit(core, "ConfidentialERC20Deployed").withArgs(usdc.target, addr); + }); - const ewBTCAddress = await redactCore.getFherc20(wBTC.target); - expect(ewBTCAddress).to.not.equal(ethers.ZeroAddress); + it("should generate correct name and symbol", async function () { + const { core, usdc } = await deployFixture(); - const deployedFherc20s = await redactCore.getDeployedFherc20s(); + await core.deployConfidentialERC20(usdc.target); + const addr = await core.getConfidentialERC20(usdc.target as string); + const eUSDC = (await ethers.getContractAt("ConfidentialERC20", addr)) as ConfidentialERC20; - // eETH is already deployed, so we expect 2 after ewBTC deployment - expect(deployedFherc20s.length).to.equal(2); - expect(deployedFherc20s[1].erc20).to.equal(wBTC.target); - expect(deployedFherc20s[1].fherc20).to.equal(ewBTCAddress); + expect(await eUSDC.name()).to.equal("ERC7984 Confidential USD Coin"); + expect(await eUSDC.symbol()).to.equal("eUSDC"); }); - it("Should revert on already deployed FHERC20", async function () { - const { redactCore, wBTC } = await setupFixture(); + it("should set RedactCore as owner of the wrapper", async function () { + const { core, usdc } = await deployFixture(); - await redactCore.deployFherc20(wBTC.target); + await core.deployConfidentialERC20(usdc.target); + const addr = await core.getConfidentialERC20(usdc.target as string); + const eUSDC = (await ethers.getContractAt("ConfidentialERC20", addr)) as ConfidentialERC20; - await expect(redactCore.deployFherc20(wBTC.target)).to.be.revertedWithCustomError( - redactCore, - "Invalid_AlreadyDeployed", - ); + expect(await eUSDC.owner()).to.equal(core.target); }); - it("Should revert on stablecoin", async function () { - const { redactCore, usdc } = await setupFixture(); - await expect(redactCore.deployFherc20(usdc.target)).to.be.revertedWithCustomError( - redactCore, - "Invalid_Stablecoin", + it("should revert if wrapper already deployed", async function () { + const { core, usdc } = await deployFixture(); + + await core.deployConfidentialERC20(usdc.target); + await expect(core.deployConfidentialERC20(usdc.target)).to.be.revertedWithCustomError( + core, + "AlreadyDeployed", ); }); - it("Should revert on wETH (wETH is already deployed)", async function () { - const { redactCore, wETH } = await setupFixture(); + it("should revert if token is WETH", async function () { + const { core, wETH } = await deployFixture(); - await expect(redactCore.deployFherc20(wETH.target)).to.be.revertedWithCustomError( - redactCore, - "Invalid_AlreadyDeployed", + await expect(core.deployConfidentialERC20(wETH.target)).to.be.revertedWithCustomError( + core, + "InvalidWETH", ); }); - }); - describe("update FHERC20 symbol", function () { - it("Should be able to update FHERC20 symbol", async function () { - const { owner, redactCore, wBTC } = await setupFixture(); + it("should deploy multiple wrappers", async function () { + const { core, usdc, wbtc } = await deployFixture(); + + await core.deployConfidentialERC20(usdc.target); + await core.deployConfidentialERC20(wbtc.target); - await redactCore.deployFherc20(wBTC.target); - const eBTCAddress = await redactCore.getFherc20(wBTC.target); - const eBTC = await ethers.getContractAt("ConfidentialERC20", eBTCAddress); + const mapped = await core.getDeployedConfidentialERC20s(); + // wETH→eETH + USDC + WBTC = 3 + expect(mapped.length).to.equal(3); + }); - expect(await eBTC.symbol()).to.equal("ewBTC"); + it("should be callable by anyone (permissionless)", async function () { + const { core, usdc, alice } = await deployFixture(); - // Change symbol - await redactCore.connect(owner).updateFherc20Symbol(eBTC, "eBTC"); - expect(await eBTC.symbol()).to.equal("eBTC"); + await expect(core.connect(alice).deployConfidentialERC20(usdc.target)).to.not.be.reverted; }); + }); - it("Should revert on non-owner", async function () { - const { bob, redactCore, wBTC } = await setupFixture(); + describe("getConfidentialERC20", function () { + it("should return address(0) for unregistered tokens", async function () { + const { core, usdc } = await deployFixture(); - await redactCore.deployFherc20(wBTC.target); - const eBTCAddress = await redactCore.getFherc20(wBTC.target); - const eBTC = await ethers.getContractAt("ConfidentialERC20", eBTCAddress); + expect(await core.getConfidentialERC20(usdc.target as string)).to.equal(ZeroAddress); + }); + }); - await expect(redactCore.connect(bob).updateFherc20Symbol(eBTC, "eBTC")).to.be.revertedWithCustomError( - redactCore, - "OwnableUnauthorizedAccount", - ); + describe("getDeployedConfidentialERC20s", function () { + it("should include wETH→eETH at initialization", async function () { + const { core, wETH, eETH } = await deployFixture(); + + const mapped = await core.getDeployedConfidentialERC20s(); + expect(mapped.length).to.equal(1); + expect(mapped[0].erc20).to.equal(wETH.target); + expect(mapped[0].confidentialERC20).to.equal(eETH.target); }); }); }); diff --git a/packages/hardhat/test/utils.ts b/packages/hardhat/test/utils.ts index 4012b52..5ef4036 100644 --- a/packages/hardhat/test/utils.ts +++ b/packages/hardhat/test/utils.ts @@ -1,66 +1,39 @@ import { expect } from "chai"; -import { ERC20, FHERC20, IFHERC20 } from "../typechain-types"; -import hre, { ethers } from "hardhat"; -import { HardhatEthersSigner } from "@nomicfoundation/hardhat-ethers/signers"; -import { TypedDataDomain } from "ethers"; +import { ERC20 } from "../typechain-types"; +import hre from "hardhat"; -// LOGS +// ERC7984 BALANCES -export const logState = (state: string) => { - console.log("Encrypt State - ", state); -}; - -export const nullLogState = () => null; - -// TICKS - -export const ticksToIndicated = async (token: FHERC20, ticks: bigint): Promise => { - const tick = await token.indicatorTick(); - return ticks * BigInt(tick); -}; - -export const tick = async (token: FHERC20): Promise => { - return token.indicatorTick(); -}; +const erc7984EncBalances = new Map(); -// BALANCES - -const indicatedBalances = new Map(); -const encBalances = new Map(); - -export const prepExpectFHERC20BalancesChange = async (token: FHERC20, account: string) => { - indicatedBalances.set(account, await token.balanceOf(account)); - const encBalanceHash = await token.encBalanceOf(account); +export const prepExpectERC7984BalancesChange = async ( + token: { confidentialBalanceOf: (account: string) => Promise }, + account: string, +) => { + const encBalanceHash = await token.confidentialBalanceOf(account); const encBalance = await hre.cofhe.mocks.getPlaintext(encBalanceHash); - encBalances.set(account, encBalance); + erc7984EncBalances.set(account, encBalance); }; -export const expectFHERC20BalancesChange = async ( - token: FHERC20, +export const expectERC7984BalancesChange = async ( + token: { confidentialBalanceOf: (account: string) => Promise; symbol: () => Promise }, account: string, - expectedIndicatedChange: bigint, expectedEncChange: bigint, ) => { const symbol = await token.symbol(); - const currIndicated = await token.balanceOf(account); - const prevIndicated = indicatedBalances.get(account)!; - const indicatedChange = currIndicated - prevIndicated; - expect(indicatedChange).to.equal( - expectedIndicatedChange, - `${symbol} (FHERC20) indicated balance change for ${account} is incorrect. Expected: ${expectedIndicatedChange}, received: ${indicatedChange}`, - ); - - const currEncBalanceHash = await token.encBalanceOf(account); + const currEncBalanceHash = await token.confidentialBalanceOf(account); const currEncBalance = await hre.cofhe.mocks.getPlaintext(currEncBalanceHash); - const prevEncBalance = encBalances.get(account)!; + const prevEncBalance = erc7984EncBalances.get(account)!; const encChange = currEncBalance - prevEncBalance; expect(encChange).to.equal( expectedEncChange, - `${symbol} (FHERC20) encrypted balance change for ${account} is incorrect. Expected: ${expectedEncChange}, received: ${encChange}`, + `${symbol} (ERC7984) encrypted balance change for ${account} is incorrect. Expected: ${expectedEncChange}, received: ${encChange}`, ); }; +// ERC20 BALANCES + const erc20Balances = new Map(); export const prepExpectERC20BalancesChange = async (token: ERC20, account: string) => { @@ -78,70 +51,3 @@ export const expectERC20BalancesChange = async (token: ERC20, account: string, e `${symbol} (ERC20) balance change for ${account} is incorrect. Expected: ${expectedChange}, received: ${delta}`, ); }; - -// EncTransferFromPermit -type GeneratePermitOptions = { - signer: HardhatEthersSigner; - token: FHERC20; - owner: string; - spender: string; - valueHash: bigint; - nonce?: bigint; - deadline?: bigint; -}; - -export const getNowTimestamp = () => { - return BigInt(Date.now()) / 1000n; -}; - -export const generateTransferFromPermit = async ( - options: GeneratePermitOptions, -): Promise => { - let { token, signer, owner, spender, valueHash, nonce, deadline } = options; - - const { name, version, chainId, verifyingContract } = await token.eip712Domain(); - - // If nonce is not provided, get it from the token - if (nonce == null) nonce = await token.nonces(owner); - - // If deadline is not provided, set it to 24 hours from now - if (deadline == null) deadline = getNowTimestamp() + BigInt(24 * 60 * 60); - - const domain: TypedDataDomain = { - name, - version, - chainId, - verifyingContract, - }; - - const types = { - Permit: [ - { name: "owner", type: "address" }, - { name: "spender", type: "address" }, - { name: "value_hash", type: "uint256" }, - { name: "nonce", type: "uint256" }, - { name: "deadline", type: "uint256" }, - ], - }; - - const message = { - owner, - spender, - value_hash: valueHash, - nonce: nonce, - deadline: deadline, - }; - - const signature = await signer.signTypedData(domain, types, message); - const { v, r, s } = ethers.Signature.from(signature); - - return { - owner, - spender, - value_hash: valueHash, - deadline: deadline, - v, - r, - s, - }; -};