From d58a16e7d947ca5fd5b7943da6aa99ebc4b2e502 Mon Sep 17 00:00:00 2001 From: Corban Brook Date: Wed, 3 Jul 2024 06:01:18 -0400 Subject: [PATCH] Ethers v6 (#184) * Updating deps for ethers v6 * Migrating to ethers-v6 wip * Fixing remaing type errors * hardhat tests running now * wip 2 * wip 3 * Updating ethers v6.13.0 and github actions * expect nonces to return bigints * Gas estimation should be bigint * Fixing test wallet deployment * Fixing bigint in LibBytes test * Fixing bigint in MainModule test * Fixing sig hash of interface functions * Fixing remaining tests * Updating github actions for node 20 * Fixing bench tests * Revert "Fixing bench tests" This reverts commit 436be02870f10c793f74cfb336f70c18a12ca7ae. * SequenceWallet deploy should just return ContractTransactionResponse --- .github/workflows/ci.yml | 14 +- hardhat.config.ts | 12 +- networks/hardhat.json | 22 + package.json | 19 +- pnpm-lock.yaml | 274 ++++--- test/ChainedSignatures.spec.ts | 50 +- test/ERC165.spec.ts | 10 +- test/Factory.spec.ts | 45 +- test/GasEstimation.spec.ts | 325 +++++--- test/GuestModule.spec.ts | 52 +- test/LibBytes.spec.ts | 93 ++- test/LibString.spec.ts | 89 ++- test/MainModule.bench.ts | 84 ++- test/MainModule.spec.ts | 1288 +++++++++++++++++++------------- test/MerkleSignatures.spec.ts | 5 +- test/MultiCallUtils.spec.ts | 289 +++---- test/utils/contracts.ts | 80 +- test/utils/imposter.ts | 23 +- test/utils/index.ts | 120 +-- test/utils/sequence.ts | 252 +++---- test/utils/wallet.ts | 188 +++-- tsconfig.json | 13 +- utils/config-loader.ts | 2 +- utils/deploy-contracts.ts | 185 +++-- utils/workers/bench-worker.ts | 44 +- 25 files changed, 2055 insertions(+), 1523 deletions(-) create mode 100644 networks/hardhat.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c1cae3f..820aa13c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: name: Benchmark runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/install-dependencies - run: pnpm build - run: pnpm benchmark @@ -16,7 +16,7 @@ jobs: name: Typescript lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ./.github/actions/install-dependencies - run: pnpm lint:ts @@ -53,12 +53,12 @@ jobs: name: Huff tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - name: Install Huff - uses: huff-language/huff-toolchain@v2 + uses: huff-language/huff-toolchain@v3 with: version: nightly @@ -79,7 +79,7 @@ jobs: version: nightly - name: Install Huff - uses: huff-language/huff-toolchain@v2 + uses: huff-language/huff-toolchain@v3 with: version: nightly @@ -90,7 +90,7 @@ jobs: name: Foundry tests (long arrays) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive @@ -100,7 +100,7 @@ jobs: version: nightly - name: Install Huff - uses: huff-language/huff-toolchain@v2 + uses: huff-language/huff-toolchain@v3 with: version: nightly diff --git a/hardhat.config.ts b/hardhat.config.ts index 80ed4a30..15254e77 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,11 +1,11 @@ import { HardhatUserConfig, task } from 'hardhat/config' import { networkConfig } from './utils/config-loader' +import '@nomicfoundation/hardhat-ethers' +import '@nomicfoundation/hardhat-verify' import '@nomiclabs/hardhat-truffle5' -import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-web3' -import '@nomiclabs/hardhat-etherscan' -import "@tenderly/hardhat-tenderly" +import '@tenderly/hardhat-tenderly' import 'hardhat-gas-reporter' import 'solidity-coverage' @@ -23,7 +23,7 @@ const config: HardhatUserConfig = { settings: { optimizer: { enabled: true, - runs: 500000, + runs: 500000 } } }, @@ -64,8 +64,8 @@ const config: HardhatUserConfig = { showTimeSpent: true }, tenderly: { - project: "horizon/sequence-dev-1", - username: "Agusx1211-horizon", + project: 'horizon/sequence-dev-1', + username: 'Agusx1211-horizon' } } diff --git a/networks/hardhat.json b/networks/hardhat.json new file mode 100644 index 00000000..67df434c --- /dev/null +++ b/networks/hardhat.json @@ -0,0 +1,22 @@ +[ + { + "contractName": "WalletFactory", + "address": "0xFaA5c0b14d1bED5C888Ca655B9a8A5911F78eF4A" + }, + { + "contractName": "MainModule", + "address": "0xE1846F966D116B86d65481Fe81886219D698b60D" + }, + { + "contractName": "MainModuleUpgradable", + "address": "0xB39E1ed61caC9E8eE8CDD3218765cF6a7fE390db" + }, + { + "contractName": "GuestModule", + "address": "0x28Ec0675C7b40ed78EBcBBbDF39e5652c0787B18" + }, + { + "contractName": "SequenceUtils", + "address": "0x4817Fe9f2352E88991A7c84E4385708Ccff05704" + } +] \ No newline at end of file diff --git a/package.json b/package.json index 6bba33e8..422e4173 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,11 @@ "build": "pnpm compile && pnpm adapter", "compile": "hardhat --max-memory 4096 compile", "clean": "rimraf artifacts && rimraf cache", + "typecheck": "tsc --noEmit", "test": "hardhat test", "benchmark": "BENCHMARK=true pnpm test", "coverage": "COVERAGE=true NET_ID=1 hardhat coverage", - "deploy": "hardhat run utils/deploy-contracts.ts --network", + "deploy": "hardhat run utils/deploy-contracts.ts --network hardhat", "verify": "hardhat verify --network", "release": "pnpm publish src", "lint": "pnpm lint:ts && pnpm lint:sol", @@ -20,7 +21,7 @@ "lint:ts": "eslint -c .eslintrc.js \"./**/*.ts\"", "lint:ts:fix": "eslint -c .eslintrc.js --fix \"./**/*.ts\"", "format": "prettier --write ./**/*.ts", - "adapter": "typechain --target ethers-v5 --out-dir gen/typechain \"./artifacts/contracts/**/*[^dbg].json\"", + "adapter": "typechain --target ethers-v6 --out-dir gen/typechain \"./artifacts/contracts/**/*[^dbg].json\"", "prepare": "husky" }, "types": "gen/typechain/index.ts", @@ -36,9 +37,9 @@ } }, "devDependencies": { - "@nomiclabs/hardhat-ethers": "^2.0.6", - "@nomiclabs/hardhat-etherscan": "^3.1.7", - "@nomiclabs/hardhat-truffle5": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-verify": "^2.0.4", + "@nomiclabs/hardhat-truffle5": "^2.0.7", "@nomiclabs/hardhat-web3": "^2.0.0", "@tenderly/hardhat-tenderly": "^1.0.11", "@types/chai-as-promised": "^7.1.0", @@ -61,7 +62,7 @@ "ethereum-waffle": "^3.4.4", "ganache-cli": "6.12.2", "hardhat": "^2.20.1", - "hardhat-gas-reporter": "1.0.4", + "hardhat-gas-reporter": "1.0.10", "husky": "^9.0.11", "ora": "^5.4.1", "rimraf": "^3.0.2", @@ -70,7 +71,7 @@ "solidity-coverage": "0.8.3", "threads": "^1.7.0", "ts-node": "^10.9.1", - "typechain": "^8.1.0", + "typechain": "^8.3.2", "typescript": "^4.7.4", "yesno": "^0.3.1" }, @@ -84,8 +85,8 @@ "extra": "" }, "dependencies": { - "@typechain/ethers-v5": "^7.0.1", - "ethers": "^5.7.2", + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.13.0", "keccak256": "^1.0.6" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3ebc561..3c4567aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,25 +8,25 @@ importers: .: dependencies: - '@typechain/ethers-v5': - specifier: ^7.0.1 - version: 7.0.1(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.1.0(typescript@4.7.4))(typescript@4.7.4) + '@typechain/ethers-v6': + specifier: ^0.5.1 + version: 0.5.1(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@4.7.4))(typescript@4.7.4) ethers: - specifier: ^5.7.2 - version: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + specifier: ^6.13.0 + version: 6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) keccak256: specifier: ^1.0.6 version: 1.0.6 devDependencies: - '@nomiclabs/hardhat-ethers': - specifier: ^2.0.6 - version: 2.0.6(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-etherscan': - specifier: ^3.1.7 - version: 3.1.7(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-ethers': + specifier: ^3.0.5 + version: 3.0.6(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-verify': + specifier: ^2.0.4 + version: 2.0.8(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10)) '@nomiclabs/hardhat-truffle5': - specifier: ^2.0.0 - version: 2.0.0(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + specifier: ^2.0.7 + version: 2.0.7(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@nomiclabs/hardhat-web3': specifier: ^2.0.0 version: 2.0.0(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) @@ -94,8 +94,8 @@ importers: specifier: ^2.20.1 version: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) hardhat-gas-reporter: - specifier: 1.0.4 - version: 1.0.4(bufferutil@4.0.8)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + specifier: 1.0.10 + version: 1.0.10(bufferutil@4.0.8)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) husky: specifier: ^9.0.11 version: 9.0.11 @@ -121,8 +121,8 @@ importers: specifier: ^10.9.1 version: 10.9.1(@types/node@20.11.20)(typescript@4.7.4) typechain: - specifier: ^8.1.0 - version: 8.1.0(typescript@4.7.4) + specifier: ^8.3.2 + version: 8.3.2(typescript@4.7.4) typescript: specifier: ^4.7.4 version: 4.7.4 @@ -136,6 +136,9 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@babel/code-frame@7.12.11': resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} @@ -337,12 +340,19 @@ packages: resolution: {integrity: sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==} engines: {node: '>=12.0.0'} + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + '@noble/curves@1.3.0': resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} '@noble/hashes@1.2.0': resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + '@noble/hashes@1.3.3': resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} engines: {node: '>= 16'} @@ -424,6 +434,17 @@ packages: resolution: {integrity: sha512-gsA4IhmtWHI4BofKy3kio9W+dqZQs5Ji5mLjLYxHCkat+JQBUt5szjRKra2F9nGDJ2XcI/wWb0YWUFNgln4zRQ==} engines: {node: '>=18'} + '@nomicfoundation/hardhat-ethers@3.0.6': + resolution: {integrity: sha512-/xzkFQAaHQhmIAYOQmvHBPwL+NkwLzT9gRZBsgWUYeV+E6pzXsBQsHfRYbAZ3XEYare+T7S+5Tg/1KDJgepSkA==} + peerDependencies: + ethers: ^6.1.0 + hardhat: ^2.0.0 + + '@nomicfoundation/hardhat-verify@2.0.8': + resolution: {integrity: sha512-x/OYya7A2Kcz+3W/J78dyDHxr0ezU23DKTrRKfy5wDPCnePqnr79vm8EXqX3gYps6IjPBYyGPZ9K6E5BnrWx5Q==} + peerDependencies: + hardhat: ^2.0.4 + '@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1': resolution: {integrity: sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==} engines: {node: '>= 10'} @@ -488,23 +509,11 @@ packages: resolution: {integrity: sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==} engines: {node: '>= 12'} - '@nomiclabs/hardhat-ethers@2.0.6': - resolution: {integrity: sha512-q2Cjp20IB48rEn2NPjR1qxsIQBvFVYW9rFRCFq+bC4RUrn1Ljz3g4wM8uSlgIBZYBi2JMXxmOzFqHraczxq4Ng==} - peerDependencies: - ethers: ^5.0.0 - hardhat: ^2.0.0 - - '@nomiclabs/hardhat-etherscan@3.1.7': - resolution: {integrity: sha512-tZ3TvSgpvsQ6B6OGmo1/Au6u8BrAkvs1mIC/eURA3xgIfznUZBhmpne8hv7BXUzw9xNL3fXdpOYgOQlVMTcoHQ==} - deprecated: The @nomiclabs/hardhat-etherscan package is deprecated, please use @nomicfoundation/hardhat-verify instead - peerDependencies: - hardhat: ^2.0.4 - - '@nomiclabs/hardhat-truffle5@2.0.0': - resolution: {integrity: sha512-JLjyfeXTiSqa0oLHcN3i8kD4coJa4Gx6uAXybGv3aBiliEbHddLSzmBWx0EU69a1/Ad5YDdGSqVnjB8mkUCr/g==} + '@nomiclabs/hardhat-truffle5@2.0.7': + resolution: {integrity: sha512-Pw8451IUZp1bTp0QqCHCYfCHs66sCnyxPcaorapu9mfOV9xnZsVaFdtutnhNEiXdiZwbed7LFKpRsde4BjFwig==} peerDependencies: '@nomiclabs/hardhat-web3': ^2.0.0 - hardhat: ^2.0.0 + hardhat: ^2.6.4 web3: ^1.0.0-beta.36 '@nomiclabs/hardhat-web3@2.0.0': @@ -611,37 +620,46 @@ packages: '@truffle/abi-utils@1.0.3': resolution: {integrity: sha512-AWhs01HCShaVKjml7Z4AbVREr/u4oiWxCcoR7Cktm0mEvtT04pvnxW5xB/cI4znRkrbPdFQlFt67kgrAjesYkw==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/blockchain-utils@0.1.9': resolution: {integrity: sha512-RHfumgbIVo68Rv9ofDYfynjnYZIfP/f1vZy4RoqkfYAO+fqfc58PDRzB1WAGq2U6GPuOnipOJxQhnqNnffORZg==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/codec@0.17.3': resolution: {integrity: sha512-Ko/+dsnntNyrJa57jUD9u4qx9nQby+H4GsUO6yjiCPSX0TQnEHK08XWqBSg0WdmCH2+h0y1nr2CXSx8gbZapxg==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/compile-common@0.9.8': resolution: {integrity: sha512-DTpiyo32t/YhLI1spn84D3MHYHrnoVqO+Gp7ZHrYNwDs86mAxtNiH5lsVzSb8cPgiqlvNsRCU9nm9R0YmKMTBQ==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/contract-schema@3.4.16': resolution: {integrity: sha512-g0WNYR/J327DqtJPI70ubS19K1Fth/1wxt2jFqLsPmz5cGZVjCwuhiie+LfBde4/Mc9QR8G+L3wtmT5cyoBxAg==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/debug-utils@6.0.57': resolution: {integrity: sha512-Q6oI7zLaeNLB69ixjwZk2UZEWBY6b2OD1sjLMGDKBGR7GaHYiw96GLR2PFgPH1uwEeLmV4N78LYaQCrDsHbNeA==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/error@0.1.1': resolution: {integrity: sha512-sE7c9IHIGdbK4YayH4BC8i8qMjoAOeg6nUXUDZZp8wlU21/EMpaG+CLx+KqcIPyR+GSWIW3Dm0PXkr2nlggFDA==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/error@0.2.2': resolution: {integrity: sha512-TqbzJ0O8DHh34cu8gDujnYl4dUl6o2DE4PR6iokbybvnIm/L2xl6+Gv1VC+YJS45xfH83Yo3/Zyg/9Oq8/xZWg==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@truffle/interface-adapter@0.5.37': resolution: {integrity: sha512-lPH9MDgU+7sNDlJSClwyOwPCfuOimqsCx0HfGkznL3mcFRymc1pukAR1k17zn7ErHqBwJjiKAZ6Ri72KkS+IWw==} engines: {node: ^16.20 || ^18.16 || >=20} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. '@trufflesuite/chromafi@3.0.0': resolution: {integrity: sha512-oqWcOqn8nT1bwlPPfidfzS55vqcIDdpfzo3HbU9EnUmcSTX+I8z0UyUFI3tZQjByVJulbzxHxUGS3ZJPwK/GPQ==} @@ -664,15 +682,12 @@ packages: ethers: ^5.0.0 typechain: ^3.0.0 - '@typechain/ethers-v5@7.0.1': - resolution: {integrity: sha512-mXEJ7LG0pOYO+MRPkHtbf30Ey9X2KAsU0wkeoVvjQIn7iAY6tB3k3s+82bbmJAUMyENbQ04RDOZit36CgSG6Gg==} + '@typechain/ethers-v6@0.5.1': + resolution: {integrity: sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==} peerDependencies: - '@ethersproject/abi': ^5.0.0 - '@ethersproject/bytes': ^5.0.0 - '@ethersproject/providers': ^5.0.0 - ethers: ^5.1.3 - typechain: ^5.0.0 - typescript: '>=4.0.0' + ethers: 6.x + typechain: ^8.3.2 + typescript: '>=4.7.0' '@types/bignumber.js@5.0.0': resolution: {integrity: sha512-0DH7aPGCClywOFaxxjE6UwpN2kQYe9LwuDQMv+zYA97j5GkOMo8e66LYT+a8JYU7jfmUFRZLa9KycxHDsKXJCA==} @@ -744,6 +759,9 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@18.15.13': + resolution: {integrity: sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==} + '@types/node@20.11.20': resolution: {integrity: sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==} @@ -879,6 +897,9 @@ packages: aes-js@3.1.2: resolution: {integrity: sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==} + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -1007,6 +1028,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array-uniq@1.0.3: + resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==} + engines: {node: '>=0.10.0'} + array-unique@0.3.2: resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} engines: {node: '>=0.10.0'} @@ -2366,6 +2391,10 @@ packages: ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} + ethers@6.13.0: + resolution: {integrity: sha512-+yyQQQWEntY5UVbCv++guA14RRVFm1rSnO1GoLFdrK7/XRWMoktNgyG9UjwxrQqGBfGyFKknNZ81YpUS2emCgg==} + engines: {node: '>=14.0.0'} + ethjs-unit@0.1.6: resolution: {integrity: sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=} engines: {node: '>=6.5.0', npm: '>=3'} @@ -2683,6 +2712,7 @@ packages: glob@7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} + deprecated: Glob versions prior to v9 are no longer supported glob@7.2.0: resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} @@ -2761,8 +2791,8 @@ packages: engines: {node: '>=6'} deprecated: this library is no longer supported - hardhat-gas-reporter@1.0.4: - resolution: {integrity: sha512-G376zKh81G3K9WtDA+SoTLWsoygikH++tD1E7llx+X7J+GbIqfwhDKKgvJjcnEesMrtR9UqQHK02lJuXY1RTxw==} + hardhat-gas-reporter@1.0.10: + resolution: {integrity: sha512-02N4+So/fZrzJ88ci54GqwVA3Zrf0C9duuTyGt0CFRIh/CdNwbnTgkXkRfojOMLBQ+6t+lBIkgbsOtqMvNwikA==} peerDependencies: hardhat: ^2.0.2 @@ -3439,6 +3469,12 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -4993,6 +5029,9 @@ packages: tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + tsort@0.0.1: resolution: {integrity: sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y=} @@ -5052,8 +5091,8 @@ packages: resolution: {integrity: sha512-ft4KVmiN3zH4JUFu2WJBrwfHeDf772Tt2d8bssDTo/YcckKW2D+OwFrHXRC6hJvO3mHjFQTihoMV6fJOi0Hngg==} hasBin: true - typechain@8.1.0: - resolution: {integrity: sha512-5jToLgKTjHdI1VKqs/K8BLYy42Sr3o8bV5ojh4MnR9ExHO83cyyUdw+7+vMJCpKXUiVUvARM4qmHTFuyaCMAZQ==} + typechain@8.3.2: + resolution: {integrity: sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==} hasBin: true peerDependencies: typescript: '>=4.3.0' @@ -5626,6 +5665,18 @@ packages: utf-8-validate: optional: true + ws@8.5.0: + resolution: {integrity: sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xhr-request-promise@0.1.3: resolution: {integrity: sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==} @@ -5717,6 +5768,8 @@ snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} + '@adraffy/ens-normalize@1.10.1': {} + '@babel/code-frame@7.12.11': dependencies: '@babel/highlight': 7.23.4 @@ -5864,7 +5917,7 @@ snapshots: '@ethereumjs/tx@3.3.2': dependencies: - '@ethereumjs/common': 2.5.0 + '@ethereumjs/common': 2.6.5 ethereumjs-util: 7.1.5 '@ethereumjs/tx@3.5.2': @@ -6173,12 +6226,18 @@ snapshots: tweetnacl: 1.0.3 tweetnacl-util: 0.15.1 + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + '@noble/curves@1.3.0': dependencies: '@noble/hashes': 1.3.3 '@noble/hashes@1.2.0': {} + '@noble/hashes@1.3.2': {} + '@noble/hashes@1.3.3': {} '@noble/secp256k1@1.7.1': {} @@ -6321,6 +6380,30 @@ snapshots: - c-kzg - supports-color + '@nomicfoundation/hardhat-ethers@3.0.6(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))': + dependencies: + debug: 4.3.4(supports-color@8.1.1) + ethers: 6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + hardhat: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) + lodash.isequal: 4.5.0 + transitivePeerDependencies: + - supports-color + + '@nomicfoundation/hardhat-verify@2.0.8(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))': + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/address': 5.7.0 + cbor: 8.1.0 + chalk: 2.4.2 + debug: 4.3.4(supports-color@8.1.1) + hardhat: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) + lodash.clonedeep: 4.5.0 + semver: 6.3.1 + table: 6.8.1 + undici: 5.28.3 + transitivePeerDependencies: + - supports-color + '@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1': optional: true @@ -6364,34 +6447,13 @@ snapshots: '@nomicfoundation/solidity-analyzer-win32-ia32-msvc': 0.1.1 '@nomicfoundation/solidity-analyzer-win32-x64-msvc': 0.1.1 - '@nomiclabs/hardhat-ethers@2.0.6(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))': - dependencies: - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) - - '@nomiclabs/hardhat-etherscan@3.1.7(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))': - dependencies: - '@ethersproject/abi': 5.7.0 - '@ethersproject/address': 5.7.0 - cbor: 8.1.0 - chalk: 2.4.2 - debug: 4.3.4(supports-color@8.1.1) - fs-extra: 7.0.1 - hardhat: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) - lodash: 4.17.21 - semver: 6.3.1 - table: 6.8.1 - undici: 5.28.3 - transitivePeerDependencies: - - supports-color - - '@nomiclabs/hardhat-truffle5@2.0.0(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': + '@nomiclabs/hardhat-truffle5@2.0.7(@nomiclabs/hardhat-web3@2.0.0(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(encoding@0.1.13)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': dependencies: '@nomiclabs/hardhat-web3': 2.0.0(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@nomiclabs/truffle-contract': 4.5.10(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)(web3-core-helpers@1.10.4)(web3-core-promievent@1.10.4)(web3-eth-abi@1.10.4)(web3-utils@1.10.4)(web3@1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@types/chai': 4.3.12 chai: 4.3.4 - ethereumjs-util: 6.2.1 + ethereumjs-util: 7.1.5 fs-extra: 7.0.1 hardhat: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) web3: 1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) @@ -6660,13 +6722,12 @@ snapshots: ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) typechain: 3.0.0(typescript@4.7.4) - '@typechain/ethers-v5@7.0.1(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.1.0(typescript@4.7.4))(typescript@4.7.4)': + '@typechain/ethers-v6@0.5.1(ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@4.7.4))(typescript@4.7.4)': dependencies: - '@ethersproject/abi': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/providers': 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - ethers: 5.7.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - typechain: 8.1.0(typescript@4.7.4) + ethers: 6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + lodash: 4.17.21 + ts-essentials: 7.0.3(typescript@4.7.4) + typechain: 8.3.2(typescript@4.7.4) typescript: 4.7.4 '@types/bignumber.js@5.0.0': @@ -6685,7 +6746,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 12.20.55 + '@types/node': 20.11.20 '@types/responselike': 1.0.3 '@types/chai-as-promised@7.1.0': @@ -6700,7 +6761,7 @@ snapshots: '@types/concat-stream@1.6.1': dependencies: - '@types/node': 8.10.66 + '@types/node': 20.11.20 '@types/debug@4.1.12': dependencies: @@ -6708,7 +6769,7 @@ snapshots: '@types/form-data@0.0.33': dependencies: - '@types/node': 8.10.66 + '@types/node': 20.11.20 '@types/glob@7.2.0': dependencies: @@ -6723,7 +6784,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 12.20.55 + '@types/node': 20.11.20 '@types/lru-cache@5.1.1': {} @@ -6746,6 +6807,8 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@18.15.13': {} + '@types/node@20.11.20': dependencies: undici-types: 5.26.5 @@ -6771,7 +6834,7 @@ snapshots: '@types/responselike@1.0.3': dependencies: - '@types/node': 12.20.55 + '@types/node': 20.11.20 '@types/secp256k1@4.0.6': dependencies: @@ -6891,6 +6954,8 @@ snapshots: aes-js@3.1.2: optional: true + aes-js@4.0.0-beta.5: {} + agent-base@6.0.2: dependencies: debug: 4.3.4(supports-color@8.1.1) @@ -7003,6 +7068,8 @@ snapshots: array-union@2.1.0: {} + array-uniq@1.0.3: {} + array-unique@0.3.2: {} array.prototype.flat@1.3.2: @@ -7065,7 +7132,7 @@ snapshots: async@2.6.2: dependencies: - lodash: 4.17.20 + lodash: 4.17.21 asynckit@0.4.0: {} @@ -7116,7 +7183,7 @@ snapshots: convert-source-map: 1.9.0 debug: 2.6.9 json5: 0.5.1 - lodash: 4.17.20 + lodash: 4.17.21 minimatch: 3.1.2 path-is-absolute: 1.0.1 private: 0.1.8 @@ -7132,7 +7199,7 @@ snapshots: babel-types: 6.26.0 detect-indent: 4.0.0 jsesc: 1.3.0 - lodash: 4.17.20 + lodash: 4.17.21 source-map: 0.5.7 trim-right: 1.0.1 @@ -7158,7 +7225,7 @@ snapshots: babel-helper-function-name: 6.24.1 babel-runtime: 6.26.0 babel-types: 6.26.0 - lodash: 4.17.20 + lodash: 4.17.21 transitivePeerDependencies: - supports-color @@ -7199,7 +7266,7 @@ snapshots: dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 - lodash: 4.17.20 + lodash: 4.17.21 babel-helper-remap-async-to-generator@6.24.1: dependencies: @@ -7265,7 +7332,7 @@ snapshots: babel-template: 6.26.0 babel-traverse: 6.26.0 babel-types: 6.26.0 - lodash: 4.17.20 + lodash: 4.17.21 transitivePeerDependencies: - supports-color @@ -7453,7 +7520,7 @@ snapshots: babel-runtime: 6.26.0 core-js: 2.6.12 home-or-tmp: 2.0.0 - lodash: 4.17.20 + lodash: 4.17.21 mkdirp: 0.5.6 source-map-support: 0.4.18 transitivePeerDependencies: @@ -7470,7 +7537,7 @@ snapshots: babel-traverse: 6.26.0 babel-types: 6.26.0 babylon: 6.18.0 - lodash: 4.17.20 + lodash: 4.17.21 transitivePeerDependencies: - supports-color @@ -7484,7 +7551,7 @@ snapshots: debug: 2.6.9 globals: 9.18.0 invariant: 2.2.4 - lodash: 4.17.20 + lodash: 4.17.21 transitivePeerDependencies: - supports-color @@ -7492,7 +7559,7 @@ snapshots: dependencies: babel-runtime: 6.26.0 esutils: 2.0.3 - lodash: 4.17.20 + lodash: 4.17.21 to-fast-properties: 1.0.3 babelify@7.3.0: @@ -9127,6 +9194,19 @@ snapshots: - bufferutil - utf-8-validate + ethers@6.13.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 18.15.13 + aes-js: 4.0.0-beta.5 + tslib: 2.4.0 + ws: 8.5.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + ethjs-unit@0.1.6: dependencies: bn.js: 4.11.6 @@ -9700,8 +9780,9 @@ snapshots: ajv: 6.12.6 har-schema: 2.0.0 - hardhat-gas-reporter@1.0.4(bufferutil@4.0.8)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): + hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): dependencies: + array-uniq: 1.0.3 eth-gas-reporter: 0.2.27(bufferutil@4.0.8)(utf-8-validate@5.0.10) hardhat: 2.20.1(bufferutil@4.0.8)(ts-node@10.9.1(@types/node@20.11.20)(typescript@4.7.4))(typescript@4.7.4)(utf-8-validate@5.0.10) sha1: 1.1.1 @@ -10442,6 +10523,10 @@ snapshots: lodash.camelcase@4.3.0: {} + lodash.clonedeep@4.5.0: {} + + lodash.isequal@4.5.0: {} + lodash.merge@4.6.2: {} lodash.truncate@4.4.2: {} @@ -12292,6 +12377,8 @@ snapshots: tslib@1.14.1: {} + tslib@2.4.0: {} + tsort@0.0.1: {} tsutils@3.21.0(typescript@4.7.4): @@ -12347,7 +12434,7 @@ snapshots: - supports-color - typescript - typechain@8.1.0(typescript@4.7.4): + typechain@8.3.2(typescript@4.7.4): dependencies: '@types/prettier': 2.7.3 debug: 4.3.4(supports-color@8.1.1) @@ -13339,6 +13426,11 @@ snapshots: bufferutil: 4.0.8 utf-8-validate: 5.0.10 + ws@8.5.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + xhr-request-promise@0.1.3: dependencies: xhr-request: 1.1.0 diff --git a/test/ChainedSignatures.spec.ts b/test/ChainedSignatures.spec.ts index 58badb82..5aab0892 100644 --- a/test/ChainedSignatures.spec.ts +++ b/test/ChainedSignatures.spec.ts @@ -21,25 +21,19 @@ contract('Chained signatures', (accounts: string[]) => { }) const chain = (top: string, ...rest: { sig: string }[]) => { - const encodedRest = ethers.utils.solidityPack( + const encodedRest = ethers.solidityPacked( rest.map(() => ['uint24', 'bytes']).flat(), - rest.map((r) => [ethers.utils.arrayify(r.sig).length, r.sig]).flat() + rest.map(r => [ethers.getBytes(r.sig).length, r.sig]).flat() ) - return ethers.utils.solidityPack( - ['uint8', 'uint24', 'bytes', 'bytes'], - [3, ethers.utils.arrayify(top).length, top, encodedRest] - ) + return ethers.solidityPacked(['uint8', 'uint24', 'bytes', 'bytes'], [3, ethers.getBytes(top).length, top, encodedRest]) } const hashSetImageHash = (imageHash: string) => { - return ethers.utils.keccak256(ethers.utils.defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [typeHash, imageHash] - )) + return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'bytes32'], [typeHash, imageHash])) } - it("Should accept a single chained signature", async () => { + it('Should accept a single chained signature', async () => { const wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address }) const hsih = hashSetImageHash(wallet_b.imageHash) @@ -51,12 +45,12 @@ contract('Chained signatures', (accounts: string[]) => { await wallet_b.relayTransactions([{}], bundled) }) - it("Should accept two chained signatures", async () => { + it('Should accept two chained signatures', async () => { let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address, signing: 2, idle: 1 }) let wallet_c = SequenceWallet.basicWallet(context, { address: wallet_b.address, signing: 3, idle: 7 }) - const checkpoint1 = ethers.BigNumber.from(wallet.config.checkpoint).add(1) - const checkpoint2 = checkpoint1.add(1) + const checkpoint1 = BigInt(wallet.config.checkpoint) + 1n + const checkpoint2 = checkpoint1 + 1n wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint1 }) wallet_c = wallet_c.useConfig({ ...wallet_c.config, checkpoint: checkpoint2 }) @@ -73,12 +67,12 @@ contract('Chained signatures', (accounts: string[]) => { await wallet_c.relayTransactions([{}], bundled) }) - it("Should reject chained signatures if checkpoint is wrongly ordered", async () => { + it('Should reject chained signatures if checkpoint is wrongly ordered', async () => { let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address, signing: 2, idle: 1 }) let wallet_c = SequenceWallet.basicWallet(context, { address: wallet_b.address, signing: 3, idle: 7 }) - const checkpoint1 = ethers.BigNumber.from(wallet.config.checkpoint).add(1) - const checkpoint2 = checkpoint1.sub(1) + const checkpoint1 = BigInt(wallet.config.checkpoint) + 1n + const checkpoint2 = checkpoint1 - 1n wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint1 }) wallet_c = wallet_c.useConfig({ ...wallet_c.config, checkpoint: checkpoint2 }) @@ -96,20 +90,20 @@ contract('Chained signatures', (accounts: string[]) => { await expectToBeRejected(tx, `WrongChainedCheckpointOrder(${checkpoint1}, ${checkpoint2})`) }) - it("Should accept top level signature encoded as chained", async () => { + it('Should accept top level signature encoded as chained', async () => { const sig = await wallet.signTransactions([{}]) await wallet.relayTransactions([{}], chain(sig)) }) - it("Should accept top level signature encoded as chained twice", async () => { + it('Should accept top level signature encoded as chained twice', async () => { const sig = await wallet.signTransactions([{}]) await wallet.relayTransactions([{}], chain(chain(sig))) }) - it("Should reject signature if current checkpoint is equal to signed checkpoint", async () => { + it('Should reject signature if current checkpoint is equal to signed checkpoint', async () => { let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address }) - const checkpoint = ethers.BigNumber.from(wallet.config.checkpoint) + const checkpoint = BigInt(wallet.config.checkpoint) wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint }) const hsih = hashSetImageHash(wallet_b.imageHash) @@ -117,13 +111,16 @@ contract('Chained signatures', (accounts: string[]) => { const topsig = await wallet_b.signTransactions([{}]) const bundled = chain(topsig, { sig: sig }) - await expectToBeRejected(wallet_b.relayTransactions([{}], bundled), `WrongChainedCheckpointOrder(${checkpoint}, ${checkpoint})`) + await expectToBeRejected( + wallet_b.relayTransactions([{}], bundled), + `WrongChainedCheckpointOrder(${checkpoint}, ${checkpoint})` + ) }) - it("Should reject signature if current checkpoint is above to signed checkpoint", async () => { + it('Should reject signature if current checkpoint is above to signed checkpoint', async () => { let wallet_b = SequenceWallet.basicWallet(context, { address: wallet.address }) - const checkpoint = ethers.BigNumber.from(wallet.config.checkpoint).sub(1) + const checkpoint = BigInt(wallet.config.checkpoint) - 1n wallet_b = wallet_b.useConfig({ ...wallet_b.config, checkpoint: checkpoint }) const hsih = hashSetImageHash(wallet_b.imageHash) @@ -131,6 +128,9 @@ contract('Chained signatures', (accounts: string[]) => { const topsig = await wallet_b.signTransactions([{}]) const bundled = chain(topsig, { sig: sig }) - await expectToBeRejected(wallet_b.relayTransactions([{}], bundled), `WrongChainedCheckpointOrder(${wallet.config.checkpoint}, ${checkpoint})`) + await expectToBeRejected( + wallet_b.relayTransactions([{}], bundled), + `WrongChainedCheckpointOrder(${wallet.config.checkpoint}, ${checkpoint})` + ) }) }) diff --git a/test/ERC165.spec.ts b/test/ERC165.spec.ts index 3cd718ff..f55cf6da 100644 --- a/test/ERC165.spec.ts +++ b/test/ERC165.spec.ts @@ -1,4 +1,4 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { ContractType, deploySequenceContext, ERC165CheckerMock, SequenceContext } from './utils/contracts' import { SequenceWallet } from './utils/wallet' import { expect, interfaceIdOf, randomHex } from './utils' @@ -33,8 +33,8 @@ contract('ERC165', () => { describe('Implement all interfaces for ERC165 on MainModule', () => { interfaceIds.forEach(element => { it(`Should return implements ${element} interfaceId`, async () => { - const interfaceId = interfaceIdOf(new ethers.utils.Interface(artifacts.require(element).abi)) - expect(ethers.BigNumber.from(interfaceId).isZero()).to.be.false + const interfaceId = interfaceIdOf(new ethers.Interface(artifacts.require(element).abi)) + expect(BigInt(interfaceId) === 0n).to.be.false const erc165result = await erc165checker.doesContractImplementInterface(wallet.address, interfaceId) expect(erc165result).to.be.true @@ -48,8 +48,8 @@ contract('ERC165', () => { interfaceIds.concat('IModuleAuthUpgradable').forEach(element => { it(`Should return implements ${element} interfaceId`, async () => { - const interfaceId = interfaceIdOf(new ethers.utils.Interface(artifacts.require(element).abi)) - expect(ethers.BigNumber.from(interfaceId).isZero()).to.be.false + const interfaceId = interfaceIdOf(new ethers.Interface(artifacts.require(element).abi)) + expect(BigInt(interfaceId) === 0n).to.be.false const erc165result = await erc165checker.doesContractImplementInterface(wallet.address, interfaceId) expect(erc165result).to.be.true diff --git a/test/Factory.spec.ts b/test/Factory.spec.ts index ea53c2cf..c4988574 100644 --- a/test/Factory.spec.ts +++ b/test/Factory.spec.ts @@ -1,4 +1,4 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { ethers as hethers } from 'hardhat' import { addressOf } from './utils/sequence' @@ -12,41 +12,54 @@ contract('Factory', () => { beforeEach(async () => { module = await ModuleMock.deploy() factory = await Factory.deploy() + + await module.waitForDeployment() + await factory.waitForDeployment() }) describe('Deploy wallets', () => { it('Should deploy wallet', async () => { - await factory.deploy(module.address, ethers.utils.defaultAbiCoder.encode(['address'], [ethers.Wallet.createRandom().address])) + await factory.deploy( + await module.getAddress(), + ethers.AbiCoder.defaultAbiCoder().encode(['address'], [ethers.Wallet.createRandom().address]) + ) + await factory.waitForDeployment() }) it('Should predict wallet address', async () => { - const hash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - const predict = addressOf(factory.address, module.address, hash) - await factory.deploy(module.address, hash) + const hash = ethers.hexlify(ethers.randomBytes(32)) + const predict = addressOf(await factory.getAddress(), await module.getAddress(), hash) + await factory.deploy(await module.getAddress(), hash) + await factory.waitForDeployment() expect(await hethers.provider.getCode(predict)).to.not.equal('0x') }) it('Should initialize with main module', async () => { - const hash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - await factory.deploy(module.address, hash) - const address = addressOf(factory.address, module.address, hash) + const hash = ethers.hexlify(ethers.randomBytes(32)) + await factory.deploy(await module.getAddress(), hash) + const address = addressOf(await factory.getAddress(), await module.getAddress(), hash) const wallet = await ModuleMock.attach(address) const receipt = await (await wallet.ping()).wait() - expect(wallet.interface.parseLog(receipt.logs[0]).name).to.equal('Pong') + + if (!receipt) { + throw new Error('No receipt') + } + + expect(wallet.interface.parseLog(receipt.logs[0])?.name).to.equal('Pong') }) it('Should fail to deploy twice', async () => { - const hash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - await factory.deploy(module.address, hash) + const hash = ethers.hexlify(ethers.randomBytes(32)) + await factory.deploy(await module.getAddress(), hash) - const tx2 = factory.deploy(module.address, hash) - await expectToBeRejected(tx2, `DeployFailed("${module.address}", "${hash}")`) + const tx2 = factory.deploy(await module.getAddress(), hash) + await expectToBeRejected(tx2, `DeployFailed("${await module.getAddress()}", "${hash}")`) }) it('Should fail to deploy with not enough gas', async () => { - const hash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - const tx = factory.deploy(module.address, hash, { gasLimit: 80000 }) - await expectToBeRejected(tx, `DeployFailed("${module.address}", "${hash}")`) + const hash = ethers.hexlify(ethers.randomBytes(32)) + const tx = factory.deploy(await module.getAddress(), hash, { gasLimit: 80000 }) + await expectToBeRejected(tx, `DeployFailed("${await module.getAddress()}", "${hash}")`) }) }) }) diff --git a/test/GasEstimation.spec.ts b/test/GasEstimation.spec.ts index eb7281d9..850d6499 100644 --- a/test/GasEstimation.spec.ts +++ b/test/GasEstimation.spec.ts @@ -1,67 +1,95 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { expect } from './utils' -import { CallReceiverMock, ContractType, deploySequenceContext, GasEstimator, GuestModule, ModuleMock, SequenceContext, MainModuleGasEstimation, MainModule } from './utils/contracts' +import { + CallReceiverMock, + ContractType, + deploySequenceContext, + GasEstimator, + GuestModule, + ModuleMock, + SequenceContext, + MainModuleGasEstimation, + MainModule +} from './utils/contracts' import { applyTxDefaults, toSimplifiedConfig, Transaction } from './utils/sequence' import { SequenceWallet } from './utils/wallet' - function txBaseCost(data: ethers.BytesLike): number { - const bytes = ethers.utils.arrayify(data) - return bytes.reduce((p, c) => c == 0 ? p.add(4) : p.add(16), ethers.constants.Zero).add(21000).toNumber() + const bytes = ethers.getBytes(data) + return Number(bytes.reduce((p, c) => (c == 0 ? p + 4n : p + 16n), 0n) + 21000n) } contract('Estimate gas usage', () => { let context: SequenceContext let gasEstimator: ContractType - let callReceiver: ContractType< typeof CallReceiverMock> + let callReceiver: ContractType let guestModule: ContractType let moduleMock: ContractType let wallet: SequenceWallet - const bundleWithDeploy = (txData: string) => { - return applyTxDefaults([{ - target: context.factory.address, - data: context.factory.interface.encodeFunctionData('deploy', [context.mainModule.address, wallet.imageHash]), - }, { - target: wallet.address, - data: txData - }]) + const bundleWithDeploy = async (txData: string) => { + return applyTxDefaults([ + { + target: await context.factory.getAddress(), + data: context.factory.interface.encodeFunctionData('deploy', [await context.mainModule.getAddress(), wallet.imageHash]) + }, + { + target: wallet.address, + data: txData + } + ]) } - const estimate = (address: string, data: ethers.BytesLike) => ({ call: async () => { - return gasEstimator.callStatic.estimate(address, data) - }}) + const estimate = (address: string, data: ethers.BytesLike) => ({ + call: async () => { + return gasEstimator.estimate.staticCall(address, data) + } + }) - const estimateGasUsage = async (txs: Partial[], wallet: SequenceWallet, deploy: boolean = false, nonce: ethers.BigNumberish = 0) => { + const estimateGasUsage = async ( + txs: Partial[], + wallet: SequenceWallet, + deploy: boolean = false, + nonce: ethers.BigNumberish = 0 + ) => { const stubSignature = await SequenceWallet.detailedWallet(context, { threshold: wallet.config.threshold, signers: toSimplifiedConfig(wallet.config).signers.map(() => ethers.Wallet.createRandom()) - }).signMessage(ethers.utils.randomBytes(32)) + }).signMessage(ethers.randomBytes(32)) const txData = wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(txs), nonce, stubSignature]) if (!deploy) { const res = await estimate(wallet.address, txData).call() - return res.gas.add(txBaseCost(txData)).toNumber() + return Number(res.gas + BigInt(txBaseCost(txData))) } - const guestModuleData = guestModule.interface.encodeFunctionData('execute', [bundleWithDeploy(txData), 0, []]) - const res = await estimate(guestModule.address, guestModuleData).call() - return res.gas.add(txBaseCost(txData)).toNumber() + const guestModuleData = guestModule.interface.encodeFunctionData('execute', [ + await bundleWithDeploy(txData), + 0, + new Uint8Array([]) + ]) + const res = await estimate(await guestModule.getAddress(), guestModuleData).call() + return Number(res.gas + BigInt(txBaseCost(txData))) } - const gasUsedFor = async (tx: Promise | ethers.ContractTransaction) => { - const receipt = await (await tx).wait() - return ethers.BigNumber.from(receipt.gasUsed).toNumber() + const gasUsedFor = async (tx: ethers.ContractTransactionResponse) => { + const receipt = await tx.wait() + + if (!receipt) { + throw new Error('No receipt') + } + + return Number(BigInt(receipt.gasUsed)) } before(async () => { context = await deploySequenceContext() // Deploy MainModuleGasEstimation (hardhat doesn't support overwrites, so we use this as the real module) - context.mainModule = await MainModuleGasEstimation.deploy() as any as ContractType + context.mainModule = (await MainModuleGasEstimation.deploy()) as any as ContractType gasEstimator = await GasEstimator.deploy() guestModule = await GuestModule.deploy() @@ -77,97 +105,121 @@ contract('Estimate gas usage', () => { describe('without wallet deployed', () => { it('Should estimate wallet deployment', async () => { wallet = SequenceWallet.basicWallet(context) - const factoryData = context.factory.interface.encodeFunctionData('deploy', [context.mainModule.address, wallet.imageHash]) + const factoryData = context.factory.interface.encodeFunctionData('deploy', [ + await context.mainModule.getAddress(), + wallet.imageHash + ]) - const estimated = ethers.BigNumber.from((await estimate(context.factory.address, factoryData).call()).gas).toNumber() - const realTx = await context.factory.deploy(context.mainModule.address, wallet.imageHash) + const estimated = Number(BigInt((await estimate(await context.factory.getAddress(), factoryData).call()).gas)) + const realTx = await context.factory.deploy(await context.mainModule.getAddress(), wallet.imageHash) expect(estimated + txBaseCost(factoryData)).to.approximately(await gasUsedFor(realTx), 5000) }) it('Should estimate wallet deployment + upgrade', async () => { - const transactions = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [moduleMock.address]) - }] + const transactions = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()]) + } + ] const estimated = await estimateGasUsage(transactions, wallet, true) const signature = await wallet.signTransactions(transactions, 0) - const executeTxData = bundleWithDeploy(wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])) - const realTx = await guestModule.execute(executeTxData, 0, []) + const executeTxData = await bundleWithDeploy( + wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature]) + ) + const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([])) expect(estimated).to.approximately(await gasUsedFor(realTx), 5000) }) it('Should estimate wallet deployment + upgrade + transaction', async () => { - const transactions = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [moduleMock.address]) - }, { - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }] + const transactions = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()]) + }, + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet, true) const signature = await wallet.signTransactions(transactions, 0) - const executeTxData = bundleWithDeploy(wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])) - const realTx = await guestModule.execute(executeTxData, 0, []) + const executeTxData = await bundleWithDeploy( + wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature]) + ) + const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([])) expect(estimated).to.approximately(await gasUsedFor(realTx), 5000) - expect(await callReceiver.lastValA()).to.equal(1) + expect(await callReceiver.lastValA()).to.equal(1n) }) it('Should estimate wallet deployment + upgrade + failed transaction', async () => { await callReceiver.setRevertFlag(true) - const transactions = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [moduleMock.address]) - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }] + const transactions = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()]) + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet, true) const signature = await wallet.signTransactions(transactions, 0) - const executeTxData = bundleWithDeploy(wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])) - const realTx = await guestModule.execute(executeTxData, 0, []) + const executeTxData = await bundleWithDeploy( + wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature]) + ) + const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([])) expect(estimated).to.approximately(await gasUsedFor(realTx), 5000) }) it('Should estimate wallet deployment + upgrade + fixed gas transaction', async () => { - const transactions = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [moduleMock.address]) - }, { - revertOnError: false, - gasLimit: 900000, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }] + const transactions = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await moduleMock.getAddress()]) + }, + { + revertOnError: false, + gasLimit: 900000, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet, true) const signature = await wallet.signTransactions(transactions, 0) - const executeTxData = bundleWithDeploy(wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature])) - const realTx = await guestModule.execute(executeTxData, 0, []) + const executeTxData = await bundleWithDeploy( + wallet.mainModule.interface.encodeFunctionData('execute', [applyTxDefaults(transactions), 0, signature]) + ) + const realTx = await guestModule.execute(executeTxData, 0, new Uint8Array([])) expect(estimated).to.approximately(await gasUsedFor(realTx), 5000) }) }) - - ;([{ - name: "single signer", - signers: 1 - }, { - name: "many signers", - signers: 32 - }]).map((o) => { + ;[ + { + name: 'single signer', + signers: 1 + }, + { + name: 'many signers', + signers: 32 + } + ].map(o => { describe(`with wallet deployed and ${o.name}`, () => { beforeEach(async () => { wallet = SequenceWallet.basicWallet(context, { signing: o.signers }) @@ -175,44 +227,61 @@ contract('Estimate gas usage', () => { }) it('Should estimate single transaction', async () => { - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }] + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet) - const gasUsed = await wallet.sendTransactions(transactions).then((t) => t.wait()).then((r) => r.gasUsed.toNumber()) + const gasUsed = await wallet + .sendTransactions(transactions) + .then(t => t.wait()) + .then(r => Number(r?.gasUsed)) expect(estimated).to.approximately(gasUsed, 5000) }) it('Should estimate multiple transactions', async () => { - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }, { - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(2299))]) - }] + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + }, + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(2299))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet) - const gasUsed = await wallet.sendTransactions(transactions).then((t) => t.wait()).then((r) => r.gasUsed.toNumber()) + const gasUsed = await wallet + .sendTransactions(transactions) + .then(t => t.wait()) + .then(r => Number(r?.gasUsed)) // TODO: The estimator overEstimates the gas usage due to the gas refund expect(gasUsed).to.be.below(estimated) }) it('Should estimate multiple transactions with bad nonce', async () => { - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }, { - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(2299))]) - }] + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + }, + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(2299))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet, false, 999999999) - const gasUsed = await wallet.sendTransactions(transactions).then((t) => t.wait()).then((r) => r.gasUsed.toNumber()) + const gasUsed = await wallet + .sendTransactions(transactions) + .then(t => t.wait()) + .then(r => Number(r?.gasUsed)) // TODO: The estimator overEstimates the gas usage due to the gas refund expect(gasUsed).to.be.below(estimated) @@ -222,20 +291,27 @@ contract('Estimate gas usage', () => { const altCallReceiver = await CallReceiverMock.deploy() await altCallReceiver.setRevertFlag(true) - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }, { - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(2299))]) - }, { - revertOnError: false, - target: altCallReceiver.address, - data: altCallReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(229))]) - }] + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + }, + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(2299))]) + }, + { + revertOnError: false, + target: await altCallReceiver.getAddress(), + data: altCallReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(229))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet) - const gasUsed = await wallet.sendTransactions(transactions).then((t) => t.wait()).then((r) => r.gasUsed.toNumber()) + const gasUsed = await wallet + .sendTransactions(transactions) + .then(t => t.wait()) + .then(r => Number(r?.gasUsed)) // TODO: The estimator overEstimates the gas usage due to the gas refund expect(gasUsed).to.be.below(estimated) @@ -245,21 +321,28 @@ contract('Estimate gas usage', () => { const altCallReceiver = await CallReceiverMock.deploy() await altCallReceiver.setRevertFlag(true) - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(299))]) - }, { - gasLimit: 90000, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(12))]) - }, { - revertOnError: false, - target: altCallReceiver.address, - data: altCallReceiver.interface.encodeFunctionData('testCall', [1, ethers.utils.hexlify(ethers.utils.randomBytes(229))]) - }] + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(299))]) + }, + { + gasLimit: 90000, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(12))]) + }, + { + revertOnError: false, + target: await altCallReceiver.getAddress(), + data: altCallReceiver.interface.encodeFunctionData('testCall', [1, ethers.hexlify(ethers.randomBytes(229))]) + } + ] const estimated = await estimateGasUsage(transactions, wallet) - const gasUsed = await wallet.sendTransactions(transactions).then((t) => t.wait()).then((r) => r.gasUsed.toNumber()) + const gasUsed = await wallet + .sendTransactions(transactions) + .then(t => t.wait()) + .then(r => Number(r?.gasUsed)) // TODO: The estimator overEstimates the gas usage due to the gas refund expect(gasUsed).to.be.below(estimated) diff --git a/test/GuestModule.spec.ts b/test/GuestModule.spec.ts index 59b651f5..7d466e38 100644 --- a/test/GuestModule.spec.ts +++ b/test/GuestModule.spec.ts @@ -1,10 +1,9 @@ import { expect, expectToBeRejected } from './utils' import { ethers as hethers } from 'hardhat' -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { CallReceiverMock, ContractType, GuestModule, HookCallerMock, MainModule } from './utils/contracts' import { applyTxDefaults, Transaction } from './utils/sequence' - contract('GuestModule', () => { let guestModule: ContractType let callReceiver: ContractType @@ -17,23 +16,25 @@ contract('GuestModule', () => { hookCallerMock = await HookCallerMock.deploy() }) - let valA: ethers.BigNumber + let valA: bigint let valB: string let transactions: Transaction[] beforeEach(async () => { - valA = ethers.BigNumber.from(ethers.utils.randomBytes(3)) - valB = ethers.utils.hexlify(ethers.utils.randomBytes(120)) + valA = ethers.toBigInt(ethers.randomBytes(3)) + valB = ethers.hexlify(ethers.randomBytes(120)) - transactions = applyTxDefaults([{ - target: callReceiver.address, + transactions = applyTxDefaults([ + { + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) - }]) + } + ]) }) it('Should accept transactions without signature', async () => { - await guestModule.execute(transactions, 0, []) + await guestModule.execute(transactions, 0, new Uint8Array([])) expect(await callReceiver.lastValA()).to.equal(valA) expect(await callReceiver.lastValB()).to.equal(valB) @@ -45,7 +46,7 @@ contract('GuestModule', () => { expect(await callReceiver.lastValB()).to.equal(valB) }) it('Should accept transactions with random signature', async () => { - const signature = ethers.utils.randomBytes(96) + const signature = ethers.randomBytes(96) await guestModule.execute(transactions, 0, signature) @@ -55,35 +56,40 @@ contract('GuestModule', () => { it('Should accept transactions with random nonce', async () => { const nonce = 9123891 - await guestModule.execute(transactions, nonce, []) + await guestModule.execute(transactions, nonce, new Uint8Array([])) expect(await callReceiver.lastValA()).to.equal(valA) expect(await callReceiver.lastValB()).to.equal(valB) }) it('Should revert on delegateCall transactions', async () => { - const transactions = applyTxDefaults([{ - delegateCall: true - }]) + const transactions = applyTxDefaults([ + { + delegateCall: true + } + ]) const tx = guestModule.selfExecute(transactions) await expectToBeRejected(tx, 'DelegateCallNotAllowed(0)') }) it('Should not accept ETH', async () => { - const tx = hethers.provider.getSigner().sendTransaction({ value: 1, to: guestModule.address }) + const signer = await hethers.provider.getSigner() + const tx = signer.sendTransaction({ value: 1, to: await guestModule.getAddress() }) await expect(tx).to.be.rejected }) it('Should not implement hooks', async () => { - const tx = hookCallerMock.callERC1155Received(guestModule.address) + const tx = hookCallerMock.callERC1155Received(await guestModule.getAddress()) await expect(tx).to.be.rejected }) it('Should not be upgradeable', async () => { - const mainModule = await MainModule.attach(guestModule.address) - const newImageHash = ethers.utils.hexlify(ethers.utils.randomBytes(32)) - - const migrateBundle = applyTxDefaults([{ - target: mainModule.address, - data: mainModule.interface.encodeFunctionData('updateImageHash', [newImageHash]) - }]) + const mainModule = await MainModule.attach(await guestModule.getAddress()) + const newImageHash = ethers.hexlify(ethers.randomBytes(32)) + + const migrateBundle = applyTxDefaults([ + { + target: await mainModule.getAddress(), + data: mainModule.interface.encodeFunctionData('updateImageHash', [newImageHash]) + } + ]) const tx = guestModule.selfExecute(migrateBundle) await expect(tx).to.be.rejected diff --git a/test/LibBytes.spec.ts b/test/LibBytes.spec.ts index 0c5b8042..0212ccb8 100644 --- a/test/LibBytes.spec.ts +++ b/test/LibBytes.spec.ts @@ -1,4 +1,4 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { expect, expectStaticToBeRejected, randomHex } from './utils' import { ContractType, LibBytesImpl, LibBytesPointerImpl } from './utils/contracts' @@ -7,7 +7,6 @@ contract('LibBytes', () => { let libBytes: ContractType let libBytesPointer: ContractType - before(async () => { libBytes = await LibBytesImpl.deploy() libBytesPointer = await LibBytesPointerImpl.deploy() @@ -16,20 +15,20 @@ contract('LibBytes', () => { describe('readFirstUint16', () => { it('Should read first uint16', async () => { const res = await libBytesPointer.readFirstUint16('0x03021e4453120a') - expect(res[0]).to.equal(770) - expect(res[1]).to.equal(2) + expect(res[0]).to.equal(770n) + expect(res[1]).to.equal(2n) }) it('Should read first uint16 of 2 byte array', async () => { const res = await libBytesPointer.readFirstUint16('0xff0a') - expect(res[0]).to.equal(65290) - expect(res[1]).to.equal(2) + expect(res[0]).to.equal(65290n) + expect(res[1]).to.equal(2n) }) it('Should read first uint16 out of bounds', async () => { const res = await libBytesPointer.readFirstUint16('0x') - expect(res[0]).to.equal(0) - expect(res[1]).to.equal(2) + expect(res[0]).to.equal(0n) + expect(res[1]).to.equal(2n) }) }) @@ -45,9 +44,7 @@ contract('LibBytes', () => { expect(res).to.equal(bytes32) }) it('Should read bytes32 at given index', async () => { - const data = randomHex(12) - .concat(bytes32.slice(2)) - .concat(randomHex(44).slice(2)) + const data = randomHex(12).concat(bytes32.slice(2)).concat(randomHex(44).slice(2)) const res = await libBytes.readBytes32(data, 12) expect(res).to.equal(bytes32) @@ -61,128 +58,128 @@ contract('LibBytes', () => { it('Should read bytes32 out of bounds', async () => { const data = randomHex(11).concat(bytes32.slice(2)) const res = await libBytes.readBytes32(data, 12) - expect(res).to.equal("0x" + data.slice(26, 26 + 64) + '00') + expect(res).to.equal('0x' + data.slice(26, 26 + 64) + '00') }) it('Should read bytes32 totally out of bounds', async () => { const res = await libBytes.readBytes32('0x010203', 3145) - expect(res).to.equal(ethers.constants.HashZero) + expect(res).to.equal(ethers.ZeroHash) }) }) describe('readUint32', () => { it('Should read uint32 at index zero', async () => { const res = await libBytes.readUint32('0x837fc8a10d', 0) - expect(res).to.equal(2206189729) + expect(res).to.equal(2206189729n) }) it('Should read uint32 at given index', async () => { const res = await libBytes.readUint32('0x5a9c2a992a8c22199af0', 3) - expect(res).to.equal(2569702434) + expect(res).to.equal(2569702434n) }) it('Should read uint32 at last index', async () => { const res = await libBytes.readUint32('0x029183af982299dfa001', 6) - expect(res).to.equal(2581569537) + expect(res).to.equal(2581569537n) }) it('Should read zeros uint32 out of bounds', async () => { const res1 = await libBytes.readUint32('0x2293', 1) const res2 = await libBytes.readUint32('0x2193000000', 1) - expect(res1).to.equal(2466250752) + expect(res1).to.equal(2466250752n) expect(res1).to.equal(res2) }) it('Should read all zeros uint32 fully out of bounds', async () => { const res = await libBytes.readUint32('0xff92a09f339922', 15) - expect(res).to.equal(0) + expect(res).to.equal(0n) }) }) describe('readUint16', () => { it('Should read uint16 at index zero', async () => { const res = await libBytesPointer.readUint16('0x5202', 0) - expect(res[0]).to.equal(20994) - expect(res[1]).to.equal(2) + expect(res[0]).to.equal(20994n) + expect(res[1]).to.equal(2n) }) it('Should read uint16 at given index', async () => { const res = await libBytesPointer.readUint16('0x5a9c2a1019d401d3', 3) - expect(res[0]).to.equal(4121) - expect(res[1]).to.equal(5) + expect(res[0]).to.equal(4121n) + expect(res[1]).to.equal(5n) }) it('Should read uint16 at last index', async () => { const res = await libBytesPointer.readUint16('0x020414', 1) - expect(res[0]).to.equal(1044) - expect(res[1]).to.equal(3) + expect(res[0]).to.equal(1044n) + expect(res[1]).to.equal(3n) }) it('Should read zeros uint16 out of bounds', async () => { const res1 = await libBytesPointer.readUint16('0x5a', 0) const res2 = await libBytesPointer.readUint16('0x5a00', 0) - expect(res1[0]).to.equal(23040) + expect(res1[0]).to.equal(23040n) expect(res1[0]).to.equal(res2[0]) - expect(res1[1]).to.equal(2) + expect(res1[1]).to.equal(2n) }) it('Should read zeros uint16 fully out of bounds', async () => { const res = await libBytesPointer.readUint16('0x5a9ca2', 12) - expect(res[0]).to.equal(0) - expect(res[1]).to.equal(14) + expect(res[0]).to.equal(0n) + expect(res[1]).to.equal(14n) }) }) describe('readUint24', () => { it('Should read uint24 at index zero', async () => { const res = await libBytesPointer.readUint24('0x5202fa', 0) - expect(res[0]).to.equal(5374714) - expect(res[1]).to.equal(3) + expect(res[0]).to.equal(5374714n) + expect(res[1]).to.equal(3n) }) it('Should read uint24 at given index', async () => { const res = await libBytesPointer.readUint24('0x5202f7220c', 2) - expect(res[0]).to.equal(16196108) - expect(res[1]).to.equal(5) + expect(res[0]).to.equal(16196108n) + expect(res[1]).to.equal(5n) }) it('Should read uint24 at last index', async () => { const res = await libBytesPointer.readUint24('0x021f2b00', 1) - expect(res[0]).to.equal(2042624) - expect(res[1]).to.equal(4) + expect(res[0]).to.equal(2042624n) + expect(res[1]).to.equal(4n) }) it('Should read zeros uint24 out of bounds', async () => { const res1 = await libBytesPointer.readUint24('0xf598', 0) const res2 = await libBytesPointer.readUint24('0xf59800', 0) - expect(res1[0]).to.equal(16095232) + expect(res1[0]).to.equal(16095232n) expect(res1[0]).to.equal(res2[0]) - expect(res1[1]).to.equal(3) + expect(res1[1]).to.equal(3n) }) it('Should read zeros uint24 fully out of bounds', async () => { const res = await libBytesPointer.readUint24('0x5a9ca221', 12) - expect(res[0]).to.equal(0) - expect(res[1]).to.equal(15) + expect(res[0]).to.equal(0n) + expect(res[1]).to.equal(15n) }) }) describe('readUint64', () => { it('Should read uint64 at index zero', async () => { const res = await libBytesPointer.readUint64('0xc1725050681dcb2a', 0) - expect(res[0]).to.equal(ethers.BigNumber.from("13939292102939495210")) - expect(res[1]).to.equal(8) + expect(res[0]).to.equal(13939292102939495210n) + expect(res[1]).to.equal(8n) }) it('Should read uint64 at given index', async () => { const res = await libBytesPointer.readUint64('0x0837acc1725050681dcb2a01cc', 3) - expect(res[0]).to.equal(ethers.BigNumber.from("13939292102939495210")) - expect(res[1]).to.equal(11) + expect(res[0]).to.equal(13939292102939495210n) + expect(res[1]).to.equal(11n) }) it('Should read uint64 at last index', async () => { const res = await libBytesPointer.readUint64('0x0837acc1725050681dcb2a', 3) - expect(res[0]).to.equal(ethers.BigNumber.from("13939292102939495210")) - expect(res[1]).to.equal(11) + expect(res[0]).to.equal(13939292102939495210n) + expect(res[1]).to.equal(11n) }) it('Should read zeros uint64 out of bounds', async () => { const res1 = await libBytesPointer.readUint64('0x5a', 0) const res2 = await libBytesPointer.readUint64('0x5a00', 0) const res3 = await libBytesPointer.readUint64('0x5a00000000000000', 0) - expect(res1[0]).to.equal(ethers.BigNumber.from("6485183463413514240")) + expect(res1[0]).to.equal(6485183463413514240n) expect(res1[0]).to.equal(res2[0]) expect(res1[0]).to.equal(res3[0]) - expect(res1[1]).to.equal(8) + expect(res1[1]).to.equal(8n) }) it('Should read zeros uint64 fully out of bounds', async () => { const res = await libBytesPointer.readUint64('0x5a9ca2', 12) - expect(res[0]).to.equal(0) - expect(res[1]).to.equal(20) + expect(res[0]).to.equal(0n) + expect(res[1]).to.equal(20n) }) }) }) diff --git a/test/LibString.spec.ts b/test/LibString.spec.ts index 438c367e..7ca5f5c9 100644 --- a/test/LibString.spec.ts +++ b/test/LibString.spec.ts @@ -1,4 +1,4 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { expect, expectStaticToBeRejected, randomHex } from './utils' import { ContractType, LibStringImp } from './utils/contracts' @@ -14,7 +14,7 @@ contract('LibString', () => { new Array(99).fill(0).map((_, i) => { it(`Should convert ${i} bytes to hexadecimal`, async () => { const bytes = randomHex(i) - const expected = ethers.utils.hexlify(bytes) + const expected = ethers.hexlify(bytes) const result = await libString.bytesToHexadecimal(bytes) expect(result).to.eq(expected.slice(2)) @@ -25,44 +25,53 @@ contract('LibString', () => { }) describe('bytesToBase32', () => { - ([ - ["0x", "b"], - ["0x69", "bne"], - ["0x8775", "bq52q"], - ["0x91e0f3", "bshqpg"], - ["0x4867e789", "bjbt6pci"], - ["0x456fe65b8d", "bivx6mw4n"], - ["0xb20b0f525320", "bwifq6ustea"], - ["0xd292f6c85f4e5a", "b2kjpnsc7jzna"], - ["0xf78beb02b622adc7", "b66f6wavwekw4o"], - ["0xfad9c9851352f0ae0e", "b7lm4tbitklyk4dq"], - ["0x30b4bca5f67cc95312be", "bgc2lzjpwptevgev6"], - ["0xebc9fd66e4ae84644d502b", "b5pe72zxev2cgitkqfm"], - ["0x0f198dc28836e24782e8d182", "bb4my3quig3repaxi2gba"], - ["0x50059d23099f062187e9d52d8a", "bkacz2iyjt4dcdb7j2uwyu"], - ["0x4c0e454dc43d303c896ef5f9c449", "bjqhektoehuydzclo6x44isi"], - ["0x1ee41f4e940e4bf14df57a4fc06dd9", "bd3sb6tuubzf7ctpvpjh4a3oz"], - ["0x04a8d211d53b45d29fed9a624bb2d5e0", "basuneeovhnc5fh7ntjrexmwv4a"], - ["0x0a8b878364cdd555bda61db7c67503bb5e", "bbkfypa3ezxkvlpngdw34m5idxnpa"], - ["0x313b3c6e446ee6eb6477c0b0b01b70799cb6", "bge5ty3sen3towzdxycylag3qpgolm"], - ["0xe8be4a44970232babf052c3e75f611df8099c5", "b5c7eurexaizlvpyffq7hl5qr36ajtri"], - ["0x461eb18d034475a35d4084a89e0e3a47980f3408", "biyplddidir22gxkaqsuj4dr2i6ma6nai"], - ["0x519e139092a60229fe1a4db06d26fd199af9d39680", "bkgpbheesuybct7q2jwyg2jx5dgnptu4wqa"], - ["0x0fdd1a5696148daf6986505e2b3dc9af16ed91452720", "bb7oruvuwcsg262mgkbpcwpojv4lo3ekfe4qa"], - ["0x63a77d886c3f6593b6c6d37c7ab1f16ca3e34bd67e1ae1", "bmotx3cdmh5szhnwg2n6hvmprnsr6gs6wpynoc"], - ["0x1198cc96b431176fc2d5bc8a8c49ce041e184649ec3b9cbb", "bcgmmzfvugelw7qwvxsfiysooaqpbqrsj5q5zzoy"], - ["0x6b8d9af44a31c799ef835a522f8b630435c8e77e844360ad3d", "bnogzv5ckghdzt34dljjc7c3daq24rz36qrbwblj5"], - ["0xbb2cb7057a24b8588bafd87907ee7579b159027dc7224a3f6540", "bxmwlobl2es4frc5p3b4qp3tvpgyvsat5y4reup3fia"], - ["0x1f64b537d0e4b0158142dfa7cbfd9af76af0931862e48df02ba35b", "bd5slkn6q4syblakc36t4x7m265vpbeyymlsi34blunnq"], - ["0x11c5655f9d5496039fe6b1a9651014d4a19e62331d61e73b48fec776", "bchcwkx45kslahh7gwguwkeau2sqz4yrtdvq6oo2i73dxm"], - ["0x752c3c5cb4a3f06e4feff3f543278e97c7626569994b12b0d9ba9c7c83", "bouwdyxfuupyg4t7p6p2ugj4os7dwezljtffrfmgzxkohzay"], - ["0xb6bd83f8bba80bc8aca3706f7090cd5a0f0cc6adf4e89614bff58c149880", "bw26yh6f3vaf4rlfdobxxbegnlihqzrvn6tujmff76wgbjgea"], - ["0x90f4b198a1a1b2d3e01c36ba3460f46249f180dfa67b152f81552266a18708", "bsd2ldgfbugznhya4g25diyhumje7dag7uz5rkl4bkurgnimhba"], - ["0x24cda83efac2802425b1c35a2d21b01576e71d0e512bcc80840f85f9e0af2faa", "betg2qpx2ykacijnrynnc2inqcv3oohiokev4zaeeb6c7tyfpf6va"], - ["0x169dc221f4cec8756de592c3205a23177867f18c383d2251bd1a5c03cc346c3fe8", "bc2o4eipuz3ehk3pfslbsawrdc54gp4mmha6seun5djoahtbunq76q"], - ["0x65b13b1aef3e44180314108b7f0434ddc23ad740a0df4df7984ad0bf1fdd84edcbaf", "bmwytwgxphzcbqayuccfx6bbu3xbdvv2audpu354yjlil6h65qtw4xly"] - ]).map(([bytes, base32Encoded]) => { - it(`Should convert ${ethers.utils.arrayify(bytes).length} bytes to base32`, async () => { + ;[ + ['0x', 'b'], + ['0x69', 'bne'], + ['0x8775', 'bq52q'], + ['0x91e0f3', 'bshqpg'], + ['0x4867e789', 'bjbt6pci'], + ['0x456fe65b8d', 'bivx6mw4n'], + ['0xb20b0f525320', 'bwifq6ustea'], + ['0xd292f6c85f4e5a', 'b2kjpnsc7jzna'], + ['0xf78beb02b622adc7', 'b66f6wavwekw4o'], + ['0xfad9c9851352f0ae0e', 'b7lm4tbitklyk4dq'], + ['0x30b4bca5f67cc95312be', 'bgc2lzjpwptevgev6'], + ['0xebc9fd66e4ae84644d502b', 'b5pe72zxev2cgitkqfm'], + ['0x0f198dc28836e24782e8d182', 'bb4my3quig3repaxi2gba'], + ['0x50059d23099f062187e9d52d8a', 'bkacz2iyjt4dcdb7j2uwyu'], + ['0x4c0e454dc43d303c896ef5f9c449', 'bjqhektoehuydzclo6x44isi'], + ['0x1ee41f4e940e4bf14df57a4fc06dd9', 'bd3sb6tuubzf7ctpvpjh4a3oz'], + ['0x04a8d211d53b45d29fed9a624bb2d5e0', 'basuneeovhnc5fh7ntjrexmwv4a'], + ['0x0a8b878364cdd555bda61db7c67503bb5e', 'bbkfypa3ezxkvlpngdw34m5idxnpa'], + ['0x313b3c6e446ee6eb6477c0b0b01b70799cb6', 'bge5ty3sen3towzdxycylag3qpgolm'], + ['0xe8be4a44970232babf052c3e75f611df8099c5', 'b5c7eurexaizlvpyffq7hl5qr36ajtri'], + ['0x461eb18d034475a35d4084a89e0e3a47980f3408', 'biyplddidir22gxkaqsuj4dr2i6ma6nai'], + ['0x519e139092a60229fe1a4db06d26fd199af9d39680', 'bkgpbheesuybct7q2jwyg2jx5dgnptu4wqa'], + ['0x0fdd1a5696148daf6986505e2b3dc9af16ed91452720', 'bb7oruvuwcsg262mgkbpcwpojv4lo3ekfe4qa'], + ['0x63a77d886c3f6593b6c6d37c7ab1f16ca3e34bd67e1ae1', 'bmotx3cdmh5szhnwg2n6hvmprnsr6gs6wpynoc'], + ['0x1198cc96b431176fc2d5bc8a8c49ce041e184649ec3b9cbb', 'bcgmmzfvugelw7qwvxsfiysooaqpbqrsj5q5zzoy'], + ['0x6b8d9af44a31c799ef835a522f8b630435c8e77e844360ad3d', 'bnogzv5ckghdzt34dljjc7c3daq24rz36qrbwblj5'], + ['0xbb2cb7057a24b8588bafd87907ee7579b159027dc7224a3f6540', 'bxmwlobl2es4frc5p3b4qp3tvpgyvsat5y4reup3fia'], + ['0x1f64b537d0e4b0158142dfa7cbfd9af76af0931862e48df02ba35b', 'bd5slkn6q4syblakc36t4x7m265vpbeyymlsi34blunnq'], + ['0x11c5655f9d5496039fe6b1a9651014d4a19e62331d61e73b48fec776', 'bchcwkx45kslahh7gwguwkeau2sqz4yrtdvq6oo2i73dxm'], + ['0x752c3c5cb4a3f06e4feff3f543278e97c7626569994b12b0d9ba9c7c83', 'bouwdyxfuupyg4t7p6p2ugj4os7dwezljtffrfmgzxkohzay'], + ['0xb6bd83f8bba80bc8aca3706f7090cd5a0f0cc6adf4e89614bff58c149880', 'bw26yh6f3vaf4rlfdobxxbegnlihqzrvn6tujmff76wgbjgea'], + ['0x90f4b198a1a1b2d3e01c36ba3460f46249f180dfa67b152f81552266a18708', 'bsd2ldgfbugznhya4g25diyhumje7dag7uz5rkl4bkurgnimhba'], + [ + '0x24cda83efac2802425b1c35a2d21b01576e71d0e512bcc80840f85f9e0af2faa', + 'betg2qpx2ykacijnrynnc2inqcv3oohiokev4zaeeb6c7tyfpf6va' + ], + [ + '0x169dc221f4cec8756de592c3205a23177867f18c383d2251bd1a5c03cc346c3fe8', + 'bc2o4eipuz3ehk3pfslbsawrdc54gp4mmha6seun5djoahtbunq76q' + ], + [ + '0x65b13b1aef3e44180314108b7f0434ddc23ad740a0df4df7984ad0bf1fdd84edcbaf', + 'bmwytwgxphzcbqayuccfx6bbu3xbdvv2audpu354yjlil6h65qtw4xly' + ] + ].map(([bytes, base32Encoded]) => { + it(`Should convert ${ethers.getBytes(bytes).length} bytes to base32`, async () => { const result = await libString.bytesToBase32(bytes) expect(result).to.equal(base32Encoded.slice(1)) diff --git a/test/MainModule.bench.ts b/test/MainModule.bench.ts index eb317f2b..bf192462 100644 --- a/test/MainModule.bench.ts +++ b/test/MainModule.bench.ts @@ -1,19 +1,18 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { deploySequenceContext, SequenceContext } from './utils/contracts' import { SequenceWallet } from './utils/wallet' +import { Transaction } from './utils/sequence' -const optimalGasLimit = ethers.constants.Two.pow(22) +const optimalGasLimit = 2n ** 22n const runs = 256 function report2(values: ethers.BigNumberish[]) { - const bns = values.map(v => ethers.BigNumber.from(v)) + const bns = values.map(v => BigInt(v)) - const min = bns.reduce((a, b) => a.lt(b) ? a : b) - const max = bns.reduce((a, b) => a.gt(b) ? a : b) - const avg = bns - .reduce((p, n) => p.add(n)) - .div(values.length) + const min = bns.reduce((a, b) => (a < b ? a : b)) + const max = bns.reduce((a, b) => (a > b ? a : b)) + const avg = bns.reduce((p, n) => (p + n) / BigInt(values.length)) return { min, max, avg } } @@ -31,7 +30,7 @@ contract('MainModule', () => { }) if (process.env.BENCHMARK) { - describe.only('Benchmark', function() { + describe.only('Benchmark', function () { ;(this as any).timeout(0) it('Deploy a wallet', async () => { @@ -39,7 +38,12 @@ contract('MainModule', () => { for (let i = 0; i < runs; i++) { const tx = await SequenceWallet.basicWallet(context).deploy() - const receipt = await tx.wait() + const receipt = await tx?.wait() + + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) } @@ -55,6 +59,10 @@ contract('MainModule', () => { const tx = await wallet.sendTransactions([{}]) const receipt = await tx.wait() + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) } @@ -70,9 +78,9 @@ contract('MainModule', () => { delegateCall: false, revertOnError: true, gasLimit: optimalGasLimit, - target: ethers.constants.AddressZero, - value: ethers.constants.Zero, - data: "0x0000000000000000000000007109709ecfa91a80626ff3989d68f67f5b1dd12e0000000000000000000000007109709ecfa91a80626ff3989d68f67f5b1dd12e" + target: ethers.ZeroAddress, + value: 0n, + data: '0x0000000000000000000000007109709ecfa91a80626ff3989d68f67f5b1dd12e0000000000000000000000007109709ecfa91a80626ff3989d68f67f5b1dd12e' })) for (let i = 0; i < runs; i++) { @@ -81,6 +89,10 @@ contract('MainModule', () => { const tx = await wallet.sendTransactions(transactions) const receipt = await tx.wait() + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) } @@ -94,25 +106,27 @@ contract('MainModule', () => { it(`Relay 1/1 ${ntxs} transactions and ${nfailing} failing transactions`, async () => { const results: ethers.BigNumberish[] = [] - const transactions = new Array(ntxs) + const transactions: Transaction[] = new Array(ntxs) .fill(0) .map(() => ({ delegateCall: false, revertOnError: true, gasLimit: optimalGasLimit, - target: ethers.constants.AddressZero, - value: ethers.constants.Zero, - data: [] + target: ethers.ZeroAddress, + value: 0n, + data: new Uint8Array([]) })) .concat( - new Array(nfailing).fill(0).map(() => ({ - delegateCall: false, - revertOnError: false, - gasLimit: optimalGasLimit, - target: context.factory.address, - value: ethers.constants.Zero, - data: [] - })) + await Promise.all( + new Array(nfailing).fill(0).map(async () => ({ + delegateCall: false, + revertOnError: false, + gasLimit: optimalGasLimit, + target: await context.factory.getAddress(), + value: 0n, + data: new Uint8Array([]) + })) + ) ) for (let i = 0; i < runs; i++) { @@ -121,6 +135,10 @@ contract('MainModule', () => { const tx = await wallet.sendTransactions(transactions) const receipt = await tx.wait() + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) } @@ -128,13 +146,13 @@ contract('MainModule', () => { }) }) - const transaction = { + const transaction: Transaction = { delegateCall: false, revertOnError: true, gasLimit: optimalGasLimit, - target: ethers.constants.AddressZero, - value: ethers.constants.Zero, - data: [] + target: ethers.ZeroAddress, + value: 0n, + data: new Uint8Array([]) } it('Relay 2/5 transaction', async () => { @@ -146,6 +164,10 @@ contract('MainModule', () => { const tx = await wallet.sendTransactions([transaction]) const receipt = await tx.wait() + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) } @@ -162,6 +184,10 @@ contract('MainModule', () => { const tx = await wallet.sendTransactions([transaction], undefined, { gasLimit: 60000000 }) const receipt = await tx.wait() + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) } diff --git a/test/MainModule.spec.ts b/test/MainModule.spec.ts index 35685f6c..b6204f9f 100644 --- a/test/MainModule.spec.ts +++ b/test/MainModule.spec.ts @@ -1,11 +1,37 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { ethers as hethers } from 'hardhat' -import { bytes32toAddress, CHAIN_ID, expect, expectToBeRejected, randomHex } from './utils' - -import { CallReceiverMock, ContractType, deploySequenceContext, ModuleMock, SequenceContext, DelegateCallMock, HookMock, HookCallerMock, GasBurnerMock, AlwaysRevertMock } from './utils/contracts' +import { bytes32toAddress, getChainId, expect, expectToBeRejected, randomHex, getSigHash } from './utils' + +import { + CallReceiverMock, + ContractType, + deploySequenceContext, + ModuleMock, + SequenceContext, + DelegateCallMock, + HookMock, + HookCallerMock, + GasBurnerMock, + AlwaysRevertMock +} from './utils/contracts' import { Imposter } from './utils/imposter' -import { applyTxDefaults, computeStorageKey, digestOf, encodeNonce, leavesOf, legacyTopology, merkleTopology, optimize2SignersTopology, printTopology, SignatureType, SimplifiedWalletConfig, subdigestOf, toSimplifiedConfig, WalletConfig } from './utils/sequence' +import { + applyTxDefaults, + computeStorageKey, + digestOf, + encodeNonce, + leavesOf, + legacyTopology, + merkleTopology, + optimize2SignersTopology, + printTopology, + SignatureType, + SimplifiedWalletConfig, + subdigestOf, + toSimplifiedConfig, + WalletConfig +} from './utils/sequence' import { SequenceWallet, StaticSigner } from './utils/wallet' contract('MainModule', (accounts: string[]) => { @@ -20,6 +46,7 @@ contract('MainModule', (accounts: string[]) => { beforeEach(async () => { callReceiver = await CallReceiverMock.deploy() + await callReceiver.waitForDeployment() wallet = SequenceWallet.basicWallet(context) await wallet.deploy() @@ -49,11 +76,11 @@ contract('MainModule', (accounts: string[]) => { wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a] }) await wallet.deploy() - const valA = 4123222 + const valA = 4123222n const valB = randomHex(99) const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } @@ -76,11 +103,11 @@ contract('MainModule', (accounts: string[]) => { wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a, wallet_b] }) await wallet.deploy() - const valA = 4123222 + const valA = 4123222n const valB = randomHex(99) const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } @@ -102,11 +129,11 @@ contract('MainModule', (accounts: string[]) => { wallet = SequenceWallet.detailedWallet(context, { threshold: 1, signers: [wallet_a, singer_b] }) await wallet.deploy() - const valA = 4123222 + const valA = 4123222n const valB = randomHex(99) const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } @@ -115,51 +142,65 @@ contract('MainModule', (accounts: string[]) => { expect(await callReceiver.lastValA()).to.equal(valA) expect(await callReceiver.lastValB()).to.equal(valB) }) - - ;([{ - name: "2 nested sequence wallets", - childs: 1, - depth: 2 - }, { - name: "64 nested sequence wallets", - childs: 1, - depth: 64 - }, { - name: "97 nested sequence wallets", - childs: 1, - depth: 97 - }, { - name: "binary tree of sequence wallets", - childs: 2, - depth: 5 - }, { - name: "ternary tree of sequence wallets", - childs: 3, - depth: 4 - }, { - name: "hexary tree of sequence wallets", - childs: 16, - depth: 2 - }, { - name: "random tree of sequence wallets (depth 1)", - depth: 1 - }, { - name: "random tree of sequence wallets (depth 2)", - depth: 2 - }, { - name: "random tree of sequence wallets (depth 3)", - depth: 3 - }, { - name: "random tree of sequence wallets (depth 4)", - depth: 4 - }]).map((c) => { + ;[ + { + name: '2 nested sequence wallets', + childs: 1, + depth: 2 + }, + { + name: '64 nested sequence wallets', + childs: 1, + depth: 64 + }, + { + name: '97 nested sequence wallets', + childs: 1, + depth: 97 + }, + { + name: 'binary tree of sequence wallets', + childs: 2, + depth: 5 + }, + { + name: 'ternary tree of sequence wallets', + childs: 3, + depth: 4 + }, + { + name: 'hexary tree of sequence wallets', + childs: 16, + depth: 2 + }, + { + name: 'random tree of sequence wallets (depth 1)', + depth: 1 + }, + { + name: 'random tree of sequence wallets (depth 2)', + depth: 2 + }, + { + name: 'random tree of sequence wallets (depth 3)', + depth: 3 + }, + { + name: 'random tree of sequence wallets (depth 4)', + depth: 4 + } + ].map(c => { it(`Should handle ${c.name}`, async () => { - const genWallet = async (depth: number, numChilds: number | undefined, max: number): Promise => { + const genWallet = async ( + depth: number, + numChilds: number | undefined, + max: number + ): Promise => { if (depth === max) { return ethers.Wallet.createRandom() } - const nchilds = numChilds || (Math.floor(Math.random() * 5) + 1) + const nchilds = numChilds || Math.floor(Math.random() * 5) + 1 const childs = await Promise.all(new Array(nchilds).fill(0).map(async () => genWallet(depth + 1, nchilds, max))) const wallet = SequenceWallet.detailedWallet(context, { threshold: childs.length, signers: childs }) await wallet.deploy() @@ -167,13 +208,13 @@ contract('MainModule', (accounts: string[]) => { return wallet } - wallet = await genWallet(0, c.childs, c.depth) as SequenceWallet + wallet = (await genWallet(0, c.childs, c.depth)) as SequenceWallet - const valA = 5423 + const valA = 5423n const valB = randomHex(120) const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } @@ -199,12 +240,12 @@ contract('MainModule', (accounts: string[]) => { wallet = wallet.useSigners([wallet_a, wallet_b]) const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [5423, randomHex(32)]) } - const subdigest = subdigestOf(wallet.address, digestOf([transaction], await wallet.getNonce())) - const badNestedSignature = await wallet_b.signDigest(subdigest).then((s) => s + '03') + const subdigest = await subdigestOf(wallet.address, digestOf([transaction], await wallet.getNonce())) + const badNestedSignature = await wallet_b.signDigest(subdigest).then(s => s + '03') const tx = wallet.sendTransactions([transaction]) await expectToBeRejected(tx, `InvalidNestedSignature("${subdigest}", "${wallet_b.address}", "${badNestedSignature}")`) @@ -221,7 +262,7 @@ contract('MainModule', (accounts: string[]) => { await wallet.deploy() const signauture = await wallet.signTransactions([{}]) - const subdigest = subdigestOf(wallet.address, digestOf([{}], await wallet.getNonce())) + const subdigest = await subdigestOf(wallet.address, digestOf([{}], await wallet.getNonce())) const tx = wallet.relayTransactions([{}], signauture) await expect(tx).to.be.rejected @@ -237,14 +278,17 @@ contract('MainModule', (accounts: string[]) => { const wallet_c = SequenceWallet.basicWallet(context) await wallet_c.deploy() - wallet = SequenceWallet.detailedWallet(context, { threshold: 2, signers: [wallet_a, wallet_b, { weight: 2, value: wallet_c }] }) + wallet = SequenceWallet.detailedWallet(context, { + threshold: 2, + signers: [wallet_a, wallet_b, { weight: 2, value: wallet_c }] + }) await wallet.deploy() - const valA = 5423 + const valA = 5423n const valB = randomHex(120) const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } @@ -267,14 +311,14 @@ contract('MainModule', (accounts: string[]) => { describe('Network ID', () => { it('Should reject a transaction of another network id', async () => { - wallet = wallet.useChainId(CHAIN_ID() + 1) + wallet = wallet.useChainId((await getChainId()) + 1n) const tx = wallet.sendTransactions([{}]) await expectToBeRejected(tx, 'InvalidSignature') }) describe('Universal network signatures', async () => { it('Should reject signature for another network id, even if encoded as universal', async () => { - wallet = wallet.useChainId(CHAIN_ID() + 1) + wallet = wallet.useChainId((await getChainId()) + 1n) const tx = wallet.sendTransactions([{}]) await expectToBeRejected(tx, 'InvalidSignature') }) @@ -293,38 +337,38 @@ contract('MainModule', (accounts: string[]) => { }) describe('Nonce', () => { - const spaces = [ - ethers.BigNumber.from(0), - ethers.BigNumber.from(1), - ethers.BigNumber.from(7342), - ethers.BigNumber.from(ethers.utils.randomBytes(20)), - ethers.constants.Two.pow(160).sub(ethers.constants.One) - ] + const spaces = [0n, 1n, 7342n, ethers.toBigInt(ethers.randomBytes(20)), 2n ** 160n - 1n] describe('Using non-encoded nonce', () => { it('Should default to space zero', async () => { await wallet.sendTransactions([{}]) - expect(await wallet.mainModule.nonce()).to.equal(1) + expect(await wallet.mainModule.nonce()).to.equal(1n) }) it('Should work with zero as initial nonce', async () => { await wallet.sendTransactions([{}]) - expect(await wallet.mainModule.readNonce(0)).to.equal(1) + expect(await wallet.mainModule.readNonce(0)).to.equal(1n) }) it('Should emit NonceChange event', async () => { - const receipt1 = await wallet.sendTransactions([{}]).then((t) => t.wait()) - const receipt2 = await wallet.sendTransactions([{}]).then((t) => t.wait()) - - const ev1 = receipt1.events!.find(l => l.event === 'NonceChange') - expect(ev1!.event).to.be.eql('NonceChange') - expect(ev1!.args!._space).to.equal(0) - expect(ev1!.args!._newNonce).to.equal(1) - - const ev2 = receipt2.events!.find(l => l.event === 'NonceChange') - expect(ev2!.event).to.be.eql('NonceChange') - expect(ev1!.args!._space).to.equal(0) - expect(ev2!.args!._newNonce).to.equal(2) + const receipt1 = await wallet.sendTransactions([{}]).then(t => t.wait()) + const receipt2 = await wallet.sendTransactions([{}]).then(t => t.wait()) + + if (!receipt1 || !receipt2) { + throw new Error('No receipt') + } + + const events1 = receipt1.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev1 = events1.find(ev => ev.eventName === 'NonceChange') + expect(ev1!.eventName).to.be.eql('NonceChange') + expect(ev1!.args._space).to.equal(0n) + expect(ev1!.args._newNonce).to.equal(1n) + + const events2 = receipt2.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev2 = events2.find(ev => ev.eventName === 'NonceChange') + expect(ev2!.eventName).to.be.eql('NonceChange') + expect(ev1!.args._space).to.equal(0n) + expect(ev2!.args._newNonce).to.equal(2n) }) it('Should fail if nonce did not change', async () => { @@ -341,32 +385,38 @@ contract('MainModule', (accounts: string[]) => { }) spaces.forEach(space => { - describe(`using ${space.toHexString()} space`, () => { + describe(`using ${ethers.toBeHex(space)} space`, () => { it('Should work with zero as initial nonce', async () => { await wallet.sendTransactions([{}], encodeNonce(space, 0)) - expect(await wallet.mainModule.readNonce(space)).to.equal(1) + expect(await wallet.mainModule.readNonce(space)).to.equal(1n) }) it('Should emit NonceChange event', async () => { - const receipt1 = await wallet.sendTransactions([{}], encodeNonce(space, 0)).then((t) => t.wait()) - const receipt2 =await wallet.sendTransactions([{}], encodeNonce(space, 1)).then((t) => t.wait()) - - const ev1 = receipt1.events!.find(l => l.event === 'NonceChange') - expect(ev1!.event).to.be.eql('NonceChange') - expect(ev1!.args!._space).to.equal(space.toString()) - expect(ev1!.args!._newNonce).to.equal(1) - - const ev2 = receipt2.events!.find(l => l.event === 'NonceChange') - expect(ev2!.event).to.be.eql('NonceChange') - expect(ev2!.args!._space).to.equal(space.toString()) - expect(ev2!.args!._newNonce).to.equal(2) + const receipt1 = await wallet.sendTransactions([{}], encodeNonce(space, 0)).then(t => t.wait()) + const receipt2 = await wallet.sendTransactions([{}], encodeNonce(space, 1)).then(t => t.wait()) + + if (!receipt1 || !receipt2) { + throw new Error('No receipt') + } + + const events1 = receipt1.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev1 = events1.find(ev => ev.eventName === 'NonceChange') + expect(ev1!.eventName).to.be.eql('NonceChange') + expect(ev1!.args!._space).to.equal(space) + expect(ev1!.args!._newNonce).to.equal(1n) + + const events2 = receipt2.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev2 = events2.find(ev => ev.eventName === 'NonceChange') + expect(ev2!.eventName).to.be.eql('NonceChange') + expect(ev2!.args!._space).to.equal(space) + expect(ev2!.args!._newNonce).to.equal(2n) }) it('Should accept next nonce', async () => { await wallet.sendTransactions([{}], encodeNonce(space, 0)) await wallet.sendTransactions([{}], encodeNonce(space, 1)) - expect(await wallet.mainModule.readNonce(space)).to.equal(2) + expect(await wallet.mainModule.readNonce(space)).to.equal(2n) }) it('Should fail if nonce did not change', async () => { @@ -384,13 +434,13 @@ contract('MainModule', (accounts: string[]) => { }) it('Should use nonces storage keys', async () => { - const subkey = ethers.utils.defaultAbiCoder.encode(['uint256'], [space]) + const subkey = ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [space]) const storageKey = computeStorageKey('org.arcadeum.module.calls.nonce', subkey) await wallet.sendTransactions([{}], encodeNonce(space, 0)) - const storageValue = await hethers.provider.getStorageAt(wallet.address, storageKey) - expect(ethers.BigNumber.from(storageValue)).to.equal(1) + const storageValue = await hethers.provider.getStorage(wallet.address, storageKey) + expect(BigInt(storageValue)).to.equal(1n) }) }) }) @@ -399,36 +449,42 @@ contract('MainModule', (accounts: string[]) => { it('Should keep separated nonce counts', async () => { await wallet.sendTransactions([{}], encodeNonce(1, 0)) - expect(await wallet.mainModule.readNonce(1)).to.equal(1) - expect(await wallet.mainModule.readNonce(2)).to.equal(0) + expect(await wallet.mainModule.readNonce(1)).to.equal(1n) + expect(await wallet.mainModule.readNonce(2)).to.equal(0n) await wallet.sendTransactions([{}], encodeNonce(2, 0)) - expect(await wallet.mainModule.readNonce(1)).to.equal(1) - expect(await wallet.mainModule.readNonce(2)).to.equal(1) + expect(await wallet.mainModule.readNonce(1)).to.equal(1n) + expect(await wallet.mainModule.readNonce(2)).to.equal(1n) await wallet.sendTransactions([{}], encodeNonce(2, 1)) await wallet.sendTransactions([{}], encodeNonce(2, 2)) - expect(await wallet.mainModule.readNonce(1)).to.equal(1) - expect(await wallet.mainModule.readNonce(2)).to.equal(3) + expect(await wallet.mainModule.readNonce(1)).to.equal(1n) + expect(await wallet.mainModule.readNonce(2)).to.equal(3n) }) it('Should emit different events', async () => { await wallet.sendTransactions([{}], encodeNonce(1, 0)) await wallet.sendTransactions([{}], encodeNonce(1, 1)) - const receipt1 = await wallet.sendTransactions([{}], encodeNonce(1, 2)).then((t) => t.wait()) - const receipt2 = await wallet.sendTransactions([{}], encodeNonce(2, 0)).then((t) => t.wait()) + const receipt1 = await wallet.sendTransactions([{}], encodeNonce(1, 2)).then(t => t.wait()) + const receipt2 = await wallet.sendTransactions([{}], encodeNonce(2, 0)).then(t => t.wait()) - const ev1 = receipt1.events!.find(l => l.event === 'NonceChange') - expect(ev1!.event).to.be.eql('NonceChange') - expect(ev1!.args!._space).to.equal(1) - expect(ev1!.args!._newNonce).to.equal(3) + if (!receipt1 || !receipt2) { + throw new Error('No receipt') + } - const ev2 = receipt2.events!.find(l => l.event === 'NonceChange') - expect(ev2!.event).to.be.eql('NonceChange') - expect(ev2!.args!._space).to.equal(2) - expect(ev2!.args!._newNonce).to.equal(1) + const events1 = receipt1.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev1 = events1.find(ev => ev.eventName === 'NonceChange') + expect(ev1!.eventName).to.be.eql('NonceChange') + expect(ev1!.args!._space).to.equal(1n) + expect(ev1!.args!._newNonce).to.equal(3n) + + const events2 = receipt2.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev2 = events2.find(ev => ev.eventName === 'NonceChange') + expect(ev2!.eventName).to.be.eql('NonceChange') + expect(ev2!.args!._space).to.equal(2n) + expect(ev2!.args!._newNonce).to.equal(1n) }) it('Should not accept nonce of different space', async () => { @@ -446,7 +502,7 @@ contract('MainModule', (accounts: string[]) => { it('Should reject signature with bad encoding type', async () => { const tx = wallet.relayTransactions([{}], '0x2012') - const subdigest = subdigestOf(wallet.address, digestOf([{}], 0)) + const subdigest = await subdigestOf(wallet.address, digestOf([{}], 0)) await expectToBeRejected(tx, `InvalidSignatureType("0x20")`) }) @@ -470,9 +526,7 @@ contract('MainModule', (accounts: string[]) => { const transaction = { target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData( - 'updateImplementation', [newImplementation.address] - ) + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [await newImplementation.getAddress()]) } await wallet.sendTransactions([transaction]) @@ -480,50 +534,52 @@ contract('MainModule', (accounts: string[]) => { const mock_wallet = ModuleMock.attach(wallet.address) const tx = await mock_wallet.ping() const receipt = await tx.wait() - expect(receipt.events![0].event).to.equal('Pong') + + const events = receipt!.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + + expect(events![0].eventName).to.equal('Pong') }) it('Should fail to set implementation to address 0', async () => { const transaction = { target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData( - 'updateImplementation', [ethers.constants.AddressZero] - ) + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [ethers.ZeroAddress]) } const tx = wallet.sendTransactions([transaction]) - await expectToBeRejected(tx, `InvalidImplementation("${ethers.constants.AddressZero}")`) + await expectToBeRejected(tx, `InvalidImplementation("${ethers.ZeroAddress}")`) }) it('Should fail to set implementation to non-contract', async () => { const transaction = { target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData( - 'updateImplementation', [accounts[1]] - ) + data: wallet.mainModule.interface.encodeFunctionData('updateImplementation', [accounts[1]]) } const tx = wallet.sendTransactions([transaction]) await expectToBeRejected(tx, `InvalidImplementation("${accounts[1]}")`) }) it('Should use implementation storage key', async () => { - await wallet.updateImageHash(ethers.utils.randomBytes(32)) + await wallet.updateImageHash(ethers.randomBytes(32)) - const storageValue = await hethers.provider.getStorageAt(wallet.address, wallet.address) - expect(bytes32toAddress(storageValue)).to.equal(context.mainModuleUpgradable.address) + const storageValue = await hethers.provider.getStorage(wallet.address, wallet.address) + expect(bytes32toAddress(storageValue)).to.equal(await context.mainModuleUpgradable.getAddress()) }) }) describe('Extra image hashes', () => { - ([{ - name: 'using MainModule', - beforeEach: () => {}, - }, { - name: 'using MainModuleUpgradable', - beforeEach: async () => { - const newConfig = SequenceWallet.basicWallet(context) - await wallet.updateImageHash(newConfig.imageHash) - wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers) + ;[ + { + name: 'using MainModule', + beforeEach: () => {} + }, + { + name: 'using MainModuleUpgradable', + beforeEach: async () => { + const newConfig = SequenceWallet.basicWallet(context) + await wallet.updateImageHash(newConfig.imageHash) + wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers) + } } - }]).map((c) => { + ].map(c => { describe(c.name, () => { beforeEach(c.beforeEach) @@ -582,8 +638,8 @@ contract('MainModule', (accounts: string[]) => { .useConfig({ ...altWallet1.config, address: undefined }) .useSigners(altWallet1.signers) - expect(await wallet.mainModule.extraImageHash(altWallet1.imageHash)).to.equal(0) - expect(await wallet.mainModule.extraImageHash(altWallet2.imageHash)).to.equal(0) + expect(await wallet.mainModule.extraImageHash(altWallet1.imageHash)).to.equal(0n) + expect(await wallet.mainModule.extraImageHash(altWallet2.imageHash)).to.equal(0n) await expect(badWallet1.sendTransactions([{}])).to.be.rejected await expect(badWallet2.sendTransactions([{}])).to.be.rejected @@ -605,21 +661,24 @@ contract('MainModule', (accounts: string[]) => { }) describe('Static merkle digests', () => { - ([{ - name: 'using MainModule', - beforeEach: () => {}, - }, { - name: 'using MainModuleUpgradable', - beforeEach: async () => { - const newConfig = SequenceWallet.basicWallet(context) - await wallet.updateImageHash(newConfig.imageHash) - wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers) + ;[ + { + name: 'using MainModule', + beforeEach: () => {} + }, + { + name: 'using MainModuleUpgradable', + beforeEach: async () => { + const newConfig = SequenceWallet.basicWallet(context) + await wallet.updateImageHash(newConfig.imageHash) + wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers) + } } - }]).map((c) => { + ].map(c => { describe(c.name, () => { it('Should reject proof for another subdigest', async () => { - const digests = new Array(2).fill(0).map(() => ethers.utils.hexlify(ethers.utils.randomBytes(32))) - const subdigests = digests.map((d) => ({ subdigest: subdigestOf(wallet.address, d, 0) })) + const digests = new Array(2).fill(0).map(() => ethers.hexlify(ethers.randomBytes(32))) + const subdigests = await Promise.all(digests.map(async d => ({ subdigest: await subdigestOf(wallet.address, d, 0) }))) const subdigestsMerkle = merkleTopology([...subdigests]) const prevLeaves = leavesOf(wallet.config.topology) @@ -632,7 +691,7 @@ contract('MainModule', (accounts: string[]) => { await wallet.sendTransactions([]) - const subdigest = ethers.utils.hexlify(subdigests[0].subdigest) + const subdigest = ethers.hexlify(subdigests[0].subdigest) const encoded = wallet.staticSubdigestSign(subdigest) const res = await wallet.mainModule['isValidSignature(bytes32,bytes)'](digests[1], encoded) expect(res).to.equal('0x00000000') @@ -641,8 +700,8 @@ contract('MainModule', (accounts: string[]) => { it('Should accept merkle proof', async () => { wallet = SequenceWallet.basicWallet(context, { signing: 10, idle: 11 }) - const digests = new Array(33).fill(0).map(() => ethers.utils.hexlify(ethers.utils.randomBytes(32))) - const subdigests = digests.map((d) => ({ subdigest: subdigestOf(wallet.address, d, 0) })) + const digests = new Array(33).fill(0).map(() => ethers.hexlify(ethers.randomBytes(32))) + const subdigests = await Promise.all(digests.map(async d => ({ subdigest: await subdigestOf(wallet.address, d, 0) }))) const subdigestsMerkle = merkleTopology([...subdigests]) const prevLeaves = leavesOf(wallet.config.topology) @@ -656,7 +715,7 @@ contract('MainModule', (accounts: string[]) => { await wallet.sendTransactions([]) for (let i = 0; i < subdigests.length; i++) { - const subdigest = ethers.utils.hexlify(subdigests[i].subdigest) + const subdigest = ethers.hexlify(subdigests[i].subdigest) const encoded = wallet.staticSubdigestSign(subdigest) const res = await wallet.mainModule['isValidSignature(bytes32,bytes)'](digests[i], encoded) expect(res).to.equal('0x1626ba7e') @@ -672,13 +731,16 @@ contract('MainModule', (accounts: string[]) => { const simplifiedConfig = { threshold: 1, checkpoint: 2, - signers: [{ - address: ethers.Wallet.createRandom().address, - weight: 1 - }, { - ...toSimplifiedConfig(nested.config), - weight: 1 - }] + signers: [ + { + address: ethers.Wallet.createRandom().address, + weight: 1 + }, + { + ...toSimplifiedConfig(nested.config), + weight: 1 + } + ] } const config = { @@ -696,13 +758,16 @@ contract('MainModule', (accounts: string[]) => { const simplifiedConfig = { threshold: 5, checkpoint: 2, - signers: [{ - ...toSimplifiedConfig(nested.config), - weight: 5 - }, { - address: ethers.Wallet.createRandom().address, - weight: 1 - }] + signers: [ + { + ...toSimplifiedConfig(nested.config), + weight: 5 + }, + { + address: ethers.Wallet.createRandom().address, + weight: 1 + } + ] } const config = { @@ -721,13 +786,16 @@ contract('MainModule', (accounts: string[]) => { const simplifiedConfig = { threshold: 1, checkpoint: 2, - signers: [{ - ...toSimplifiedConfig(nested.config), - weight: 5 - }, { - address: ethers.Wallet.createRandom().address, - weight: 1 - }] + signers: [ + { + ...toSimplifiedConfig(nested.config), + weight: 5 + }, + { + address: ethers.Wallet.createRandom().address, + weight: 1 + } + ] } const config = { @@ -747,13 +815,16 @@ contract('MainModule', (accounts: string[]) => { const simplifiedConfig = { threshold: 2, checkpoint: 2, - signers: [{ - ...toSimplifiedConfig(nested.config), - weight: 1 - }, { - address: ethers.Wallet.createRandom().address, - weight: 1 - }] + signers: [ + { + ...toSimplifiedConfig(nested.config), + weight: 1 + }, + { + address: ethers.Wallet.createRandom().address, + weight: 1 + } + ] } const config = { @@ -770,11 +841,11 @@ contract('MainModule', (accounts: string[]) => { }) describe('External calls', () => { - let { valA, valB } = { valA: Math.floor(Date.now()), valB: randomHex(120) } + let { valA, valB } = { valA: BigInt(Math.floor(Date.now())), valB: randomHex(120) } it('Should perform call to contract', async () => { const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } @@ -786,8 +857,8 @@ contract('MainModule', (accounts: string[]) => { await callReceiver.setRevertFlag(true) const transaction = { - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) } const tx = wallet.sendTransactions([transaction]) @@ -795,20 +866,23 @@ contract('MainModule', (accounts: string[]) => { }) describe('Batch transactions', () => { let callReceiver2: ContractType - let { val2A, val2B } = { val2A: 9422, val2B: randomHex(31) } + let { val2A, val2B } = { val2A: 9422n, val2B: randomHex(31) } beforeEach(async () => { callReceiver2 = await CallReceiverMock.deploy() }) it('Should perform multiple calls to contracts in one tx', async () => { - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) - }, { - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('testCall', [val2A, val2B]) - }] + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) + }, + { + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('testCall', [val2A, val2B]) + } + ] await wallet.sendTransactions(transactions) expect(await callReceiver.lastValA()).to.equal(valA) @@ -818,36 +892,44 @@ contract('MainModule', (accounts: string[]) => { }) it('Should perform call a contract and transfer eth in one tx', async () => { - await hethers.provider.getSigner().sendTransaction({ to: wallet.address, value: 100 }) - const receiver = new ethers.Wallet(ethers.utils.randomBytes(32)) - - const transactions = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) - }, { - target: receiver.address, - value: 26, - }] + const signer = await hethers.provider.getSigner() + await signer.sendTransaction({ to: wallet.address, value: 100 }) + const receiver = new ethers.Wallet(ethers.hexlify(ethers.randomBytes(32))) + + const transactions = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) + }, + { + target: receiver.address, + value: 26 + } + ] await wallet.sendTransactions(transactions) expect(await callReceiver.lastValA()).to.equal(valA) expect(await callReceiver.lastValB()).to.equal(valB) - expect(await hethers.provider.getBalance(receiver.address)).to.equal(26) + expect(await hethers.provider.getBalance(receiver.address)).to.equal(26n) }) it('Should fail if one transaction fails', async () => { - await hethers.provider.getSigner().sendTransaction({ to: wallet.address, value: 100 }) + const signer = await hethers.provider.getSigner() + await signer.sendTransaction({ to: wallet.address, value: 100 }) await callReceiver.setRevertFlag(true) - const transactions = [{ - target: ethers.Wallet.createRandom().address, - value: 26 - }, { - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) - }] + const transactions = [ + { + target: ethers.Wallet.createRandom().address, + value: 26 + }, + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + } + ] const tx = wallet.sendTransactions(transactions) await expect(tx).to.be.rejectedWith('CallReceiverMock#testCall: REVERT_FLAG') @@ -865,7 +947,7 @@ contract('MainModule', (accounts: string[]) => { it('Should delegate call to module', async () => { const transaction1 = { delegateCall: true, - target: delegateCallMock.address, + target: await delegateCallMock.getAddress(), data: delegateCallMock.interface.encodeFunctionData('write', [11, 45]) } @@ -873,30 +955,37 @@ contract('MainModule', (accounts: string[]) => { const transaction2 = { delegateCall: true, - target: delegateCallMock.address, + target: await delegateCallMock.getAddress(), data: delegateCallMock.interface.encodeFunctionData('read', [11]) } const tx = await wallet.sendTransactions([transaction2]) const receipt = await tx.wait() - const val = ethers.BigNumber.from(receipt.logs.slice(-2)[0].data) - expect(val).to.equal(45) + + if (!receipt) { + throw new Error('No receipt') + } + + const val = BigInt(receipt.logs.slice(-2)[0].data) + expect(val).to.equal(45n) }) describe('on delegate call revert', () => { beforeEach(async () => { - await wallet.sendTransactions([{ - delegateCall: true, - target: delegateCallMock.address, - data: delegateCallMock.interface.encodeFunctionData('setRevertFlag', [true]) - }]) + await wallet.sendTransactions([ + { + delegateCall: true, + target: await delegateCallMock.getAddress(), + data: delegateCallMock.interface.encodeFunctionData('setRevertFlag', [true]) + } + ]) }) it('Should pass if revertOnError is false', async () => { const transaction = { delegateCall: true, revertOnError: false, - target: delegateCallMock.address, + target: await delegateCallMock.getAddress(), data: delegateCallMock.interface.encodeFunctionData('write', [11, 45]) } @@ -906,7 +995,7 @@ contract('MainModule', (accounts: string[]) => { it('Should fail if delegate call fails', async () => { const transaction = { delegateCall: true, - target: delegateCallMock.address, + target: await delegateCallMock.getAddress(), data: delegateCallMock.interface.encodeFunctionData('write', [11, 45]) } @@ -918,11 +1007,13 @@ contract('MainModule', (accounts: string[]) => { describe('Handle ETH', () => { it('Should receive ETH', async () => { - hethers.provider.getSigner().sendTransaction({ to: wallet.address, value: 1 }) + const signer = await hethers.provider.getSigner() + signer.sendTransaction({ to: wallet.address, value: 1 }) }) it('Should transfer ETH', async () => { - hethers.provider.getSigner().sendTransaction({ to: wallet.address, value: 100 }) + const signer = await hethers.provider.getSigner() + signer.sendTransaction({ to: wallet.address, value: 100 }) const receiver = ethers.Wallet.createRandom().address @@ -932,24 +1023,25 @@ contract('MainModule', (accounts: string[]) => { } await wallet.sendTransactions([transaction]) - expect(await hethers.provider.getBalance(receiver)).to.equal(25) + expect(await hethers.provider.getBalance(receiver)).to.equal(25n) }) it('Should call payable function', async () => { - hethers.provider.getSigner().sendTransaction({ to: wallet.address, value: 100 }) + const signer = await hethers.provider.getSigner() + signer.sendTransaction({ to: wallet.address, value: 100 }) - const valA = 63129 + const valA = 63129n const valB = randomHex(120) - const value = 33 + const value = 33n const transaction = { - target: callReceiver.address, + target: await callReceiver.getAddress(), value: value, data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) } await wallet.sendTransactions([transaction]) - expect(await hethers.provider.getBalance(callReceiver.address)).to.equal(value) + expect(await hethers.provider.getBalance(await callReceiver.getAddress())).to.equal(value) expect(await callReceiver.lastValA()).to.equal(valA) expect(await callReceiver.lastValB()).to.equal(valB) }) @@ -961,20 +1053,26 @@ contract('MainModule', (accounts: string[]) => { const transaction = { revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + } + + const receipt = await wallet.sendTransactions([transaction]).then(r => r.wait()) + + if (!receipt) { + throw new Error('No receipt') } - const receipt = await wallet.sendTransactions([transaction]).then((r) => r.wait()) - const event = receipt.events!.pop() + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const event = events.pop() - const reason = ethers.utils.defaultAbiCoder.decode(['string'], "0x" + event!.args!._reason.slice(10))[0] + const reason = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event!.args!._reason.slice(10))[0] expect(reason).to.equal('CallReceiverMock#testCall: REVERT_FLAG') }) describe('With multiple transactions', () => { let callReceiver2: ContractType - const { valA, valB } = { valA: 912341, valB: randomHex(30) } + const { valA, valB } = { valA: 912341n, valB: randomHex(30) } beforeEach(async () => { callReceiver2 = await CallReceiverMock.deploy() @@ -983,20 +1081,29 @@ contract('MainModule', (accounts: string[]) => { it('Should skip failing transaction within batch', async () => { await callReceiver.setRevertFlag(true) - const transactions = [{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) - }, { - revertOnError: false, - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('testCall', [valA, valB]) - }] + const transactions = [ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + }, + { + revertOnError: false, + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('testCall', [valA, valB]) + } + ] - const receipt = await wallet.sendTransactions(transactions).then((r) => r.wait()) - const event = receipt.events!.find(l => l.event === 'TxFailed') + const receipt = await wallet.sendTransactions(transactions).then(r => r.wait()) - const reason = ethers.utils.defaultAbiCoder.decode(['string'], "0x" + event!.args!._reason.slice(10))[0] + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const ev = events.find(ev => ev.eventName === 'TxFailed') + + const reason = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + ev!.args!._reason.slice(10))[0] expect(reason).to.equal('CallReceiverMock#testCall: REVERT_FLAG') expect(await callReceiver2.lastValA()).to.equal(valA) @@ -1006,27 +1113,37 @@ contract('MainModule', (accounts: string[]) => { it('Should skip multiple failing transactions within batch', async () => { await callReceiver.setRevertFlag(true) - const transactions = [{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) - }, { - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('testCall', [valA, valB]) - }] + const transactions = [ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + }, + { + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('testCall', [valA, valB]) + } + ] - const txHash = subdigestOf(wallet.address, digestOf(transactions, await wallet.getNonce())) - const receipt = await wallet.sendTransactions(transactions).then((r) => r.wait()) + const txHash = await subdigestOf(wallet.address, digestOf(transactions, await wallet.getNonce())) + const receipt = await wallet.sendTransactions(transactions).then(r => r.wait()) - const event1 = receipt.events![1] - const event2 = receipt.events![2] + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + + const event1 = events[1] + const event2 = events[2] - const reason1 = ethers.utils.defaultAbiCoder.decode(['string'], "0x" + event1.args!._reason.slice(10))[0] - const reason2 = ethers.utils.defaultAbiCoder.decode(['string'], "0x" + event2.args!._reason.slice(10))[0] + const reason1 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event1.args!._reason.slice(10))[0] + const reason2 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event2.args!._reason.slice(10))[0] expect(reason1).to.equal('CallReceiverMock#testCall: REVERT_FLAG') expect(reason2).to.equal('CallReceiverMock#testCall: REVERT_FLAG') @@ -1041,22 +1158,32 @@ contract('MainModule', (accounts: string[]) => { it('Should skip all failing transactions within a batch', async () => { await callReceiver.setRevertFlag(true) - const transactions = [{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [0, []]) - }] + const transactions = [ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [0, new Uint8Array([])]) + } + ] + + const receipt = await wallet.sendTransactions(transactions).then(r => r.wait()) + + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] - const receipt = await wallet.sendTransactions(transactions).then((r) => r.wait()) - const event1 = receipt.events!.pop() - const event2 = receipt.events!.pop() + const event1 = events.pop() + const event2 = events.pop() - const reason1 = ethers.utils.defaultAbiCoder.decode(['string'], "0x" + event1!.args!._reason.slice(10))[0] - const reason2 = ethers.utils.defaultAbiCoder.decode(['string'], "0x" + event2!.args!._reason.slice(10))[0] + const reason1 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event1!.args!._reason.slice(10))[0] + const reason2 = ethers.AbiCoder.defaultAbiCoder().decode(['string'], '0x' + event2!.args!._reason.slice(10))[0] expect(reason1).to.equal('CallReceiverMock#testCall: REVERT_FLAG') expect(reason2).to.equal('CallReceiverMock#testCall: REVERT_FLAG') @@ -1088,7 +1215,7 @@ contract('MainModule', (accounts: string[]) => { describe('ERC1271 Wallet', () => { let message = randomHex(250) - let hash = ethers.utils.keccak256(ethers.utils.randomBytes(32)) + let hash = ethers.keccak256(ethers.randomBytes(32)) it('Should validate arbitrary signed data', async () => { const signature = await wallet.signMessage(message) @@ -1121,30 +1248,31 @@ contract('MainModule', (accounts: string[]) => { before(async () => { hookMock = await HookMock.deploy() - hookSelector = hookMock.interface.getSighash('onHookMockCall') + const fragment = hookMock.interface.getFunction('onHookMockCall') + hookSelector = getSigHash(fragment) }) it('Should return zero if hook is not registered', async () => { - expect(await wallet.mainModule.readHook(hookSelector)).to.be.equal(ethers.constants.AddressZero) + expect(await wallet.mainModule.readHook(hookSelector)).to.be.equal(ethers.ZeroAddress) }) describe('With registered hook', () => { beforeEach(async () => { const transaction = { target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('addHook', [hookSelector, hookMock.address]) + data: wallet.mainModule.interface.encodeFunctionData('addHook', [hookSelector, await hookMock.getAddress()]) } await wallet.sendTransactions([transaction]) }) it('Should read added hook', async () => { - expect(await wallet.mainModule.readHook(hookSelector)).to.be.equal(hookMock.address) + expect(await wallet.mainModule.readHook(hookSelector)).to.be.equal(await hookMock.getAddress()) }) it('Should forward call to external hook', async () => { const walletHook = HookMock.attach(wallet.address) - expect(await walletHook.onHookMockCall(21)).to.equal(42) + expect(await walletHook.onHookMockCall(21)).to.equal(42n) }) it('Should not forward call to deregistered hook', async () => { @@ -1160,46 +1288,49 @@ contract('MainModule', (accounts: string[]) => { }) it('Should use hooks storage key', async () => { - const subkey = ethers.utils.defaultAbiCoder.encode(['bytes4'], [hookSelector]) + const subkey = ethers.AbiCoder.defaultAbiCoder().encode(['bytes4'], [hookSelector]) const storageKey = computeStorageKey('org.arcadeum.module.hooks.hooks', subkey) - const storageValue = await hethers.provider.getStorageAt(wallet.address, storageKey) + const storageValue = await hethers.provider.getStorage(wallet.address, storageKey) const addr = (() => { try { - return ethers.utils.getAddress(ethers.utils.defaultAbiCoder.decode(['address'], storageValue)[0]) + return ethers.getAddress(ethers.AbiCoder.defaultAbiCoder().decode(['address'], storageValue)[0]) } catch { - return ethers.utils.getAddress(storageValue) + return ethers.getAddress(storageValue) } })() - expect(addr).to.equal(hookMock.address) + expect(addr).to.equal(await hookMock.getAddress()) }) }) it('Should pass calling a non registered hook', async () => { - const data = ethers.utils.defaultAbiCoder.encode(['bytes4'], [hookSelector]) - await hethers.provider.getSigner().sendTransaction({ to: wallet.address, data: data }) + const data = ethers.AbiCoder.defaultAbiCoder().encode(['bytes4'], [hookSelector]) + const signer = await hethers.provider.getSigner() + await signer.sendTransaction({ to: wallet.address, data: data }) }) }) it('Should not forward msg.data with less than 4 bytes', async () => { const alwaysRevertMock = await AlwaysRevertMock.deploy() - const paddedSelector = "0x11223300" + const paddedSelector = '0x11223300' const transaction = { target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('addHook', [paddedSelector, alwaysRevertMock.address]) + data: wallet.mainModule.interface.encodeFunctionData('addHook', [paddedSelector, await alwaysRevertMock.getAddress()]) } await wallet.sendTransactions([transaction]) + const signer = await hethers.provider.getSigner() + // Calling the wallet with '0x112233' should not forward the call to the hook - const tx = hethers.provider.getSigner().sendTransaction({ to: wallet.address, data: "0x112233" }).then((t) => t.wait()) + const tx = signer.sendTransaction({ to: wallet.address, data: '0x112233' }).then(t => t.wait()) await expect(tx).to.be.fulfilled // Calling the wallet with '0x11223300' should forward the call to the hook (and thus revert) - const tx2 = hethers.provider.getSigner().sendTransaction({ to: wallet.address, data: "0x11223300" }).then((t) => t.wait()) + const tx2 = signer.sendTransaction({ to: wallet.address, data: '0x11223300' }).then(t => t.wait()) await expect(tx2).to.be.rejected }) @@ -1212,12 +1343,17 @@ contract('MainModule', (accounts: string[]) => { data: wallet.mainModule.interface.encodeFunctionData('addHook', [selector, implementation]) } - const receipt = await wallet.sendTransactions([transaction]).then((t) => t.wait()) - const event = receipt.events?.find((e) => e.event === 'DefinedHook') + const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait()) + + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const event = events.find(ev => ev.eventName === 'DefinedHook') expect(event).to.not.be.undefined - const decode = event!.decode!(event!.data) - expect(decode._signature).to.equal(selector) - expect(decode._implementation).to.equal(implementation) + expect(event!.args._signature).to.equal(selector) + expect(event!.args._implementation).to.equal(implementation) }) it('Should emit an event when removing a hook', async () => { @@ -1228,23 +1364,25 @@ contract('MainModule', (accounts: string[]) => { target: wallet.address, data: wallet.mainModule.interface.encodeFunctionData('addHook', [selector, implementation]) } - - await wallet.sendTransactions([transaction1]).then((t) => t.wait()) + await wallet.sendTransactions([transaction1]).then(t => t.wait()) const transaction2 = { target: wallet.address, data: wallet.mainModule.interface.encodeFunctionData('removeHook', [selector]) } - - const receipt = await wallet.sendTransactions([transaction2]).then((t) => t.wait()) + const receipt = await wallet.sendTransactions([transaction2]).then(t => t.wait()) - const event = receipt.events?.find((e) => e.event === 'DefinedHook') + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + const event = events.find(ev => ev.eventName === 'DefinedHook') expect(event).to.not.be.undefined - const decode = event!.decode!(event!.data) - expect(decode._signature).to.equal(selector) - expect(decode._implementation).to.equal(ethers.constants.AddressZero) + expect(event?.args._signature).to.equal(selector) + expect(event?.args._implementation).to.equal(ethers.ZeroAddress) }) }) @@ -1256,12 +1394,12 @@ contract('MainModule', (accounts: string[]) => { }) it('Should fail to update to invalid image hash', async () => { - const tx = wallet.updateImageHash(ethers.constants.HashZero) + const tx = wallet.updateImageHash(ethers.ZeroHash) await expectToBeRejected(tx, 'ImageHashIsZero()') }) it('Should fail to change image hash from non-self address', async () => { - const tx = wallet.mainModule.updateImageHash(ethers.utils.randomBytes(32)) + const tx = wallet.mainModule.updateImageHash(ethers.randomBytes(32)) await expectToBeRejected(tx, `OnlySelfAuth("${accounts[0]}", "${wallet.address}")`) }) @@ -1284,19 +1422,19 @@ contract('MainModule', (accounts: string[]) => { }) it('Should fail to update to invalid image hash', async () => { - const tx = walletb.updateImageHash(ethers.constants.HashZero) + const tx = walletb.updateImageHash(ethers.ZeroHash) await expectToBeRejected(tx, 'ImageHashIsZero()') }) it('Should fail to change image hash from non-self address', async () => { - const tx = wallet.mainModuleUpgradable.updateImageHash(ethers.utils.randomBytes(32)) + const tx = wallet.mainModuleUpgradable.updateImageHash(ethers.randomBytes(32)) await expectToBeRejected(tx, `OnlySelfAuth("${accounts[0]}", "${wallet.address}")`) }) it('Should use image hash storage key', async () => { const storageKey = computeStorageKey('org.arcadeum.module.auth.upgradable.image.hash') - const storageValue = await hethers.provider.getStorageAt(wallet.address, storageKey) - expect(ethers.utils.defaultAbiCoder.encode(['bytes32'], [storageValue])).to.equal(walletb.imageHash) + const storageValue = await hethers.provider.getStorage(wallet.address, storageKey) + expect(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32'], [storageValue])).to.equal(walletb.imageHash) }) it('Should fail to execute transactions on moduleUpgradable implementation', async () => { @@ -1327,29 +1465,34 @@ contract('MainModule', (accounts: string[]) => { it('Should use image hash storage key', async () => { const storageKey = computeStorageKey('org.arcadeum.module.auth.upgradable.image.hash') - const storageValue = await hethers.provider.getStorageAt(walletb.address, storageKey) - expect(ethers.utils.defaultAbiCoder.encode(['bytes32'], [storageValue])).to.equal(walletc.imageHash) + const storageValue = await hethers.provider.getStorage(walletb.address, storageKey) + expect(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32'], [storageValue])).to.equal(walletc.imageHash) }) }) }) }) describe('Multisignature', async () => { - const modes = [{ - name: "Forced dynamic part encoding, legacy signature type", - encodingOptions: { forceDynamicEncoding: true, signatureType: SignatureType.Legacy } - }, { - name: "Default part encoding, legacy signature encoding", - encodingOptions: { signatureType: SignatureType.Legacy } - }, { - name: "Forced dynamic part encoding, dynamic signature type", - encodingOptions: { forceDynamicEncoding: true, signatureType: SignatureType.Dynamic } - }, { - name: "Default part encoding, dynamic signature type", - encodingOptions: { signatureType: SignatureType.Legacy } - }] - - modes.map((mode) => { + const modes = [ + { + name: 'Forced dynamic part encoding, legacy signature type', + encodingOptions: { forceDynamicEncoding: true, signatureType: SignatureType.Legacy } + }, + { + name: 'Default part encoding, legacy signature encoding', + encodingOptions: { signatureType: SignatureType.Legacy } + }, + { + name: 'Forced dynamic part encoding, dynamic signature type', + encodingOptions: { forceDynamicEncoding: true, signatureType: SignatureType.Dynamic } + }, + { + name: 'Default part encoding, dynamic signature type', + encodingOptions: { signatureType: SignatureType.Legacy } + } + ] + + modes.map(mode => { describe(mode.name, () => { let encodingOptions = mode.encodingOptions @@ -1437,7 +1580,11 @@ contract('MainModule', (accounts: string[]) => { let signer3 = ethers.Wallet.createRandom() beforeEach(async () => { - wallet = SequenceWallet.detailedWallet(context, { threshold: 2, signers: [signer1, signer2, signer3], encodingOptions }) + wallet = SequenceWallet.detailedWallet(context, { + threshold: 2, + signers: [signer1, signer2, signer3], + encodingOptions + }) await wallet.deploy() }) @@ -1493,7 +1640,9 @@ contract('MainModule', (accounts: string[]) => { }) it('Should reject message if signed with a wrong configuration', async () => { - const tx = wallet.useConfig(SequenceWallet.detailedWallet(context, { threshold: 3, signers: [signer1, signer2] })).sendTransactions([{}]) + const tx = wallet + .useConfig(SequenceWallet.detailedWallet(context, { threshold: 3, signers: [signer1, signer2] })) + .sendTransactions([{}]) await expect(tx).to.be.rejected }) }) @@ -1529,28 +1678,26 @@ contract('MainModule', (accounts: string[]) => { it('Should reject message signed by all non-owners', async () => { const tx = wallet - .useSigners(wallet.signers.map((s) => Imposter.random(s as ethers.Wallet))) - .sendTransactions([{}], undefined, { gasLimit: 60000000 }) + .useSigners(wallet.signers.map(s => Imposter.random(s as ethers.Wallet))) + .sendTransactions([{}], undefined, { gasLimit: 60000000 }) await expect(tx).to.be.rejected }) it('Should reject message missing a signature', async () => { - const tx = wallet - .useSigners(wallet.signers.slice(1)) - .sendTransactions([{}], undefined, { gasLimit: 60000000 }) + const tx = wallet.useSigners(wallet.signers.slice(1)).sendTransactions([{}], undefined, { gasLimit: 60000000 }) await expect(tx).to.be.rejected }) }) describe('With weighted owners', () => { - let signers: ethers.Wallet[] + let signers: ethers.BaseWallet[] beforeEach(async () => { signers = new Array(5).fill(null).map(() => ethers.Wallet.createRandom()) wallet = SequenceWallet.detailedWallet(context, { threshold: 4, - signers: ([3, 3, 1, 1, 1]).map((weight, i) => ({ weight, value: signers[i] })) + signers: [3, 3, 1, 1, 1].map((weight, i) => ({ weight, value: signers[i] })) }) await wallet.deploy() }) @@ -1572,7 +1719,7 @@ contract('MainModule', (accounts: string[]) => { }) it('Should reject signed message with (1)/4 weight', async () => { - const tx = wallet.useSigners([signers[3]]).sendTransactions([{}]) + const tx = wallet.useSigners([signers[3]]).sendTransactions([{}]) await expect(tx).to.be.rejected }) @@ -1597,42 +1744,45 @@ contract('MainModule', (accounts: string[]) => { }) it('Should reject message signed by non-owner', async () => { - const tx = wallet.useSigners([signers[0], Imposter.random(signers[3])]).sendTransactions([{}]) + const tx = wallet.useSigners([signers[0], Imposter.random(signers[3])]).sendTransactions([{}]) await expect(tx).to.be.rejected - }) - - describe('Reject invalid signatures', () => { - beforeEach(async () => { - wallet = SequenceWallet.basicWallet(context, { encodingOptions }) - await wallet.deploy() }) - it("Should reject invalid signature type", async () => { - const signature = await wallet.signTransactions([{}]) - const badSignature = signature.slice(0, -2) + 'ff' - const tx = wallet.relayTransactions([{}], badSignature) - await expect(tx).to.be.rejected - }) + describe('Reject invalid signatures', () => { + beforeEach(async () => { + wallet = SequenceWallet.basicWallet(context, { encodingOptions }) + await wallet.deploy() + }) - it("Should reject invalid s value", async () => { - const signature = await wallet.signTransactions([{}]) - const prefix = mode.encodingOptions.forceDynamicEncoding ? 118 : 74 - const invalidSignature = signature.slice(0, prefix) + "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1" + signature.slice(prefix + 64) - const tx = wallet.relayTransactions([{}], invalidSignature) - await expect(tx).to.be.reverted - }) + it('Should reject invalid signature type', async () => { + const signature = await wallet.signTransactions([{}]) + const badSignature = signature.slice(0, -2) + 'ff' + const tx = wallet.relayTransactions([{}], badSignature) + await expect(tx).to.be.rejected + }) - it("Should reject invalid v value", async () => { - const signature = await wallet.signTransactions([{}]) - const prefix = mode.encodingOptions.forceDynamicEncoding ? 182 : 138 - const invalidSignature = signature.slice(0, prefix) + "1a" + signature.slice(prefix + 2) - const tx = wallet.relayTransactions([{}], invalidSignature) - await expect(tx).to.be.reverted + it('Should reject invalid s value', async () => { + const signature = await wallet.signTransactions([{}]) + const prefix = mode.encodingOptions.forceDynamicEncoding ? 118 : 74 + const invalidSignature = + signature.slice(0, prefix) + + '7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1' + + signature.slice(prefix + 64) + const tx = wallet.relayTransactions([{}], invalidSignature) + await expect(tx).to.be.reverted + }) + + it('Should reject invalid v value', async () => { + const signature = await wallet.signTransactions([{}]) + const prefix = mode.encodingOptions.forceDynamicEncoding ? 182 : 138 + const invalidSignature = signature.slice(0, prefix) + '1a' + signature.slice(prefix + 2) + const tx = wallet.relayTransactions([{}], invalidSignature) + await expect(tx).to.be.reverted + }) }) }) }) }) - }) }) describe('Gas limit', () => { @@ -1647,12 +1797,17 @@ contract('MainModule', (accounts: string[]) => { const transaction = { gasLimit: gas, - target: gasBurner.address, + target: await gasBurner.getAddress(), data: gasBurner.interface.encodeFunctionData('burnGas', [0]) } - const receipt = await wallet.sendTransactions([transaction]).then((t) => t.wait()) - const reported = ethers.BigNumber.from(receipt.logs.slice(-2)[0].data) + const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait()) + + if (!receipt) { + throw new Error('No receipt') + } + + const reported = Number(BigInt(receipt.logs.slice(-2)[0].data)) expect(reported).to.be.below(gas) }) @@ -1660,20 +1815,27 @@ contract('MainModule', (accounts: string[]) => { const gasA = 10000 const gasB = 350000 - const transactions = [{ - gasLimit: gasA, - target: gasBurner.address, - data: gasBurner.interface.encodeFunctionData('burnGas', [8000]) - }, { - gasLimit: gasB, - target: gasBurner.address, - data: gasBurner.interface.encodeFunctionData('burnGas', [340000]) - }] + const transactions = [ + { + gasLimit: gasA, + target: await gasBurner.getAddress(), + data: gasBurner.interface.encodeFunctionData('burnGas', [8000]) + }, + { + gasLimit: gasB, + target: await gasBurner.getAddress(), + data: gasBurner.interface.encodeFunctionData('burnGas', [340000]) + } + ] - const receipt = await wallet.sendTransactions(transactions).then((t) => t.wait()) + const receipt = await wallet.sendTransactions(transactions).then(t => t.wait()) - const reportedB = ethers.BigNumber.from(receipt.logs.slice(-2)[0].data) - const reportedA = ethers.BigNumber.from(receipt.logs.slice(-4)[0].data) + if (!receipt) { + throw new Error('No receipt') + } + + const reportedB = Number(BigInt(receipt.logs.slice(-2)[0].data)) + const reportedA = Number(BigInt(receipt.logs.slice(-4)[0].data)) expect(reportedA).to.be.below(gasA) expect(reportedB).to.be.below(gasB) @@ -1685,7 +1847,7 @@ contract('MainModule', (accounts: string[]) => { const transaction = { gasLimit: gas, - target: gasBurner.address, + target: await gasBurner.getAddress(), data: gasBurner.interface.encodeFunctionData('burnGas', [11000]) } @@ -1699,36 +1861,52 @@ contract('MainModule', (accounts: string[]) => { const transaction = { revertOnError: false, gasLimit: gas, - target: gasBurner.address, + target: await gasBurner.getAddress(), data: gasBurner.interface.encodeFunctionData('burnGas', [200000]) } - const receipt = await wallet.sendTransactions([transaction]).then((t) => t.wait()) - const log = receipt.events!.pop() - expect(log!.event).to.be.equal('TxFailed') + const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait()) + + if (!receipt) { + throw new Error('No receipt') + } + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + + const log = events.pop() + expect(log!.eventName).to.be.equal('TxFailed') }) it('Should continue execution if optional call runs out of gas', async () => { const gas = 10000 - const valA = 9512358833 + const valA = 9512358833n const valB = randomHex(1600) - const transactions = [{ - revertOnError: false, - gasLimit: gas, - target: gasBurner.address, - data: gasBurner.interface.encodeFunctionData('burnGas', [200000]) - }, { - revertOnError: true, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) - }] + const transactions = [ + { + revertOnError: false, + gasLimit: gas, + target: await gasBurner.getAddress(), + data: gasBurner.interface.encodeFunctionData('burnGas', [200000]) + }, + { + revertOnError: true, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [valA, valB]) + } + ] + + const receipt = await wallet.sendTransactions(transactions).then(t => t.wait()) + + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] - const receipt = await wallet.sendTransactions(transactions).then((t) => t.wait()) - const log = receipt.events!.slice(-2)[0] + const log = events.slice(-2)[0] - expect(log.event).to.be.equal('TxFailed') + expect(log.eventName).to.be.equal('TxFailed') expect(await callReceiver.lastValA()).to.equal(valA) expect(await callReceiver.lastValB()).to.equal(valB) }) @@ -1752,22 +1930,30 @@ contract('MainModule', (accounts: string[]) => { data: wallet.mainModule.interface.encodeFunctionData('createContract', [deployCode]) } - const receipt = await wallet.sendTransactions([transaction]).then((t) => t.wait()) - const log = receipt.events!.find(l => l.event === 'CreatedContract') + const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait()) - expect(log!.event).to.equal('CreatedContract') + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + + const log = events!.find(l => l.eventName === 'CreatedContract') + + expect(log!.eventName).to.equal('CreatedContract') const deployed = CallReceiverMock.attach(log!.args!._contract) await deployed.testCall(12345, '0x552299') - expect(await deployed.lastValA()).to.equal(12345) + expect(await deployed.lastValA()).to.equal(12345n) expect(await deployed.lastValB()).to.equal('0x552299') }) it('Should create a contract with value', async () => { const deployCode = CallReceiverMock.factory().bytecode - await hethers.provider.getSigner().sendTransaction({ to: wallet.address, value: 100 }) + const signer = await hethers.provider.getSigner() + await signer.sendTransaction({ to: wallet.address, value: 100 }) const transaction = { target: wallet.address, @@ -1775,10 +1961,17 @@ contract('MainModule', (accounts: string[]) => { data: wallet.mainModule.interface.encodeFunctionData('createContract', [deployCode]) } - const receipt = await wallet.sendTransactions([transaction]).then((t) => t.wait()) - const log = receipt.events!.find(l => l.event === 'CreatedContract') + const receipt = await wallet.sendTransactions([transaction]).then(t => t.wait()) - expect(await hethers.provider.getBalance(log!.args!._contract)).to.equal(99) + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] + + const log = events!.find(l => l.eventName === 'CreatedContract') + + expect(await hethers.provider.getBalance(log!.args!._contract)).to.equal(99n) }) it('Should fail to create a contract from non-self', async () => { @@ -1789,30 +1982,41 @@ contract('MainModule', (accounts: string[]) => { describe('Transaction events', () => { it('Should emit TxExecuted event', async () => { - const txHash = subdigestOf(wallet.address, digestOf([{}], await wallet.getNonce())) - const receipt = await wallet.sendTransactions([{}]).then((t) => t.wait()) + const txHash = await subdigestOf(wallet.address, digestOf([{}], await wallet.getNonce())) + const res = await wallet.sendTransactions([{}]) + const receipt = await res.wait() + + if (!receipt) { + throw new Error('No receipt') + } + + const events = receipt.logs.filter(log => log instanceof ethers.EventLog) as ethers.EventLog[] - const log = receipt.events!.find(l => l.event === 'TxExecuted')! + const log = events!.find(l => l.eventName === 'TxExecuted')! expect(log.topics.length).to.equal(2) expect(log.topics[1]).to.be.equal(txHash) - expect(log.data).to.be.equal(ethers.utils.solidityPack(['uint256'], [0])) + expect(log.data).to.be.equal(ethers.solidityPacked(['uint256'], [0])) }) it('Should emit multiple TxExecuted events', async () => { - const txHash = subdigestOf(wallet.address, digestOf([{}, {}], await wallet.getNonce())) - const receipt = await wallet.sendTransactions([{}, {}]).then((t) => t.wait()) + const txHash = await subdigestOf(wallet.address, digestOf([{}, {}], await wallet.getNonce())) + const receipt = await wallet.sendTransactions([{}, {}]).then(t => t.wait()) + + if (!receipt) { + throw new Error('No receipt') + } const log1 = receipt.logs[1] const log2 = receipt.logs[2] expect(log1.topics.length).to.equal(2) expect(log1.topics[1]).to.be.equal(txHash) - expect(log1.data).to.be.equal(ethers.utils.solidityPack(['uint256'], [0])) + expect(log1.data).to.be.equal(ethers.solidityPacked(['uint256'], [0])) expect(log2.topics.length).to.equal(2) expect(log1.topics[1]).to.be.equal(txHash) - expect(log2.data).to.be.equal(ethers.utils.solidityPack(['uint256'], [1])) + expect(log2.data).to.be.equal(ethers.solidityPacked(['uint256'], [1])) }) }) @@ -1823,23 +2027,28 @@ contract('MainModule', (accounts: string[]) => { const expected1 = randomHex(552) const expected2 = randomHex(24) - const bundle = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1]) - }, { - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2]) - }] + const bundle = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1]) + }, + { + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2]) + } + ] - const transaction = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)]) - }] + const transaction = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)]) + } + ] await wallet.sendTransactions(transaction) - expect(await callReceiver.lastValA()).to.equal(11) - expect(await callReceiver2.lastValA()).to.equal(12) + expect(await callReceiver.lastValA()).to.equal(11n) + expect(await callReceiver2.lastValA()).to.equal(12n) expect(await callReceiver.lastValB()).to.equal(expected1) expect(await callReceiver2.lastValB()).to.equal(expected2) @@ -1848,28 +2057,34 @@ contract('MainModule', (accounts: string[]) => { it('Should execute multiple internal bundles', async () => { const data = [ [ - { i: 0, a: 142, b: 412 }, - { i: 1, a: 123, b: 2 } + { i: 0, a: 142n, b: 412 }, + { i: 1, a: 123n, b: 2 } ], [ - { i: 2, a: 142, b: 2 }, - { i: 3, a: 642, b: 33 }, - { i: 4, a: 122, b: 12 }, - { i: 5, a: 611, b: 53 } + { i: 2, a: 142n, b: 2 }, + { i: 3, a: 642n, b: 33 }, + { i: 4, a: 122n, b: 12 }, + { i: 5, a: 611n, b: 53 } ], - [{ i: 6, a: 2, b: 1 }], + [{ i: 6, a: 2n, b: 1 }], [] ] - const contracts = await Promise.all((data).flat().map(() => CallReceiverMock.deploy())) - const expectedb = await Promise.all((data).flat().map((d) => randomHex(d.b))) - - const bundles = data.map(bundle => { - return applyTxDefaults(bundle.map(obj => ({ - target: contracts[obj.i].address, - data: contracts[obj.i].interface.encodeFunctionData('testCall', [obj.a, expectedb[obj.i]]) - }))) - }) + const contracts = await Promise.all(data.flat().map(() => CallReceiverMock.deploy())) + const expectedb = await Promise.all(data.flat().map(d => randomHex(d.b))) + + const bundles = await Promise.all( + data.map(async bundle => { + return applyTxDefaults( + await Promise.all( + bundle.map(async obj => ({ + target: await contracts[obj.i].getAddress(), + data: contracts[obj.i].interface.encodeFunctionData('testCall', [obj.a, expectedb[obj.i]]) + })) + ) + ) + }) + ) const transactions = bundles.map(bundle => ({ target: wallet.address, @@ -1878,8 +2093,8 @@ contract('MainModule', (accounts: string[]) => { await wallet.sendTransactions(transactions) - const lastValsA = await Promise.all(contracts.map((c) => c.lastValA())) - const lastValsB = await Promise.all(contracts.map((c) => c.lastValB())) + const lastValsA = await Promise.all(contracts.map(c => c.lastValA())) + const lastValsB = await Promise.all(contracts.map(c => c.lastValB())) lastValsA.forEach((val, i) => expect(val).to.equal(data.flat()[i].a)) lastValsB.forEach((val, i) => expect(val).to.equal(expectedb[i])) @@ -1891,28 +2106,35 @@ contract('MainModule', (accounts: string[]) => { const expected1 = randomHex(552) const expected2 = randomHex(24) - const bundle = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1]) - }, { - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2]) - }] + const bundle = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1]) + }, + { + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2]) + } + ] - const nestedBundle = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)]) - }] + const nestedBundle = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)]) + } + ] - const transactions = [{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(nestedBundle)]) - }] + const transactions = [ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(nestedBundle)]) + } + ] await wallet.sendTransactions(transactions) - expect(await callReceiver.lastValA()).to.equal(11) - expect(await callReceiver2.lastValA()).to.equal(12) + expect(await callReceiver.lastValA()).to.equal(11n) + expect(await callReceiver2.lastValA()).to.equal(12n) expect(await callReceiver.lastValB()).to.equal(expected1) expect(await callReceiver2.lastValB()).to.equal(expected2) @@ -1926,66 +2148,75 @@ contract('MainModule', (accounts: string[]) => { const expected2 = randomHex(24) const expected3 = randomHex(11) - const bundle = [{ - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1]) - }, { - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2]) - }, { - // This transaction will revert - // because Factory has no fallback - revertOnError: true, - target: context.factory.address, - data: callReceiver.interface.encodeFunctionData('testCall', [12, expected2]) - }] - - const transaction = [{ - revertOnError: false, - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)]) - }, { - revertOnError: false, - target: callReceiver3.address, - data: callReceiver3.interface.encodeFunctionData('testCall', [51, expected3]) - }] + const bundle = [ + { + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [11, expected1]) + }, + { + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('testCall', [12, expected2]) + }, + { + // This transaction will revert + // because Factory has no fallback + revertOnError: true, + target: await context.factory.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [12, expected2]) + } + ] + + const transaction = [ + { + revertOnError: false, + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('selfExecute', [applyTxDefaults(bundle)]) + }, + { + revertOnError: false, + target: await callReceiver3.getAddress(), + data: callReceiver3.interface.encodeFunctionData('testCall', [51, expected3]) + } + ] await wallet.sendTransactions(transaction) - expect(await callReceiver.lastValA()).to.equal(0) - expect(await callReceiver2.lastValA()).to.equal(0) - expect(await callReceiver3.lastValA()).to.equal(51) + expect(await callReceiver.lastValA()).to.equal(0n) + expect(await callReceiver2.lastValA()).to.equal(0n) + expect(await callReceiver3.lastValA()).to.equal(51n) - expect(await callReceiver.lastValB()).to.equal("0x") - expect(await callReceiver2.lastValB()).to.equal("0x") + expect(await callReceiver.lastValB()).to.equal('0x') + expect(await callReceiver2.lastValB()).to.equal('0x') expect(await callReceiver3.lastValB()).to.equal(expected3) }) }) describe('Update imageHash and IPFS at once', () => { - ([{ - name: 'using MainModule', - beforeEach: () => {}, - }, { - name: 'using MainModuleUpgradable', - beforeEach: async () => { - const newConfig = SequenceWallet.basicWallet(context) - await wallet.updateImageHash(newConfig.imageHash) - wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers) + ;[ + { + name: 'using MainModule', + beforeEach: () => {} + }, + { + name: 'using MainModuleUpgradable', + beforeEach: async () => { + const newConfig = SequenceWallet.basicWallet(context) + await wallet.updateImageHash(newConfig.imageHash) + wallet = wallet.useAddress().useConfig(newConfig.config).useSigners(newConfig.signers) + } } - }]).map((c) => { + ].map(c => { describe(c.name, () => { it('Should update imageHash and IPFS at the same time', async () => { const ipfs = randomHex(32) const imageHash = randomHex(32) - await wallet.sendTransactions([{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData( - 'updateImageHashAndIPFS', - [imageHash, ipfs] - ) - }]) + await wallet.sendTransactions([ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('updateImageHashAndIPFS', [imageHash, ipfs]) + } + ]) expect(await wallet.mainModuleUpgradable.imageHash()).to.equal(imageHash) expect(await wallet.mainModule.ipfsRootBytes32()).to.equal(ipfs) @@ -2000,13 +2231,12 @@ contract('MainModule', (accounts: string[]) => { const nextWallet = SequenceWallet.basicWallet(context) const ipfs = randomHex(32) - await wallet.sendTransactions([{ - target: wallet.address, - data: wallet.mainModule.interface.encodeFunctionData( - 'updateImageHashAndIPFS', - [nextWallet.imageHash, ipfs] - ) - }]) + await wallet.sendTransactions([ + { + target: wallet.address, + data: wallet.mainModule.interface.encodeFunctionData('updateImageHashAndIPFS', [nextWallet.imageHash, ipfs]) + } + ]) wallet = wallet.useAddress(wallet.address).useConfig(nextWallet.config).useSigners(nextWallet.signers) await wallet.sendTransactions([{}]) diff --git a/test/MerkleSignatures.spec.ts b/test/MerkleSignatures.spec.ts index ca96065f..bbcc2f87 100644 --- a/test/MerkleSignatures.spec.ts +++ b/test/MerkleSignatures.spec.ts @@ -1,9 +1,8 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { deploySequenceContext, SequenceContext } from './utils/contracts' import { legacyTopology, merkleTopology, printTopology, toSimplifiedConfig } from './utils/sequence' import { SequenceWallet } from './utils/wallet' - contract('MerkleSignatures', () => { let context: SequenceContext @@ -11,7 +10,7 @@ contract('MerkleSignatures', () => { context = await deploySequenceContext() }) - it("Should display config topology", async () => { + it('Should display config topology', async () => { const wallet = SequenceWallet.basicWallet(context, { signing: 10 }) const simplifiedConfig = toSimplifiedConfig(wallet.config) const topology1 = legacyTopology(simplifiedConfig) diff --git a/test/MultiCallUtils.spec.ts b/test/MultiCallUtils.spec.ts index 6a881309..085e7b78 100644 --- a/test/MultiCallUtils.spec.ts +++ b/test/MultiCallUtils.spec.ts @@ -1,13 +1,13 @@ -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { ethers as hethers } from 'hardhat' -import { b, CHAIN_ID, encodeError, expect, expectStaticToBeRejected } from './utils' +import { getChainId, encodeError, expect, expectStaticToBeRejected } from './utils' import { CallReceiverMock, ContractType, MultiCallUtils } from './utils/contracts' import { applyTxDefault, applyTxDefaults } from './utils/sequence' contract('Multi call utils', (accounts: string[]) => { let multiCall: ContractType - let callReceiver: ContractType< typeof CallReceiverMock> + let callReceiver: ContractType before(async () => { multiCall = await MultiCallUtils.deploy() @@ -20,189 +20,226 @@ contract('Multi call utils', (accounts: string[]) => { describe('Call multiple contracts', () => { it('Should execute empty call', async () => { - const res = await multiCall.callStatic.multiCall([]) + const res = await multiCall.multiCall.staticCall([]) expect(res[0].length).to.equal(0) expect(res[1].length).to.equal(0) }) it('Should execute single call', async () => { - await callReceiver.testCall(5123, []) - const res = await multiCall.callStatic.multiCall(applyTxDefaults([{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }])) + await callReceiver.testCall(5123, new Uint8Array([])) + const res = await multiCall.multiCall.staticCall( + applyTxDefaults([ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + } + ]) + ) expect(res[0].length).to.equal(1) expect(res[1].length).to.equal(1) expect(res[0][0]).to.be.true - expect(res[1][0]).to.be.equal(ethers.utils.defaultAbiCoder.encode(['uint256'], [5123])) + expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [5123])) }) it('Should execute two calls', async () => { - const bytes = ethers.utils.randomBytes(422) + const bytes = ethers.randomBytes(422) await callReceiver.testCall(55522, bytes) - const res = await multiCall.callStatic.multiCall(applyTxDefaults([{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValB') - }])) + const res = await multiCall.multiCall.staticCall( + applyTxDefaults([ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValB') + } + ]) + ) expect(res[0].length).to.equal(2) expect(res[1].length).to.equal(2) expect(res[0][0]).to.be.true - expect(res[1][0]).to.be.equal(ethers.utils.defaultAbiCoder.encode(['uint256'], [55522])) + expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [55522])) expect(res[0][1]).to.be.true - expect(ethers.utils.defaultAbiCoder.decode(['bytes'], res[1][1])[0]).to.be.equal(ethers.utils.hexlify(bytes)) + expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][1])[0]).to.be.equal(ethers.hexlify(bytes)) }) it('Should execute calls to multiple contracts', async () => { const callReceiver2 = await CallReceiverMock.deploy() - const bytes = ethers.utils.hexlify(ethers.utils.randomBytes(21)) + const bytes = ethers.hexlify(ethers.randomBytes(21)) await callReceiver.testCall(55522, bytes) - await callReceiver2.testCall(66623, []) - - const res = await multiCall.callStatic.multiCall(applyTxDefaults([{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValB') - }, { - revertOnError: false, - target: callReceiver2.address, - data: callReceiver2.interface.encodeFunctionData('lastValA') - }])) + await callReceiver2.testCall(66623, new Uint8Array([])) + + const res = await multiCall.multiCall.staticCall( + applyTxDefaults([ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValB') + }, + { + revertOnError: false, + target: await callReceiver2.getAddress(), + data: callReceiver2.interface.encodeFunctionData('lastValA') + } + ]) + ) expect(res[0].length).to.equal(3) expect(res[1].length).to.equal(3) expect(res[0][0]).to.be.true - expect(res[1][0]).to.be.equal(ethers.utils.defaultAbiCoder.encode(['uint256'], [55522])) + expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [55522])) expect(res[0][1]).to.be.true - expect(ethers.utils.defaultAbiCoder.decode(['bytes'], res[1][1])[0]).to.be.equal(bytes) + expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][1])[0]).to.be.equal(bytes) expect(res[0][2]).to.be.true - expect(res[1][2]).to.be.equal(ethers.utils.defaultAbiCoder.encode(['uint256'], [66623])) + expect(res[1][2]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [66623])) }) it('Return other calls even if single call fails', async () => { - const bytes = ethers.utils.randomBytes(422) + const bytes = ethers.randomBytes(422) await callReceiver.testCall(55522, bytes) await callReceiver.setRevertFlag(true) - const res = await multiCall.callStatic.multiCall(applyTxDefaults([{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]) - }])) + const res = await multiCall.multiCall.staticCall( + applyTxDefaults([ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]) + } + ]) + ) expect(res[0].length).to.equal(2) expect(res[1].length).to.equal(2) expect(res[0][0]).to.be.true - expect(res[1][0]).to.be.equal(ethers.utils.defaultAbiCoder.encode(['uint256'], [55522])) + expect(res[1][0]).to.be.equal(ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], [55522])) expect(res[0][1]).to.be.false }) it('Fail if call with revert on error fails', async () => { - const bytes = ethers.utils.randomBytes(422) + const bytes = ethers.randomBytes(422) await callReceiver.testCall(55522, bytes) await callReceiver.setRevertFlag(true) - const tx = multiCall.callStatic.multiCall(applyTxDefaults([{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }, { - revertOnError: true, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]) - }])) - - await expectStaticToBeRejected(tx, `CallReverted(uint256,bytes)`, 1, encodeError("CallReceiverMock#testCall: REVERT_FLAG")) + const tx = multiCall.multiCall.staticCall( + applyTxDefaults([ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + }, + { + revertOnError: true, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]) + } + ]) + ) + + await expectStaticToBeRejected(tx, `CallReverted(uint256,bytes)`, 1, encodeError('CallReceiverMock#testCall: REVERT_FLAG')) }) it('Fail if batch includes delegate call', async () => { - const bytes = ethers.utils.randomBytes(422) + const bytes = ethers.randomBytes(422) await callReceiver.testCall(55522, bytes) - const tx = multiCall.callStatic.multiCall(applyTxDefaults([{ - delegateCall: true, - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]) - }])) + const tx = multiCall.multiCall.staticCall( + applyTxDefaults([ + { + delegateCall: true, + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]) + } + ]) + ) await expectStaticToBeRejected(tx, `DelegateCallNotAllowed(uint256)`, 0) }) it('Fail if not enough gas for call', async () => { - const bytes = ethers.utils.randomBytes(422) + const bytes = ethers.randomBytes(422) await callReceiver.testCall(55522, bytes) - const tx = multiCall.callStatic.multiCall(applyTxDefaults([{ - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('lastValA') - }, { - revertOnError: false, - target: callReceiver.address, - data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]), - gasLimit: b(2) - .pow(256) - .sub(b(1)) - }])) - - await expectStaticToBeRejected(tx, `NotEnoughGas(uint256,uint256,uint256)`, 1, b(2).pow(256).sub(b(1)), '*') + const tx = multiCall.multiCall.staticCall( + applyTxDefaults([ + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('lastValA') + }, + { + revertOnError: false, + target: await callReceiver.getAddress(), + data: callReceiver.interface.encodeFunctionData('testCall', [111, bytes]), + gasLimit: 2n ** 256n - 1n + } + ]) + ) + + await expectStaticToBeRejected(tx, `NotEnoughGas(uint256,uint256,uint256)`, 1, 2n ** 256n - 1n, '*') }) it('Should call globals', async () => { const i = multiCall.interface const emptyAccount = ethers.Wallet.createRandom().address - await multiCall.callStatic.multiCall([]) + await multiCall.multiCall.staticCall([]) const lastBlock = await hethers.provider.getBlock('latest') - const txs = [ - i.encodeFunctionData('callBlockhash', [lastBlock.number - 1]), - i.encodeFunctionData('callCoinbase'), - i.encodeFunctionData('callDifficulty'), - i.encodeFunctionData('callGasLimit'), - i.encodeFunctionData('callBlockNumber'), - i.encodeFunctionData('callTimestamp'), - i.encodeFunctionData('callGasLeft'), - i.encodeFunctionData('callGasPrice'), - i.encodeFunctionData('callOrigin'), - i.encodeFunctionData('callBalanceOf', [accounts[0]]), - i.encodeFunctionData('callBalanceOf', [emptyAccount]), - i.encodeFunctionData('callCodeSize', [accounts[0]]), - i.encodeFunctionData('callCodeSize', [callReceiver.address]), - i.encodeFunctionData('callCode', [accounts[0]]), - i.encodeFunctionData('callCode', [callReceiver.address]), - i.encodeFunctionData('callCodeHash', [accounts[0]]), - i.encodeFunctionData('callCodeHash', [callReceiver.address]), - i.encodeFunctionData('callChainId') - ].map(data => (applyTxDefault({ - revertOnError: false, - target: multiCall.address, - data - }))) - - const res = await multiCall.callStatic.multiCall(txs, { gasPrice: 1 }) - - const emptyBytes32 = ethers.utils.defaultAbiCoder.encode(['uint256'], ['0']) + const txs = await Promise.all( + [ + i.encodeFunctionData('callBlockhash', [lastBlock!.number - 1]), + i.encodeFunctionData('callCoinbase'), + i.encodeFunctionData('callDifficulty'), + i.encodeFunctionData('callGasLimit'), + i.encodeFunctionData('callBlockNumber'), + i.encodeFunctionData('callTimestamp'), + i.encodeFunctionData('callGasLeft'), + i.encodeFunctionData('callGasPrice'), + i.encodeFunctionData('callOrigin'), + i.encodeFunctionData('callBalanceOf', [accounts[0]]), + i.encodeFunctionData('callBalanceOf', [emptyAccount]), + i.encodeFunctionData('callCodeSize', [accounts[0]]), + i.encodeFunctionData('callCodeSize', [await callReceiver.getAddress()]), + i.encodeFunctionData('callCode', [accounts[0]]), + i.encodeFunctionData('callCode', [await callReceiver.getAddress()]), + i.encodeFunctionData('callCodeHash', [accounts[0]]), + i.encodeFunctionData('callCodeHash', [await callReceiver.getAddress()]), + i.encodeFunctionData('callChainId') + ].map(async data => + applyTxDefault({ + revertOnError: false, + target: await multiCall.getAddress(), + data + }) + ) + ) + + const res = await multiCall.multiCall.staticCall(txs, { gasPrice: 1 }) + + const emptyBytes32 = ethers.AbiCoder.defaultAbiCoder().encode(['uint256'], ['0']) expect(res[0].length).to.equal(txs.length) expect(res[1].length).to.equal(txs.length) @@ -221,25 +258,25 @@ contract('Multi call utils', (accounts: string[]) => { expect(res[1][4]).to.not.equal(emptyBytes32, 'return block number') expect(res[1][5]).to.not.equal(emptyBytes32, 'return timestamp') expect(res[1][6]).to.not.equal(emptyBytes32, 'return gas left') - expect(ethers.BigNumber.from(res[1][7])).to.equal(1, 'return gas price') + expect(BigInt(res[1][7])).to.equal(1n, 'return gas price') expect(res[1][8]).to.not.equal(emptyBytes32, 'return origin') expect(res[1][9]).to.not.equal(emptyBytes32, 'return balance of 0x') expect(res[1][10]).to.equal(emptyBytes32, 'return balance of empty account') expect(res[1][11]).to.equal(emptyBytes32, 'return code size of empty account') expect(res[1][12]).to.not.equal(emptyBytes32, 'return code size of contract') - expect(ethers.utils.defaultAbiCoder.decode(['bytes'], res[1][13])[0]).to.equal('0x') + expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][13])[0]).to.equal('0x') - const codeSize = ethers.utils.defaultAbiCoder.decode(['uint256'], res[1][12])[0] - expect(ethers.utils.defaultAbiCoder.decode(['bytes'], res[1][14])[0].length).to.equal( - 2 + codeSize * 2, + const codeSize = ethers.AbiCoder.defaultAbiCoder().decode(['uint256'], res[1][12])[0] + expect(ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], res[1][14])[0].length).to.equal( + 2 + Number(codeSize) * 2, 'return code of correct size' ) expect(res[1][15]).to.not.equal(emptyBytes32) expect(res[1][16]).to.not.equal(emptyBytes32) - expect(ethers.utils.defaultAbiCoder.decode(['uint256'], res[1][17])[0].toNumber()).to.equal(CHAIN_ID(), 'return chain id') + expect(ethers.AbiCoder.defaultAbiCoder().decode(['uint256'], res[1][17])[0]).to.equal(await getChainId(), 'return chain id') }) }) }) diff --git a/test/utils/contracts.ts b/test/utils/contracts.ts index 48063924..c2d6561b 100644 --- a/test/utils/contracts.ts +++ b/test/utils/contracts.ts @@ -1,57 +1,56 @@ -import * as ethers from "ethers" -import { ethers as hethers } from "hardhat" -import * as t from "../../gen/typechain" +import { ethers } from 'ethers' +import { ethers as hethers } from 'hardhat' +import * as t from '../../gen/typechain' const cachedFactories: { [name: string]: ethers.ContractFactory } = {} -async function deploy(name: string, ...args: any[]) { +async function deploy(name: string, ...args: any[]) { const factory = await hethers.getContractFactory(name) cachedFactories[name] = factory - return await factory.deploy(...args) as Y + return (await factory.deploy(...args)) as unknown as Y } -function attach(name: string, address: string) { +function attach(name: string, address: string) { return cachedFactories[name].attach(address) as Y } -type Adapter = { +type Adapter = { cache: () => Promise deploy: (...args: any[]) => Promise attach: (address: string) => T factory: () => ethers.ContractFactory } -function adapt(name: string): Adapter { +function adapt(name: string): Adapter { return { - cache: async () => cachedFactories[name] = await hethers.getContractFactory(name), + cache: async () => (cachedFactories[name] = await hethers.getContractFactory(name)), deploy: (...args: any[]) => deploy(name, ...args), attach: (address: string) => attach(name, address), - factory: () => cachedFactories[name], + factory: () => cachedFactories[name] } } export type ContractType> = T extends Adapter ? U : never -export const LibBytesImpl = adapt("LibBytesImpl") -export const LibBytesPointerImpl = adapt("LibBytesPointerImpl") -export const Factory = adapt("Factory") -export const MainModule = adapt("MainModule") -export const MainModuleUpgradable = adapt("MainModuleUpgradable") -export const ERC165CheckerMock = adapt("ERC165CheckerMock") -export const ModuleMock = adapt("ModuleMock") -export const CallReceiverMock = adapt("CallReceiverMock") -export const MultiCallUtils = adapt("MultiCallUtils") -export const GuestModule = adapt("GuestModule") -export const HookMock = adapt("HookMock") -export const HookCallerMock = adapt("HookCallerMock") -export const RequireUtils = adapt("RequireUtils") -export const DelegateCallMock = adapt("DelegateCallMock") -export const GasBurnerMock = adapt("GasBurnerMock") -export const GasEstimator = adapt("GasEstimator") -export const MainModuleGasEstimation = adapt("MainModuleGasEstimation") -export const LibStringImp = adapt("LibStringImp") -export const AlwaysRevertMock = adapt("AlwaysRevertMock") - +export const LibBytesImpl = adapt('LibBytesImpl') +export const LibBytesPointerImpl = adapt('LibBytesPointerImpl') +export const Factory = adapt('Factory') +export const MainModule = adapt('MainModule') +export const MainModuleUpgradable = adapt('MainModuleUpgradable') +export const ERC165CheckerMock = adapt('ERC165CheckerMock') +export const ModuleMock = adapt('ModuleMock') +export const CallReceiverMock = adapt('CallReceiverMock') +export const MultiCallUtils = adapt('MultiCallUtils') +export const GuestModule = adapt('GuestModule') +export const HookMock = adapt('HookMock') +export const HookCallerMock = adapt('HookCallerMock') +export const RequireUtils = adapt('RequireUtils') +export const DelegateCallMock = adapt('DelegateCallMock') +export const GasBurnerMock = adapt('GasBurnerMock') +export const GasEstimator = adapt('GasEstimator') +export const MainModuleGasEstimation = adapt('MainModuleGasEstimation') +export const LibStringImp = adapt('LibStringImp') +export const AlwaysRevertMock = adapt('AlwaysRevertMock') ;[ LibBytesImpl, Factory, @@ -67,23 +66,22 @@ export const AlwaysRevertMock = adapt("AlwaysRevertMock") RequireUtils, DelegateCallMock, GasEstimator -].map((c) => c.cache()) +].map(c => c.cache()) export const deploySequenceContext = async (owner?: string) => { const factory = await Factory.deploy() - const mainModuleUpgradable = await MainModuleUpgradable.deploy() + const mainModule = await MainModule.deploy(await factory.getAddress(), await mainModuleUpgradable.getAddress()) - const mainModule = await MainModule.deploy( - factory.address, - mainModuleUpgradable.address - ) - - return { factory, mainModule, mainModuleUpgradable } + return { + factory: await factory.waitForDeployment(), + mainModule: await mainModule.waitForDeployment(), + mainModuleUpgradable: await mainModuleUpgradable.waitForDeployment() + } } export type SequenceContext = { - factory: ContractType, - mainModule: ContractType, + factory: ContractType + mainModule: ContractType mainModuleUpgradable: ContractType -} \ No newline at end of file +} diff --git a/test/utils/imposter.ts b/test/utils/imposter.ts index 4045915c..f8da84b4 100644 --- a/test/utils/imposter.ts +++ b/test/utils/imposter.ts @@ -1,12 +1,17 @@ import { ethers } from 'ethers' import { AnyStaticSigner, StaticSigner } from './wallet' -export class Imposter extends ethers.Signer implements StaticSigner { +export class Imposter extends ethers.AbstractSigner implements StaticSigner { static random(identity: string | AnyStaticSigner) { return new Imposter(identity, ethers.Wallet.createRandom()) } - constructor (public identity: string | AnyStaticSigner, public signer: ethers.Signer) { super() } + constructor( + public identity: string | AnyStaticSigner, + public signer: ethers.Signer + ) { + super() + } get address() { return typeof this.identity === 'string' ? this.identity : this.identity.address @@ -16,15 +21,23 @@ export class Imposter extends ethers.Signer implements StaticSigner { return this.address } - signMessage(message: string | ethers.utils.Bytes): Promise { + signMessage(message: string | Uint8Array): Promise { return this.signer.signMessage(message) } - signTransaction(transaction: ethers.utils.Deferrable): Promise { + signTypedData( + domain: ethers.TypedDataDomain, + types: Record, + value: Record + ): Promise { + return this.signer.signTypedData(domain, types, value) + } + + signTransaction(transaction: ethers.TransactionRequest): Promise { return this.signer.signTransaction(transaction) } - connect(provider: ethers.providers.Provider): ethers.Signer { + connect(provider: ethers.Provider): ethers.Signer { return this.signer.connect(provider) } } diff --git a/test/utils/index.ts b/test/utils/index.ts index 84727caf..86eab632 100644 --- a/test/utils/index.ts +++ b/test/utils/index.ts @@ -1,22 +1,18 @@ import * as chai from 'chai' import chaiAsPromised from 'chai-as-promised' import chaiString from 'chai-string' -import * as ethers from 'ethers' +import { ethers } from 'ethers' import { solidity } from 'ethereum-waffle' import { ethers as hethers } from 'hardhat' -ethers.utils.Logger.setLogLevel(ethers.utils.Logger.levels.ERROR) +export const getChainId = async (): Promise => + process.env.NET_ID ? BigInt(process.env.NET_ID) : (await hethers.provider.getNetwork()).chainId -export const CHAIN_ID = () => process.env.NET_ID ? ethers.BigNumber.from(process.env.NET_ID).toNumber() : hethers.provider.network.chainId - -export const { assert, expect } = chai - .use(chaiString) - .use(chaiAsPromised) - .use(solidity) +export const { assert, expect } = chai.use(chaiString).use(chaiAsPromised).use(solidity) export function bytes32toAddress(bytes32: ethers.BytesLike): string { - const paddedValue = ethers.utils.zeroPad(bytes32, 32) - return ethers.utils.getAddress(ethers.utils.defaultAbiCoder.decode(['address'], paddedValue)[0]) + const paddedValue = ethers.zeroPadValue(bytes32, 32) + return ethers.getAddress(ethers.AbiCoder.defaultAbiCoder().decode(['address'], paddedValue)[0]) } export function shuffle(a: T[]): T[] { @@ -28,12 +24,8 @@ export function shuffle(a: T[]): T[] { return a } -export function b(raw: ethers.BigNumberish): ethers.BigNumber { - return ethers.BigNumber.from(raw) -} - export function randomHex(length: number): string { - return ethers.utils.hexlify(ethers.utils.randomBytes(length)) + return ethers.hexlify(ethers.randomBytes(length)) } export async function expectToBeRejected(promise: Promise, error: string) { @@ -45,64 +37,82 @@ export async function expectToBeRejected(promise: Promise, error: string) { } export async function expectStaticToBeRejected(promise: Promise, signature: string, ...args: any[]) { - await expectToBeRejected(promise, `errorName="${signature.split("(")[0]}"`) - await expectToBeRejected(promise, `errorSignature="${signature}"`) + // await expectToBeRejected(promise, `errorName="${signature.split('(')[0]}"`) + // await expectToBeRejected(promise, `errorSignature="${signature}"`) + + await expectToBeRejected(promise, `${signature.split('(')[0]}`) - const sigTypes = signature.split("(")[1].split(")")[0].split(",") + const sigTypes = signature.split('(')[1].split(')')[0].split(',') expect(sigTypes.length).to.equal(args.length) - const formattedArgs = args.map((arg, i) => { - const type = sigTypes[i] + const formattedArgs = args + .map((arg, i) => { + const type = sigTypes[i] - if (arg === "*") return '*' + if (arg === '*') return '*' - switch (type) { - case "bytes": - if (typeof arg === 'string' && arg.length === 0) return ethers.utils.hexlify([]) - return `"${ethers.utils.hexlify(arg).toLowerCase()}"` - case "string": - return `"${arg.toString()}"` - } + switch (type) { + case 'bytes': + if (typeof arg === 'string' && arg.length === 0) { + return ethers.hexlify(new Uint8Array([])) + } + return `"${ethers.hexlify(arg).toLowerCase()}"` + case 'string': + return `"${arg.toString()}"` + } - if (type.startsWith("uint") || type.startsWith("int")) { - return `{"type":"BigNumber","hex":"${ethers.BigNumber.from(arg).toHexString()}"}` - } + if (type.startsWith('uint') || type.startsWith('int')) { + //return `{"type":"BigNumber","hex":"${ethers.toBeHex(BigInt(arg))}"}` + return BigInt(arg).toString() + } - throw new Error(`Unknown type: ${type}`) - }).join(",") + throw new Error(`Unknown type: ${type}`) + }) + .join(', ') const groups = formattedArgs.split('*') - if (groups.length === 1) { - await expectToBeRejected(promise, `errorArgs=[${formattedArgs}]`) - } else { - for (let i = 0; i < groups.length; i++) { - const group = groups[i] - if (i === 0) { - await expectToBeRejected(promise, `errorArgs=[${group}`) - } else if (i === groups.length - 1) { - await expectToBeRejected(promise, `${group}]`) - } else { - await expectToBeRejected(promise, `${group}`) - } - } + + for (let i = 0; i < groups.length; i++) { + await expectToBeRejected(promise, `${groups[i]}`) } + + // if (groups.length === 1) { + // // await expectToBeRejected(promise, `errorArgs=[${formattedArgs}]`) + // await expectToBeRejected(promise, `${formattedArgs}`) + // } else { + // for (let i = 0; i < groups.length; i++) { + // const group = groups[i] + // if (i === 0) { + // // await expectToBeRejected(promise, `errorArgs=[${group}`) + // await expectToBeRejected(promise, `${group}`) + // } else if (i === groups.length - 1) { + // await expectToBeRejected(promise, `${group}]`) + // } else { + // await expectToBeRejected(promise, `${group}`) + // } + // } + // } } export function encodeError(error: string): string { - return "0x08c379a0" + ethers.utils.defaultAbiCoder.encode(['string'], [error]).slice(2) + return '0x08c379a0' + ethers.AbiCoder.defaultAbiCoder().encode(['string'], [error]).slice(2) } function xor(a: any, b: any) { - if (!Buffer.isBuffer(a)) a = Buffer.from(ethers.utils.arrayify(a)) - if (!Buffer.isBuffer(b)) b = Buffer.from(ethers.utils.arrayify(b)) - return ethers.utils.hexlify(a.map((v: number, i: number) => v ^ b[i])) + if (!Buffer.isBuffer(a)) a = Buffer.from(ethers.getBytes(a)) + if (!Buffer.isBuffer(b)) b = Buffer.from(ethers.getBytes(b)) + return ethers.hexlify(a.map((v: number, i: number) => v ^ b[i])) } -export function interfaceIdOf(int: ethers.utils.Interface): string { - const signatures = Object.keys(int.functions) - .filter((k) => k.indexOf('(') !== -1) - .map((k) => int.getSighash(int.functions[k])) - +export function interfaceIdOf(int: ethers.Interface): string { + const signatures: string[] = [] + int.forEachFunction(fragment => { + signatures.push(getSigHash(fragment)) + }) return signatures.reduce((p, c) => xor(p, c)) } + +export function getSigHash(fragment: ethers.FunctionFragment): string { + return ethers.dataSlice(ethers.id(fragment.format('sighash')), 0, 4) +} diff --git a/test/utils/sequence.ts b/test/utils/sequence.ts index ec0a3c24..065192ae 100644 --- a/test/utils/sequence.ts +++ b/test/utils/sequence.ts @@ -1,16 +1,16 @@ -import { BigNumberish, BytesLike, ethers, Wallet } from "ethers" -import { CHAIN_ID } from "." +import { BigNumberish, BytesLike, ethers, Wallet } from 'ethers' +import { getChainId } from '.' export const WALLET_CODE = '0x603a600e3d39601a805130553df3363d3d373d3d3d363d30545af43d82803e903d91601857fd5bf3' export enum SignatureType { Legacy = 0, Dynamic = 1, - NoChaindDynamic = 2, + NoChaindDynamic = 2 } export type SignerLeaf = { - address: string, + address: string weight: BigNumberish } @@ -19,48 +19,47 @@ export type SubdigestLeaf = { } export type NestedLeaf = { - tree: ConfigTopology, - internalThreshold: BigNumberish, - externalWeight: BigNumberish, + tree: ConfigTopology + internalThreshold: BigNumberish + externalWeight: BigNumberish } export type ConfigLeaf = SubdigestLeaf | SignerLeaf | NestedLeaf export type ImageHashNode = { - left: ConfigTopology, + left: ConfigTopology right: ConfigTopology } export type ConfigTopology = ImageHashNode | ConfigLeaf export type WalletConfig = { - threshold: ethers.BigNumberish, - checkpoint: ethers.BigNumberish, + threshold: ethers.BigNumberish + checkpoint: ethers.BigNumberish topology: ConfigTopology } export type SimplifiedNestedWalletConfig = { - threshold: ethers.BigNumberish, - weight: ethers.BigNumberish, + threshold: ethers.BigNumberish + weight: ethers.BigNumberish signers: SimplifiedConfigMember[] } export type SimplifiedWalletConfig = { - threshold: BigNumberish, - checkpoint: BigNumberish, + threshold: BigNumberish + checkpoint: BigNumberish signers: SimplifiedConfigMember[] } - export type SimplifiedConfigMember = SignerLeaf | SimplifiedNestedWalletConfig export type Transaction = { - delegateCall: boolean; - revertOnError: boolean; - gasLimit: BigNumberish; - target: string; - value: BigNumberish; - data: BytesLike; + delegateCall: boolean + revertOnError: boolean + gasLimit: BigNumberish + target: string + value: BigNumberish + data: BytesLike } export enum SignaturePartType { @@ -74,9 +73,9 @@ export enum SignaturePartType { } export type SignaturePart = { - address: string; - type: SignaturePartType; - signature?: string; + address: string + type: SignaturePartType + signature?: string } export function applyTxDefault( @@ -87,19 +86,16 @@ export function applyTxDefault( gasLimit: 0, target: '0xFb8356E7deB64034aBE2b2a8A732634f77A2DAE4', // Random address value: 0, - data: [] + data: new Uint8Array([]) } ): Transaction { return { ...def, - ...tx, + ...tx } } -export function applyTxDefaults( - tx: Partial[], - def?: Transaction -): Transaction[] { +export function applyTxDefaults(tx: Partial[], def?: Transaction): Transaction[] { return tx.map(t => applyTxDefault(t, def)) } @@ -196,14 +192,13 @@ export function leavesOf(topology: ConfigTopology): ConfigLeaf[] { return [topology] } - return [ - ...leavesOf(topology.left), - ...leavesOf(topology.right) - ] + return [...leavesOf(topology.left), ...leavesOf(topology.right)] } export function subdigestLeaves(topology: ConfigTopology): string[] { - return leavesOf(topology).filter((l) => isSubdigestLeaf(l)).map((l: SubdigestLeaf) => l.subdigest) + return leavesOf(topology) + .filter(l => isSubdigestLeaf(l)) + .map((l: SubdigestLeaf) => l.subdigest) } export function toSimplifiedConfig(config: WalletConfig): SimplifiedWalletConfig { @@ -225,33 +220,22 @@ export function hashNode(node: ConfigTopology): string { } if (isSubdigestLeaf(node)) { - return ethers.utils.solidityKeccak256( - ['string', 'bytes32'], - ['Sequence static digest:\n', node.subdigest] - ) + return ethers.solidityPackedKeccak256(['string', 'bytes32'], ['Sequence static digest:\n', node.subdigest]) } if (isNestedLeaf(node)) { - return ethers.utils.solidityKeccak256( + return ethers.solidityPackedKeccak256( ['string', 'bytes32', 'uint256', 'uint256'], ['Sequence nested config:\n', hashNode(node.tree), node.internalThreshold, node.externalWeight] ) } - return ethers.utils.solidityKeccak256( - ['bytes32', 'bytes32'], - [hashNode(node.left), hashNode(node.right)] - ) + return ethers.solidityPackedKeccak256(['bytes32', 'bytes32'], [hashNode(node.left), hashNode(node.right)]) } export function imageHash2(threshold: ethers.BigNumberish, topology: ConfigTopology): string { const root = hashNode(topology) - return ethers.utils.keccak256( - ethers.utils.solidityPack( - ['bytes32', 'uint256'], - [root, threshold] - ) - ) + return ethers.keccak256(ethers.solidityPacked(['bytes32', 'uint256'], [root, threshold])) } export function printTopology(topology: ConfigTopology, threshold?: ethers.BigNumberish, inverse = false): string[] { @@ -293,7 +277,7 @@ export function printTopology(topology: ConfigTopology, threshold?: ethers.BigNu let printRight = printTopology(topology.right, undefined, inverse) if (inverse) { - ([printLeft, printRight] = [printRight, printLeft]) + ;[printLeft, printRight] = [printRight, printLeft] } const result = [`${root}`] @@ -311,86 +295,59 @@ export function printTopology(topology: ConfigTopology, threshold?: ethers.BigNu } export function addressOf(factory: string, firstModule: string, imageHash: string): string { - const codeHash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ['bytes', 'bytes32'], - [WALLET_CODE, ethers.utils.hexZeroPad(firstModule, 32)] - ) + const codeHash = ethers.keccak256( + ethers.solidityPacked(['bytes', 'bytes32'], [WALLET_CODE, ethers.zeroPadValue(firstModule, 32)]) ) - const hash = ethers.utils.keccak256( - ethers.utils.solidityPack( - ['bytes1', 'address', 'bytes32', 'bytes32'], - ['0xff', factory, imageHash, codeHash] - ) + const hash = ethers.keccak256( + ethers.solidityPacked(['bytes1', 'address', 'bytes32', 'bytes32'], ['0xff', factory, imageHash, codeHash]) ) - return ethers.utils.getAddress(ethers.utils.hexDataSlice(hash, 12)) + return ethers.getAddress(ethers.dataSlice(hash, 12)) } export function encodeNonce(space: BigNumberish, nonce: BigNumberish) { - return ethers.BigNumber.from(ethers.utils.solidityPack(['uint160', 'uint96'], [space, nonce])) + return BigInt(ethers.solidityPacked(['uint160', 'uint96'], [space, nonce])) } export function leafForAddressAndWeight(address: string, weight: ethers.BigNumberish) { - return ethers.utils.solidityPack( - ['uint96', 'address'], - [weight, address] - ) + return ethers.solidityPacked(['uint96', 'address'], [weight, address]) } export function imageHash(config: WalletConfig): string { const signersRoot = hashNode(config.topology) - const preImageHash = ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['bytes32', 'uint256'], - [signersRoot, config.threshold] - ) + const preImageHash = ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'uint256'], [signersRoot, config.threshold]) ) - return ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['bytes32', 'uint256'], - [preImageHash, config.checkpoint] - ) - ) + return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'uint256'], [preImageHash, config.checkpoint])) } export function digestOf(txs: Partial[], nonce: ethers.BigNumberish) { - return ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['uint256', MetaTransactionsSolidityType], - [nonce, applyTxDefaults(txs)] - ) + return ethers.keccak256( + ethers.AbiCoder.defaultAbiCoder().encode(['uint256', MetaTransactionsSolidityType], [nonce, applyTxDefaults(txs)]) ) } -export function subdigestOf(wallet: string, digest: ethers.BytesLike, chainId: ethers.BigNumberish = CHAIN_ID()) { - return ethers.utils.keccak256( - ethers.utils.solidityPack( - ['string', 'uint256', 'address', 'bytes32'], - ['\x19\x01', chainId, wallet, digest] - ) +export async function subdigestOf(wallet: string, digest: ethers.BytesLike, chainId?: ethers.BigNumberish) { + chainId = chainId ?? (await getChainId()) + return ethers.keccak256( + ethers.solidityPacked(['string', 'uint256', 'address', 'bytes32'], ['\x19\x01', chainId, wallet, digest]) ) } export function computeStorageKey(key: string, subkey?: string): string { if (!subkey) { - return ethers.utils.id(key) + return ethers.id(key) } - return ethers.utils.keccak256( - ethers.utils.defaultAbiCoder.encode( - ['bytes32', 'bytes32'], - [computeStorageKey(key), subkey] - ) - ) + return ethers.keccak256(ethers.AbiCoder.defaultAbiCoder().encode(['bytes32', 'bytes32'], [computeStorageKey(key), subkey])) } export type EncodingOptions = { - forceDynamicEncoding?: boolean, - signatureType?: SignatureType, + forceDynamicEncoding?: boolean + signatureType?: SignatureType disableTrim?: boolean } @@ -410,11 +367,11 @@ function leftSlice(topology: ConfigTopology): ConfigTopology[] { } type DecodedSignatureMember = { - weight?: ethers.BigNumberish, - address?: string, - type: SignaturePartType, - value?: string, - innerThreshold?: ethers.BigNumberish, + weight?: ethers.BigNumberish + address?: string + type: SignaturePartType + value?: string + innerThreshold?: ethers.BigNumberish } export class SignatureConstructor { @@ -440,35 +397,34 @@ export class SignatureConstructor { if (first.type !== SignaturePartType.Address && first.type !== SignaturePartType.Node) return if (second.type !== SignaturePartType.Address && second.type !== SignaturePartType.Node) return - const firstNode = first.type === SignaturePartType.Address ? leafForAddressAndWeight(first.address!, first.weight!) : first.value - const secondNode = second.type === SignaturePartType.Address ? leafForAddressAndWeight(second.address!, second.weight!) : second.value + const firstNode = + first.type === SignaturePartType.Address ? leafForAddressAndWeight(first.address!, first.weight!) : first.value + const secondNode = + second.type === SignaturePartType.Address ? leafForAddressAndWeight(second.address!, second.weight!) : second.value - const nextNode = ethers.utils.keccak256( - ethers.utils.solidityPack( - ['bytes32', 'bytes32'], - [firstNode, secondNode] - ) - ) + const nextNode = ethers.keccak256(ethers.solidityPacked(['bytes32', 'bytes32'], [firstNode, secondNode])) - this.members = [{ - type: SignaturePartType.Node, - value: nextNode - }] + this.members = [ + { + type: SignaturePartType.Node, + value: nextNode + } + ] } appendPart(weight: ethers.BigNumberish, part: SignaturePart) { switch (part.type) { case SignaturePartType.Address: this.members.push({ weight, address: part.address, type: SignaturePartType.Address }) - break; + break case SignaturePartType.Signature: this.members.push({ weight, address: part.address, type: SignaturePartType.Signature, value: part.signature }) - break; + break case SignaturePartType.Dynamic: this.members.push({ weight, address: part.address, type: SignaturePartType.Dynamic, value: part.signature }) - break; + break default: throw new Error(`Unknown signature part type: ${part.type}`) @@ -487,7 +443,7 @@ export class SignatureConstructor { } appendSubdigest(subdigest: string) { - this.members.push({ type: SignaturePartType.Subdigest, value: subdigest}) + this.members.push({ type: SignaturePartType.Subdigest, value: subdigest }) } appendNested(branch: string, weight: ethers.BigNumberish, innerThreshold: ethers.BigNumberish) { @@ -500,52 +456,46 @@ export class SignatureConstructor { for (const member of this.members) { switch (member.type) { case SignaturePartType.Address: - result = ethers.utils.solidityPack( + result = ethers.solidityPacked( ['bytes', 'uint8', 'uint8', 'address'], [result, SignaturePartType.Address, member.weight, member.address] ) - break; + break case SignaturePartType.Signature: - result = ethers.utils.solidityPack( + result = ethers.solidityPacked( ['bytes', 'uint8', 'uint8', 'bytes'], [result, SignaturePartType.Signature, member.weight, member.value] ) - break; + break case SignaturePartType.Dynamic: - const signature = ethers.utils.arrayify(member.value ?? []) - result = ethers.utils.solidityPack( + const signature = ethers.getBytes(member.value ?? new Uint8Array([])) + result = ethers.solidityPacked( ['bytes', 'uint8', 'uint8', 'address', 'uint24', 'bytes'], [result, SignaturePartType.Dynamic, member.weight, member.address, signature.length, signature] ) - break; + break case SignaturePartType.Node: - result = ethers.utils.solidityPack( - ['bytes', 'uint8', 'bytes32'], - [result, SignaturePartType.Node, member.value] - ) - break; + result = ethers.solidityPacked(['bytes', 'uint8', 'bytes32'], [result, SignaturePartType.Node, member.value]) + break case SignaturePartType.Branch: - const branch = ethers.utils.arrayify(member.value ?? []) - result = ethers.utils.solidityPack( + const branch = ethers.getBytes(member.value ?? new Uint8Array([])) + result = ethers.solidityPacked( ['bytes', 'uint8', 'uint24', 'bytes'], [result, SignaturePartType.Branch, branch.length, branch] ) break case SignaturePartType.Subdigest: - result = ethers.utils.solidityPack( - ['bytes', 'uint8', 'bytes32'], - [result, SignaturePartType.Subdigest, member.value] - ) + result = ethers.solidityPacked(['bytes', 'uint8', 'bytes32'], [result, SignaturePartType.Subdigest, member.value]) break case SignaturePartType.Nested: - const nestedBranch = ethers.utils.arrayify(member.value ?? []) - result = ethers.utils.solidityPack( + const nestedBranch = ethers.getBytes(member.value ?? new Uint8Array([])) + result = ethers.solidityPacked( ['bytes', 'uint8', 'uint8', 'uint16', 'uint24', 'bytes'], [result, SignaturePartType.Nested, member.weight, member.innerThreshold, nestedBranch.length, nestedBranch] ) @@ -565,7 +515,7 @@ export function encodeSigners( parts: SignaturePart[] | Map, subdigests: string[], options?: EncodingOptions -): { encoded: string, weight: ethers.BigNumber } { +): { encoded: string; weight: bigint } { // Map part to signers if (Array.isArray(parts)) { const partOfSigner = new Map() @@ -576,7 +526,7 @@ export function encodeSigners( } const slice = leftSlice(topology) - let weight = ethers.constants.Zero + let weight = 0n const constructor = new SignatureConstructor(options?.disableTrim) for (const node of slice) { @@ -585,18 +535,18 @@ export function encodeSigners( // we recurse the encoding, and if the result has any weight // we have to embed the whole branch, otherwise we just add the node const nested = encodeSigners(node, parts, subdigests, options) - if (nested.weight.isZero() && !options?.disableTrim) { + if (nested.weight === 0n && !options?.disableTrim) { constructor.appendNode(hashNode(node)) } else { constructor.appendBranch(nested.encoded) - weight = weight.add(nested.weight) + weight = weight + nested.weight } } else { if (isSignerLeaf(node)) { // If the node is a signer leaf, we can just add the member const part = parts.get(node.address) ?? { type: SignaturePartType.Address, address: node.address } if (part.type !== SignaturePartType.Address) { - weight = weight.add(node.weight) + weight = weight + BigInt(node.weight) } constructor.appendPart(node.weight, part) @@ -605,13 +555,13 @@ export function encodeSigners( // we recurse the encoding, and if the result has any weight // we have to embed the whole branch, otherwise we just add the node const nested = encodeSigners(node.tree, parts, subdigests, options) - if (nested.weight.isZero() && !options?.disableTrim) { + if (nested.weight === 0n && !options?.disableTrim) { constructor.appendNode(hashNode(node)) } else { // Nested configs only have weight if the inner threshold is met // and the weight is always the external weight - if (nested.weight.gte(node.internalThreshold)) { - weight = weight.add(node.externalWeight) + if (nested.weight >= BigInt(node.internalThreshold)) { + weight = weight + BigInt(node.externalWeight) } constructor.appendNested(nested.encoded, node.externalWeight, node.internalThreshold) @@ -619,7 +569,7 @@ export function encodeSigners( } else { // If the node is a subdigest add the node (unless it's an static subdigest signature) if (subdigests.includes(node.subdigest)) { - weight = weight.add(ethers.BigNumber.from(2).pow(256).sub(1)) + weight += 2n ** 256n - 1n constructor.appendSubdigest(node.subdigest) } else { constructor.appendNode(hashNode(node)) @@ -644,18 +594,18 @@ export function encodeSignature( switch (options?.signatureType || SignatureType.Legacy) { case SignatureType.Dynamic: - return ethers.utils.solidityPack( + return ethers.solidityPacked( ['uint8', 'uint16', 'uint32', 'bytes'], [SignatureType.Dynamic, config.threshold, config.checkpoint, encodedSigners.encoded] ) case SignatureType.NoChaindDynamic: - return ethers.utils.solidityPack( + return ethers.solidityPacked( ['uint8', 'uint16', 'uint32', 'bytes'], [SignatureType.NoChaindDynamic, config.threshold, config.checkpoint, encodedSigners.encoded] ) default: case SignatureType.Legacy: - return ethers.utils.solidityPack( + return ethers.solidityPacked( ['uint8', 'uint8', 'uint32', 'bytes'], [SignatureType.Legacy, config.threshold, config.checkpoint, encodedSigners.encoded] ) diff --git a/test/utils/wallet.ts b/test/utils/wallet.ts index c91299ca..110da550 100644 --- a/test/utils/wallet.ts +++ b/test/utils/wallet.ts @@ -1,9 +1,26 @@ -import { ethers, Overrides } from "ethers" -import { shuffle } from "." -import { MainModule, MainModuleUpgradable, SequenceContext } from "./contracts" -import { addressOf, applyTxDefaults, ConfigTopology, digestOf, encodeSignature, EncodingOptions, imageHash, merkleTopology, optimize2SignersTopology, SignaturePartType, SignatureType, SimplifiedWalletConfig, subdigestOf, Transaction, WalletConfig } from "./sequence" - -export type StaticSigner = (ethers.Signer & { address: string }) +import { ethers, Overrides } from 'ethers' +import { ethers as hethers } from 'hardhat' +import { shuffle } from '.' +import { MainModule, MainModuleUpgradable, SequenceContext } from './contracts' +import { + addressOf, + applyTxDefaults, + ConfigTopology, + digestOf, + encodeSignature, + EncodingOptions, + imageHash, + merkleTopology, + optimize2SignersTopology, + SignaturePartType, + SignatureType, + SimplifiedWalletConfig, + subdigestOf, + Transaction, + WalletConfig +} from './sequence' + +export type StaticSigner = ethers.Signer & { address: string } export type AnyStaticSigner = StaticSigner | SequenceWallet export function isAnyStaticSigner(s: any): s is AnyStaticSigner { @@ -33,22 +50,22 @@ export type WalletOptions = { } export type BasicWalletOptions = { - address?: string, - threshold?: number, - signing: number | number[], - idle: number | number[], - encodingOptions?: EncodingOptions, + address?: string + threshold?: number + signing: number | number[] + idle: number | number[] + encodingOptions?: EncodingOptions topologyConverter: (simple: SimplifiedWalletConfig) => ConfigTopology } export type DetailedWalletOptions = { - address?: string, - threshold: ethers.BigNumberish, - signers: (string | AnyStaticSigner | Weighted | Weighted)[], + address?: string + threshold: ethers.BigNumberish + signers: (string | AnyStaticSigner | Weighted | Weighted)[] encodingOptions?: EncodingOptions } -export type Weighted = { weight: number, value: T } +export type Weighted = { weight: number; value: T } export function isWeighted(w: any): w is Weighted { return w.weight !== undefined && w.value !== undefined @@ -76,23 +93,25 @@ export class SequenceWallet { const signersWeight = Array.isArray(options.signing) ? options.signing : new Array(options.signing).fill(0).map(() => 1) const idleWeight = Array.isArray(options.idle) ? options.idle : new Array(options.idle).fill(0).map(() => 1) - const signers = signersWeight.map((s) => isAnyStaticSigner(s) ? s : ethers.Wallet.createRandom()) - const idle = idleWeight.map(() => ethers.utils.getAddress(ethers.utils.hexlify(ethers.utils.randomBytes(20)))) + const signers = signersWeight.map(s => (isAnyStaticSigner(s) ? s : ethers.Wallet.createRandom())) + const idle = idleWeight.map(() => ethers.getAddress(ethers.hexlify(ethers.randomBytes(20)))) const checkpoint = getCheckpoint() const simplifiedConfig = { checkpoint, threshold: options.threshold ? options.threshold : signers.length, signers: shuffle( - signers.map((s, i) => ({ - address: s.address, - weight: signersWeight[i] - })).concat( - idle.map((s, i) => ({ - address: s, - weight: idleWeight[i] - }) - )) + signers + .map((s, i) => ({ + address: s.address, + weight: signersWeight[i] + })) + .concat( + idle.map((s, i) => ({ + address: s, + weight: idleWeight[i] + })) + ) ) } @@ -112,9 +131,12 @@ export class SequenceWallet { const simplifiedConfig = { threshold: opts.threshold, checkpoint: getCheckpoint(), - signers: opts.signers.map((s) => ({ + signers: opts.signers.map(s => ({ weight: isWeighted(s) ? s.weight : 1, - address: (() => { const v = weightedVal(s); return isAnyStaticSigner(v) ? v.address : v })() + address: (() => { + const v = weightedVal(s) + return isAnyStaticSigner(v) ? v.address : v + })() })) } @@ -126,7 +148,7 @@ export class SequenceWallet { ...simplifiedConfig, topology: defaultTopology(simplifiedConfig) }, - signers: opts.signers.map((s) => weightedVal(s)).filter(isAnyStaticSigner) + signers: opts.signers.map(s => weightedVal(s)).filter(isAnyStaticSigner) }) } @@ -161,7 +183,11 @@ export class SequenceWallet { get address() { if (this.options.address) return this.options.address - return addressOf(this.options.context.factory.address, this.options.context.mainModule.address, this.imageHash) + return addressOf( + this.options.context.factory.target as string, + this.options.context.mainModule.target as string, + this.imageHash + ) } getAddress() { @@ -181,85 +207,85 @@ export class SequenceWallet { } async deploy() { - if (await this.options.context.factory.provider.getCode(this.address).then((c) => c !== '0x')) { + if ((await hethers.provider.getCode(this.address)) !== '0x') { return } - return this.options.context.factory.deploy(this.options.context.mainModule.address, this.imageHash) + return await this.options.context.factory.deploy(await this.options.context.mainModule.getAddress(), this.imageHash) } async getNonce(space: ethers.BigNumberish = 0) { return this.mainModule.readNonce(space) } - async updateImageHash(input: ethers.BytesLike | WalletConfig) { - if (!ethers.utils.isBytesLike(input)) return this.updateImageHash(imageHash(input)) + async updateImageHash(input: ethers.BytesLike | WalletConfig): Promise { + if (!ethers.isBytesLike(input)) return this.updateImageHash(imageHash(input)) - return this.sendTransactions([{ - target: this.address, - data: this.options.context.mainModule.interface.encodeFunctionData( - 'updateImageHash', [input] - ) - }]) + return this.sendTransactions([ + { + target: this.address, + data: this.options.context.mainModule.interface.encodeFunctionData('updateImageHash', [input]) + } + ]) } - async addExtraImageHash(input: ethers.BytesLike | WalletConfig, expiration: ethers.BigNumberish = ethers.BigNumber.from(2).pow(248)) { - if (!ethers.utils.isBytesLike(input)) return this.addExtraImageHash(imageHash(input)) + async addExtraImageHash( + input: ethers.BytesLike | WalletConfig, + expiration: ethers.BigNumberish = 2n ** 248n + ): Promise { + if (!ethers.isBytesLike(input)) return this.addExtraImageHash(imageHash(input)) - return this.sendTransactions([{ - target: this.address, - data: this.options.context.mainModule.interface.encodeFunctionData( - 'setExtraImageHash', [input, expiration] - ) - }]) + return this.sendTransactions([ + { + target: this.address, + data: this.options.context.mainModule.interface.encodeFunctionData('setExtraImageHash', [input, expiration]) + } + ]) } async clearExtraImageHashes(imageHashes: (ethers.BytesLike | WalletConfig)[]) { - return this.sendTransactions([{ - target: this.address, - data: this.options.context.mainModule.interface.encodeFunctionData( - 'clearExtraImageHashes', [ - imageHashes.map((h) => ethers.utils.isBytesLike(h) ? h : imageHash(h)) - ] - ) - }]) + return this.sendTransactions([ + { + target: this.address, + data: this.options.context.mainModule.interface.encodeFunctionData('clearExtraImageHashes', [ + imageHashes.map(h => (ethers.isBytesLike(h) ? h : imageHash(h))) + ]) + } + ]) } async signMessage(message: ethers.BytesLike): Promise { - return this.signDigest(ethers.utils.keccak256(ethers.utils.arrayify(message))) + return this.signDigest(ethers.keccak256(ethers.getBytes(message))) } async signDigest(digest: ethers.BytesLike): Promise { - const subdigest = ethers.utils.arrayify(subdigestOf(this.address, digest, this.options.chainId)) + const subdigest = ethers.getBytes(await subdigestOf(this.address, digest, this.options.chainId)) return this.signSubdigest(subdigest) } staticSubdigestSign(subdigest: ethers.BytesLike, useNoChainId = true): string { const signatureType = useNoChainId ? SignatureType.NoChaindDynamic : this.options.encodingOptions?.signatureType - return encodeSignature( - this.config, - [], - [ ethers.utils.hexlify(subdigest) ], - { ...this.options.encodingOptions, signatureType } - ) + return encodeSignature(this.config, [], [ethers.hexlify(subdigest)], { ...this.options.encodingOptions, signatureType }) } async signSubdigest(subdigest: ethers.BytesLike): Promise { - const sigParts = await Promise.all(this.signers.map(async (s) => { - if (isSequenceSigner(s)) { - return { - address: s.address, - signature: await s.signDigest(subdigest).then((s) => s + '03'), - type: SignaturePartType.Dynamic + const sigParts = await Promise.all( + this.signers.map(async s => { + if (isSequenceSigner(s)) { + return { + address: s.address, + signature: await s.signDigest(subdigest).then(s => s + '03'), + type: SignaturePartType.Dynamic + } } - } - return { - address: await s.getAddress(), - signature: await s.signMessage(subdigest).then((s) => s + '02'), - type: SignaturePartType.Signature - } - })) + return { + address: await s.getAddress(), + signature: await s.signMessage(subdigest).then(s => s + '02'), + type: SignaturePartType.Signature + } + }) + ) return encodeSignature(this.config, sigParts, [], this.options.encodingOptions) } @@ -278,8 +304,10 @@ export class SequenceWallet { signature: string, nonce?: ethers.BigNumberish, overrides: Overrides & { from?: string | Promise } = {} - ): Promise { - if (nonce === undefined) return this.relayTransactions(ptxs, signature, await this.getNonce(), overrides) + ): Promise { + if (nonce === undefined) { + return this.relayTransactions(ptxs, signature, await this.getNonce(), overrides) + } const txs = applyTxDefaults(ptxs) @@ -290,7 +318,7 @@ export class SequenceWallet { ptxs: Partial[], nonce?: ethers.BigNumberish, overrides?: Overrides & { from?: string | Promise } - ): Promise { + ): Promise { if (nonce === undefined) return this.sendTransactions(ptxs, await this.getNonce(), overrides) const txs = applyTxDefaults(ptxs) diff --git a/tsconfig.json b/tsconfig.json index 3915d06c..c6725543 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,17 +28,10 @@ "outDir": "build", "lib": ["es2020", "dom"], - "typeRoots": [ - "./node_modules/@types" - ] + "typeRoots": ["./node_modules/@types"] }, - "include": [ - "./hardhat.config.ts", "typings", "tests", "utils" - ], + "include": ["./hardhat.config.ts", "typings", "tests", "utils", "test"], - "exclude": [ - "node_modules", - "dist" - ] + "exclude": ["node_modules", "dist"] } diff --git a/utils/config-loader.ts b/utils/config-loader.ts index 99ae5186..2714b430 100644 --- a/utils/config-loader.ts +++ b/utils/config-loader.ts @@ -26,7 +26,7 @@ export const getEnvConfig = (env: string) => { const envLoad = dotenv.config({ path: envFile }) if (envLoad.error) { - return { ETH_MNEMONIC: "client vendor advice erosion deny cute tree fatal fuel bless simple speed" } + return { ETH_MNEMONIC: 'client vendor advice erosion deny cute tree fatal fuel bless simple speed' } } return envLoad.parsed || {} diff --git a/utils/deploy-contracts.ts b/utils/deploy-contracts.ts index 48f63943..8249398c 100644 --- a/utils/deploy-contracts.ts +++ b/utils/deploy-contracts.ts @@ -10,118 +10,128 @@ import { TrustFactory__factory } from '../gen/typechain' -import { BigNumber, ContractFactory, ethers } from 'ethers' +import { ContractDeployTransaction, ContractFactory, Signer, ethers } from 'ethers' import fs from 'fs' const provider = hethers.provider -const signer = provider.getSigner(0) const singletonFactoryFactory = { address: '0xce0042B868300000d44A59004Da54A005ffdcf9f', - abi: [{ - "constant": false, - "inputs": [{ - "internalType": "bytes", - "type": "bytes" - }, { - "internalType": "bytes32", - "type": "bytes32" - }], - "name": "deploy", - "outputs": [{ - "internalType": "address payable", - "type": "address" - }], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }] + abi: [ + { + constant: false, + inputs: [ + { + internalType: 'bytes', + type: 'bytes' + }, + { + internalType: 'bytes32', + type: 'bytes32' + } + ], + name: 'deploy', + outputs: [ + { + internalType: 'address payable', + type: 'address' + } + ], + payable: false, + stateMutability: 'nonpayable', + type: 'function' + } + ] } const singletonFactoryDeployTx = '0xf9016c8085174876e8008303c4d88080b90154608060405234801561001057600080fd5b50610134806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634af63f0214602d575b600080fd5b60cf60048036036040811015604157600080fd5b810190602081018135640100000000811115605b57600080fd5b820183602082011115606c57600080fd5b80359060200191846001830284011164010000000083111715608d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250929550509135925060eb915050565b604080516001600160a01b039092168252519081900360200190f35b6000818351602085016000f5939250505056fea26469706673582212206b44f8a82cb6b156bfcc3dc6aadd6df4eefd204bc928a4397fd15dacf6d5320564736f6c634300060200331b83247000822470' const singletonFactoryDeployer = '0xBb6e024b9cFFACB947A71991E386681B1Cd1477D' const prompt = ora() -const attempVerify = async (name: string, _: new () => T, address: string, ...args: Parameters) => { +const attempVerify = async ( + name: string, + _: new () => T, + address: string, + ...args: Parameters +) => { try { - await run("verify:verify", { + await run('verify:verify', { address: address, - constructorArguments: args, + constructorArguments: args }) } catch {} try { await tenderly.verify({ name: name, - address: address, + address: address }) } catch {} } -const buildNetworkJson = (...contracts: { name: string, address: string }[]) => { - return contracts.map((c) => ({ +const buildNetworkJson = (...contracts: { name: string; address: string }[]) => { + return contracts.map(c => ({ contractName: c.name, address: c.address })) } -type simpleContractFactory> = { - getDeployTransaction: (...args: Y) => { data?: ethers.BytesLike }, - attach: (address: string) => ethers.Contract -} - -const deploy = async , Y extends Array>( +const deploy = async ( name: string, - contract: new (...args: [signer: ethers.Signer]) => T, - ...args: Y -): Promise => { + contract: new (...args: [signer?: Signer]) => ContractFactory, + ...args: any[] +): Promise => { + const signer = await provider.getSigner(0) const singletonFactory = new ethers.Contract(singletonFactoryFactory.address, singletonFactoryFactory.abi, signer) - if (ethers.utils.arrayify(await provider.getCode(singletonFactory.address)).length <= 2) { + if (ethers.getBytes(await provider.getCode(await singletonFactory.getAddress())).length <= 2) { // Deploy singleton deployer const o = ora().start(`Deploying singleton factory`) - const deployerBal = BigNumber.from('24700000000000000') - if ((await provider.getBalance(singletonFactoryDeployer)).lt(deployerBal)) { + const deployerBal = 24700000000000000n + if ((await provider.getBalance(singletonFactoryDeployer)) < deployerBal) { o.info('Funding singleton factory deployer') const tx = await signer.sendTransaction({ to: singletonFactoryDeployer, - value: deployerBal, + value: deployerBal }) await tx.wait() o.info('Funded. Deploying singleton factory') } - const tx = await provider.sendTransaction(singletonFactoryDeployTx) + const tx = await provider.broadcastTransaction(singletonFactoryDeployTx) await tx.wait() o.succeed(`Deployed singleton factory`) } const o = ora().start(`Deploying ${name}`) const c = new contract(signer) - const { data } = c.getDeployTransaction(...args) + const { data } = await c.getDeployTransaction(...args) if (!data) { throw new Error(`no data for ${name}`) } - const maxGasLimit = await provider.getBlock('latest').then((b) => b.gasLimit.mul(4).div(10)) - - const address = ethers.utils.getAddress(ethers.utils.hexDataSlice( - ethers.utils.keccak256( - ethers.utils.solidityPack( - ['bytes1', 'address', 'bytes32', 'bytes32'], - ['0xff', singletonFactory.address, ethers.constants.HashZero, ethers.utils.keccak256(data)] - ) + const maxGasLimit = await provider.getBlock('latest').then(b => (b!.gasLimit * 4n) / 10n) + + const address = ethers.getAddress( + ethers.dataSlice( + ethers.keccak256( + ethers.solidityPacked( + ['bytes1', 'address', 'bytes32', 'bytes32'], + ['0xff', await singletonFactory.getAddress(), ethers.ZeroHash, ethers.keccak256(data)] + ) + ), + 12 ) - , 12)) + ) - if (ethers.utils.arrayify(await provider.getCode(address)).length > 0) { + if (ethers.getBytes(await provider.getCode(address)).length > 0) { o.succeed(`Skipping ${name} because it has been deployed at ${address}`) return c.attach(address) } - await singletonFactory.deploy(data, ethers.constants.HashZero, { gasLimit: maxGasLimit }).then((tx) => tx.wait()) + await singletonFactory.deploy(data, ethers.ZeroHash, { gasLimit: maxGasLimit }).then(tx => tx.wait()) - if (ethers.utils.arrayify(await provider.getCode(address)).length === 0) { + if (ethers.getBytes(await provider.getCode(address)).length === 0) { throw new Error(`failed to deploy ${name}`) } @@ -130,38 +140,57 @@ const deploy = async , Y extends Array>( return c.attach(address) } - const main = async () => { + const signer = await provider.getSigner(0) + const address = await signer.getAddress() prompt.info(`Network Name: ${network.name}`) - prompt.info(`Local Deployer Address: ${await signer.getAddress()}`) - prompt.info(`Local Deployer Balance: ${await signer.getBalance()}`) - - const walletFactory = await deploy("Factory", Factory__factory) - const mainModuleUpgradeable = await deploy("MainModuleUpgradable", MainModuleUpgradable__factory) - const mainModule = await deploy("MainModule", MainModule__factory, walletFactory.address, mainModuleUpgradeable.address) - const guestModule = await deploy("GuestModule", GuestModule__factory) - const sequenceUtils = await deploy("SequenceUtils", SequenceUtils__factory) - const trustFactory = await deploy("TrustFactory", TrustFactory__factory) + prompt.info(`Local Deployer Address: ${address}`) + prompt.info(`Local Deployer Balance: ${await provider.getBalance(address)}`) + + const walletFactory = await deploy('Factory', Factory__factory) + const mainModuleUpgradeable = await deploy('MainModuleUpgradable', MainModuleUpgradable__factory) + const mainModule = await deploy( + 'MainModule', + MainModule__factory, + await walletFactory.getAddress(), + await mainModuleUpgradeable.getAddress() + ) + const guestModule = await deploy('GuestModule', GuestModule__factory) + const sequenceUtils = await deploy('SequenceUtils', SequenceUtils__factory) + const trustFactory = await deploy('TrustFactory', TrustFactory__factory) prompt.start(`writing deployment information to ${network.name}.json`) - fs.writeFileSync(`./networks/${network.name}.json`, JSON.stringify(buildNetworkJson( - { name: "WalletFactory", address: walletFactory.address }, - { name: "MainModule", address: mainModule.address }, - { name: "MainModuleUpgradable", address: mainModuleUpgradeable.address }, - { name: "GuestModule", address: guestModule.address }, - { name: "SequenceUtils", address: sequenceUtils.address }, - { name: "TrustFactory", address: trustFactory.address }, - ), null, 2)) + fs.writeFileSync( + `./networks/${network.name}.json`, + JSON.stringify( + buildNetworkJson( + { name: 'WalletFactory', address: await walletFactory.getAddress() }, + { name: 'MainModule', address: await mainModule.getAddress() }, + { name: 'MainModuleUpgradable', address: await mainModuleUpgradeable.getAddress() }, + { name: 'GuestModule', address: await guestModule.getAddress() }, + { name: 'SequenceUtils', address: await sequenceUtils.getAddress() }, + { name: 'TrustFactory', address: await trustFactory.getAddress() } + ), + null, + 2 + ) + ) prompt.succeed() prompt.start(`verifying contracts`) - await attempVerify("Factory", Factory__factory, walletFactory.address) - await attempVerify("MainModuleUpgradable", MainModuleUpgradable__factory, mainModuleUpgradeable.address) - await attempVerify("MainModule", MainModule__factory, mainModule.address, walletFactory.address, mainModuleUpgradeable.address) - await attempVerify("GuestModule", GuestModule__factory, guestModule.address) - await attempVerify("SequenceUtils", SequenceUtils__factory, sequenceUtils.address) - await attempVerify("TrustFactory", TrustFactory__factory, trustFactory.address) + await attempVerify('Factory', Factory__factory, await walletFactory.getAddress()) + await attempVerify('MainModuleUpgradable', MainModuleUpgradable__factory, await mainModuleUpgradeable.getAddress()) + await attempVerify( + 'MainModule', + MainModule__factory, + await mainModule.getAddress(), + await walletFactory.getAddress(), + await mainModuleUpgradeable.getAddress() + ) + await attempVerify('GuestModule', GuestModule__factory, await guestModule.getAddress()) + await attempVerify('SequenceUtils', SequenceUtils__factory, await sequenceUtils.getAddress()) + await attempVerify('TrustFactory', TrustFactory__factory, await trustFactory.getAddress()) prompt.succeed() } @@ -175,4 +204,4 @@ main() .catch(error => { console.error(error) process.exit(1) - }) \ No newline at end of file + }) diff --git a/utils/workers/bench-worker.ts b/utils/workers/bench-worker.ts index 1ecfac86..fc8bd144 100644 --- a/utils/workers/bench-worker.ts +++ b/utils/workers/bench-worker.ts @@ -1,8 +1,8 @@ -import { deploySequenceContext, SequenceContext } from "../../test/utils/contracts" -import { expose } from "threads/worker" +import { deploySequenceContext, SequenceContext } from '../../test/utils/contracts' +import { expose } from 'threads/worker' import { SequenceWallet } from '../../test/utils/wallet' -import { ethers } from "ethers" -import { legacyTopology, merkleTopology } from "../../test/utils/sequence" +import { ethers } from 'ethers' +import { legacyTopology, merkleTopology } from '../../test/utils/sequence' let context: SequenceContext let wallet: SequenceWallet @@ -17,25 +17,17 @@ let topologyConverter: any let prevsnapshot: any function report2(values: ethers.BigNumberish[]) { - const bns = values.map(v => ethers.BigNumber.from(v)) + const bns = values.map(v => BigInt(v)) - const min = bns.reduce((a, b) => a.lt(b) ? a : b) - const max = bns.reduce((a, b) => a.gt(b) ? a : b) - const avg = bns - .reduce((p, n) => p.add(n)) - .div(values.length) + const min = bns.reduce((a, b) => (a < b ? a : b)) + const max = bns.reduce((a, b) => (a > b ? a : b)) + const avg = bns.reduce((p, n) => (p + n) / BigInt(values.length)) return { min, max, avg } } const worker = { - async setup( - signing: number, - idle: number, - runs: number, - topology: 'legacy' | 'merkle', - disableTrim: boolean - ) { + async setup(signing: number, idle: number, runs: number, topology: 'legacy' | 'merkle', disableTrim: boolean) { if (!context) { context = await deploySequenceContext() } @@ -65,21 +57,25 @@ const worker = { const tx = await wallet.relayTransactions([{}], signature) const receipt = await tx.wait() + if (!receipt) { + throw new Error('No receipt') + } + results.push(receipt.gasUsed) - calldatas.push(ethers.utils.arrayify(signature).length) + calldatas.push(ethers.getBytes(signature).length) } const report = report2(results) const reportCalldata = report2(calldatas) return { - min: report.min.toNumber(), - max: report.max.toNumber(), - avg: report.avg.toNumber(), + min: Number(report.min), + max: Number(report.max), + avg: Number(report.avg), data: { - min: reportCalldata.min.toNumber(), - max: reportCalldata.max.toNumber(), - avg: reportCalldata.avg.toNumber() + min: Number(reportCalldata.min), + max: Number(reportCalldata.max), + avg: Number(reportCalldata.avg) }, idle: d_idle, signing: d_signing,