From 4be531e696161ce154e410578a2e8a25470114fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 01:38:19 +0200 Subject: [PATCH 1/9] ci: add format checking and test to ci --- .eslintrc | 8 + .../.gitattributes => .gitattributes | 2 +- .github/actions/setup-rust/action.yaml | 30 + .github/workflows/pr.yml | 33 + .gitignore | 9 + .prettierignore | 22 + .prettierrc.js | 17 + README.md | 1 - docker/README.md | 10 +- docker/fuel-core/chainConfig.json | 2 +- docker/l1-chain/hardhat/hardhat.config.ts | 44 +- package.json | 10 +- .../fungible-token/.github/workflows/ci.yml | 81 - packages/fungible-token/.gitignore | 5 - .../contract_message_predicate-abi.json | 62 +- .../contract_message_script-abi.json | 62 +- packages/fungible-token/package.json | 22 +- packages/fungible-token/scripts/build.js | 22 +- packages/integration-tests/.gitignore | 2 - packages/integration-tests/.prettierignore | 6 - packages/integration-tests/.prettierrc | 27 - packages/integration-tests/LICENSE | 201 -- packages/integration-tests/README.md | 2 +- packages/integration-tests/copy-builds.sh | 19 - packages/integration-tests/create-links.sh | 19 - packages/integration-tests/package.json | 8 +- .../integration-tests/scripts/bridgeERC20.ts | 31 +- .../integration-tests/scripts/bridgeETH.ts | 4 +- packages/integration-tests/scripts/setup.ts | 142 +- .../scripts/utils/ethers/commitBlock.ts | 14 +- .../scripts/utils/ethers/createRelayParams.ts | 7 +- .../utils/ethers/getOrDeployECR20Contract.ts | 33 +- .../scripts/utils/fuels/computeBlockHash.ts | 7 +- .../utils/fuels/getMessageOutReceipt.ts | 6 +- .../fuels/getOrDeployFuelTokenContract.ts | 45 +- .../scripts/utils/fuels/relayCommonMessage.ts | 56 +- .../scripts/utils/fuels/transaction.ts | 9 +- .../integration-tests/scripts/utils/logs.ts | 27 +- .../scripts/utils/parsers.ts | 10 +- .../scripts/utils/validations.ts | 26 +- .../integration-tests/tests/bridge_erc20.ts | 125 +- .../integration-tests/tests/transfer_eth.ts | 76 +- packages/message-predicates/.gitattributes | 2 - .../.github/workflows/ci.yml | 144 -- .../.github/workflows/scripts/verify_tag.sh | 35 - packages/message-predicates/.gitignore | 5 - .../contract-message-predicate/src/lib.rs | 2 +- packages/message-predicates/package.json | 22 +- packages/message-predicates/scripts/build.js | 13 +- packages/portal-contracts/.eslintrc.json | 25 - .../.github/workflows/ci.yaml | 35 - .../.github/workflows/lint.yaml | 25 - packages/portal-contracts/.prettierignore | 5 - packages/portal-contracts/.prettierrc.json | 38 - packages/portal-contracts/.solcover.js | 10 +- packages/portal-contracts/.solhint.json | 17 +- .../deployments/deployments.goerli.json | 2 +- packages/portal-contracts/hardhat.config.ts | 80 +- packages/portal-contracts/package.json | 154 +- .../portal-contracts/protocol/blockHeader.ts | 95 +- .../portal-contracts/protocol/constants.ts | 6 +- .../portal-contracts/protocol/cryptography.ts | 2 +- packages/portal-contracts/protocol/harness.ts | 266 ++- packages/portal-contracts/protocol/message.ts | 32 +- packages/portal-contracts/protocol/utils.ts | 42 +- .../portal-contracts/protocol/validators.ts | 24 +- .../portal-contracts/scripts/deployAll.ts | 136 +- .../scripts/deployImplementation.ts | 188 +- .../scripts/serveDeployments.ts | 2 +- .../portal-contracts/scripts/upgradeAll.ts | 121 +- packages/portal-contracts/scripts/utils.ts | 300 +-- .../portal-contracts/scripts/verifyAddress.ts | 104 +- .../portal-contracts/scripts/verifySource.ts | 104 +- packages/portal-contracts/test/ecdsa.ts | 120 +- .../portal-contracts/test/erc20Gateway.ts | 1856 ++++++++++------- .../portal-contracts/test/fuelChainState.ts | 604 +++--- .../portal-contracts/test/messagesIncoming.ts | 1808 +++++++++------- .../portal-contracts/test/messagesOutgoing.ts | 834 ++++---- packages/portal-contracts/test/upgrade.ts | 132 +- packages/portal-contracts/tsconfig.json | 18 +- pnpm-lock.yaml | 207 +- tsconfig.json | 3 + turbo.json | 7 + 83 files changed, 4947 insertions(+), 4022 deletions(-) create mode 100644 .eslintrc rename packages/fungible-token/.gitattributes => .gitattributes (61%) create mode 100644 .github/actions/setup-rust/action.yaml create mode 100644 .github/workflows/pr.yml create mode 100644 .prettierignore create mode 100644 .prettierrc.js delete mode 100644 packages/fungible-token/.github/workflows/ci.yml delete mode 100644 packages/fungible-token/.gitignore delete mode 100644 packages/integration-tests/.gitignore delete mode 100644 packages/integration-tests/.prettierignore delete mode 100644 packages/integration-tests/.prettierrc delete mode 100644 packages/integration-tests/LICENSE delete mode 100644 packages/integration-tests/copy-builds.sh delete mode 100644 packages/integration-tests/create-links.sh delete mode 100644 packages/message-predicates/.gitattributes delete mode 100644 packages/message-predicates/.github/workflows/ci.yml delete mode 100755 packages/message-predicates/.github/workflows/scripts/verify_tag.sh delete mode 100644 packages/message-predicates/.gitignore delete mode 100644 packages/portal-contracts/.eslintrc.json delete mode 100644 packages/portal-contracts/.github/workflows/ci.yaml delete mode 100644 packages/portal-contracts/.github/workflows/lint.yaml delete mode 100644 packages/portal-contracts/.prettierignore delete mode 100644 packages/portal-contracts/.prettierrc.json create mode 100644 tsconfig.json diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..80ca566f --- /dev/null +++ b/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": "./packages/eslint-config" + // "rules": { + // "no-await-in-loop": 0, + // "prefer-destructuring": 0, + // "no-bitwise": 0 + // } +} diff --git a/packages/fungible-token/.gitattributes b/.gitattributes similarity index 61% rename from packages/fungible-token/.gitattributes rename to .gitattributes index 2169095e..a2fe4f61 100644 --- a/packages/fungible-token/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ # Syntax highlighting of sway files as rust -*.sw linguist-language=Rust \ No newline at end of file +*.sw linguist-language=Rust diff --git a/.github/actions/setup-rust/action.yaml b/.github/actions/setup-rust/action.yaml new file mode 100644 index 00000000..69ada9c9 --- /dev/null +++ b/.github/actions/setup-rust/action.yaml @@ -0,0 +1,30 @@ +name: 'Rust & Forc Setup' + +inputs: + rust-version: + default: 0.17.0 + forc-toolchain: + default: latest + forc-date: + default: 2023-07-05 + +runs: + using: 'composite' + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ inputs.rust-version }} + components: clippy, rustfmt + + - name: Init cache + uses: Swatinem/rust-cache@v2 + + - name: Install Fuel toolchain + uses: FuelLabs/action-fuel-toolchain@v0.6.0 + with: + toolchain: ${{ inputs.forc-toolchain }} + date: ${{ inputs.forc-date }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..e9d42a5f --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,33 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + release: + types: [published] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + vaidate: + runs-on: ubuntu-latest + steps: + - uses: FuelLabs/github-actions/setups/node@master + - uses: FuelLabs/github-actions/setups/docker@master + with: + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: ./.github/actions/setup-rust + + - name: Check projects formatting + run: pnpm check + + - name: Build projects + run: pnpm build + + - name: Test projects + run: pnpm test diff --git a/.gitignore b/.gitignore index a81853b6..32b52573 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,12 @@ node_modules # Hardhat files cache artifacts + +# Cargo and Forc artifacts +out +target +*/out +*/target + +# Genearal dist folders +dist diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..43fcc3c6 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,22 @@ +# Node.js +node_modules +.env +.turbo +package.json + +# Hardhat files +cache +artifacts +typechain + +# Cargo and Forc artifacts +out +target +*/out +*/target + +# Genearal dist folders +dist + +## Ignore lock files +pnpm-lock.yaml diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..5f1d36ad --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,17 @@ +module.exports = { + plugins: ['@fuels/prettier-config', 'prettier-plugin-solidity'], + singleQuote: true, + semi: true, + overrides: [ + { + files: '*.sol', + options: { + printWidth: 120, + tabWidth: 4, + useTabs: false, + singleQuote: false, + bracketSpacing: false, + }, + }, + ], +}; diff --git a/README.md b/README.md index 9e9a6693..b1b9e1bc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - ## 📗 Table of contents - [Getting Started](./docs/GETTING_STARTED.md) diff --git a/docker/README.md b/docker/README.md index fe8d170e..8fe71790 100644 --- a/docker/README.md +++ b/docker/README.md @@ -8,16 +8,16 @@ This project runs a local Fuel development environment with both an L1 node and - docker-compose - make -*Note*: Docker Desktop only allocates 2GB of memory by default, which isn't enough to run the docker-compose services reliably. +_Note_: Docker Desktop only allocates 2GB of memory by default, which isn't enough to run the docker-compose services reliably. To allocate more memory, go to Settings > Resources in the Docker UI and use the slider to change the value (_8GB recommended_). Make sure to click Apply & Restart for the changes to take effect. - ## Commands ### Starting containers To start all containers and build it, use; + ``` make up ``` @@ -25,6 +25,7 @@ make up ### Stop containers To stop to containers, use; + ``` make stop ``` @@ -32,6 +33,7 @@ make stop ### Clean containers To remove all images and containers, use; + ``` make clean ``` @@ -39,6 +41,7 @@ make clean ### View logs To open the logs from the env, use; + ``` make logs ``` @@ -48,10 +51,11 @@ make logs A set of environment variables can be set before running. You can change this configs on the env files; + - [Fuel Core env](./envs/fuel_core.env) Fuel Core configurations. - [L1 env](./envs/l1_chain.env) L1 configurations. - [PORTS](./envs/ports.env): Exposed ports on host machine. ## License -This repo is licensed under the `Apache-2.0` license. See [`LICENSE`](./LICENSE) for more information. \ No newline at end of file +This repo is licensed under the `Apache-2.0` license. See [`LICENSE`](./LICENSE) for more information. diff --git a/docker/fuel-core/chainConfig.json b/docker/fuel-core/chainConfig.json index 3c5cb173..242038e1 100644 --- a/docker/fuel-core/chainConfig.json +++ b/docker/fuel-core/chainConfig.json @@ -39,4 +39,4 @@ "max_predicate_data_length": 1048576, "gas_price_factor": 1000000 } -} \ No newline at end of file +} diff --git a/docker/l1-chain/hardhat/hardhat.config.ts b/docker/l1-chain/hardhat/hardhat.config.ts index b9d63d28..ee6f59ce 100644 --- a/docker/l1-chain/hardhat/hardhat.config.ts +++ b/docker/l1-chain/hardhat/hardhat.config.ts @@ -7,28 +7,28 @@ const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || ''; const LOCALHOST_HTTP = process.env.LOCALHOST_HTTP || ''; const config: HardhatUserConfig = { - defaultNetwork: 'hardhat', - solidity: { - compilers: [ - { - version: '0.8.9', - settings: { - optimizer: { - enabled: true, - runs: 10000, - }, - }, - }, - ], - }, - networks: { - localhost: { - url: LOCALHOST_HTTP, - }, - }, - etherscan: { - apiKey: ETHERSCAN_API_KEY, - }, + defaultNetwork: 'hardhat', + solidity: { + compilers: [ + { + version: '0.8.9', + settings: { + optimizer: { + enabled: true, + runs: 10000, + }, + }, + }, + ], + }, + networks: { + localhost: { + url: LOCALHOST_HTTP, + }, + }, + etherscan: { + apiKey: ETHERSCAN_API_KEY, + }, }; export default config; diff --git a/package.json b/package.json index fb645bc6..ac350e7e 100644 --- a/package.json +++ b/package.json @@ -4,18 +4,26 @@ "private": true, "description": "Fuel Bridge", "scripts": { + "format": "turbo run format", + "check": "turbo run check", "build": "turbo run build", "test": "sh ./scripts/test.sh", "node:build": "make -C ./docker build", "node:up": "make -C ./docker up", "node:stop": "make -C ./docker stop", "node:clean": "make -C ./docker clean", - "node:logs": "make -C ./docker logs" + "node:logs": "make -C ./docker logs", + "prettier:check": "prettier --check .", + "prettier:format": "prettier --write ." }, "keywords": [], "author": "Fuel Labs (https://fuel.network/)", "license": "APPACHE-2.0", "dependencies": { + "@fuels/prettier-config": "^0.0.2", + "@fuels/ts-config": "^0.0.2", + "prettier": "^2.7.1", + "prettier-plugin-solidity": "^v1.0.0-rc.1", "turbo": "^1.10.7" }, "pnpm": { diff --git a/packages/fungible-token/.github/workflows/ci.yml b/packages/fungible-token/.github/workflows/ci.yml deleted file mode 100644 index a5dfedf3..00000000 --- a/packages/fungible-token/.github/workflows/ci.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: CI - -on: - push: - branches: - - master - pull_request: - release: - types: [published] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - REGISTRY: ghcr.io - RUST_VERSION: 1.70.0 - -jobs: - lint-toml-files: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_VERSION }} - - - name: Install Cargo.toml linter - uses: baptiste0928/cargo-install@v2 - with: - crate: cargo-toml-lint - version: '0.1.1' - - - name: Run Cargo.toml linter - run: git ls-files | grep Cargo.toml$ | xargs --verbose -n 1 cargo-toml-lint - - build: - runs-on: ubuntu-latest - - strategy: - matrix: - project: - [ - 'bridge-fungible-token', - 'fungible-bridge-abi', - 'FRC20-abi', - 'test-deposit-recipient-contract' - ] - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_VERSION }} - components: clippy, rustfmt - - - name: Init cache - uses: Swatinem/rust-cache@v2 - - - name: Install Fuel toolchain - uses: FuelLabs/action-fuel-toolchain@v0.6.0 - with: - toolchain: latest - date: 2023-07-05 - - - name: Sway Formatting - run: forc fmt --path ${{ matrix.project }} --check - - - name: Rust Formatting - run: cd ${{ matrix.project }} && cargo fmt --verbose --check - - - name: Build Sway - run: forc build --path ${{ matrix.project }} - - - name: Rust Tests - if: ${{ matrix.project == 'bridge-fungible-token' }} - run: cd ${{ matrix.project }} && cargo test diff --git a/packages/fungible-token/.gitignore b/packages/fungible-token/.gitignore deleted file mode 100644 index 1677b11a..00000000 --- a/packages/fungible-token/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -out -target -*/out -*/target -dist diff --git a/packages/fungible-token/bridge-message-predicates/contract_message_predicate-abi.json b/packages/fungible-token/bridge-message-predicates/contract_message_predicate-abi.json index 1aa6eaac..be3ef629 100644 --- a/packages/fungible-token/bridge-message-predicates/contract_message_predicate-abi.json +++ b/packages/fungible-token/bridge-message-predicates/contract_message_predicate-abi.json @@ -1,32 +1,32 @@ { - "types": [ - { - "typeId": 0, - "type": "bool", - "components": null, - "typeParameters": null - } - ], - "functions": [ - { - "inputs": [], - "name": "main", - "output": { - "name": "", - "type": 0, - "typeArguments": null - }, - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Predicate verifying a message input is being spent according to the rules for a valid message data relay to contract" - ] - } - ] - } - ], - "loggedTypes": [], - "messagesTypes": [], - "configurables": [] - } \ No newline at end of file + "types": [ + { + "typeId": 0, + "type": "bool", + "components": null, + "typeParameters": null + } + ], + "functions": [ + { + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Predicate verifying a message input is being spent according to the rules for a valid message data relay to contract" + ] + } + ] + } + ], + "loggedTypes": [], + "messagesTypes": [], + "configurables": [] +} diff --git a/packages/fungible-token/bridge-message-predicates/contract_message_script-abi.json b/packages/fungible-token/bridge-message-predicates/contract_message_script-abi.json index f423183e..49c0bd5d 100644 --- a/packages/fungible-token/bridge-message-predicates/contract_message_script-abi.json +++ b/packages/fungible-token/bridge-message-predicates/contract_message_script-abi.json @@ -1,32 +1,32 @@ { - "types": [ - { - "typeId": 0, - "type": "bool", - "components": null, - "typeParameters": null - } - ], - "functions": [ - { - "inputs": [], - "name": "main", - "output": { - "name": "", - "type": 0, - "typeArguments": null - }, - "attributes": [ - { - "name": "doc-comment", - "arguments": [ - " Script that relays a message and sends the message amount to a contract" - ] - } - ] - } - ], - "loggedTypes": [], - "messagesTypes": [], - "configurables": [] - } \ No newline at end of file + "types": [ + { + "typeId": 0, + "type": "bool", + "components": null, + "typeParameters": null + } + ], + "functions": [ + { + "inputs": [], + "name": "main", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Script that relays a message and sends the message amount to a contract" + ] + } + ] + } + ], + "loggedTypes": [], + "messagesTypes": [], + "configurables": [] +} diff --git a/packages/fungible-token/package.json b/packages/fungible-token/package.json index 79852dec..6c87573d 100644 --- a/packages/fungible-token/package.json +++ b/packages/fungible-token/package.json @@ -1,12 +1,14 @@ { - "name": "@fuel-bridge/fungible-token", - "private": true, - "main": "dist/index.ts", - "scripts": { - "build": "forc build && node ./scripts/build.js", - "test": "cargo test" - }, - "dependencies": { - "fuels": "0.0.0-next-20230707184416" - } + "name": "@fuel-bridge/fungible-token", + "private": true, + "main": "dist/index.ts", + "scripts": { + "format": "forc fmt && cargo fmt", + "check": "forc fmt --check && cargo fmt --check && cargo clippy", + "build": "forc build && node ./scripts/build.js", + "test": "cargo test" + }, + "dependencies": { + "fuels": "0.0.0-next-20230707184416" + } } diff --git a/packages/fungible-token/scripts/build.js b/packages/fungible-token/scripts/build.js index 7761d5e9..de5da282 100644 --- a/packages/fungible-token/scripts/build.js +++ b/packages/fungible-token/scripts/build.js @@ -2,9 +2,18 @@ const { hexlify } = require('fuels'); const { readFileSync, writeFileSync, mkdirSync } = require('fs'); const { join } = require('path'); // Set paths -const BRIDGE_FUNGIBLE_PATH = join(__dirname, '../bridge-fungible-token/out/debug/bridge_fungible_token.bin'); -const BRIDGE_FUNGIBLE_ABI_PATH = join(__dirname, '../bridge-fungible-token/out/debug/bridge_fungible_token-abi.json'); -const BRIDGE_FUNGIBLE_STORAGE_PATH = join(__dirname, '../bridge-fungible-token/out/debug/bridge_fungible_token-storage_slots.json'); +const BRIDGE_FUNGIBLE_PATH = join( + __dirname, + '../bridge-fungible-token/out/debug/bridge_fungible_token.bin' +); +const BRIDGE_FUNGIBLE_ABI_PATH = join( + __dirname, + '../bridge-fungible-token/out/debug/bridge_fungible_token-abi.json' +); +const BRIDGE_FUNGIBLE_STORAGE_PATH = join( + __dirname, + '../bridge-fungible-token/out/debug/bridge_fungible_token-storage_slots.json' +); const DIST_FOLDER = join(__dirname, '../dist'); const DIST_FILE = join(DIST_FOLDER, '/index.ts'); // Read files @@ -13,8 +22,11 @@ const bridgeFungibleAbiBytes = readFileSync(BRIDGE_FUNGIBLE_ABI_PATH); const bridgeFungibleStoageBytes = readFileSync(BRIDGE_FUNGIBLE_STORAGE_PATH); // Write file mkdirSync(DIST_FOLDER, { recursive: true }); -writeFileSync(DIST_FILE, [ +writeFileSync( + DIST_FILE, + [ `export const fungibleTokenBinary = "${hexlify(bridgeFungibleBytes)}";`, `export const fungibleTokenABI = ${bridgeFungibleAbiBytes.toString()};`, `export const fungibleTokenStorageSlots = ${bridgeFungibleStoageBytes.toString()};`, -].join('\n')); + ].join('\n') +); diff --git a/packages/integration-tests/.gitignore b/packages/integration-tests/.gitignore deleted file mode 100644 index 37d7e734..00000000 --- a/packages/integration-tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -.env diff --git a/packages/integration-tests/.prettierignore b/packages/integration-tests/.prettierignore deleted file mode 100644 index 1dc46ae0..00000000 --- a/packages/integration-tests/.prettierignore +++ /dev/null @@ -1,6 +0,0 @@ -dist -coverage -node_modules -_fuel_dev_environment -bridge-fungible-token -fuel-v2-contracts diff --git a/packages/integration-tests/.prettierrc b/packages/integration-tests/.prettierrc deleted file mode 100644 index fc5dab54..00000000 --- a/packages/integration-tests/.prettierrc +++ /dev/null @@ -1,27 +0,0 @@ -{ - "overrides": [ - { - "files": ["*.js", "*.ts"], - "options": { - "printWidth": 120, - "semi": true, - "tabWidth": 2, - "useTabs": false, - "singleQuote": true, - "bracketSpacing": true - } - }, - { - "files": ["*.js", "*.ts", "*.json"], - "options": { - "useTabs": false - } - }, - { - "files": "*.md", - "options": { - "useTabs": false - } - } - ] -} diff --git a/packages/integration-tests/LICENSE b/packages/integration-tests/LICENSE deleted file mode 100644 index d477afa3..00000000 --- a/packages/integration-tests/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2023 Fuel Labs Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/packages/integration-tests/README.md b/packages/integration-tests/README.md index 8cae16cb..639cddf1 100644 --- a/packages/integration-tests/README.md +++ b/packages/integration-tests/README.md @@ -43,4 +43,4 @@ The scripts can easily be run on other network setups like Goerli by modifying e ## License -The primary license for this repo is `UNLICENSED`, see [`LICENSE`](./LICENSE). +The primary license for this repo is `UNLICENSED`, see [`LICENSE`](../../LICENSE). diff --git a/packages/integration-tests/copy-builds.sh b/packages/integration-tests/copy-builds.sh deleted file mode 100644 index 558d7b9c..00000000 --- a/packages/integration-tests/copy-builds.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# root path -root_folder="$1" - -if [ -z "$root_folder" ]; then - echo "Please provide the root path to the bridge-message-predicates and bridge-fungible-token repos" - exit 1 -fi - -echo "Removing current folders" -rm -rf ./bridge-message-predicates -rm -rf ./bridge-fungible-token - -echo "Copying builds" -cp -R $1/bridge-message-predicates/out bridge-message-predicates -cp -R $1/bridge-fungible-token/bridge-fungible-token/out/debug/ bridge-fungible-token - -echo "Done!" \ No newline at end of file diff --git a/packages/integration-tests/create-links.sh b/packages/integration-tests/create-links.sh deleted file mode 100644 index f3569536..00000000 --- a/packages/integration-tests/create-links.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -# root path -root_folder="$1" - -if [ -z "$root_folder" ]; then - echo "Please provide the root path to the bridge-message-predicates and bridge-fungible-token repos" - exit 1 -fi - -echo "Removing current folders" -rm -rf ./bridge-message-predicates -rm -rf ./bridge-fungible-token - -echo "Creating symlinks" -ln -s $1/bridge-message-predicates/out bridge-message-predicates -ln -s $1/bridge-fungible-token/bridge-fungible-token/out/debug/ bridge-fungible-token - -echo "Done!" \ No newline at end of file diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 16501b16..dcbb7a54 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -5,11 +5,11 @@ "private": true, "license": "APACHE-2.0", "scripts": { + "format": "prettier --write ./", + "check": "prettier --check ./", "test": "pnpm mocha -b -r ts-node/register 'tests/**/*.ts'", "bridgeETH": "pnpm ts-node scripts/bridgeETH.ts", - "bridgeERC20": "pnpm ts-node scripts/bridgeERC20.ts", - "prettier:check": "prettier --check ./", - "prettier:format": "prettier --write ./" + "bridgeERC20": "pnpm ts-node scripts/bridgeERC20.ts" }, "devDependencies": { "@ethersproject/abi": "^5.7.0", @@ -24,8 +24,8 @@ "ethers": "^5.7.2", "fuels": "0.0.0-next-20230707184416", "mocha": "^10.0.0", - "prettier": "^2.7.1", "ts-node": "^10.9.1", + "typescript": "^5.1.6", "@fuel-bridge/portal-contracts": "workspace:*", "@fuel-bridge/message-predicates": "workspace:*", "@fuel-bridge/fungible-token": "workspace:*" diff --git a/packages/integration-tests/scripts/bridgeERC20.ts b/packages/integration-tests/scripts/bridgeERC20.ts index 4b7be79b..80cb0391 100644 --- a/packages/integration-tests/scripts/bridgeERC20.ts +++ b/packages/integration-tests/scripts/bridgeERC20.ts @@ -7,7 +7,10 @@ import { logETHBalances, logTokenBalances } from './utils/logs'; import { waitNextBlock } from './utils/fuels/waitNextBlock'; import { createRelayMessageParams } from './utils/ethers/createRelayParams'; import { commitBlock, mockFinalization } from './utils/ethers/commitBlock'; -import { getOrDeployECR20Contract, mintECR20 } from './utils/ethers/getOrDeployECR20Contract'; +import { + getOrDeployECR20Contract, + mintECR20, +} from './utils/ethers/getOrDeployECR20Contract'; import { getOrDeployFuelTokenContract } from './utils/fuels/getOrDeployFuelTokenContract'; import { validateFundgibleContracts } from './utils/validations'; import { getMessageOutReceipt } from './utils/fuels/getMessageOutReceipt'; @@ -38,7 +41,11 @@ const TOKEN_AMOUNT = '10'; const ethTestToken = await getOrDeployECR20Contract(env); // load Fuel side fungible token contract - const fuelTestToken = await getOrDeployFuelTokenContract(env, ethTestToken, FUEL_TX_PARAMS); + const fuelTestToken = await getOrDeployFuelTokenContract( + env, + ethTestToken, + FUEL_TX_PARAMS + ); const fuelTestTokenId = fuelTestToken.id.toHexString(); // mint tokens as starting balances @@ -54,7 +61,10 @@ const TOKEN_AMOUNT = '10'; // approve fuel erc20 gateway to spend the tokens console.log('Approving Tokens for gateway...'); - const eApproveTx = await ethTestToken.approve(gatewayContract.address, ethers_parseToken(TOKEN_AMOUNT, 18)); + const eApproveTx = await ethTestToken.approve( + gatewayContract.address, + ethers_parseToken(TOKEN_AMOUNT, 18) + ); const eApproveTxResult = await eApproveTx.wait(); if (eApproveTxResult.status !== 1) { console.log(eApproveTxResult); @@ -89,11 +99,17 @@ const TOKEN_AMOUNT = '10'; FUEL_MESSAGE_TIMEOUT_MS ); if (depositMessage == null) - throw new Error(`message took longer than ${FUEL_MESSAGE_TIMEOUT_MS}ms to arrive on Fuel`); + throw new Error( + `message took longer than ${FUEL_MESSAGE_TIMEOUT_MS}ms to arrive on Fuel` + ); // relay the message to the target contract console.log('Relaying message on Fuel...'); - const fMessageRelayTx = await relayCommonMessage(fuelAcct, depositMessage, FUEL_TX_PARAMS); + const fMessageRelayTx = await relayCommonMessage( + fuelAcct, + depositMessage, + FUEL_TX_PARAMS + ); const fMessageRelayTxResult = await fMessageRelayTx.waitForResult(); if (fMessageRelayTxResult.status.type !== 'success') { @@ -121,7 +137,10 @@ const TOKEN_AMOUNT = '10'; const scope = fuelTestToken.functions .withdraw(paddedAddress) .callParams({ - forward: { amount: fuels_parseToken(TOKEN_AMOUNT, 9), assetId: fuelTestTokenId }, + forward: { + amount: fuels_parseToken(TOKEN_AMOUNT, 9), + assetId: fuelTestTokenId, + }, }) .txParams(FUEL_TX_PARAMS); const fWithdrawTx = await scope.call(); diff --git a/packages/integration-tests/scripts/bridgeETH.ts b/packages/integration-tests/scripts/bridgeETH.ts index 664a2ab5..a4f1fabf 100644 --- a/packages/integration-tests/scripts/bridgeETH.ts +++ b/packages/integration-tests/scripts/bridgeETH.ts @@ -57,7 +57,9 @@ const ETH_AMOUNT = '0.1'; FUEL_MESSAGE_TIMEOUT_MS ); if (depositMessage == null) - throw new Error(`message took longer than ${FUEL_MESSAGE_TIMEOUT_MS}ms to arrive on Fuel`); + throw new Error( + `message took longer than ${FUEL_MESSAGE_TIMEOUT_MS}ms to arrive on Fuel` + ); console.log(''); // the sent ETH is now spendable on Fuel diff --git a/packages/integration-tests/scripts/setup.ts b/packages/integration-tests/scripts/setup.ts index 57a37552..08c60baf 100644 --- a/packages/integration-tests/scripts/setup.ts +++ b/packages/integration-tests/scripts/setup.ts @@ -3,7 +3,11 @@ import axios from 'axios'; import { ethers, Signer as EthSigner } from 'ethers'; import { Provider as EthProvider } from '@ethersproject/providers'; -import { Wallet, Provider as FuelProvider, WalletUnlocked as FuelWallet } from 'fuels'; +import { + Wallet, + Provider as FuelProvider, + WalletUnlocked as FuelWallet, +} from 'fuels'; import { fuels_parseEther, fuels_formatEther } from './utils/parsers'; import { FuelChainState, @@ -20,12 +24,18 @@ dotenv.config(); const def_http_eth: string = 'http://127.0.0.1:8545'; const def_http_deployer: string = 'http://127.0.0.1:8080'; const def_http_fuel: string = 'http://127.0.0.1:4000/graphql'; -const def_pk_eth_deployer: string = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; -const def_pk_eth_signer1: string = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; -const def_pk_eth_signer2: string = '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; -const def_pk_fuel_deployer: string = '0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298'; -const def_pk_fuel_signer1: string = '0x6fbaf19e3e42af0becc628e954557a8616d702b0c7ce9f2dd2580a16bebe357a'; -const def_pk_fuel_signer2: string = '0xd4de6ad33dae7d7987711360b8ff9298f1838c9542c6efafa0e8c74b11e5623d'; +const def_pk_eth_deployer: string = + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; +const def_pk_eth_signer1: string = + '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d'; +const def_pk_eth_signer2: string = + '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a'; +const def_pk_fuel_deployer: string = + '0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298'; +const def_pk_fuel_signer1: string = + '0x6fbaf19e3e42af0becc628e954557a8616d702b0c7ce9f2dd2580a16bebe357a'; +const def_pk_fuel_signer2: string = + '0xd4de6ad33dae7d7987711360b8ff9298f1838c9542c6efafa0e8c74b11e5623d'; // Setup options export interface SetupOptions { @@ -59,58 +69,98 @@ export interface TestEnvironment { } // The setup method for Fuel -export async function setupEnvironment(opts: SetupOptions): Promise { - const http_ethereum_client: string = opts.http_ethereum_client || process.env.HTTP_ETHEREUM_CLIENT || def_http_eth; - const http_deployer: string = opts.http_deployer || process.env.HTTP_DEPLOYER || def_http_deployer; - const http_fuel_client: string = opts.http_fuel_client || process.env.HTTP_FUEL_CLIENT || def_http_fuel; - const pk_eth_deployer: string = opts.pk_eth_deployer || process.env.PK_ETH_DEPLOYER || def_pk_eth_deployer; - const pk_eth_signer1: string = opts.pk_eth_signer1 || process.env.PK_ETH_SIGNER1 || def_pk_eth_signer1; - const pk_eth_signer2: string = opts.pk_eth_signer2 || process.env.PK_ETH_SIGNER2 || def_pk_eth_signer2; - const pk_fuel_deployer: string = opts.pk_fuel_deployer || process.env.PK_FUEL_DEPLOYER || def_pk_fuel_deployer; - const pk_fuel_signer1: string = opts.pk_fuel_signer1 || process.env.PK_FUEL_SIGNER1 || def_pk_fuel_signer1; - const pk_fuel_signer2: string = opts.pk_fuel_signer2 || process.env.PK_FUEL_SIGNER2 || def_pk_fuel_signer2; - const fuel_chain_consensus_addr: string = process.env.FUEL_CHAIN_CONSENSUS_ADDRESS || ''; - const fuel_message_portal_addr: string = process.env.FUEL_MESSAGE_PORTAL_ADDRESS || ''; - const fuel_erc20_gateway_addr: string = process.env.FUEL_ERC20_GATEWAY_ADDRESS || ''; +export async function setupEnvironment( + opts: SetupOptions +): Promise { + const http_ethereum_client: string = + opts.http_ethereum_client || + process.env.HTTP_ETHEREUM_CLIENT || + def_http_eth; + const http_deployer: string = + opts.http_deployer || process.env.HTTP_DEPLOYER || def_http_deployer; + const http_fuel_client: string = + opts.http_fuel_client || process.env.HTTP_FUEL_CLIENT || def_http_fuel; + const pk_eth_deployer: string = + opts.pk_eth_deployer || process.env.PK_ETH_DEPLOYER || def_pk_eth_deployer; + const pk_eth_signer1: string = + opts.pk_eth_signer1 || process.env.PK_ETH_SIGNER1 || def_pk_eth_signer1; + const pk_eth_signer2: string = + opts.pk_eth_signer2 || process.env.PK_ETH_SIGNER2 || def_pk_eth_signer2; + const pk_fuel_deployer: string = + opts.pk_fuel_deployer || + process.env.PK_FUEL_DEPLOYER || + def_pk_fuel_deployer; + const pk_fuel_signer1: string = + opts.pk_fuel_signer1 || process.env.PK_FUEL_SIGNER1 || def_pk_fuel_signer1; + const pk_fuel_signer2: string = + opts.pk_fuel_signer2 || process.env.PK_FUEL_SIGNER2 || def_pk_fuel_signer2; + const fuel_chain_consensus_addr: string = + process.env.FUEL_CHAIN_CONSENSUS_ADDRESS || ''; + const fuel_message_portal_addr: string = + process.env.FUEL_MESSAGE_PORTAL_ADDRESS || ''; + const fuel_erc20_gateway_addr: string = + process.env.FUEL_ERC20_GATEWAY_ADDRESS || ''; // Create provider from http_fuel_client const fuel_provider = new FuelProvider(http_fuel_client); try { await fuel_provider.getBlockNumber(); } catch (e) { - throw new Error('Failed to connect to the Fuel client at (' + http_fuel_client + "). Are you sure it's running?"); + throw new Error( + 'Failed to connect to the Fuel client at (' + + http_fuel_client + + "). Are you sure it's running?" + ); } const fuel_deployer = Wallet.fromPrivateKey(pk_fuel_deployer, fuel_provider); const fuel_deployerBalance = await fuel_deployer.getBalance(); if (fuel_deployerBalance.lt(fuels_parseEther('5'))) { - throw new Error('Fuel deployer balance is very low (' + fuels_formatEther(fuel_deployerBalance) + 'ETH)'); + throw new Error( + 'Fuel deployer balance is very low (' + + fuels_formatEther(fuel_deployerBalance) + + 'ETH)' + ); } const fuel_signer1 = Wallet.fromPrivateKey(pk_fuel_signer1, fuel_provider); const fuel_signer1Balance = await fuel_signer1.getBalance(); if (fuel_signer1Balance.lt(fuels_parseEther('1'))) { - const tx = await fuel_deployer.transfer(fuel_signer1.address, fuels_parseEther('1').toHex()); + const tx = await fuel_deployer.transfer( + fuel_signer1.address, + fuels_parseEther('1').toHex() + ); await tx.wait(); } const fuel_signer2 = Wallet.fromPrivateKey(pk_fuel_signer2, fuel_provider); const fuel_signer2Balance = await fuel_signer2.getBalance(); if (fuel_signer2Balance.lt(fuels_parseEther('1'))) { - const tx = await fuel_deployer.transfer(fuel_signer2.address, fuels_parseEther('1').toHex()); + const tx = await fuel_deployer.transfer( + fuel_signer2.address, + fuels_parseEther('1').toHex() + ); await tx.wait(); } // Create provider and signers from http_ethereum_client - const eth_provider = new ethers.providers.JsonRpcProvider(http_ethereum_client); + const eth_provider = new ethers.providers.JsonRpcProvider( + http_ethereum_client + ); try { await eth_provider.getBlockNumber(); } catch (e) { throw new Error( - 'Failed to connect to the Ethereum client at (' + http_ethereum_client + "). Are you sure it's running?" + 'Failed to connect to the Ethereum client at (' + + http_ethereum_client + + "). Are you sure it's running?" ); } const eth_deployer = new ethers.Wallet(pk_eth_deployer, eth_provider); const eth_deployerBalance = await eth_deployer.getBalance(); if (eth_deployerBalance < ethers.utils.parseEther('5')) { - throw new Error('Ethereum deployer balance is very low (' + ethers.utils.formatEther(eth_deployerBalance) + 'ETH)'); + throw new Error( + 'Ethereum deployer balance is very low (' + + ethers.utils.formatEther(eth_deployerBalance) + + 'ETH)' + ); } const eth_signer1 = new ethers.Wallet(pk_eth_signer1, eth_provider); const eth_signer1Balance = await eth_signer1.getBalance(); @@ -135,12 +185,22 @@ export async function setupEnvironment(opts: SetupOptions): Promise) { const messageOutReceipt = receipts.find( diff --git a/packages/integration-tests/scripts/utils/fuels/getOrDeployFuelTokenContract.ts b/packages/integration-tests/scripts/utils/fuels/getOrDeployFuelTokenContract.ts index 1c2f1d6e..6a0c74d5 100644 --- a/packages/integration-tests/scripts/utils/fuels/getOrDeployFuelTokenContract.ts +++ b/packages/integration-tests/scripts/utils/fuels/getOrDeployFuelTokenContract.ts @@ -3,13 +3,20 @@ import { TestEnvironment } from '../../setup'; import { Token } from '@fuel-bridge/portal-contracts'; import { Contract } from 'fuels'; -import { fungibleTokenBinary, fungibleTokenABI } from '@fuel-bridge/fungible-token'; +import { + fungibleTokenBinary, + fungibleTokenABI, +} from '@fuel-bridge/fungible-token'; import { debug } from '../logs'; import { eth_address_to_b256 } from '../parsers'; const { FUEL_FUNGIBLE_TOKEN_ADDRESS } = process.env; -export async function getOrDeployFuelTokenContract(env: TestEnvironment, ethTestToken: Token, fuelTxParams: TxParams) { +export async function getOrDeployFuelTokenContract( + env: TestEnvironment, + ethTestToken: Token, + fuelTxParams: TxParams +) { const tokenGetWay = env.eth.fuelERC20Gateway.address.replace('0x', ''); const tokenAddress = ethTestToken.address.replace('0x', ''); const fuelAcct = env.fuel.signers[1]; @@ -17,7 +24,11 @@ export async function getOrDeployFuelTokenContract(env: TestEnvironment, ethTest let fuelTestToken: Contract = null; if (FUEL_FUNGIBLE_TOKEN_ADDRESS) { try { - fuelTestToken = new Contract(FUEL_FUNGIBLE_TOKEN_ADDRESS, fungibleTokenABI, fuelAcct); + fuelTestToken = new Contract( + FUEL_FUNGIBLE_TOKEN_ADDRESS, + fungibleTokenABI, + fuelAcct + ); await fuelTestToken.functions.name().dryRun(); } catch (e) { fuelTestToken = null; @@ -31,18 +42,24 @@ export async function getOrDeployFuelTokenContract(env: TestEnvironment, ethTest let bytecodeHex = fungibleTokenBinary; debug('Replace ECR20 contract id'); debug('Deploy contract on Fuel'); - const factory = new ContractFactory(bytecodeHex, fungibleTokenABI, env.fuel.deployer); + const factory = new ContractFactory( + bytecodeHex, + fungibleTokenABI, + env.fuel.deployer + ); // Set the token gateway and token address in the contract factory.setConfigurableConstants({ BRIDGED_TOKEN_GATEWAY: eth_address_to_b256(tokenGetWay), - BRIDGED_TOKEN: eth_address_to_b256(tokenAddress) + BRIDGED_TOKEN: eth_address_to_b256(tokenAddress), }); - const { contractId, transactionRequest } = factory.createTransactionRequest({ - ...fuelTxParams, - storageSlots: [], - }); + const { contractId, transactionRequest } = factory.createTransactionRequest( + { + ...fuelTxParams, + storageSlots: [], + } + ); // This for avoiding transaction for failing because of insufficient funds // The current fund method only accounts for a static gas fee that is not // enough for deploying a contract @@ -54,8 +71,14 @@ export async function getOrDeployFuelTokenContract(env: TestEnvironment, ethTest const response = await fuelAcct.sendTransaction(transactionRequest); await response.wait(); // create contract instance - fuelTestToken = new Contract(contractId, factory.interface, factory.account); - debug(`Fuel fungible token contract created at ${fuelTestToken.id.toHexString()}.`); + fuelTestToken = new Contract( + contractId, + factory.interface, + factory.account + ); + debug( + `Fuel fungible token contract created at ${fuelTestToken.id.toHexString()}.` + ); } fuelTestToken.account = fuelAcct; const fuelTestTokenId = fuelTestToken.id.toHexString(); diff --git a/packages/integration-tests/scripts/utils/fuels/relayCommonMessage.ts b/packages/integration-tests/scripts/utils/fuels/relayCommonMessage.ts index 34655688..e0a3a1ea 100644 --- a/packages/integration-tests/scripts/utils/fuels/relayCommonMessage.ts +++ b/packages/integration-tests/scripts/utils/fuels/relayCommonMessage.ts @@ -17,7 +17,10 @@ import { } from 'fuels'; import { debug } from '../logs'; import { resourcesToInputs } from './transaction'; -import { contractMessagePredicate, contractMessageScript } from '@fuel-bridge/message-predicates'; +import { + contractMessagePredicate, + contractMessageScript, +} from '@fuel-bridge/message-predicates'; // Create a predicate for common messages const predicate = new Predicate(contractMessagePredicate, 0); @@ -33,25 +36,35 @@ const COMMON_RELAYABLE_MESSAGES: CommonMessageDetails[] = [ relayer: FuelWallet, message: Message, details: CommonMessageDetails, - txParams: Pick + txParams: Pick< + TransactionRequestLike, + 'gasLimit' | 'gasPrice' | 'maturity' + > ): Promise => { const script = arrayify(details.script); const predicate = arrayify(details.predicate); // get resources to fund the transaction - const resources = await relayer.getResourcesToSpend([{ - amount: bn.parseUnits('5'), - assetId: ZeroBytes32, - }]); + const resources = await relayer.getResourcesToSpend([ + { + amount: bn.parseUnits('5'), + assetId: ZeroBytes32, + }, + ]); // convert resources to inputs const coins = resourcesToInputs(resources); // get contract id const data = arrayify(message.data); - if (data.length < 32) throw new Error('cannot find contract ID in message data'); + if (data.length < 32) + throw new Error('cannot find contract ID in message data'); const contractId = hexlify(data.slice(0, 32)); // build the transaction - const transaction = new ScriptTransactionRequest({ script, gasLimit: MAX_GAS_PER_TX, ...txParams }); + const transaction = new ScriptTransactionRequest({ + script, + gasLimit: MAX_GAS_PER_TX, + ...txParams, + }); transaction.inputs.push({ type: InputType.Message, amount: message.amount, @@ -82,11 +95,17 @@ const COMMON_RELAYABLE_MESSAGES: CommonMessageDetails[] = [ }); transaction.witnesses.push('0x'); - debug('-------------------------------------------------------------------'); + debug( + '-------------------------------------------------------------------' + ); debug(transaction.inputs); - debug('-------------------------------------------------------------------'); + debug( + '-------------------------------------------------------------------' + ); debug(transaction.outputs); - debug('-------------------------------------------------------------------'); + debug( + '-------------------------------------------------------------------' + ); return transaction; }, @@ -109,7 +128,10 @@ type CommonMessageDetails = { export async function relayCommonMessage( relayer: FuelWallet, message: Message, - txParams: Pick = {} + txParams: Pick< + TransactionRequestLike, + 'gasLimit' | 'gasPrice' | 'maturity' + > = {} ): Promise { // find the relay details for the specified message let messageRelayDetails: CommonMessageDetails = null; @@ -121,9 +143,15 @@ export async function relayCommonMessage( break; } } - if (messageRelayDetails == null) throw new Error('message is not a common relayable message'); + if (messageRelayDetails == null) + throw new Error('message is not a common relayable message'); // build and send transaction - let transaction = await messageRelayDetails.buildTx(relayer, message, messageRelayDetails, txParams); + let transaction = await messageRelayDetails.buildTx( + relayer, + message, + messageRelayDetails, + txParams + ); return relayer.sendTransaction(transaction); } diff --git a/packages/integration-tests/scripts/utils/fuels/transaction.ts b/packages/integration-tests/scripts/utils/fuels/transaction.ts index 2204edd7..f734a835 100644 --- a/packages/integration-tests/scripts/utils/fuels/transaction.ts +++ b/packages/integration-tests/scripts/utils/fuels/transaction.ts @@ -1,4 +1,11 @@ -import { Coin, InputType, Resource, TransactionRequestInput, ZeroBytes32, isCoin } from 'fuels'; +import { + Coin, + InputType, + Resource, + TransactionRequestInput, + ZeroBytes32, + isCoin, +} from 'fuels'; export function resourcesToInputs(resources: Array) { const inputs: Array = resources diff --git a/packages/integration-tests/scripts/utils/logs.ts b/packages/integration-tests/scripts/utils/logs.ts index cbdb4c0d..a0ec75c3 100644 --- a/packages/integration-tests/scripts/utils/logs.ts +++ b/packages/integration-tests/scripts/utils/logs.ts @@ -1,5 +1,9 @@ import { formatEther } from 'ethers/lib/utils'; -import { ethers_formatToken, fuels_formatEther, fuels_formatToken } from './parsers'; +import { + ethers_formatToken, + fuels_formatEther, + fuels_formatToken, +} from './parsers'; import { Signer } from 'ethers'; import { WalletUnlocked } from 'fuels'; import { Token } from '@fuel-bridge/portal-contracts'; @@ -8,12 +12,23 @@ export const LOG_CONFIG = { debug: process.env.DEBUG || true, }; -export async function logETHBalances(ethereumAccount: Signer, fuelAccount: WalletUnlocked) { +export async function logETHBalances( + ethereumAccount: Signer, + fuelAccount: WalletUnlocked +) { const etherAccountAddress = await ethereumAccount.getAddress(); const fuelAccountAddress = await fuelAccount.address.toHexString(); console.log('Account balances:'); - console.log(` Ethereum - ${formatEther(await ethereumAccount.getBalance())} ETH (${etherAccountAddress})`); - console.log(` Fuel - ${fuels_formatEther(await fuelAccount.getBalance())} ETH (${fuelAccountAddress})`); + console.log( + ` Ethereum - ${formatEther( + await ethereumAccount.getBalance() + )} ETH (${etherAccountAddress})` + ); + console.log( + ` Fuel - ${fuels_formatEther( + await fuelAccount.getBalance() + )} ETH (${fuelAccountAddress})` + ); console.log(''); } @@ -32,7 +47,9 @@ export async function logTokenBalances( )} Tokens (${etherAccountAddress})` ); console.log( - ` Fuel - ${fuels_formatToken(await fuelAccount.getBalance(fuelTestTokenId))} Tokens (${fuelAccountAddress})` + ` Fuel - ${fuels_formatToken( + await fuelAccount.getBalance(fuelTestTokenId) + )} Tokens (${fuelAccountAddress})` ); console.log(''); } diff --git a/packages/integration-tests/scripts/utils/parsers.ts b/packages/integration-tests/scripts/utils/parsers.ts index f40f4803..7f537169 100644 --- a/packages/integration-tests/scripts/utils/parsers.ts +++ b/packages/integration-tests/scripts/utils/parsers.ts @@ -33,13 +33,19 @@ export function fuels_formatToken(value: BN, decimals: number = 9): string { } // Parse any string value using the given decimal amount -export function ethers_parseToken(value: string, decimals: number = 18): BigNumber { +export function ethers_parseToken( + value: string, + decimals: number = 18 +): BigNumber { let val = ethers.utils.parseEther(value); return val.div(10 ** (ETHEREUM_ETH_DECIMALS - decimals)); } // Format any value to a string using the given decimal amount -export function ethers_formatToken(value: BigNumber, decimals: number = 18): string { +export function ethers_formatToken( + value: BigNumber, + decimals: number = 18 +): string { value = value.mul(10 ** (ETHEREUM_ETH_DECIMALS - decimals)); return ethers.utils.formatEther(value); } diff --git a/packages/integration-tests/scripts/utils/validations.ts b/packages/integration-tests/scripts/utils/validations.ts index 0c826a90..965f8b68 100644 --- a/packages/integration-tests/scripts/utils/validations.ts +++ b/packages/integration-tests/scripts/utils/validations.ts @@ -2,10 +2,16 @@ import { Contract } from 'fuels'; import { Token } from '@fuel-bridge/portal-contracts'; import { TestEnvironment } from '../setup'; -export async function validateFundgibleContracts(env: TestEnvironment, fuelTestToken: Contract, ethTestToken: Token) { +export async function validateFundgibleContracts( + env: TestEnvironment, + fuelTestToken: Contract, + ethTestToken: Token +) { const ethTestTokenAddress = ethTestToken.address; - const l1Decimals = parseInt((await fuelTestToken.functions.bridged_token_decimals().dryRun()).value); + const l1Decimals = parseInt( + (await fuelTestToken.functions.bridged_token_decimals().dryRun()).value + ); const expectedL1Decimals = parseInt(String(await ethTestToken.decimals())); if (l1Decimals != expectedL1Decimals) { throw new Error( @@ -15,7 +21,11 @@ export async function validateFundgibleContracts(env: TestEnvironment, fuelTestT ].join(' ') ); } - const l1Token = '0x' + (await fuelTestToken.functions.bridged_token().dryRun()).value.substring(26); + const l1Token = + '0x' + + (await fuelTestToken.functions.bridged_token().dryRun()).value.substring( + 26 + ); if (l1Token.toLowerCase() != ethTestTokenAddress.toLowerCase()) { throw new Error( [ @@ -24,8 +34,14 @@ export async function validateFundgibleContracts(env: TestEnvironment, fuelTestT ].join(' ') ); } - const l1Gateway = '0x' + (await fuelTestToken.functions.bridged_token_gateway().dryRun()).value.substring(26); - if (l1Gateway.toLowerCase() != env.eth.fuelERC20Gateway.address.toLowerCase()) { + const l1Gateway = + '0x' + + ( + await fuelTestToken.functions.bridged_token_gateway().dryRun() + ).value.substring(26); + if ( + l1Gateway.toLowerCase() != env.eth.fuelERC20Gateway.address.toLowerCase() + ) { throw new Error( [ 'L1 token gateway address from the Fuel token contract does not match the actual L1 token gateway address', diff --git a/packages/integration-tests/tests/bridge_erc20.ts b/packages/integration-tests/tests/bridge_erc20.ts index 47d8ac0f..3a76e33c 100644 --- a/packages/integration-tests/tests/bridge_erc20.ts +++ b/packages/integration-tests/tests/bridge_erc20.ts @@ -3,7 +3,14 @@ import { solidity } from 'ethereum-waffle'; import { BigNumber, Signer } from 'ethers'; import { TestEnvironment, setupEnvironment } from '../scripts/setup'; import { Token } from '@fuel-bridge/portal-contracts'; -import { AbstractAddress, Address, BN, Contract, WalletUnlocked as FuelWallet, MessageProof } from 'fuels'; +import { + AbstractAddress, + Address, + BN, + Contract, + WalletUnlocked as FuelWallet, + MessageProof, +} from 'fuels'; import { relayCommonMessage } from '../scripts/utils/fuels/relayCommonMessage'; import { waitForMessage } from '../scripts/utils/fuels/waitForMessage'; import { createRelayMessageParams } from '../scripts/utils/ethers/createRelayParams'; @@ -13,7 +20,10 @@ import { getOrDeployFuelTokenContract } from '../scripts/utils/fuels/getOrDeploy import { FUEL_TX_PARAMS } from '../scripts/utils/constants'; import { getMessageOutReceipt } from '../scripts/utils/fuels/getMessageOutReceipt'; import { fuel_to_eth_address } from '../scripts/utils/parsers'; -import { commitBlock, mockFinalization } from '../scripts/utils/ethers/commitBlock'; +import { + commitBlock, + mockFinalization, +} from '../scripts/utils/ethers/commitBlock'; import { LOG_CONFIG } from '../scripts/utils/logs'; LOG_CONFIG.debug = false; @@ -39,24 +49,42 @@ describe('Bridging ERC20 tokens', async function () { env = await setupEnvironment({}); eth_testToken = await getOrDeployECR20Contract(env); eth_testTokenAddress = eth_testToken.address.toLowerCase(); - fuel_testToken = await getOrDeployFuelTokenContract(env, eth_testToken, FUEL_TX_PARAMS); + fuel_testToken = await getOrDeployFuelTokenContract( + env, + eth_testToken, + FUEL_TX_PARAMS + ); fuel_testTokenId = fuel_testToken.id.toHexString(); }); it('Setup tokens to bridge', async () => { - const { value: expectedTokenContractId } = await fuel_testToken.functions.bridged_token().get(); - const { value: expectedGatewayContractId } = await fuel_testToken.functions.bridged_token_gateway().get(); + const { value: expectedTokenContractId } = await fuel_testToken.functions + .bridged_token() + .get(); + const { value: expectedGatewayContractId } = await fuel_testToken.functions + .bridged_token_gateway() + .get(); // check that values for the test token and gateway contract match what // was compiled into the bridge-fungible-token binaries - expect(fuel_to_eth_address(expectedTokenContractId)).to.equal(eth_testTokenAddress); - expect(fuel_to_eth_address(expectedGatewayContractId)).to.equal(env.eth.fuelERC20Gateway.address.toLowerCase()); + expect(fuel_to_eth_address(expectedTokenContractId)).to.equal( + eth_testTokenAddress + ); + expect(fuel_to_eth_address(expectedGatewayContractId)).to.equal( + env.eth.fuelERC20Gateway.address.toLowerCase() + ); expect(await eth_testToken.decimals()).to.equal(18); // mint tokens as starting balances - await expect(eth_testToken.mint(await env.eth.deployer.getAddress(), 10_000)).to.not.be.reverted; - await expect(eth_testToken.mint(await env.eth.signers[0].getAddress(), 10_000)).to.not.be.reverted; - await expect(eth_testToken.mint(await env.eth.signers[1].getAddress(), 10_000)).to.not.be.reverted; + await expect( + eth_testToken.mint(await env.eth.deployer.getAddress(), 10_000) + ).to.not.be.reverted; + await expect( + eth_testToken.mint(await env.eth.signers[0].getAddress(), 10_000) + ).to.not.be.reverted; + await expect( + eth_testToken.mint(await env.eth.signers[1].getAddress(), 10_000) + ).to.not.be.reverted; }); describe('Bridge ERC20 to Fuel', async () => { @@ -74,21 +102,33 @@ describe('Bridging ERC20 tokens', async function () { ethereumTokenSender = env.eth.signers[0]; ethereumTokenSenderAddress = await ethereumTokenSender.getAddress(); await eth_testToken.mint(ethereumTokenSenderAddress, NUM_TOKENS); - ethereumTokenSenderBalance = await eth_testToken.balanceOf(ethereumTokenSenderAddress); + ethereumTokenSenderBalance = await eth_testToken.balanceOf( + ethereumTokenSenderAddress + ); fuelTokenReceiver = env.fuel.signers[0]; fuelTokenReceiverAddress = fuelTokenReceiver.address.toHexString(); - fuelTokenReceiverBalance = await fuelTokenReceiver.getBalance(fuel_testTokenId); + fuelTokenReceiverBalance = await fuelTokenReceiver.getBalance( + fuel_testTokenId + ); }); it('Bridge ERC20 via FuelERC20Gateway', async () => { // approve FuelERC20Gateway to spend the tokens - await expect(eth_testToken.connect(ethereumTokenSender).approve(env.eth.fuelERC20Gateway.address, NUM_TOKENS)).to - .not.be.reverted; + await expect( + eth_testToken + .connect(ethereumTokenSender) + .approve(env.eth.fuelERC20Gateway.address, NUM_TOKENS) + ).to.not.be.reverted; // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel let tx = await env.eth.fuelERC20Gateway .connect(ethereumTokenSender) - .deposit(fuelTokenReceiverAddress, eth_testToken.address, fuel_testTokenId, NUM_TOKENS); + .deposit( + fuelTokenReceiverAddress, + eth_testToken.address, + fuel_testTokenId, + NUM_TOKENS + ); let result = await tx.wait(); expect(result.status).to.equal(1); @@ -98,8 +138,11 @@ describe('Bridging ERC20 tokens', async function () { fuelTokenMessageReceiver = Address.fromB256(event.args.recipient); // check that the sender balance has decreased by the expected amount - let newSenderBalance = await eth_testToken.balanceOf(ethereumTokenSenderAddress); - expect(newSenderBalance.eq(ethereumTokenSenderBalance.sub(NUM_TOKENS))).to.be.true; + let newSenderBalance = await eth_testToken.balanceOf( + ethereumTokenSenderAddress + ); + expect(newSenderBalance.eq(ethereumTokenSenderBalance.sub(NUM_TOKENS))).to + .be.true; }); it('Relay message from Ethereum on Fuel', async function () { @@ -120,8 +163,14 @@ describe('Bridging ERC20 tokens', async function () { it('Check ERC20 arrived on Fuel', async () => { // check that the recipient balance has increased by the expected amount - let newReceiverBalance = await fuelTokenReceiver.getBalance(fuel_testTokenId); - expect(newReceiverBalance.eq(fuelTokenReceiverBalance.add(NUM_TOKENS / DECIMAL_DIFF))).to.be.true; + let newReceiverBalance = await fuelTokenReceiver.getBalance( + fuel_testTokenId + ); + expect( + newReceiverBalance.eq( + fuelTokenReceiverBalance.add(NUM_TOKENS / DECIMAL_DIFF) + ) + ).to.be.true; }); }); @@ -136,29 +185,41 @@ describe('Bridging ERC20 tokens', async function () { before(async () => { fuelTokenSender = env.fuel.signers[0]; - fuelTokenSenderBalance = await fuelTokenSender.getBalance(fuel_testTokenId); + fuelTokenSenderBalance = await fuelTokenSender.getBalance( + fuel_testTokenId + ); ethereumTokenReceiver = env.eth.signers[0]; ethereumTokenReceiverAddress = await ethereumTokenReceiver.getAddress(); - ethereumTokenReceiverBalance = await eth_testToken.balanceOf(ethereumTokenReceiverAddress); + ethereumTokenReceiverBalance = await eth_testToken.balanceOf( + ethereumTokenReceiverAddress + ); }); it('Bridge ERC20 via Fuel token contract', async () => { // withdraw tokens back to the base chain fuel_testToken.account = fuelTokenSender; - const paddedAddress = '0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0'); + const paddedAddress = + '0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0'); const scope = await fuel_testToken.functions .withdraw(paddedAddress) .callParams({ - forward: { amount: NUM_TOKENS / DECIMAL_DIFF, assetId: fuel_testTokenId }, + forward: { + amount: NUM_TOKENS / DECIMAL_DIFF, + assetId: fuel_testTokenId, + }, }) .fundWithRequiredCoins(); - const tx = await fuelTokenSender.sendTransaction(scope.transactionRequest); + const tx = await fuelTokenSender.sendTransaction( + scope.transactionRequest + ); const fWithdrawTxResult = await tx.waitForResult(); expect(fWithdrawTxResult.status.type).to.equal('success'); // get message proof const nextBlockId = await waitNextBlock(env); - const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); + const messageOutReceipt = getMessageOutReceipt( + fWithdrawTxResult.receipts + ); withdrawMessageProof = await fuelTokenSender.provider.getMessageProof( tx.id, messageOutReceipt.messageId, @@ -167,7 +228,11 @@ describe('Bridging ERC20 tokens', async function () { // check that the sender balance has decreased by the expected amount let newSenderBalance = await fuelTokenSender.getBalance(fuel_testTokenId); - expect(newSenderBalance.eq(fuelTokenSenderBalance.sub(NUM_TOKENS / DECIMAL_DIFF))).to.be.true; + expect( + newSenderBalance.eq( + fuelTokenSenderBalance.sub(NUM_TOKENS / DECIMAL_DIFF) + ) + ).to.be.true; }); it('Relay Message from Fuel on Ethereum', async () => { @@ -193,8 +258,12 @@ describe('Bridging ERC20 tokens', async function () { it('Check ERC20 arrived on Ethereum', async () => { // check that the recipient balance has increased by the expected amount - let newReceiverBalance = await eth_testToken.balanceOf(ethereumTokenReceiverAddress); - expect(newReceiverBalance.eq(ethereumTokenReceiverBalance.add(NUM_TOKENS))).to.be.true; + let newReceiverBalance = await eth_testToken.balanceOf( + ethereumTokenReceiverAddress + ); + expect( + newReceiverBalance.eq(ethereumTokenReceiverBalance.add(NUM_TOKENS)) + ).to.be.true; }); }); }); diff --git a/packages/integration-tests/tests/transfer_eth.ts b/packages/integration-tests/tests/transfer_eth.ts index b51df199..bcd642bb 100644 --- a/packages/integration-tests/tests/transfer_eth.ts +++ b/packages/integration-tests/tests/transfer_eth.ts @@ -2,7 +2,14 @@ import chai from 'chai'; import { solidity } from 'ethereum-waffle'; import { BigNumber, Signer } from 'ethers'; import { parseEther } from 'ethers/lib/utils'; -import { AbstractAddress, Address, BN, WalletUnlocked as FuelWallet, NativeAssetId, MessageProof } from 'fuels'; +import { + AbstractAddress, + Address, + BN, + WalletUnlocked as FuelWallet, + NativeAssetId, + MessageProof, +} from 'fuels'; import { TestEnvironment, setupEnvironment } from '../scripts/setup'; import { fuels_parseEther } from '../scripts/utils/parsers'; import { createRelayMessageParams } from '../scripts/utils/ethers/createRelayParams'; @@ -10,7 +17,10 @@ import { waitNextBlock } from '../scripts/utils/fuels/waitNextBlock'; import { getMessageOutReceipt } from '../scripts/utils/fuels/getMessageOutReceipt'; import { waitForMessage } from '../scripts/utils/fuels/waitForMessage'; import { FUEL_TX_PARAMS } from '../scripts/utils/constants'; -import { commitBlock, mockFinalization } from '../scripts/utils/ethers/commitBlock'; +import { + commitBlock, + mockFinalization, +} from '../scripts/utils/ethers/commitBlock'; chai.use(solidity); const { expect } = chai; @@ -43,14 +53,19 @@ describe('Transferring ETH', async function () { ethereumETHSenderBalance = await ethereumETHSender.getBalance(); fuelETHReceiver = env.fuel.signers[0].address; fuelETHReceiverAddress = fuelETHReceiver.toHexString(); - fuelETHReceiverBalance = await env.fuel.provider.getBalance(fuelETHReceiver, NativeAssetId); + fuelETHReceiverBalance = await env.fuel.provider.getBalance( + fuelETHReceiver, + NativeAssetId + ); }); it('Send ETH via MessagePortal', async () => { // use the FuelMessagePortal to directly send ETH which should be immediately spendable - let tx = await env.eth.fuelMessagePortal.connect(ethereumETHSender).depositETH(fuelETHReceiverAddress, { - value: parseEther(NUM_ETH), - }); + let tx = await env.eth.fuelMessagePortal + .connect(ethereumETHSender) + .depositETH(fuelETHReceiverAddress, { + value: parseEther(NUM_ETH), + }); let result = await tx.wait(); expect(result.status).to.equal(1); @@ -59,9 +74,17 @@ describe('Transferring ETH', async function () { fuelETHMessageNonce = new BN(event.args.nonce.toHexString()); // check that the sender balance has decreased by the expected amount - let newSenderBalance = await env.eth.provider.getBalance(ethereumETHSenderAddress); - let ethereumETHSenderBalanceMinusGas = ethereumETHSenderBalance.sub(result.gasUsed.mul(result.effectiveGasPrice)); - expect(newSenderBalance.eq(ethereumETHSenderBalanceMinusGas.sub(parseEther(NUM_ETH)))).to.be.true; + let newSenderBalance = await env.eth.provider.getBalance( + ethereumETHSenderAddress + ); + let ethereumETHSenderBalanceMinusGas = ethereumETHSenderBalance.sub( + result.gasUsed.mul(result.effectiveGasPrice) + ); + expect( + newSenderBalance.eq( + ethereumETHSenderBalanceMinusGas.sub(parseEther(NUM_ETH)) + ) + ).to.be.true; }); it('Wait for ETH to arrive on Fuel', async function () { @@ -69,12 +92,25 @@ describe('Transferring ETH', async function () { this.timeout(FUEL_MESSAGE_TIMEOUT_MS); // wait for message to appear in fuel client - expect(await waitForMessage(env.fuel.provider, fuelETHReceiver, fuelETHMessageNonce, FUEL_MESSAGE_TIMEOUT_MS)).to - .not.be.null; + expect( + await waitForMessage( + env.fuel.provider, + fuelETHReceiver, + fuelETHMessageNonce, + FUEL_MESSAGE_TIMEOUT_MS + ) + ).to.not.be.null; // check that the recipient balance has increased by the expected amount - let newReceiverBalance = await env.fuel.provider.getBalance(fuelETHReceiver, NativeAssetId); - expect(newReceiverBalance.eq(fuelETHReceiverBalance.add(fuels_parseEther(NUM_ETH)))).to.be.true; + let newReceiverBalance = await env.fuel.provider.getBalance( + fuelETHReceiver, + NativeAssetId + ); + expect( + newReceiverBalance.eq( + fuelETHReceiverBalance.add(fuels_parseEther(NUM_ETH)) + ) + ).to.be.true; }); }); @@ -111,7 +147,9 @@ describe('Transferring ETH', async function () { const lastBlockId = await waitNextBlock(env); // get message proof - const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); + const messageOutReceipt = getMessageOutReceipt( + fWithdrawTxResult.receipts + ); withdrawMessageProof = await fuelETHSender.provider.getMessageProof( fWithdrawTx.id, messageOutReceipt.messageId, @@ -123,7 +161,9 @@ describe('Transferring ETH', async function () { // Get just the first 3 digits of the balance to compare to the expected balance // this is required because the payment of gas fees is not deterministic - const diffOnSenderBalance = newSenderBalance.sub(fuelETHSenderBalance).formatUnits(); + const diffOnSenderBalance = newSenderBalance + .sub(fuelETHSenderBalance) + .formatUnits(); expect(diffOnSenderBalance.startsWith(NUM_ETH)).to.be.true; }); @@ -151,7 +191,11 @@ describe('Transferring ETH', async function () { it('Check ETH arrived on Ethereum', async () => { // check that the recipient balance has increased by the expected amount let newReceiverBalance = await ethereumETHReceiver.getBalance(); - expect(newReceiverBalance.eq(ethereumETHReceiverBalance.add(parseEther(NUM_ETH)))).to.be.true; + expect( + newReceiverBalance.eq( + ethereumETHReceiverBalance.add(parseEther(NUM_ETH)) + ) + ).to.be.true; }); }); }); diff --git a/packages/message-predicates/.gitattributes b/packages/message-predicates/.gitattributes deleted file mode 100644 index 2169095e..00000000 --- a/packages/message-predicates/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Syntax highlighting of sway files as rust -*.sw linguist-language=Rust \ No newline at end of file diff --git a/packages/message-predicates/.github/workflows/ci.yml b/packages/message-predicates/.github/workflows/ci.yml deleted file mode 100644 index a8b3ab53..00000000 --- a/packages/message-predicates/.github/workflows/ci.yml +++ /dev/null @@ -1,144 +0,0 @@ -name: CI - -on: - push: - branches: - - master - pull_request: - release: - types: [published] - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true - -env: - CARGO_TERM_COLOR: always - DASEL_VERSION: https://github.com/TomWright/dasel/releases/download/v1.24.3/dasel_linux_amd64 - REGISTRY: ghcr.io - RUST_VERSION: 1.69.0 - PATH_TO_SCRIPTS: ./.github/workflows/scripts - -jobs: - # Ensure cargo files are properly linted - lint-toml-files: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.RUST_VERSION }} - - name: Install Cargo.toml linter - uses: baptiste0928/cargo-install@v1 - with: - crate: cargo-toml-lint - version: '0.1' - - name: Run Cargo.toml linter - run: git ls-files | grep Cargo.toml$ | xargs --verbose -n 1 cargo-toml-lint - - # Ensure CI is using the same minimum toolchain specified in fuels Cargo.toml - verify-rust-version: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: | - curl -sSLf "$DASEL_VERSION" -L -o dasel && chmod +x dasel - mv ./dasel /usr/local/bin/dasel - MIN_VERSION=$(cat Cargo.toml | dasel -r toml 'workspace.package.rust-version') - RUST_VERSION="${{ env.RUST_VERSION }}" - echo "Comparing minimum supported toolchain ($MIN_VERSION) with ci toolchain (RUST_VERSION)" - test "$MIN_VERSION" == "$RUST_VERSION" - - # Ensure workspace is buildable - build-verification: - needs: - - verify-rust-version - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_VERSION }} - components: clippy,rustfmt - - - name: Init cache - uses: Swatinem/rust-cache@v1 - - - name: Install a modern linker (mold) - uses: rui314/setup-mold@v1 - - - name: Force Rust to use mold globally for compilation - run: | - touch ~/.cargo/config.toml - echo "[target.x86_64-unknown-linux-gnu]" > ~/.cargo/config.toml - echo 'linker = "clang"' >> ~/.cargo/config.toml - echo 'rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/mold"]' >> ~/.cargo/config.toml - - - name: Install Fuel toolchain - uses: FuelLabs/action-fuel-toolchain@v0.6.0 - with: - toolchain: beta-3 - - - name: Check Sway formatting - run: forc fmt --check - - - name: Build Sway - run: forc build - - - name: Check Rust formatting - run: cargo fmt --verbose --check - - - name: Check Rust linting - run: cargo clippy - - - name: Run tests - run: cargo test - - # Ensure workspace is publishable - publish-crates-check: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_VERSION }} - - - name: Publish crate check - uses: katyo/publish-crates@v2 - with: - dry-run: true - check-repo: false - ignore-unpublished-changes: true - - # Publish crates on release - publish: - needs: - - build-verification - - publish-crates-check - # Only do this job if publishing a release - if: github.event_name == 'release' && github.event.action == 'published' - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.RUST_VERSION }} - - - name: Verify tag version - run: | - curl -sSLf "$DASEL_VERSION" -L -o dasel && chmod +x dasel - mv ./dasel /usr/local/bin/dasel - ${{ env.PATH_TO_SCRIPTS }}/verify_tag.sh ${{ github.ref_name }} Cargo.toml - - name: Publish crate - uses: katyo/publish-crates@v2 - with: - publish-delay: 30000 - registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/packages/message-predicates/.github/workflows/scripts/verify_tag.sh b/packages/message-predicates/.github/workflows/scripts/verify_tag.sh deleted file mode 100755 index 4db9db28..00000000 --- a/packages/message-predicates/.github/workflows/scripts/verify_tag.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -set -e - -err() { - echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2; -} - -status() { - WIDTH=12 - printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2" -} - -REF=$1 -MANIFEST=$2 - -if [ -z "$REF" ]; then - err "Expected ref to be set" - exit 1 -fi - -if [ -z "$MANIFEST" ]; then - err "Expected manifest to be set" - exit 1 -fi - -# strip preceeding 'v' if it exists on tag -REF=${REF/#v} -TOML_VERSION=$(cat $MANIFEST | dasel -r toml 'workspace.package.version') - -if [ "$TOML_VERSION" != "$REF" ]; then - err "Crate version $TOML_VERSION, doesn't match tag version $REF" - exit 1 -else - status "Crate version matches tag $TOML_VERSION" -fi diff --git a/packages/message-predicates/.gitignore b/packages/message-predicates/.gitignore deleted file mode 100644 index a2527c21..00000000 --- a/packages/message-predicates/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -out -target -*/out -*/target -dist \ No newline at end of file diff --git a/packages/message-predicates/contract-message-predicate/src/lib.rs b/packages/message-predicates/contract-message-predicate/src/lib.rs index 9aa8aab8..2d0b4acc 100644 --- a/packages/message-predicates/contract-message-predicate/src/lib.rs +++ b/packages/message-predicates/contract-message-predicate/src/lib.rs @@ -19,6 +19,6 @@ pub fn script_hash() -> [u8; 32] { // Gets the root of the message-to-contract predicate pub fn predicate_root(cparams: &ConsensusParameters) -> [u8; 32] { let predicate = predicate_asm::bytecode(); - let root = Input::predicate_owner(predicate, &cparams); + let root = Input::predicate_owner(predicate, cparams); root.into() } diff --git a/packages/message-predicates/package.json b/packages/message-predicates/package.json index a1d79405..2f675236 100644 --- a/packages/message-predicates/package.json +++ b/packages/message-predicates/package.json @@ -1,12 +1,14 @@ { - "name": "@fuel-bridge/message-predicates", - "private": true, - "main": "./dist/index.ts", - "scripts": { - "test": "cargo test", - "build": "forc build && cargo run && node ./scripts/build.js" - }, - "devDependencies": { - "fuels": "0.0.0-next-20230707184416" - } + "name": "@fuel-bridge/message-predicates", + "private": true, + "main": "./dist/index.ts", + "scripts": { + "test": "cargo test", + "check": "forc fmt --check && cargo fmt --check && cargo clippy", + "format": "forc fmt && cargo fmt", + "build": "forc build && cargo run && node ./scripts/build.js" + }, + "devDependencies": { + "fuels": "0.0.0-next-20230707184416" + } } diff --git a/packages/message-predicates/scripts/build.js b/packages/message-predicates/scripts/build.js index 2f2aa8ef..e24c9aa5 100644 --- a/packages/message-predicates/scripts/build.js +++ b/packages/message-predicates/scripts/build.js @@ -11,11 +11,14 @@ const contractMessagePredicate = readFileSync(CONTRACT_PATH); const contractMessageScript = readFileSync(SCRIPT_PATH); // Create export function createExport(name, value) { - return `export const ${name} = "${value}";`; + return `export const ${name} = "${value}";`; } // Write file mkdirSync(DIST_FOLDER, { recursive: true }); -writeFileSync(DIST_FILE, [ - createExport("contractMessagePredicate", hexlify(contractMessagePredicate)), - createExport("contractMessageScript", hexlify(contractMessageScript)), -].join('\n')); +writeFileSync( + DIST_FILE, + [ + createExport('contractMessagePredicate', hexlify(contractMessagePredicate)), + createExport('contractMessageScript', hexlify(contractMessageScript)), + ].join('\n') +); diff --git a/packages/portal-contracts/.eslintrc.json b/packages/portal-contracts/.eslintrc.json deleted file mode 100644 index 98243f16..00000000 --- a/packages/portal-contracts/.eslintrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], - "extends": [ - "plugin:@typescript-eslint/recommended", - "prettier" - ], - "rules": { - "no-await-in-loop": 0, - "prefer-destructuring": 0, - "no-bitwise": 0 - }, - - // Disable no-unused-expressions to allow chai 'expect' expressions in testing - "overrides": [ - { - "files": ["test/*.ts", "test/*/*.ts", "*test.ts"], - "rules": { - "@typescript-eslint/no-unused-expressions": "off" - } - } - ] - -} diff --git a/packages/portal-contracts/.github/workflows/ci.yaml b/packages/portal-contracts/.github/workflows/ci.yaml deleted file mode 100644 index 7b7c7a94..00000000 --- a/packages/portal-contracts/.github/workflows/ci.yaml +++ /dev/null @@ -1,35 +0,0 @@ -on: - push: - branches: - - master - pull_request: - branches: - - "**" # matches every branch -name: "Node.js Tests and Coverage" -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - persist-credentials: false - - name: Reconfigure git to use HTTP authentication - run: > - git config --global url."https://github.com/".insteadOf - ssh://git@github.com/ - - uses: actions/setup-node@v1 - with: - node-version: 16.x - - run: npm ci - - run: npm run build - - run: npm test - - run: npm run coverage - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.json - flags: unittests - name: codecov-umbrella - fail_ci_if_error: true diff --git a/packages/portal-contracts/.github/workflows/lint.yaml b/packages/portal-contracts/.github/workflows/lint.yaml deleted file mode 100644 index ae54a6da..00000000 --- a/packages/portal-contracts/.github/workflows/lint.yaml +++ /dev/null @@ -1,25 +0,0 @@ -on: - push: - branches: - - master - pull_request: - branches: - - "**" # matches every branch -name: "Linting and Formatting" -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - persist-credentials: false - - name: Reconfigure git to use HTTP authentication - run: > - git config --global url."https://github.com/".insteadOf - ssh://git@github.com/ - - uses: actions/setup-node@v1 - with: - node-version: 16.x - - run: npm ci - - run: npm run lint - - run: npm run format-check diff --git a/packages/portal-contracts/.prettierignore b/packages/portal-contracts/.prettierignore deleted file mode 100644 index db8e285d..00000000 --- a/packages/portal-contracts/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -build -.coverage_* -coverage -**/typechain/**/* -node_modules diff --git a/packages/portal-contracts/.prettierrc.json b/packages/portal-contracts/.prettierrc.json deleted file mode 100644 index d0aaea82..00000000 --- a/packages/portal-contracts/.prettierrc.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "overrides": [ - { - "files": "*.sol", - "options": { - "printWidth": 120, - "tabWidth": 4, - "useTabs": false, - "singleQuote": false, - "bracketSpacing": false - } - }, - { - "files": ["*.js", "*.ts"], - "options": { - "printWidth": 120, - "semi": true, - "tabWidth": 4, - "useTabs": false, - "singleQuote": true, - "bracketSpacing": true, - "trailingComma": "es5" - } - }, - { - "files": ["*.json"], - "options": { - "useTabs": true - } - }, - { - "files": "*.md", - "options": { - "useTabs": false - } - } - ] -} diff --git a/packages/portal-contracts/.solcover.js b/packages/portal-contracts/.solcover.js index 95f48720..b8ea1b43 100644 --- a/packages/portal-contracts/.solcover.js +++ b/packages/portal-contracts/.solcover.js @@ -1,7 +1,7 @@ module.exports = { - skipFiles: ['/vendor', '/lib', '/test'], - mocha: { - grep: '@skip-on-coverage', // Find everything with this tag - invert: true, // Run the grep's inverse set. - }, + skipFiles: ['/vendor', '/lib', '/test'], + mocha: { + grep: '@skip-on-coverage', // Find everything with this tag + invert: true, // Run the grep's inverse set. + }, }; diff --git a/packages/portal-contracts/.solhint.json b/packages/portal-contracts/.solhint.json index 76fd8f69..d1d739ba 100644 --- a/packages/portal-contracts/.solhint.json +++ b/packages/portal-contracts/.solhint.json @@ -1,11 +1,10 @@ { - "extends": ["solhint:recommended"], - "plugins": [], - "rules": { - "compiler-version": ["error", "^0.8.9"], - "var-name-mixedcase": null, - "no-inline-assembly": null, - "avoid-tx-origin": null, - "func-visibility": ["error", { "ignoreConstructors": true }] - } + "extends": ["solhint:recommended"], + "rules": { + "compiler-version": ["error", "^0.8.9"], + "var-name-mixedcase": null, + "no-inline-assembly": null, + "avoid-tx-origin": null, + "func-visibility": ["error", { "ignoreConstructors": true }] + } } diff --git a/packages/portal-contracts/deployments/deployments.goerli.json b/packages/portal-contracts/deployments/deployments.goerli.json index 691271b6..aa5eaa66 100644 --- a/packages/portal-contracts/deployments/deployments.goerli.json +++ b/packages/portal-contracts/deployments/deployments.goerli.json @@ -5,4 +5,4 @@ "FuelChainState_impl": "0x7224B859CDe9970A846FaFa435156b7116D304e5", "FuelMessagePortal_impl": "0xf28E2F7f01Bf04B1c10Dd19875Cfb0b89C9aEeD0", "FuelERC20Gateway_impl": "0x25Bf04BA81b1bc8346350622a0AaCa03c74008fB" -} \ No newline at end of file +} diff --git a/packages/portal-contracts/hardhat.config.ts b/packages/portal-contracts/hardhat.config.ts index b710a21c..880e0834 100644 --- a/packages/portal-contracts/hardhat.config.ts +++ b/packages/portal-contracts/hardhat.config.ts @@ -14,49 +14,55 @@ const CONTRACTS_RPC_URL = process.env.CONTRACTS_RPC_URL || ''; const CONTRACTS_DEPLOYER_KEY = process.env.CONTRACTS_DEPLOYER_KEY || ''; const config: HardhatUserConfig = { - defaultNetwork: 'hardhat', - solidity: { - compilers: [ - { - version: '0.8.9', - settings: { - optimizer: { - enabled: true, - runs: 10000, - }, - }, - }, - ], - }, - mocha: { - timeout: 180_000, - }, - networks: { - hardhat: { - accounts: { - count: 128, - }, - }, - localhost: { - url: 'http://127.0.0.1:8545/', - }, - custom: { - url: 'http://127.0.0.1:8545/', + defaultNetwork: 'hardhat', + solidity: { + compilers: [ + { + version: '0.8.9', + settings: { + optimizer: { + enabled: true, + runs: 10000, + }, }, + }, + ], + }, + mocha: { + timeout: 180_000, + }, + networks: { + hardhat: { + accounts: { + count: 128, + }, + }, + localhost: { + url: 'http://127.0.0.1:8545/', }, - etherscan: { - apiKey: ETHERSCAN_API_KEY, + custom: { + url: 'http://127.0.0.1:8545/', }, + }, + etherscan: { + apiKey: ETHERSCAN_API_KEY, + }, }; // Override network configuration with environment variables -if (CONTRACTS_RPC_URL && CONTRACTS_DEPLOYER_KEY && config.networks && config.networks.custom) { - config.networks.custom = { - accounts: [CONTRACTS_DEPLOYER_KEY], - url: CONTRACTS_RPC_URL, - live: true, - }; - if (process.env.CONTRACTS_GAS_PRICE) config.networks.custom.gasPrice = parseInt(process.env.CONTRACTS_GAS_PRICE); +if ( + CONTRACTS_RPC_URL && + CONTRACTS_DEPLOYER_KEY && + config.networks && + config.networks.custom +) { + config.networks.custom = { + accounts: [CONTRACTS_DEPLOYER_KEY], + url: CONTRACTS_RPC_URL, + live: true, + }; + if (process.env.CONTRACTS_GAS_PRICE) + config.networks.custom.gasPrice = parseInt(process.env.CONTRACTS_GAS_PRICE); } export default config; diff --git a/packages/portal-contracts/package.json b/packages/portal-contracts/package.json index f046cf4c..8077da25 100644 --- a/packages/portal-contracts/package.json +++ b/packages/portal-contracts/package.json @@ -1,79 +1,77 @@ { - "name": "@fuel-bridge/portal-contracts", - "version": "0.0.0", - "description": "The Fuel v2 Solidity smart contracts.", - "main": "typechain/index.ts", - "private": true, - "license": "APACHE-2.0", - "scripts": { - "build": "pnpm run clean && pnpm run compile", - "clean": "pnpm hardhat clean", - "compile": "pnpm hardhat compile --show-stack-traces", - "coverage": "pnpm run build && pnpm hardhat coverage --temp artifacts --network hardhat", - "format": "prettier --write \"contracts/**/*.sol\" \"**/*.ts\" \"**/*.js\" \"**/*.md\"", - "format-check": "prettier --check \"contracts/**/*.sol\" \"**/*.ts\" \"**/*.js\" \"**/*.md\"", - "lint": "pnpx solhint \"contracts/**/*.sol\" && pnpx eslint . && pnpx markdownlint --ignore node_modules **/*.md", - "node": "pnpm hardhat node --network hardhat", - "node-deploy": "QUICK_DEPLOY=true pnpm hardhat run --network localhost scripts/deployAll.ts", - "script-deploy": "pnpm hardhat run --network custom scripts/deployAll.ts", - "script-deploy-impl": "pnpm hardhat run --network custom scripts/deployImplementation.ts", - "script-upgrade": "pnpm hardhat run --network custom scripts/upgradeAll.ts", - "script-verify-source": "pnpm hardhat run --network custom scripts/verifySource.ts", - "script-verify-address": "pnpm hardhat run --network custom scripts/verifyAddress.ts", - "serve-deployments": "pnpx ts-node scripts/serveDeployments.ts", - "test": "pnpm hardhat test", - "test-no-compile": "pnpm hardhat test --no-compile", - "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register" - }, - "dependencies": { - "@fuel-contracts/merkle-sol": "^0.1.4", - "@nomiclabs/hardhat-ethers": "^2.2.1", - "@nomiclabs/hardhat-etherscan": "^3.1.2", - "@openzeppelin/contracts": "^4.8.0", - "@openzeppelin/contracts-upgradeable": "^4.8.0", - "@openzeppelin/hardhat-upgrades": "^1.21.0", - "@typechain/ethers-v5": "^6.0.5", - "@types/express": "^4.17.14", - "express": "^4.18.2", - "hardhat": "^2.12.2", - "hardhat-typechain": "^0.3.5", - "node-fetch": "^2.6.6" - }, - "devDependencies": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/providers": "^5.7.0", - "@fuel-ts/merkle": "^0.21.2", - "@nomiclabs/hardhat-waffle": "^2.0.3", - "@types/chai": "^4.3.4", - "@types/mocha": "^10.0.0", - "@types/node": "^18.11.9", - "@typescript-eslint/eslint-plugin": "^5.43.0", - "@typescript-eslint/parser": "^5.43.0", - "chai": "^4.3.7", - "dotenv": "^16.0.3", - "eslint": "^8.27.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-react": "^7.31.10", - "eslint-plugin-react-hooks": "^4.6.0", - "ethereum-waffle": "^3.4.4", - "ethers": "^5.7.2", - "hardhat-gas-reporter": "^1.0.9", - "markdownlint": "^0.26.2", - "markdownlint-cli": "^0.32.2", - "prettier": "^2.7.1", - "prettier-plugin-solidity": "^v1.0.0-rc.1", - "solc": "^0.8.17", - "solhint": "3.3.7", - "solidity-coverage": "^0.8.2", - "ts-generator": "^0.1.1", - "ts-node": "^10.9.1", - "typechain": "^4.0.3", - "typescript": "^4.9.3" - } -} \ No newline at end of file + "name": "@fuel-bridge/portal-contracts", + "version": "0.0.0", + "description": "The Fuel v2 Solidity smart contracts.", + "main": "typechain/index.ts", + "private": true, + "license": "APACHE-2.0", + "scripts": { + "build": "pnpm run clean && pnpm run compile", + "clean": "pnpm hardhat clean", + "compile": "pnpm hardhat compile --show-stack-traces", + "coverage": "pnpm run build && pnpm hardhat coverage --temp artifacts --network hardhat", + "check": "pnpm solhint \"contracts/**/*.sol\"", + "node:start": "pnpm hardhat node --network hardhat", + "contract:deploy": "QUICK_DEPLOY=true pnpm hardhat run --network localhost scripts/deployAll.ts", + "script-deploy": "pnpm hardhat run --network custom scripts/deployAll.ts", + "script-deploy-impl": "pnpm hardhat run --network custom scripts/deployImplementation.ts", + "script-upgrade": "pnpm hardhat run --network custom scripts/upgradeAll.ts", + "script-verify-source": "pnpm hardhat run --network custom scripts/verifySource.ts", + "script-verify-address": "pnpm hardhat run --network custom scripts/verifyAddress.ts", + "serve-deployments": "pnpx ts-node scripts/serveDeployments.ts", + "test": "pnpm hardhat test", + "test-no-compile": "pnpm hardhat test --no-compile", + "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register" + }, + "dependencies": { + "@fuel-contracts/merkle-sol": "^0.1.4", + "@nomiclabs/hardhat-ethers": "^2.2.1", + "@nomiclabs/hardhat-etherscan": "^3.1.2", + "@openzeppelin/contracts": "^4.8.0", + "@openzeppelin/contracts-upgradeable": "^4.8.0", + "@openzeppelin/hardhat-upgrades": "^1.21.0", + "@typechain/ethers-v5": "^6.0.5", + "@types/express": "^4.17.14", + "express": "^4.18.2", + "hardhat": "^2.12.2", + "hardhat-typechain": "^0.3.5", + "node-fetch": "^2.6.6" + }, + "devDependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@fuel-ts/merkle": "^0.21.2", + "@nomiclabs/hardhat-waffle": "^2.0.3", + "@types/chai": "^4.3.4", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.9", + "@typescript-eslint/eslint-plugin": "^5.43.0", + "@typescript-eslint/parser": "^5.43.0", + "chai": "^4.3.7", + "dotenv": "^16.0.3", + "eslint": "^8.27.0", + "eslint-config-airbnb-typescript": "^17.0.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-jsx-a11y": "^6.6.1", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.31.10", + "eslint-plugin-react-hooks": "^4.6.0", + "ethereum-waffle": "^3.4.4", + "ethers": "^5.7.2", + "hardhat-gas-reporter": "^1.0.9", + "markdownlint": "^0.26.2", + "markdownlint-cli": "^0.32.2", + "prettier": "^2.7.1", + "prettier-plugin-solidity": "^v1.0.0-rc.1", + "solc": "^0.8.17", + "solhint": "3.3.7", + "solidity-coverage": "^0.8.2", + "ts-generator": "^0.1.1", + "ts-node": "^10.9.1", + "typechain": "^4.0.3", + "typescript": "^5.1.6" + } +} diff --git a/packages/portal-contracts/protocol/blockHeader.ts b/packages/portal-contracts/protocol/blockHeader.ts index 3da22d87..4ade79f6 100644 --- a/packages/portal-contracts/protocol/blockHeader.ts +++ b/packages/portal-contracts/protocol/blockHeader.ts @@ -3,79 +3,86 @@ import hash from './cryptography'; // The BlockHeader structure. class BlockHeader { - constructor( - // Consensus - public prevRoot: string, - public height: string, - public timestamp: string, + constructor( + // Consensus + public prevRoot: string, + public height: string, + public timestamp: string, - // Application - public daHeight: string, - public txCount: string, - public outputMessagesCount: string, - public txRoot: string, - public outputMessagesRoot: string - ) {} + // Application + public daHeight: string, + public txCount: string, + public outputMessagesCount: string, + public txRoot: string, + public outputMessagesRoot: string + ) {} } // Serialize a block application header. export function serializeApplicationHeader(blockHeader: BlockHeader): string { - return utils.solidityPack( - ['uint64', 'uint64', 'uint64', 'bytes32', 'bytes32'], - [ - blockHeader.daHeight, - blockHeader.txCount, - blockHeader.outputMessagesCount, - blockHeader.txRoot, - blockHeader.outputMessagesRoot, - ] - ); + return utils.solidityPack( + ['uint64', 'uint64', 'uint64', 'bytes32', 'bytes32'], + [ + blockHeader.daHeight, + blockHeader.txCount, + blockHeader.outputMessagesCount, + blockHeader.txRoot, + blockHeader.outputMessagesRoot, + ] + ); } // Produce the block application header hash. export function computeApplicationHeaderHash(blockHeader: BlockHeader): string { - return hash(serializeApplicationHeader(blockHeader)); + return hash(serializeApplicationHeader(blockHeader)); } // Serialize a block consensus header. export function serializeConsensusHeader(blockHeader: BlockHeader): string { - return utils.solidityPack( - ['bytes32', 'uint32', 'uint64', 'bytes32'], - [blockHeader.prevRoot, blockHeader.height, blockHeader.timestamp, computeApplicationHeaderHash(blockHeader)] - ); + return utils.solidityPack( + ['bytes32', 'uint32', 'uint64', 'bytes32'], + [ + blockHeader.prevRoot, + blockHeader.height, + blockHeader.timestamp, + computeApplicationHeaderHash(blockHeader), + ] + ); } // Produce the block consensus header hash. export function computeConsensusHeaderHash(blockHeader: BlockHeader): string { - return hash(serializeConsensusHeader(blockHeader)); + return hash(serializeConsensusHeader(blockHeader)); } // Produce the block ID (aka the consensus header hash). export function computeBlockId(blockHeader: BlockHeader): string { - return computeConsensusHeaderHash(blockHeader); + return computeConsensusHeaderHash(blockHeader); } // The BlockHeader structure with only consensus data. export class BlockHeaderLite { - constructor( - // Consensus - public prevRoot: string, - public height: string, - public timestamp: string, - public applicationHash: string - ) {} + constructor( + // Consensus + public prevRoot: string, + public height: string, + public timestamp: string, + public applicationHash: string + ) {} } // Generates the lite version of the block header. -export function generateBlockHeaderLite(blockHeader: BlockHeader): BlockHeaderLite { - const header: BlockHeaderLite = { - prevRoot: blockHeader.prevRoot, - height: blockHeader.height, - timestamp: blockHeader.timestamp, - applicationHash: computeApplicationHeaderHash(blockHeader), - }; +export function generateBlockHeaderLite( + blockHeader: BlockHeader +): BlockHeaderLite { + const header: BlockHeaderLite = { + prevRoot: blockHeader.prevRoot, + height: blockHeader.height, + timestamp: blockHeader.timestamp, + applicationHash: computeApplicationHeaderHash(blockHeader), + }; - return header; + return header; } export default BlockHeader; diff --git a/packages/portal-contracts/protocol/constants.ts b/packages/portal-contracts/protocol/constants.ts index 7b2f1054..43811604 100644 --- a/packages/portal-contracts/protocol/constants.ts +++ b/packages/portal-contracts/protocol/constants.ts @@ -1,3 +1,5 @@ // Useful and defined constants of the Fuel system -export const EMPTY = '0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; -export const ZERO = '0x0000000000000000000000000000000000000000000000000000000000000000'; +export const EMPTY = + '0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'; +export const ZERO = + '0x0000000000000000000000000000000000000000000000000000000000000000'; diff --git a/packages/portal-contracts/protocol/cryptography.ts b/packages/portal-contracts/protocol/cryptography.ts index b5e3ffc2..784d067a 100644 --- a/packages/portal-contracts/protocol/cryptography.ts +++ b/packages/portal-contracts/protocol/cryptography.ts @@ -2,5 +2,5 @@ import { utils, BytesLike } from 'ethers'; // The primary hash function for Fuel. export default function hash(data: BytesLike): string { - return utils.sha256(data); + return utils.sha256(data); } diff --git a/packages/portal-contracts/protocol/harness.ts b/packages/portal-contracts/protocol/harness.ts index 31dcd242..a21b5615 100644 --- a/packages/portal-contracts/protocol/harness.ts +++ b/packages/portal-contracts/protocol/harness.ts @@ -9,144 +9,182 @@ import { Token } from '../typechain/Token.d'; // All deployable contracts. export interface DeployedContracts { - fuelMessagePortal: FuelMessagePortal; - fuelChainState: FuelChainState; - fuelERC20Gateway: FuelERC20Gateway; + fuelMessagePortal: FuelMessagePortal; + fuelChainState: FuelChainState; + fuelERC20Gateway: FuelERC20Gateway; } export interface DeployedContractAddresses { - FuelMessagePortal: string; - FuelChainState: string; - FuelERC20Gateway: string; - FuelMessagePortal_impl: string; - FuelChainState_impl: string; - FuelERC20Gateway_impl: string; + FuelMessagePortal: string; + FuelChainState: string; + FuelERC20Gateway: string; + FuelMessagePortal_impl: string; + FuelChainState_impl: string; + FuelERC20Gateway_impl: string; } // The harness object. export interface HarnessObject { - contractAddresses: DeployedContractAddresses; - fuelMessagePortal: FuelMessagePortal; - fuelChainState: FuelChainState; - fuelERC20Gateway: FuelERC20Gateway; - token: Token; - signer: string; - signers: Array; - addresses: Array; - initialTokenAmount: BN; + contractAddresses: DeployedContractAddresses; + fuelMessagePortal: FuelMessagePortal; + fuelChainState: FuelChainState; + fuelERC20Gateway: FuelERC20Gateway; + token: Token; + signer: string; + signers: Array; + addresses: Array; + initialTokenAmount: BN; } // Gets a blank set of addresses for the deployed contracts. export function getBlankAddresses(): DeployedContractAddresses { - return { - FuelChainState: '', - FuelMessagePortal: '', - FuelERC20Gateway: '', - FuelChainState_impl: '', - FuelMessagePortal_impl: '', - FuelERC20Gateway_impl: '', - }; + return { + FuelChainState: '', + FuelMessagePortal: '', + FuelERC20Gateway: '', + FuelChainState_impl: '', + FuelMessagePortal_impl: '', + FuelERC20Gateway_impl: '', + }; } // Gets the addresses of the deployed contracts. -export async function getContractAddresses(contracts: DeployedContracts): Promise { - return { - FuelChainState: contracts.fuelChainState.address, - FuelMessagePortal: contracts.fuelMessagePortal.address, - FuelERC20Gateway: contracts.fuelERC20Gateway.address, - FuelChainState_impl: await upgrades.erc1967.getImplementationAddress(contracts.fuelChainState.address), - FuelMessagePortal_impl: await upgrades.erc1967.getImplementationAddress(contracts.fuelMessagePortal.address), - FuelERC20Gateway_impl: await upgrades.erc1967.getImplementationAddress(contracts.fuelERC20Gateway.address), - }; +export async function getContractAddresses( + contracts: DeployedContracts +): Promise { + return { + FuelChainState: contracts.fuelChainState.address, + FuelMessagePortal: contracts.fuelMessagePortal.address, + FuelERC20Gateway: contracts.fuelERC20Gateway.address, + FuelChainState_impl: await upgrades.erc1967.getImplementationAddress( + contracts.fuelChainState.address + ), + FuelMessagePortal_impl: await upgrades.erc1967.getImplementationAddress( + contracts.fuelMessagePortal.address + ), + FuelERC20Gateway_impl: await upgrades.erc1967.getImplementationAddress( + contracts.fuelERC20Gateway.address + ), + }; } // The setup method for Fuel. export async function setupFuel(): Promise { - // Get signers - const signer = (await ethers.getSigners())[0].address; - const signers = await ethers.getSigners(); - - // Deploy Fuel contracts - const contracts = await deployFuel(); - - // Deploy a token for gateway testing - const tokenFactory = await ethers.getContractFactory('Token'); - const token: Token = (await tokenFactory.deploy()) as Token; - await token.deployed(); - - // Mint some dummy token for deposit testing - const initialTokenAmount = ethers.utils.parseEther('1000000'); - for (let i = 0; i < signers.length; i += 1) { - await token.mint(await signers[i].getAddress(), initialTokenAmount); - } - - // Return the Fuel harness object - return { - contractAddresses: await getContractAddresses(contracts), - fuelChainState: contracts.fuelChainState, - fuelMessagePortal: contracts.fuelMessagePortal, - fuelERC20Gateway: contracts.fuelERC20Gateway, - token, - signer, - signers, - addresses: (await ethers.getSigners()).map((v) => v.address), - initialTokenAmount, - }; + // Get signers + const signer = (await ethers.getSigners())[0].address; + const signers = await ethers.getSigners(); + + // Deploy Fuel contracts + const contracts = await deployFuel(); + + // Deploy a token for gateway testing + const tokenFactory = await ethers.getContractFactory('Token'); + const token: Token = (await tokenFactory.deploy()) as Token; + await token.deployed(); + + // Mint some dummy token for deposit testing + const initialTokenAmount = ethers.utils.parseEther('1000000'); + for (let i = 0; i < signers.length; i += 1) { + await token.mint(await signers[i].getAddress(), initialTokenAmount); + } + + // Return the Fuel harness object + return { + contractAddresses: await getContractAddresses(contracts), + fuelChainState: contracts.fuelChainState, + fuelMessagePortal: contracts.fuelMessagePortal, + fuelERC20Gateway: contracts.fuelERC20Gateway, + token, + signer, + signers, + addresses: (await ethers.getSigners()).map((v) => v.address), + initialTokenAmount, + }; } // The full contract deployment for Fuel. export async function deployFuel(): Promise { - // Deploy fuel chain state contract - const FuelChainState = await ethers.getContractFactory('FuelChainState'); - const fuelChainState = (await upgrades.deployProxy(FuelChainState, [], { - initializer: 'initialize', - })) as FuelChainState; - await fuelChainState.deployed(); - - // Deploy message portal contract - const FuelMessagePortal = await ethers.getContractFactory('FuelMessagePortal'); - const fuelMessagePortal = (await upgrades.deployProxy(FuelMessagePortal, [fuelChainState.address], { - initializer: 'initialize', - })) as FuelMessagePortal; - await fuelMessagePortal.deployed(); - - // Deploy gateway contract for ERC20 bridging - const FuelERC20Gateway = await ethers.getContractFactory('FuelERC20Gateway'); - const fuelERC20Gateway = (await upgrades.deployProxy(FuelERC20Gateway, [fuelMessagePortal.address], { - initializer: 'initialize', - })) as FuelERC20Gateway; - await fuelERC20Gateway.deployed(); - - // Return deployed contracts - return { - fuelChainState, - fuelMessagePortal, - fuelERC20Gateway, - }; + // Deploy fuel chain state contract + const FuelChainState = await ethers.getContractFactory('FuelChainState'); + const fuelChainState = (await upgrades.deployProxy(FuelChainState, [], { + initializer: 'initialize', + })) as FuelChainState; + await fuelChainState.deployed(); + + // Deploy message portal contract + const FuelMessagePortal = await ethers.getContractFactory( + 'FuelMessagePortal' + ); + const fuelMessagePortal = (await upgrades.deployProxy( + FuelMessagePortal, + [fuelChainState.address], + { + initializer: 'initialize', + } + )) as FuelMessagePortal; + await fuelMessagePortal.deployed(); + + // Deploy gateway contract for ERC20 bridging + const FuelERC20Gateway = await ethers.getContractFactory('FuelERC20Gateway'); + const fuelERC20Gateway = (await upgrades.deployProxy( + FuelERC20Gateway, + [fuelMessagePortal.address], + { + initializer: 'initialize', + } + )) as FuelERC20Gateway; + await fuelERC20Gateway.deployed(); + + // Return deployed contracts + return { + fuelChainState, + fuelMessagePortal, + fuelERC20Gateway, + }; } // The full contract deployment for Fuel. export async function upgradeFuel( - contracts: DeployedContractAddresses, - signer?: Signer + contracts: DeployedContractAddresses, + signer?: Signer ): Promise { - // Upgrade fuel chain state contract - const FuelChainState = await ethers.getContractFactory('FuelChainState', signer); - await upgrades.forceImport(contracts.FuelChainState, FuelChainState, { kind: 'uups' }); - await upgrades.upgradeProxy(contracts.FuelChainState, FuelChainState); - - // Upgrade message portal contract - const FuelMessagePortal = await ethers.getContractFactory('FuelMessagePortal', signer); - await upgrades.forceImport(contracts.FuelMessagePortal, FuelMessagePortal, { kind: 'uups' }); - await upgrades.upgradeProxy(contracts.FuelMessagePortal, FuelMessagePortal); - - // Upgrade gateway contract for ERC20 bridging - const FuelERC20Gateway = await ethers.getContractFactory('FuelERC20Gateway', signer); - await upgrades.forceImport(contracts.FuelERC20Gateway, FuelERC20Gateway, { kind: 'uups' }); - await upgrades.upgradeProxy(contracts.FuelERC20Gateway, FuelERC20Gateway); - - // Return deployed contracts - contracts.FuelChainState_impl = await upgrades.erc1967.getImplementationAddress(contracts.FuelChainState); - contracts.FuelMessagePortal_impl = await upgrades.erc1967.getImplementationAddress(contracts.FuelMessagePortal); - contracts.FuelERC20Gateway_impl = await upgrades.erc1967.getImplementationAddress(contracts.FuelERC20Gateway); - return contracts; + // Upgrade fuel chain state contract + const FuelChainState = await ethers.getContractFactory( + 'FuelChainState', + signer + ); + await upgrades.forceImport(contracts.FuelChainState, FuelChainState, { + kind: 'uups', + }); + await upgrades.upgradeProxy(contracts.FuelChainState, FuelChainState); + + // Upgrade message portal contract + const FuelMessagePortal = await ethers.getContractFactory( + 'FuelMessagePortal', + signer + ); + await upgrades.forceImport(contracts.FuelMessagePortal, FuelMessagePortal, { + kind: 'uups', + }); + await upgrades.upgradeProxy(contracts.FuelMessagePortal, FuelMessagePortal); + + // Upgrade gateway contract for ERC20 bridging + const FuelERC20Gateway = await ethers.getContractFactory( + 'FuelERC20Gateway', + signer + ); + await upgrades.forceImport(contracts.FuelERC20Gateway, FuelERC20Gateway, { + kind: 'uups', + }); + await upgrades.upgradeProxy(contracts.FuelERC20Gateway, FuelERC20Gateway); + + // Return deployed contracts + contracts.FuelChainState_impl = + await upgrades.erc1967.getImplementationAddress(contracts.FuelChainState); + contracts.FuelMessagePortal_impl = + await upgrades.erc1967.getImplementationAddress( + contracts.FuelMessagePortal + ); + contracts.FuelERC20Gateway_impl = + await upgrades.erc1967.getImplementationAddress(contracts.FuelERC20Gateway); + return contracts; } diff --git a/packages/portal-contracts/protocol/message.ts b/packages/portal-contracts/protocol/message.ts index e4e18344..fa2f0766 100644 --- a/packages/portal-contracts/protocol/message.ts +++ b/packages/portal-contracts/protocol/message.ts @@ -3,23 +3,29 @@ import hash from './cryptography'; // The Message structure. class Message { - constructor( - public sender: string, - public recipient: string, - public amount: BN, - public nonce: string, - public data: string - ) {} + constructor( + public sender: string, + public recipient: string, + public amount: BN, + public nonce: string, + public data: string + ) {} } // Computes the message ID. export function computeMessageId(message: Message): string { - return hash( - ethers.utils.solidityPack( - ['bytes32', 'bytes32', 'bytes32', 'uint64', 'bytes'], - [message.sender, message.recipient, message.nonce, message.amount, message.data] - ) - ); + return hash( + ethers.utils.solidityPack( + ['bytes32', 'bytes32', 'bytes32', 'uint64', 'bytes'], + [ + message.sender, + message.recipient, + message.nonce, + message.amount, + message.data, + ] + ) + ); } export default Message; diff --git a/packages/portal-contracts/protocol/utils.ts b/packages/portal-contracts/protocol/utils.ts index f668a351..4854aeaf 100644 --- a/packages/portal-contracts/protocol/utils.ts +++ b/packages/portal-contracts/protocol/utils.ts @@ -3,44 +3,48 @@ import { BigNumber as BN } from 'ethers'; import hash from './cryptography'; export function randomAddress(): string { - return hash(BN.from(Math.floor(Math.random() * 1_000_000)).toHexString()).slice(0, 42); + return hash( + BN.from(Math.floor(Math.random() * 1_000_000)).toHexString() + ).slice(0, 42); } export function randomBytes32(): string { - return hash(BN.from(Math.floor(Math.random() * 1_000_000)).toHexString()); + return hash(BN.from(Math.floor(Math.random() * 1_000_000)).toHexString()); } export function randomInt(max: number): number { - return Math.floor(Math.random() * max); + return Math.floor(Math.random() * max); } export function randomBytes(length: number): string { - return hash(BN.from(Math.floor(Math.random() * 1_000_000)).toHexString()).slice(0, length * 2 + 2); + return hash( + BN.from(Math.floor(Math.random() * 1_000_000)).toHexString() + ).slice(0, length * 2 + 2); } export function uintToBytes32(i: number): string { - const value = BN.from(i).toHexString(); - let trimmedValue = value.slice(2); - trimmedValue = '0'.repeat(64 - trimmedValue.length).concat(trimmedValue); - return '0x'.concat(trimmedValue); + const value = BN.from(i).toHexString(); + let trimmedValue = value.slice(2); + trimmedValue = '0'.repeat(64 - trimmedValue.length).concat(trimmedValue); + return '0x'.concat(trimmedValue); } export function padUint(value: BN): string { - // uint256 is encoded as 32 bytes, so pad that string. - let trimmedValue = value.toHexString().slice(2); - trimmedValue = '0'.repeat(64 - trimmedValue.length).concat(trimmedValue); - return '0x'.concat(trimmedValue); + // uint256 is encoded as 32 bytes, so pad that string. + let trimmedValue = value.toHexString().slice(2); + trimmedValue = '0'.repeat(64 - trimmedValue.length).concat(trimmedValue); + return '0x'.concat(trimmedValue); } export function padBytes(value: string): string { - let trimmedValue = value.slice(2); - trimmedValue = '0'.repeat(64 - trimmedValue.length).concat(trimmedValue); - return '0x'.concat(trimmedValue); + let trimmedValue = value.slice(2); + trimmedValue = '0'.repeat(64 - trimmedValue.length).concat(trimmedValue); + return '0x'.concat(trimmedValue); } export function tai64Time(millis: number): string { - const zeroPointOffset = '4611686018427387914'; - return BN.from(Math.floor(millis / 1000)) - .add(zeroPointOffset) - .toHexString(); + const zeroPointOffset = '4611686018427387914'; + return BN.from(Math.floor(millis / 1000)) + .add(zeroPointOffset) + .toHexString(); } diff --git a/packages/portal-contracts/protocol/validators.ts b/packages/portal-contracts/protocol/validators.ts index 8d3f1efe..5f2c7f19 100644 --- a/packages/portal-contracts/protocol/validators.ts +++ b/packages/portal-contracts/protocol/validators.ts @@ -3,16 +3,22 @@ import { Signature } from 'ethers'; import { SigningKey } from 'ethers/lib/utils'; // Sign a messag with a signer, returning the signature object (v, r, s components) -export async function componentSign(signer: SigningKey, message: string): Promise { - const flatSig = await signer.signDigest(ethers.utils.arrayify(message)); - const sig = ethers.utils.splitSignature(flatSig); - return sig; +export async function componentSign( + signer: SigningKey, + message: string +): Promise { + const flatSig = await signer.signDigest(ethers.utils.arrayify(message)); + const sig = ethers.utils.splitSignature(flatSig); + return sig; } // Sign a message with as signer, returning a 64-byte compact ECDSA signature -export async function compactSign(signer: SigningKey, message: string): Promise { - const sig = await componentSign(signer, message); - // eslint-disable-next-line no-underscore-dangle - const compactSig = sig.r.concat(sig._vs.slice(2)); - return compactSig; +export async function compactSign( + signer: SigningKey, + message: string +): Promise { + const sig = await componentSign(signer, message); + // eslint-disable-next-line no-underscore-dangle + const compactSig = sig.r.concat(sig._vs.slice(2)); + return compactSig; } diff --git a/packages/portal-contracts/scripts/deployAll.ts b/packages/portal-contracts/scripts/deployAll.ts index a243203f..dcd6ab51 100644 --- a/packages/portal-contracts/scripts/deployAll.ts +++ b/packages/portal-contracts/scripts/deployAll.ts @@ -1,13 +1,18 @@ import { ethers } from 'hardhat'; -import { DeployedContractAddresses, DeployedContracts, deployFuel, getContractAddresses } from '../protocol/harness'; import { - isNetworkVerifiable, - publishProxySourceVerification, - publishImplementationSourceVerification, - getNetworkName, - saveDeploymentsFile, - confirmationPrompt, - waitForConfirmations, + DeployedContractAddresses, + DeployedContracts, + deployFuel, + getContractAddresses, +} from '../protocol/harness'; +import { + isNetworkVerifiable, + publishProxySourceVerification, + publishImplementationSourceVerification, + getNetworkName, + saveDeploymentsFile, + confirmationPrompt, + waitForConfirmations, } from './utils'; // Script to deploy the Fuel v2 system @@ -23,68 +28,73 @@ import { const QUICK_DEPLOY = !!process.env.QUICK_DEPLOY; async function main() { - // Check that the node is up - try { - await ethers.provider.getBlockNumber(); - } catch (e) { - throw new Error( - `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` - ); - } + // Check that the node is up + try { + await ethers.provider.getBlockNumber(); + } catch (e) { + throw new Error( + `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` + ); + } - // Get the current connected network - const networkName = await getNetworkName(); + // Get the current connected network + const networkName = await getNetworkName(); - // Get confirmation - let confirm = true; - if (!QUICK_DEPLOY) { - console.log(''); // eslint-disable-line no-console - confirm = await confirmationPrompt( - `Are you sure you want to deploy ALL proxy and implementation contracts on "${networkName}" (Y/n)? ` - ); + // Get confirmation + let confirm = true; + if (!QUICK_DEPLOY) { + console.log(''); // eslint-disable-line no-console + confirm = await confirmationPrompt( + `Are you sure you want to deploy ALL proxy and implementation contracts on "${networkName}" (Y/n)? ` + ); + } + if (confirm) { + // Setup Fuel + let contracts: DeployedContracts; + let deployments: DeployedContractAddresses; + try { + console.log('Deploying contracts...'); // eslint-disable-line no-console + contracts = await deployFuel(); + deployments = await getContractAddresses(contracts); + } catch (e) { + throw new Error( + `Failed to deploy contracts. Make sure all configuration is correct and the proper permissions are in place.` + ); } - if (confirm) { - // Setup Fuel - let contracts: DeployedContracts; - let deployments: DeployedContractAddresses; - try { - console.log('Deploying contracts...'); // eslint-disable-line no-console - contracts = await deployFuel(); - deployments = await getContractAddresses(contracts); - } catch (e) { - throw new Error( - `Failed to deploy contracts. Make sure all configuration is correct and the proper permissions are in place.` - ); - } - const deployedBlock = await ethers.provider.getBlockNumber(); + const deployedBlock = await ethers.provider.getBlockNumber(); - // Emit the addresses of the deployed contracts - console.log('Successfully deployed contracts!\n'); // eslint-disable-line no-console - Object.entries(deployments).forEach(([key, value]) => { - console.log(`${key}: ${value}`); // eslint-disable-line no-console - }); + // Emit the addresses of the deployed contracts + console.log('Successfully deployed contracts!\n'); // eslint-disable-line no-console + Object.entries(deployments).forEach(([key, value]) => { + console.log(`${key}: ${value}`); // eslint-disable-line no-console + }); - // Write deployments to file - await saveDeploymentsFile(deployments); + // Write deployments to file + await saveDeploymentsFile(deployments); - // Confirm source verification/publishing - if (!QUICK_DEPLOY && (await isNetworkVerifiable())) { - console.log(''); // eslint-disable-line no-console - const confirmVerification = await confirmationPrompt( - `Do you want to publish contract source code for verification (Y/n)? ` - ); - if (confirmVerification) { - await waitForConfirmations(deployedBlock, 5); - await publishProxySourceVerification(deployments); - await publishImplementationSourceVerification(deployments, true, true, true); - } - } + // Confirm source verification/publishing + if (!QUICK_DEPLOY && (await isNetworkVerifiable())) { + console.log(''); // eslint-disable-line no-console + const confirmVerification = await confirmationPrompt( + `Do you want to publish contract source code for verification (Y/n)? ` + ); + if (confirmVerification) { + await waitForConfirmations(deployedBlock, 5); + await publishProxySourceVerification(deployments); + await publishImplementationSourceVerification( + deployments, + true, + true, + true + ); + } } + } } main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); // eslint-disable-line no-console - process.exit(1); - }); + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); // eslint-disable-line no-console + process.exit(1); + }); diff --git a/packages/portal-contracts/scripts/deployImplementation.ts b/packages/portal-contracts/scripts/deployImplementation.ts index c51d78f5..720e671c 100644 --- a/packages/portal-contracts/scripts/deployImplementation.ts +++ b/packages/portal-contracts/scripts/deployImplementation.ts @@ -1,12 +1,12 @@ import { ethers, upgrades } from 'hardhat'; import { - isNetworkVerifiable, - publishImplementationSourceVerification, - getNetworkName, - loadDeploymentsFile, - saveDeploymentsFile, - confirmationPrompt, - waitForConfirmations, + isNetworkVerifiable, + publishImplementationSourceVerification, + getNetworkName, + loadDeploymentsFile, + saveDeploymentsFile, + confirmationPrompt, + waitForConfirmations, } from './utils'; // Script to upgrade the Fuel v2 system contracts @@ -20,99 +20,109 @@ import { // You can then connect to localhost (ethers, metamask, etc.) and the Fuel system will be deployed there at the addresses given async function main() { - // Check that the node is up - try { - await ethers.provider.getBlockNumber(); - } catch (e) { - throw new Error( - `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` - ); - } + // Check that the node is up + try { + await ethers.provider.getBlockNumber(); + } catch (e) { + throw new Error( + `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` + ); + } - // Get the current connected network - const networkName = await getNetworkName(); + // Get the current connected network + const networkName = await getNetworkName(); - // Read existing deployments file - const deployments = await loadDeploymentsFile(); + // Read existing deployments file + const deployments = await loadDeploymentsFile(); - // Get confirmation - console.log(''); // eslint-disable-line no-console - const confirmFuelChainState = await confirmationPrompt( - `Are you sure you want to deploy the implementation for FuelChainState on "${networkName}" (Y/n)? ` - ); - const confirmFuelMessagePortal = await confirmationPrompt( - `Are you sure you want to deploy the implementation for FuelMessagePortal on "${networkName}" (Y/n)? ` - ); - const confirmFuelERC20Gateway = await confirmationPrompt( - `Are you sure you want to deploy the implementation for FuelERC20Gateway on "${networkName}" (Y/n)? ` - ); + // Get confirmation + console.log(''); // eslint-disable-line no-console + const confirmFuelChainState = await confirmationPrompt( + `Are you sure you want to deploy the implementation for FuelChainState on "${networkName}" (Y/n)? ` + ); + const confirmFuelMessagePortal = await confirmationPrompt( + `Are you sure you want to deploy the implementation for FuelMessagePortal on "${networkName}" (Y/n)? ` + ); + const confirmFuelERC20Gateway = await confirmationPrompt( + `Are you sure you want to deploy the implementation for FuelERC20Gateway on "${networkName}" (Y/n)? ` + ); - // Deploy FuelChainState implementation - let fuelChainStateAddress = null; - if (confirmFuelChainState) { - console.log('Deploying FuelChainState implementation...'); // eslint-disable-line no-console - const FuelChainState = await ethers.getContractFactory('FuelChainState'); - fuelChainStateAddress = (await upgrades.deployImplementation(FuelChainState)).toString(); - } + // Deploy FuelChainState implementation + let fuelChainStateAddress = null; + if (confirmFuelChainState) { + console.log('Deploying FuelChainState implementation...'); // eslint-disable-line no-console + const FuelChainState = await ethers.getContractFactory('FuelChainState'); + fuelChainStateAddress = ( + await upgrades.deployImplementation(FuelChainState) + ).toString(); + } - // Deploy FuelMessagePortal implementation - let fuelMessagePortalAddress = null; - if (confirmFuelMessagePortal) { - console.log('Deploying FuelMessagePortal implementation...'); // eslint-disable-line no-console - const FuelMessagePortal = await ethers.getContractFactory('FuelMessagePortal'); - fuelMessagePortalAddress = (await upgrades.deployImplementation(FuelMessagePortal)).toString(); - } + // Deploy FuelMessagePortal implementation + let fuelMessagePortalAddress = null; + if (confirmFuelMessagePortal) { + console.log('Deploying FuelMessagePortal implementation...'); // eslint-disable-line no-console + const FuelMessagePortal = await ethers.getContractFactory( + 'FuelMessagePortal' + ); + fuelMessagePortalAddress = ( + await upgrades.deployImplementation(FuelMessagePortal) + ).toString(); + } - // Deploy FuelERC20Gateway implementation - let fuelERC20GatewayAddress = null; - if (confirmFuelERC20Gateway) { - console.log('Deploying FuelERC20Gateway implementation...'); // eslint-disable-line no-console - const FuelERC20Gateway = await ethers.getContractFactory('FuelERC20Gateway'); - fuelERC20GatewayAddress = (await upgrades.deployImplementation(FuelERC20Gateway)).toString(); - } + // Deploy FuelERC20Gateway implementation + let fuelERC20GatewayAddress = null; + if (confirmFuelERC20Gateway) { + console.log('Deploying FuelERC20Gateway implementation...'); // eslint-disable-line no-console + const FuelERC20Gateway = await ethers.getContractFactory( + 'FuelERC20Gateway' + ); + fuelERC20GatewayAddress = ( + await upgrades.deployImplementation(FuelERC20Gateway) + ).toString(); + } - // Remember the current block so we can wait for confirmation later - const deployedBlock = await ethers.provider.getBlockNumber(); + // Remember the current block so we can wait for confirmation later + const deployedBlock = await ethers.provider.getBlockNumber(); - // Emit the addresses of the deployed contracts - console.log('Successfully deployed contract implementations!\n'); // eslint-disable-line no-console - if (fuelChainStateAddress) { - console.log(`FuelChainState: ${fuelChainStateAddress}`); // eslint-disable-line no-console - deployments.FuelChainState_impl = fuelChainStateAddress; - } - if (fuelMessagePortalAddress) { - console.log(`FuelMessagePortal: ${fuelMessagePortalAddress}`); // eslint-disable-line no-console - deployments.FuelMessagePortal_impl = fuelMessagePortalAddress; - } - if (fuelERC20GatewayAddress) { - console.log(`FuelERC20Gateway: ${fuelERC20GatewayAddress}`); // eslint-disable-line no-console - deployments.FuelERC20Gateway_impl = fuelERC20GatewayAddress; - } + // Emit the addresses of the deployed contracts + console.log('Successfully deployed contract implementations!\n'); // eslint-disable-line no-console + if (fuelChainStateAddress) { + console.log(`FuelChainState: ${fuelChainStateAddress}`); // eslint-disable-line no-console + deployments.FuelChainState_impl = fuelChainStateAddress; + } + if (fuelMessagePortalAddress) { + console.log(`FuelMessagePortal: ${fuelMessagePortalAddress}`); // eslint-disable-line no-console + deployments.FuelMessagePortal_impl = fuelMessagePortalAddress; + } + if (fuelERC20GatewayAddress) { + console.log(`FuelERC20Gateway: ${fuelERC20GatewayAddress}`); // eslint-disable-line no-console + deployments.FuelERC20Gateway_impl = fuelERC20GatewayAddress; + } - // Write deployments to file - await saveDeploymentsFile(deployments); + // Write deployments to file + await saveDeploymentsFile(deployments); - // Confirm source verification/publishing - if (await isNetworkVerifiable()) { - console.log(''); // eslint-disable-line no-console - const confirmVerification = await confirmationPrompt( - `Do you want to publish contract source code for verification (Y/n)? ` - ); - if (confirmVerification) { - await waitForConfirmations(deployedBlock, 5); - await publishImplementationSourceVerification( - deployments, - confirmFuelChainState, - confirmFuelMessagePortal, - confirmFuelERC20Gateway - ); - } + // Confirm source verification/publishing + if (await isNetworkVerifiable()) { + console.log(''); // eslint-disable-line no-console + const confirmVerification = await confirmationPrompt( + `Do you want to publish contract source code for verification (Y/n)? ` + ); + if (confirmVerification) { + await waitForConfirmations(deployedBlock, 5); + await publishImplementationSourceVerification( + deployments, + confirmFuelChainState, + confirmFuelMessagePortal, + confirmFuelERC20Gateway + ); } + } } main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); // eslint-disable-line no-console - process.exit(1); - }); + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); // eslint-disable-line no-console + process.exit(1); + }); diff --git a/packages/portal-contracts/scripts/serveDeployments.ts b/packages/portal-contracts/scripts/serveDeployments.ts index fe7c6322..236158fc 100644 --- a/packages/portal-contracts/scripts/serveDeployments.ts +++ b/packages/portal-contracts/scripts/serveDeployments.ts @@ -16,5 +16,5 @@ const app: Express = express(); app.use('/', express.static('deployments')); app.listen(port, () => { - console.log(`Server is running at https://localhost:${port}`); // eslint-disable-line no-console + console.log(`Server is running at https://localhost:${port}`); // eslint-disable-line no-console }); diff --git a/packages/portal-contracts/scripts/upgradeAll.ts b/packages/portal-contracts/scripts/upgradeAll.ts index a8b5abd6..482dac92 100644 --- a/packages/portal-contracts/scripts/upgradeAll.ts +++ b/packages/portal-contracts/scripts/upgradeAll.ts @@ -1,13 +1,13 @@ import { ethers } from 'hardhat'; import { upgradeFuel } from '../protocol/harness'; import { - isNetworkVerifiable, - publishImplementationSourceVerification, - getNetworkName, - loadDeploymentsFile, - saveDeploymentsFile, - confirmationPrompt, - waitForConfirmations, + isNetworkVerifiable, + publishImplementationSourceVerification, + getNetworkName, + loadDeploymentsFile, + saveDeploymentsFile, + confirmationPrompt, + waitForConfirmations, } from './utils'; // Script to upgrade the Fuel v2 system contracts @@ -23,64 +23,69 @@ import { // You can then connect to localhost (ethers, metamask, etc.) and the Fuel system will be deployed there at the addresses given async function main() { - // Check that the node is up - try { - await ethers.provider.getBlockNumber(); - } catch (e) { - throw new Error( - `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` - ); - } + // Check that the node is up + try { + await ethers.provider.getBlockNumber(); + } catch (e) { + throw new Error( + `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` + ); + } - // Get the current connected network - const networkName = await getNetworkName(); + // Get the current connected network + const networkName = await getNetworkName(); - // Read existing deployments file - const deployments = await loadDeploymentsFile(); + // Read existing deployments file + const deployments = await loadDeploymentsFile(); - // Get confirmation - console.log(''); // eslint-disable-line no-console - const confirmUpgrade = await confirmationPrompt( - `Are you sure you want to upgrade ALL contracts on "${networkName}" (Y/n)? ` - ); - if (confirmUpgrade) { - // Upgrade Fuel - try { - console.log('Upgrading contracts...'); // eslint-disable-line no-console - await upgradeFuel(deployments); - } catch (e) { - throw new Error( - `Failed to deploy contracts. Make sure all configuration is correct and the proper permissions are in place.` - ); - } - const deployedBlock = await ethers.provider.getBlockNumber(); + // Get confirmation + console.log(''); // eslint-disable-line no-console + const confirmUpgrade = await confirmationPrompt( + `Are you sure you want to upgrade ALL contracts on "${networkName}" (Y/n)? ` + ); + if (confirmUpgrade) { + // Upgrade Fuel + try { + console.log('Upgrading contracts...'); // eslint-disable-line no-console + await upgradeFuel(deployments); + } catch (e) { + throw new Error( + `Failed to deploy contracts. Make sure all configuration is correct and the proper permissions are in place.` + ); + } + const deployedBlock = await ethers.provider.getBlockNumber(); - // Emit the addresses of the deployed contracts - console.log('Successfully upgraded contracts!\n'); // eslint-disable-line no-console - Object.entries(deployments).forEach(([key, value]) => { - console.log(`${key}: ${value}`); // eslint-disable-next-line no-console - }); + // Emit the addresses of the deployed contracts + console.log('Successfully upgraded contracts!\n'); // eslint-disable-line no-console + Object.entries(deployments).forEach(([key, value]) => { + console.log(`${key}: ${value}`); // eslint-disable-next-line no-console + }); - // Write deployments to file - await saveDeploymentsFile(deployments); + // Write deployments to file + await saveDeploymentsFile(deployments); - // Confirm source verification/publishing - if (await isNetworkVerifiable()) { - console.log(''); // eslint-disable-line no-console - const confirmVerification = await confirmationPrompt( - `Do you want to publish contract source code for verification (Y/n)? ` - ); - if (confirmVerification) { - await waitForConfirmations(deployedBlock, 5); - await publishImplementationSourceVerification(deployments, true, true, true); - } - } + // Confirm source verification/publishing + if (await isNetworkVerifiable()) { + console.log(''); // eslint-disable-line no-console + const confirmVerification = await confirmationPrompt( + `Do you want to publish contract source code for verification (Y/n)? ` + ); + if (confirmVerification) { + await waitForConfirmations(deployedBlock, 5); + await publishImplementationSourceVerification( + deployments, + true, + true, + true + ); + } } + } } main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); // eslint-disable-line no-console - process.exit(1); - }); + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); // eslint-disable-line no-console + process.exit(1); + }); diff --git a/packages/portal-contracts/scripts/utils.ts b/packages/portal-contracts/scripts/utils.ts index 79eff27d..6ef911b5 100644 --- a/packages/portal-contracts/scripts/utils.ts +++ b/packages/portal-contracts/scripts/utils.ts @@ -2,169 +2,233 @@ import hardhat, { ethers } from 'hardhat'; import { promises as fs } from 'fs'; import fetch from 'node-fetch'; import readline from 'readline'; -import { DeployedContractAddresses, getBlankAddresses } from '../protocol/harness'; +import { + DeployedContractAddresses, + getBlankAddresses, +} from '../protocol/harness'; // Script utils for deploy the Fuel v2 system const DEPLOYMENTS_FILE = './deployments/deployments.*.json'; // Loads the deployment addresses for the currently connected network. -export async function loadDeploymentsFile(saveTemplateOnNotFound = true): Promise { - let fileString: string; - const networkName = await getNetworkName(); - const filename = DEPLOYMENTS_FILE.replace('*', networkName); - try { - fileString = await fs.readFile(filename, 'utf-8'); - } catch (e) { - if (saveTemplateOnNotFound) { - const deployments = getBlankAddresses(); - await fs.writeFile(filename, JSON.stringify(deployments, null, ' '), 'utf-8'); - } - throw new Error( - `Failed to read file "${filename}". Make sure the file "${filename}" is properly set and the contracts have been deployed before upgrading.` - ); - } - try { - return JSON.parse(fileString); - } catch (e) { - throw new Error(`Failed to parse file "${filename}". Make sure it's properly formatted.`); +export async function loadDeploymentsFile( + saveTemplateOnNotFound = true +): Promise { + let fileString: string; + const networkName = await getNetworkName(); + const filename = DEPLOYMENTS_FILE.replace('*', networkName); + try { + fileString = await fs.readFile(filename, 'utf-8'); + } catch (e) { + if (saveTemplateOnNotFound) { + const deployments = getBlankAddresses(); + await fs.writeFile( + filename, + JSON.stringify(deployments, null, ' '), + 'utf-8' + ); } + throw new Error( + `Failed to read file "${filename}". Make sure the file "${filename}" is properly set and the contracts have been deployed before upgrading.` + ); + } + try { + return JSON.parse(fileString); + } catch (e) { + throw new Error( + `Failed to parse file "${filename}". Make sure it's properly formatted.` + ); + } } // Saves the deployed addresses. -export async function saveDeploymentsFile(deployments: DeployedContractAddresses) { - const networkName = await getNetworkName(); - const filename = DEPLOYMENTS_FILE.replace('*', networkName); - await fs.writeFile(filename, JSON.stringify(deployments, null, ' '), 'utf-8'); +export async function saveDeploymentsFile( + deployments: DeployedContractAddresses +) { + const networkName = await getNetworkName(); + const filename = DEPLOYMENTS_FILE.replace('*', networkName); + await fs.writeFile(filename, JSON.stringify(deployments, null, ' '), 'utf-8'); } // Gets the name of common EVM netwroks based on the connected networks reported chain ID. export async function getNetworkName(): Promise { - try { - //common list of networks and chain ids can be found here: https://chainlist.org/ - const network = await ethers.provider.getNetwork(); - if (network.chainId == 1) return 'mainnet'; - if (network.chainId == 5) return 'goerli'; - if (network.chainId == 31337) return 'local'; - return 'unknown'; - } catch (e) { - throw new Error(`Failed to get network info from RPC "${ethers.provider.connection.url}".`); - } + try { + //common list of networks and chain ids can be found here: https://chainlist.org/ + const network = await ethers.provider.getNetwork(); + if (network.chainId == 1) return 'mainnet'; + if (network.chainId == 5) return 'goerli'; + if (network.chainId == 31337) return 'local'; + return 'unknown'; + } catch (e) { + throw new Error( + `Failed to get network info from RPC "${ethers.provider.connection.url}".` + ); + } } // Simple confirmation loop for CLI input. export async function confirmationPrompt(prompt: string): Promise { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - return new Promise(function (resolve) { - rl.question(prompt, async function (answer) { - rl.close(); - resolve(answer.trim().toLowerCase() === 'y'); - }); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + return new Promise(function (resolve) { + rl.question(prompt, async function (answer) { + rl.close(); + resolve(answer.trim().toLowerCase() === 'y'); }); + }); } // Publishes source code for verification of proxy contracts. -export async function publishProxySourceVerification(deployments: DeployedContractAddresses) { - await verifyEtherscan('FuelChainState proxy', deployments.FuelChainState); - await verifyEtherscan('FuelMessagePortal proxy', deployments.FuelMessagePortal); - await verifyEtherscan('FuelERC20Gateway proxy', deployments.FuelERC20Gateway); - - await verifySourcifyFromEtherscan('FuelChainState proxy', deployments.FuelChainState); - await verifySourcifyFromEtherscan('FuelMessagePortal proxy', deployments.FuelMessagePortal); - await verifySourcifyFromEtherscan('FuelERC20Gateway proxy', deployments.FuelERC20Gateway); +export async function publishProxySourceVerification( + deployments: DeployedContractAddresses +) { + await verifyEtherscan('FuelChainState proxy', deployments.FuelChainState); + await verifyEtherscan( + 'FuelMessagePortal proxy', + deployments.FuelMessagePortal + ); + await verifyEtherscan('FuelERC20Gateway proxy', deployments.FuelERC20Gateway); + + await verifySourcifyFromEtherscan( + 'FuelChainState proxy', + deployments.FuelChainState + ); + await verifySourcifyFromEtherscan( + 'FuelMessagePortal proxy', + deployments.FuelMessagePortal + ); + await verifySourcifyFromEtherscan( + 'FuelERC20Gateway proxy', + deployments.FuelERC20Gateway + ); } // Publishes source code for verification of implementation contracts. export async function publishImplementationSourceVerification( - deployments: DeployedContractAddresses, - publishFuelChainState: boolean, - publishFuelMessagePortal: boolean, - publishFuelERC20Gateway: boolean + deployments: DeployedContractAddresses, + publishFuelChainState: boolean, + publishFuelMessagePortal: boolean, + publishFuelERC20Gateway: boolean ) { - if (publishFuelChainState) { - await verifyEtherscan('FuelChainState implementation', deployments.FuelChainState_impl); - await verifySourcifyFromEtherscan('FuelChainState implementation', deployments.FuelChainState_impl); - } - - if (publishFuelMessagePortal) { - await verifyEtherscan('FuelMessagePortal implementation', deployments.FuelMessagePortal_impl); - await verifySourcifyFromEtherscan('FuelMessagePortal implementation', deployments.FuelMessagePortal_impl); - } - - if (publishFuelERC20Gateway) { - await verifyEtherscan('FuelERC20Gateway implementation', deployments.FuelERC20Gateway_impl); - await verifySourcifyFromEtherscan('FuelERC20Gateway implementation', deployments.FuelERC20Gateway_impl); - } + if (publishFuelChainState) { + await verifyEtherscan( + 'FuelChainState implementation', + deployments.FuelChainState_impl + ); + await verifySourcifyFromEtherscan( + 'FuelChainState implementation', + deployments.FuelChainState_impl + ); + } + + if (publishFuelMessagePortal) { + await verifyEtherscan( + 'FuelMessagePortal implementation', + deployments.FuelMessagePortal_impl + ); + await verifySourcifyFromEtherscan( + 'FuelMessagePortal implementation', + deployments.FuelMessagePortal_impl + ); + } + + if (publishFuelERC20Gateway) { + await verifyEtherscan( + 'FuelERC20Gateway implementation', + deployments.FuelERC20Gateway_impl + ); + await verifySourcifyFromEtherscan( + 'FuelERC20Gateway implementation', + deployments.FuelERC20Gateway_impl + ); + } } // Gets if the currently connected network is verifiable. export async function isNetworkVerifiable(): Promise { - const networkName = await getNetworkName(); - return networkName === 'mainnet' || networkName === 'goerli'; + const networkName = await getNetworkName(); + return networkName === 'mainnet' || networkName === 'goerli'; } // Waits for the given number of confirmations. -export async function waitForConfirmations(blockNum: number, confirmations: number) { - let currentBlock = await ethers.provider.getBlockNumber(); - let diff = currentBlock - blockNum; - if (diff < confirmations) { - process.stdout.write(`Waiting for ${confirmations} block confirmations.`); - while (currentBlock - blockNum < confirmations) { - process.stdout.write('.'); - if (currentBlock - blockNum != diff) process.stdout.write(`${confirmations - diff - 1}`); - diff = currentBlock - blockNum; - - await sleep(5000); - currentBlock = await ethers.provider.getBlockNumber(); - } - console.log(''); // eslint-disable-line no-console +export async function waitForConfirmations( + blockNum: number, + confirmations: number +) { + let currentBlock = await ethers.provider.getBlockNumber(); + let diff = currentBlock - blockNum; + if (diff < confirmations) { + process.stdout.write(`Waiting for ${confirmations} block confirmations.`); + while (currentBlock - blockNum < confirmations) { + process.stdout.write('.'); + if (currentBlock - blockNum != diff) + process.stdout.write(`${confirmations - diff - 1}`); + diff = currentBlock - blockNum; + + await sleep(5000); + currentBlock = await ethers.provider.getBlockNumber(); } + console.log(''); // eslint-disable-line no-console + } } // Publishes source code verification on Etherscan. async function verifyEtherscan(contractName: string, contractAddress: string) { - try { - console.log(`\nPublishing ${contractName} source verification on Etherscan...`); // eslint-disable-line no-console - await hardhat.run('verify:verify', { - address: contractAddress, - constructorArguments: [], - }); - } catch (e) { - let message = 'An uknown issue occurred while verifying on Etherscan.'; - if (e instanceof Error) message = e.message; - console.error(message); // eslint-disable-line no-console - } + try { + console.log( + `\nPublishing ${contractName} source verification on Etherscan...` + ); // eslint-disable-line no-console + await hardhat.run('verify:verify', { + address: contractAddress, + constructorArguments: [], + }); + } catch (e) { + let message = 'An uknown issue occurred while verifying on Etherscan.'; + if (e instanceof Error) message = e.message; + console.error(message); // eslint-disable-line no-console + } } // Verifies source code on Sourcify from Etherscan. -async function verifySourcifyFromEtherscan(contractName: string, contractAddress: string) { - try { - console.log(`\nVerifying ${contractName} source on Sourcify from Etherscan...`); // eslint-disable-line no-console - const network = await ethers.provider.getNetwork(); - const body = { address: contractAddress, chain: network.chainId }; - const response = await fetch('https://sourcify.dev/server/verify/etherscan', { - method: 'post', - body: JSON.stringify(body), - headers: { 'Content-Type': 'application/json' }, - }); - const data = await response.json(); - if (data.error) throw new Error(data.error); - if (data.result) { - if (data.result.storageTimestamp) console.log('Contract source code already verified'); // eslint-disable-line no-console - if (data.result.status == 'perfect') console.log('Contract source code perfectly verified!'); // eslint-disable-line no-console - if (data.result.status == 'partial') console.log('Contract source code partially verified.'); // eslint-disable-line no-console - } - } catch (e) { - let message = 'An uknown issue occurred while verifying on Sourcify.'; - if (e instanceof Error) message = e.message; - console.error(message); // eslint-disable-line no-console +async function verifySourcifyFromEtherscan( + contractName: string, + contractAddress: string +) { + try { + console.log( + `\nVerifying ${contractName} source on Sourcify from Etherscan...` + ); // eslint-disable-line no-console + const network = await ethers.provider.getNetwork(); + const body = { address: contractAddress, chain: network.chainId }; + const response = await fetch( + 'https://sourcify.dev/server/verify/etherscan', + { + method: 'post', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' }, + } + ); + const data = await response.json(); + if (data.error) throw new Error(data.error); + if (data.result) { + if (data.result.storageTimestamp) + console.log('Contract source code already verified'); // eslint-disable-line no-console + if (data.result.status == 'perfect') + console.log('Contract source code perfectly verified!'); // eslint-disable-line no-console + if (data.result.status == 'partial') + console.log('Contract source code partially verified.'); // eslint-disable-line no-console } + } catch (e) { + let message = 'An uknown issue occurred while verifying on Sourcify.'; + if (e instanceof Error) message = e.message; + console.error(message); // eslint-disable-line no-console + } } // Sleep for the given number of milliseconds function sleep(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } diff --git a/packages/portal-contracts/scripts/verifyAddress.ts b/packages/portal-contracts/scripts/verifyAddress.ts index 73bed56a..b65930ae 100644 --- a/packages/portal-contracts/scripts/verifyAddress.ts +++ b/packages/portal-contracts/scripts/verifyAddress.ts @@ -4,70 +4,76 @@ import readline from 'readline'; // Script to verify the deployed code of the Fuel v2 system contracts async function main() { - // Check that the node is up - try { - await ethers.provider.getBlockNumber(); - } catch (e) { - throw new Error( - `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` - ); - } + // Check that the node is up + try { + await ethers.provider.getBlockNumber(); + } catch (e) { + throw new Error( + `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` + ); + } - // Get the contract address to verify - console.log(`\nPlease enter the deployed contract address you would like to verify the source for.`); // eslint-disable-line no-console - const contractAddress = await contractAddressPrompt(); + // Get the contract address to verify + console.log( + `\nPlease enter the deployed contract address you would like to verify the source for.` + ); // eslint-disable-line no-console + const contractAddress = await contractAddressPrompt(); - // Verify to the project source files - try { - console.log(`\nVerifying deployed contract source...`); // eslint-disable-line no-console - await hardhat.run('verify', { - address: contractAddress, - constructorArguments: [], - }); - } catch (e) { - let message = 'An uknown issue occurred while verifying deployed contract.'; - if (e instanceof Error) message = e.message; - console.error(message); // eslint-disable-line no-console - } + // Verify to the project source files + try { + console.log(`\nVerifying deployed contract source...`); // eslint-disable-line no-console + await hardhat.run('verify', { + address: contractAddress, + constructorArguments: [], + }); + } catch (e) { + let message = 'An uknown issue occurred while verifying deployed contract.'; + if (e instanceof Error) message = e.message; + console.error(message); // eslint-disable-line no-console + } } // Simple CLI input loop for getting a contract address. export async function contractAddressPrompt(): Promise { - let contractAddress = validateContractAddress(await textPrompt('Contract address: ')); - if (contractAddress === null) { - do { - console.log('Invalid contract address.'); // eslint-disable-line no-console - contractAddress = validateContractAddress(await textPrompt('Contract address: ')); - } while (contractAddress === null); - } - return contractAddress; + let contractAddress = validateContractAddress( + await textPrompt('Contract address: ') + ); + if (contractAddress === null) { + do { + console.log('Invalid contract address.'); // eslint-disable-line no-console + contractAddress = validateContractAddress( + await textPrompt('Contract address: ') + ); + } while (contractAddress === null); + } + return contractAddress; } // Simple confirmation loop for CLI input. export async function textPrompt(prompt: string): Promise { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - return new Promise(function (resolve) { - rl.question(prompt, async function (answer) { - rl.close(); - resolve(answer.trim()); - }); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + return new Promise(function (resolve) { + rl.question(prompt, async function (answer) { + rl.close(); + resolve(answer.trim()); }); + }); } // Checks if the given contract address is valid (returns null if invalid) function validateContractAddress(address: string): string | null { - try { - return ethers.utils.getAddress(address.toLowerCase()); - } catch (e) {} - return null; + try { + return ethers.utils.getAddress(address.toLowerCase()); + } catch (e) {} + return null; } main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); // eslint-disable-line no-console - process.exit(1); - }); + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); // eslint-disable-line no-console + process.exit(1); + }); diff --git a/packages/portal-contracts/scripts/verifySource.ts b/packages/portal-contracts/scripts/verifySource.ts index c1ebf807..a585bbfa 100644 --- a/packages/portal-contracts/scripts/verifySource.ts +++ b/packages/portal-contracts/scripts/verifySource.ts @@ -1,63 +1,63 @@ import { ethers } from 'hardhat'; import { - loadDeploymentsFile, - getNetworkName, - confirmationPrompt, - publishProxySourceVerification, - publishImplementationSourceVerification, + loadDeploymentsFile, + getNetworkName, + confirmationPrompt, + publishProxySourceVerification, + publishImplementationSourceVerification, } from './utils'; // Script to publish source code for verification of the Fuel v2 system contracts async function main() { - // Check that the node is up - try { - await ethers.provider.getBlockNumber(); - } catch (e) { - throw new Error( - `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` - ); - } - - // Read existing deployments file - const deployments = await loadDeploymentsFile(); - - // Get the current connected network - const networkName = await getNetworkName(); - - // Get confirmation for proxy contracts - console.log(''); // eslint-disable-line no-console - const confirmProxies = await confirmationPrompt( - `Are you sure you want to publish the verification of source code for ALL contract proxies on "${networkName}" (Y/n)? ` - ); - - // Verify contract implementations - if (confirmProxies) await publishProxySourceVerification(deployments); - - // Get confirmation for implementation contracts - console.log(''); // eslint-disable-line no-console - const confirmFuelChainStateImpl = await confirmationPrompt( - `Are you sure you want to publish the verification of source code for the FuelChainState implementation on "${networkName}" (Y/n)? ` - ); - const confirmFuelMessagePortalImpl = await confirmationPrompt( - `Are you sure you want to publish the verification of source code for the FuelMessagePortal implementation on "${networkName}" (Y/n)? ` - ); - const confirmFuelERC20GatewayImpl = await confirmationPrompt( - `Are you sure you want to publish the verification of source code for the FuelERC20Gateway implementation on "${networkName}" (Y/n)? ` - ); - - // Verify contract implementations - await publishImplementationSourceVerification( - deployments, - confirmFuelChainStateImpl, - confirmFuelMessagePortalImpl, - confirmFuelERC20GatewayImpl + // Check that the node is up + try { + await ethers.provider.getBlockNumber(); + } catch (e) { + throw new Error( + `Failed to connect to RPC "${ethers.provider.connection.url}". Make sure your environment variables and configuration are correct.` ); + } + + // Read existing deployments file + const deployments = await loadDeploymentsFile(); + + // Get the current connected network + const networkName = await getNetworkName(); + + // Get confirmation for proxy contracts + console.log(''); // eslint-disable-line no-console + const confirmProxies = await confirmationPrompt( + `Are you sure you want to publish the verification of source code for ALL contract proxies on "${networkName}" (Y/n)? ` + ); + + // Verify contract implementations + if (confirmProxies) await publishProxySourceVerification(deployments); + + // Get confirmation for implementation contracts + console.log(''); // eslint-disable-line no-console + const confirmFuelChainStateImpl = await confirmationPrompt( + `Are you sure you want to publish the verification of source code for the FuelChainState implementation on "${networkName}" (Y/n)? ` + ); + const confirmFuelMessagePortalImpl = await confirmationPrompt( + `Are you sure you want to publish the verification of source code for the FuelMessagePortal implementation on "${networkName}" (Y/n)? ` + ); + const confirmFuelERC20GatewayImpl = await confirmationPrompt( + `Are you sure you want to publish the verification of source code for the FuelERC20Gateway implementation on "${networkName}" (Y/n)? ` + ); + + // Verify contract implementations + await publishImplementationSourceVerification( + deployments, + confirmFuelChainStateImpl, + confirmFuelMessagePortalImpl, + confirmFuelERC20GatewayImpl + ); } main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); // eslint-disable-line no-console - process.exit(1); - }); + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); // eslint-disable-line no-console + process.exit(1); + }); diff --git a/packages/portal-contracts/test/ecdsa.ts b/packages/portal-contracts/test/ecdsa.ts index c4d734e8..2f97c2ee 100644 --- a/packages/portal-contracts/test/ecdsa.ts +++ b/packages/portal-contracts/test/ecdsa.ts @@ -8,65 +8,87 @@ import { SigningKey } from 'ethers/lib/utils'; chai.use(solidity); const { expect } = chai; -const SECP256K1N = BN.from('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141'); +const SECP256K1N = BN.from( + '0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' +); describe('ECDSA', async () => { - let mockCrypto: Contract; - let signer: SigningKey; - before(async () => { - const mockCryptoFactory = await ethers.getContractFactory('MockCryptography'); - mockCrypto = await mockCryptoFactory.deploy(); - await mockCrypto.deployed(); + let mockCrypto: Contract; + let signer: SigningKey; + before(async () => { + const mockCryptoFactory = await ethers.getContractFactory( + 'MockCryptography' + ); + mockCrypto = await mockCryptoFactory.deploy(); + await mockCrypto.deployed(); - signer = new SigningKey('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'); - }); + signer = new SigningKey( + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' + ); + }); - it('rejects component signatures with high s-value', async () => { - const msg = ethers.utils.hexlify(ethers.utils.randomBytes(32)); - const sig = await componentSign(signer, msg); - const vOrig = BN.from(sig.v).sub(27); // take v as 0 or 1 + it('rejects component signatures with high s-value', async () => { + const msg = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + const sig = await componentSign(signer, msg); + const vOrig = BN.from(sig.v).sub(27); // take v as 0 or 1 - // flip v and ensure it is 27 or 28 - const vFlipped = vOrig.xor(1).add(27); - // flip s to secp256k1n - original s. This defines a unique - // signature over the same data, which we want to reject. - const sFlipped = SECP256K1N.sub(sig.s); - const badSig = { v: vFlipped, r: sig.r, s: sFlipped }; + // flip v and ensure it is 27 or 28 + const vFlipped = vOrig.xor(1).add(27); + // flip s to secp256k1n - original s. This defines a unique + // signature over the same data, which we want to reject. + const sFlipped = SECP256K1N.sub(sig.s); + const badSig = { v: vFlipped, r: sig.r, s: sFlipped }; - await expect(mockCrypto.addressFromSignatureComponents(badSig.v, badSig.r, badSig.s, msg)).to.be.revertedWith( - 'signature-invalid-s' - ); - }); + await expect( + mockCrypto.addressFromSignatureComponents( + badSig.v, + badSig.r, + badSig.s, + msg + ) + ).to.be.revertedWith('signature-invalid-s'); + }); - it('rejects component signatures from the zero address', async () => { - const msg = ethers.utils.hexlify(ethers.utils.randomBytes(32)); - const sig = await componentSign(signer, msg); - // an r value < 1 makes the signature invalid. ecrecover will return 0x0 - const badSig = { v: sig.v, r: ethers.constants.HashZero, s: sig.s }; + it('rejects component signatures from the zero address', async () => { + const msg = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + const sig = await componentSign(signer, msg); + // an r value < 1 makes the signature invalid. ecrecover will return 0x0 + const badSig = { v: sig.v, r: ethers.constants.HashZero, s: sig.s }; - await expect(mockCrypto.addressFromSignatureComponents(badSig.v, badSig.r, badSig.s, msg)).to.be.revertedWith( - 'signature-invalid' - ); - }); + await expect( + mockCrypto.addressFromSignatureComponents( + badSig.v, + badSig.r, + badSig.s, + msg + ) + ).to.be.revertedWith('signature-invalid'); + }); - it('rejects invalid compact signatures', async () => { - const msg = ethers.utils.hexlify(ethers.utils.randomBytes(32)); - const sig = await componentSign(signer, msg); + it('rejects invalid compact signatures', async () => { + const msg = ethers.utils.hexlify(ethers.utils.randomBytes(32)); + const sig = await componentSign(signer, msg); - // an r value < 1 makes the signature invalid. ecrecover will return 0x0 - const badRValue = ethers.constants.HashZero; - // eslint-disable-next-line no-underscore-dangle - const badSigCompact = badRValue.concat(sig._vs.slice(2)); - await expect(mockCrypto.addressFromSignature(badSigCompact, msg)).to.be.revertedWith('signature-invalid'); + // an r value < 1 makes the signature invalid. ecrecover will return 0x0 + const badRValue = ethers.constants.HashZero; + // eslint-disable-next-line no-underscore-dangle + const badSigCompact = badRValue.concat(sig._vs.slice(2)); + await expect( + mockCrypto.addressFromSignature(badSigCompact, msg) + ).to.be.revertedWith('signature-invalid'); - // signature too short - // eslint-disable-next-line no-underscore-dangle - const shortSig = sig.r.concat(sig._vs.slice(4)); - await expect(mockCrypto.addressFromSignature(shortSig, msg)).to.be.revertedWith('signature-invalid-length'); + // signature too short + // eslint-disable-next-line no-underscore-dangle + const shortSig = sig.r.concat(sig._vs.slice(4)); + await expect( + mockCrypto.addressFromSignature(shortSig, msg) + ).to.be.revertedWith('signature-invalid-length'); - // signature too long - // eslint-disable-next-line no-underscore-dangle - const longSig = sig.r.concat(sig._vs.slice(2)).concat('aa'); - await expect(mockCrypto.addressFromSignature(longSig, msg)).to.be.revertedWith('signature-invalid-length'); - }); + // signature too long + // eslint-disable-next-line no-underscore-dangle + const longSig = sig.r.concat(sig._vs.slice(2)).concat('aa'); + await expect( + mockCrypto.addressFromSignature(longSig, msg) + ).to.be.revertedWith('signature-invalid-length'); + }); }); diff --git a/packages/portal-contracts/test/erc20Gateway.ts b/packages/portal-contracts/test/erc20Gateway.ts index e069afb2..891a95aa 100644 --- a/packages/portal-contracts/test/erc20Gateway.ts +++ b/packages/portal-contracts/test/erc20Gateway.ts @@ -5,7 +5,11 @@ import { BigNumber as BN } from 'ethers'; import { Provider } from '@ethersproject/abstract-provider'; import { constructTree, calcRoot, getProof } from '@fuel-ts/merkle'; import { HarnessObject, setupFuel } from '../protocol/harness'; -import BlockHeader, { BlockHeaderLite, computeBlockId, generateBlockHeaderLite } from '../protocol/blockHeader'; +import BlockHeader, { + BlockHeaderLite, + computeBlockId, + generateBlockHeaderLite, +} from '../protocol/blockHeader'; import { EMPTY, ZERO } from '../protocol/constants'; import Message, { computeMessageId } from '../protocol/message'; import { randomAddress, randomBytes32, tai64Time } from '../protocol/utils'; @@ -16,793 +20,1127 @@ const { expect } = chai; // Merkle tree node structure // TODO: should be importable from @fuel-ts/merkle declare class TreeNode { - left: number; - right: number; - parent: number; - hash: string; - data: string; - index: number; + left: number; + right: number; + parent: number; + hash: string; + data: string; + index: number; } // Merkle proof class declare class MerkleProof { - key: number; - proof: string[]; + key: number; + proof: string[]; } // Computes data for message function computeMessageData( - fuelTokenId: string, - tokenId: string, - from: string, - to: string, - amount: number, - data?: number[] + fuelTokenId: string, + tokenId: string, + from: string, + to: string, + amount: number, + data?: number[] ): string { - if (data) { - const depositToContractFlag = ethers.utils - .keccak256(ethers.utils.toUtf8Bytes('DEPOSIT_TO_CONTRACT')) - .substring(0, 4); - if (data.length == 0) { - return ethers.utils.solidityPack( - ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint256', 'bytes1'], - [fuelTokenId, tokenId, from, to, amount, depositToContractFlag] - ); - } else { - return ethers.utils.solidityPack( - ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint256', 'bytes1', 'bytes'], - [fuelTokenId, tokenId, from, to, amount, depositToContractFlag, data] - ); - } + if (data) { + const depositToContractFlag = ethers.utils + .keccak256(ethers.utils.toUtf8Bytes('DEPOSIT_TO_CONTRACT')) + .substring(0, 4); + if (data.length == 0) { + return ethers.utils.solidityPack( + ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint256', 'bytes1'], + [fuelTokenId, tokenId, from, to, amount, depositToContractFlag] + ); + } else { + return ethers.utils.solidityPack( + [ + 'bytes32', + 'bytes32', + 'bytes32', + 'bytes32', + 'uint256', + 'bytes1', + 'bytes', + ], + [fuelTokenId, tokenId, from, to, amount, depositToContractFlag, data] + ); } - return ethers.utils.solidityPack( - ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint256'], - [fuelTokenId, tokenId, from, to, amount] - ); + } + return ethers.utils.solidityPack( + ['bytes32', 'bytes32', 'bytes32', 'bytes32', 'uint256'], + [fuelTokenId, tokenId, from, to, amount] + ); } // Create a simple block function createBlock( - prevRoot: string, - blockHeight: number, - timestamp?: string, - outputMessagesCount?: string, - outputMessagesRoot?: string + prevRoot: string, + blockHeight: number, + timestamp?: string, + outputMessagesCount?: string, + outputMessagesRoot?: string ): BlockHeader { - const header: BlockHeader = { - prevRoot: prevRoot ? prevRoot : ZERO, - height: blockHeight.toString(), - timestamp: timestamp ? timestamp : '0', - daHeight: '0', - txCount: '0', - outputMessagesCount: outputMessagesCount ? outputMessagesCount : '0', - txRoot: EMPTY, - outputMessagesRoot: outputMessagesRoot ? outputMessagesRoot : ZERO, - }; - return header; + const header: BlockHeader = { + prevRoot: prevRoot ? prevRoot : ZERO, + height: blockHeight.toString(), + timestamp: timestamp ? timestamp : '0', + daHeight: '0', + txCount: '0', + outputMessagesCount: outputMessagesCount ? outputMessagesCount : '0', + txRoot: EMPTY, + outputMessagesRoot: outputMessagesRoot ? outputMessagesRoot : ZERO, + }; + return header; } // Get proof for the leaf function getLeafIndexKey(nodes: TreeNode[], data: string): number { - for (let n = 0; n < nodes.length; n += 1) { - if (nodes[n].data === data) { - return nodes[n].index; - } + for (let n = 0; n < nodes.length; n += 1) { + if (nodes[n].data === data) { + return nodes[n].index; } - return 0; + } + return 0; } describe('ERC20 Gateway', async () => { - let env: HarnessObject; - - // Contract constants - const TIME_TO_FINALIZE = 10800; - const BLOCKS_PER_COMMIT_INTERVAL = 10800; - - // Message data - const fuelTokenTarget1 = randomBytes32(); - const fuelTokenTarget2 = randomBytes32(); - const messageIds: string[] = []; - let messageNodes: TreeNode[]; - let gatewayAddress: string; - let tokenAddress: string; - - // Messages - let messageWithdrawal1: Message; - let messageWithdrawal2: Message; - let messageWithdrawal3: Message; - let messageTooLarge: Message; - let messageTooSmall: Message; - let messageBadL2Token: Message; - let messageBadL1Token: Message; - let messageBadSender: Message; - - // Arrays of committed block headers and their IDs - const blockHeaders: BlockHeader[] = []; - const blockIds: string[] = []; - let endOfCommitIntervalHeader: BlockHeader; - let endOfCommitIntervalHeaderLite: BlockHeaderLite; - let prevBlockNodes: TreeNode[]; - - // Helper function to setup test data - function generateProof(message: Message, prevBlockDistance = 1): [string, BlockHeader, MerkleProof, MerkleProof] { - const messageBlockIndex = BLOCKS_PER_COMMIT_INTERVAL - 1 - prevBlockDistance; - const messageBlockHeader = blockHeaders[messageBlockIndex]; - const messageBlockLeafIndexKey = getLeafIndexKey(prevBlockNodes, blockIds[messageBlockIndex]); - const blockInHistoryProof = { - key: messageBlockLeafIndexKey, - proof: getProof(prevBlockNodes, messageBlockLeafIndexKey), - }; - const messageID = computeMessageId(message); - const messageLeafIndexKey = getLeafIndexKey(messageNodes, messageID); - const messageInBlockProof = { - key: messageLeafIndexKey, - proof: getProof(messageNodes, messageLeafIndexKey), - }; - return [messageID, messageBlockHeader, blockInHistoryProof, messageInBlockProof]; + let env: HarnessObject; + + // Contract constants + const TIME_TO_FINALIZE = 10800; + const BLOCKS_PER_COMMIT_INTERVAL = 10800; + + // Message data + const fuelTokenTarget1 = randomBytes32(); + const fuelTokenTarget2 = randomBytes32(); + const messageIds: string[] = []; + let messageNodes: TreeNode[]; + let gatewayAddress: string; + let tokenAddress: string; + + // Messages + let messageWithdrawal1: Message; + let messageWithdrawal2: Message; + let messageWithdrawal3: Message; + let messageTooLarge: Message; + let messageTooSmall: Message; + let messageBadL2Token: Message; + let messageBadL1Token: Message; + let messageBadSender: Message; + + // Arrays of committed block headers and their IDs + const blockHeaders: BlockHeader[] = []; + const blockIds: string[] = []; + let endOfCommitIntervalHeader: BlockHeader; + let endOfCommitIntervalHeaderLite: BlockHeaderLite; + let prevBlockNodes: TreeNode[]; + + // Helper function to setup test data + function generateProof( + message: Message, + prevBlockDistance = 1 + ): [string, BlockHeader, MerkleProof, MerkleProof] { + const messageBlockIndex = + BLOCKS_PER_COMMIT_INTERVAL - 1 - prevBlockDistance; + const messageBlockHeader = blockHeaders[messageBlockIndex]; + const messageBlockLeafIndexKey = getLeafIndexKey( + prevBlockNodes, + blockIds[messageBlockIndex] + ); + const blockInHistoryProof = { + key: messageBlockLeafIndexKey, + proof: getProof(prevBlockNodes, messageBlockLeafIndexKey), + }; + const messageID = computeMessageId(message); + const messageLeafIndexKey = getLeafIndexKey(messageNodes, messageID); + const messageInBlockProof = { + key: messageLeafIndexKey, + proof: getProof(messageNodes, messageLeafIndexKey), + }; + return [ + messageID, + messageBlockHeader, + blockInHistoryProof, + messageInBlockProof, + ]; + } + + before(async () => { + env = await setupFuel(); + + // get data for building messages + gatewayAddress = env.fuelERC20Gateway.address + .split('0x') + .join('0x000000000000000000000000') + .toLowerCase(); + tokenAddress = env.token.address; + + // message from trusted sender + messageWithdrawal1 = new Message( + fuelTokenTarget1, + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[2], + tokenAddress, + 100, + ]) + ); + messageWithdrawal2 = new Message( + fuelTokenTarget1, + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + tokenAddress, + 75, + ]) + ); + messageWithdrawal3 = new Message( + fuelTokenTarget2, + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + tokenAddress, + 250, + ]) + ); + // message with amount too large + messageTooLarge = new Message( + fuelTokenTarget2, + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + tokenAddress, + 1000, + ]) + ); + // message with zero value + messageTooSmall = new Message( + fuelTokenTarget2, + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + tokenAddress, + 0, + ]) + ); + // message with bad L2 token + messageBadL2Token = new Message( + randomBytes32(), + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + tokenAddress, + 10, + ]) + ); + // message with bad L1 token + messageBadL1Token = new Message( + fuelTokenTarget2, + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + randomAddress(), + 10, + ]) + ); + // message from untrusted sender + messageBadSender = new Message( + randomBytes32(), + gatewayAddress, + BN.from(0), + randomBytes32(), + env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ + env.addresses[3], + tokenAddress, + 250, + ]) + ); + + // compile all message IDs + messageIds.push(computeMessageId(messageWithdrawal1)); + messageIds.push(computeMessageId(messageWithdrawal2)); + messageIds.push(computeMessageId(messageWithdrawal3)); + messageIds.push(computeMessageId(messageTooLarge)); + messageIds.push(computeMessageId(messageTooSmall)); + messageIds.push(computeMessageId(messageBadL2Token)); + messageIds.push(computeMessageId(messageBadL1Token)); + messageIds.push(computeMessageId(messageBadSender)); + messageNodes = constructTree(messageIds); + + // create blocks + const messageCount = messageIds.length.toString(); + const messagesRoot = calcRoot(messageIds); + for (let i = 0; i < BLOCKS_PER_COMMIT_INTERVAL - 1; i++) { + const blockHeader = createBlock('', i, '', messageCount, messagesRoot); + const blockId = computeBlockId(blockHeader); + + // append block header and Id to arrays + blockHeaders.push(blockHeader); + blockIds.push(blockId); } + endOfCommitIntervalHeader = createBlock( + calcRoot(blockIds), + blockIds.length, + tai64Time(new Date().getTime()), + messageCount, + messagesRoot + ); + endOfCommitIntervalHeaderLite = generateBlockHeaderLite( + endOfCommitIntervalHeader + ); + prevBlockNodes = constructTree(blockIds); + + // finalize blocks in the state contract + await env.fuelChainState.commit( + computeBlockId(endOfCommitIntervalHeader), + 0 + ); + ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); + + // set token approval for gateway + await env.token.approve( + env.fuelERC20Gateway.address, + env.initialTokenAmount + ); + }); + describe('Verify access control', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + let signer0: string; + let signer1: string; + let signer2: string; before(async () => { - env = await setupFuel(); + signer0 = env.addresses[0]; + signer1 = env.addresses[1]; + signer2 = env.addresses[2]; + }); - // get data for building messages - gatewayAddress = env.fuelERC20Gateway.address.split('0x').join('0x000000000000000000000000').toLowerCase(); - tokenAddress = env.token.address; + it('Should be able to grant admin role', async () => { + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + + // Grant admin role + await expect(env.fuelERC20Gateway.grantRole(defaultAdminRole, signer1)).to + .not.be.reverted; + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + }); - // message from trusted sender - messageWithdrawal1 = new Message( - fuelTokenTarget1, - gatewayAddress, - BN.from(0), + it('Should be able to renounce admin role', async () => { + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke admin role + await expect(env.fuelERC20Gateway.renounceRole(defaultAdminRole, signer0)) + .to.not.be.reverted; + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + + it('Should not be able to grant admin role as non-admin', async () => { + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + + // Attempt grant admin role + await expect( + env.fuelERC20Gateway.grantRole(defaultAdminRole, signer0) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + + it('Should be able to grant then revoke admin role', async () => { + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + + // Grant admin role + await expect( + env.fuelERC20Gateway + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer0) + ).to.not.be.reverted; + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke previous admin + await expect(env.fuelERC20Gateway.revokeRole(defaultAdminRole, signer1)) + .to.not.be.reverted; + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + }); + + it('Should be able to grant pauser role', async () => { + expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal( + false + ); + + // Grant pauser role + await expect(env.fuelERC20Gateway.grantRole(pauserRole, signer1)).to.not + .be.reverted; + expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal( + true + ); + }); + + it('Should not be able to grant permission as pauser', async () => { + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer2)).to.equal( + false + ); + + // Attempt grant admin role + await expect( + env.fuelERC20Gateway + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + + // Attempt grant pauser role + await expect( + env.fuelERC20Gateway + .connect(env.signers[1]) + .grantRole(pauserRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer2)).to.equal( + false + ); + }); + + it('Should be able to revoke pauser role', async () => { + expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal( + true + ); + + // Grant pauser role + await expect(env.fuelERC20Gateway.revokeRole(pauserRole, signer1)).to.not + .be.reverted; + expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal( + false + ); + }); + }); + + describe('Make both valid and invalid ERC20 deposits', async () => { + let provider: Provider; + before(async () => { + provider = env.fuelMessagePortal.provider; + }); + + it('Should not be able to deposit zero', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + expect( + ( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget1 + ) + ).add( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget2 + ) + ) + ).to.be.equal(gatewayBalance); + + // Attempt deposit + await expect( + env.fuelERC20Gateway.deposit( + randomBytes32(), + tokenAddress, + fuelTokenTarget1, + 0 + ) + ).to.be.revertedWith('Cannot deposit zero'); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + }); + + it('Should not be able to deposit with zero balance', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + expect( + ( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget1 + ) + ).add( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget2 + ) + ) + ).to.be.equal(gatewayBalance); + + // Attempt deposit + await expect( + env.fuelERC20Gateway + .connect(env.signers[1]) + .deposit(randomBytes32(), tokenAddress, fuelTokenTarget1, 175) + ).to.be.revertedWith('ERC20: insufficient allowance'); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + + // Attempt deposit with data + await expect( + env.fuelERC20Gateway + .connect(env.signers[1]) + .depositWithData( randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[2], - tokenAddress, - 100, - ]) - ); - messageWithdrawal2 = new Message( + tokenAddress, fuelTokenTarget1, - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[3], - tokenAddress, - 75, - ]) - ); - messageWithdrawal3 = new Message( - fuelTokenTarget2, - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[3], - tokenAddress, - 250, - ]) - ); - // message with amount too large - messageTooLarge = new Message( - fuelTokenTarget2, - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[3], - tokenAddress, - 1000, - ]) - ); - // message with zero value - messageTooSmall = new Message( - fuelTokenTarget2, - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [env.addresses[3], tokenAddress, 0]) - ); - // message with bad L2 token - messageBadL2Token = new Message( - randomBytes32(), - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[3], - tokenAddress, - 10, - ]) - ); - // message with bad L1 token - messageBadL1Token = new Message( - fuelTokenTarget2, - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[3], - randomAddress(), - 10, - ]) - ); - // message from untrusted sender - messageBadSender = new Message( - randomBytes32(), - gatewayAddress, - BN.from(0), - randomBytes32(), - env.fuelERC20Gateway.interface.encodeFunctionData('finalizeWithdrawal', [ - env.addresses[3], - tokenAddress, - 250, - ]) - ); - - // compile all message IDs - messageIds.push(computeMessageId(messageWithdrawal1)); - messageIds.push(computeMessageId(messageWithdrawal2)); - messageIds.push(computeMessageId(messageWithdrawal3)); - messageIds.push(computeMessageId(messageTooLarge)); - messageIds.push(computeMessageId(messageTooSmall)); - messageIds.push(computeMessageId(messageBadL2Token)); - messageIds.push(computeMessageId(messageBadL1Token)); - messageIds.push(computeMessageId(messageBadSender)); - messageNodes = constructTree(messageIds); - - // create blocks - const messageCount = messageIds.length.toString(); - const messagesRoot = calcRoot(messageIds); - for (let i = 0; i < BLOCKS_PER_COMMIT_INTERVAL - 1; i++) { - const blockHeader = createBlock('', i, '', messageCount, messagesRoot); - const blockId = computeBlockId(blockHeader); - - // append block header and Id to arrays - blockHeaders.push(blockHeader); - blockIds.push(blockId); - } - endOfCommitIntervalHeader = createBlock( - calcRoot(blockIds), - blockIds.length, - tai64Time(new Date().getTime()), - messageCount, - messagesRoot - ); - endOfCommitIntervalHeaderLite = generateBlockHeaderLite(endOfCommitIntervalHeader); - prevBlockNodes = constructTree(blockIds); - - // finalize blocks in the state contract - await env.fuelChainState.commit(computeBlockId(endOfCommitIntervalHeader), 0); - ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); - - // set token approval for gateway - await env.token.approve(env.fuelERC20Gateway.address, env.initialTokenAmount); - }); - - describe('Verify access control', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - let signer0: string; - let signer1: string; - let signer2: string; - before(async () => { - signer0 = env.addresses[0]; - signer1 = env.addresses[1]; - signer2 = env.addresses[2]; - }); - - it('Should be able to grant admin role', async () => { - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1)).to.equal(false); - - // Grant admin role - await expect(env.fuelERC20Gateway.grantRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1)).to.equal(true); - }); - - it('Should be able to renounce admin role', async () => { - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke admin role - await expect(env.fuelERC20Gateway.renounceRole(defaultAdminRole, signer0)).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should not be able to grant admin role as non-admin', async () => { - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0)).to.equal(false); - - // Attempt grant admin role - await expect(env.fuelERC20Gateway.grantRole(defaultAdminRole, signer0)).to.be.revertedWith( - `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should be able to grant then revoke admin role', async () => { - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0)).to.equal(false); - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1)).to.equal(true); - - // Grant admin role - await expect(env.fuelERC20Gateway.connect(env.signers[1]).grantRole(defaultAdminRole, signer0)).to.not.be - .reverted; - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke previous admin - await expect(env.fuelERC20Gateway.revokeRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer1)).to.equal(false); - }); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal(false); - - // Grant pauser role - await expect(env.fuelERC20Gateway.grantRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal(true); - }); - - it('Should not be able to grant permission as pauser', async () => { - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer2)).to.equal(false); - expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer2)).to.equal(false); - - // Attempt grant admin role - await expect( - env.fuelERC20Gateway.connect(env.signers[1]).grantRole(defaultAdminRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelERC20Gateway.hasRole(defaultAdminRole, signer2)).to.equal(false); - - // Attempt grant pauser role - await expect( - env.fuelERC20Gateway.connect(env.signers[1]).grantRole(pauserRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer2)).to.equal(false); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal(true); - - // Grant pauser role - await expect(env.fuelERC20Gateway.revokeRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(pauserRole, signer1)).to.equal(false); - }); - }); - - describe('Make both valid and invalid ERC20 deposits', async () => { - let provider: Provider; - before(async () => { - provider = env.fuelMessagePortal.provider; - }); - - it('Should not be able to deposit zero', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - expect( - (await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget1)).add( - await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget2) - ) - ).to.be.equal(gatewayBalance); - - // Attempt deposit - await expect( - env.fuelERC20Gateway.deposit(randomBytes32(), tokenAddress, fuelTokenTarget1, 0) - ).to.be.revertedWith('Cannot deposit zero'); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - }); - - it('Should not be able to deposit with zero balance', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - expect( - (await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget1)).add( - await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget2) - ) - ).to.be.equal(gatewayBalance); - - // Attempt deposit - await expect( - env.fuelERC20Gateway - .connect(env.signers[1]) - .deposit(randomBytes32(), tokenAddress, fuelTokenTarget1, 175) - ).to.be.revertedWith('ERC20: insufficient allowance'); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - - // Attempt deposit with data - await expect( - env.fuelERC20Gateway - .connect(env.signers[1]) - .depositWithData(randomBytes32(), tokenAddress, fuelTokenTarget1, 175, []) - ).to.be.revertedWith('ERC20: insufficient allowance'); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - }); - - it('Should be able to deposit tokens', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - expect( - (await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget1)).add( - await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget2) - ) - ).to.be.equal(gatewayBalance); - - // Deposit to fuelTokenTarget1 - const depositAmount1 = 175; - await expect(env.fuelERC20Gateway.deposit(randomBytes32(), tokenAddress, fuelTokenTarget1, depositAmount1)) - .to.not.be.reverted; - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal( - gatewayBalance.add(depositAmount1) - ); - - // Deposit to fuelTokenTarget2 - const toAddress = randomBytes32(); - const depositAmount2 = 250; - await expect(env.fuelERC20Gateway.deposit(toAddress, tokenAddress, fuelTokenTarget2, depositAmount2)).to.not - .be.reverted; - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal( - gatewayBalance.add(depositAmount1).add(depositAmount2) - ); - - // Verify MessageSent event to l2contract - const messageData = computeMessageData( - fuelTokenTarget2, - tokenAddress.split('0x').join('0x000000000000000000000000'), - env.addresses[0].split('0x').join('0x000000000000000000000000'), - toAddress, - depositAmount2 - ); - const filter2 = { - address: env.fuelMessagePortal.address, - }; - const logs2 = await provider.getLogs(filter2); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs2[logs2.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal(gatewayAddress); - expect(messageSentEvent.args.data).to.equal(messageData); - expect(messageSentEvent.args.amount).to.equal(0); - }); - - it('Should be able to deposit tokens with data', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - expect( - (await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget1)).add( - await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget2) - ) - ).to.be.equal(gatewayBalance); - - // Deposit to fuelTokenTarget1 - const toAddress = randomBytes32(); - const depositData = [3, 2, 6, 9, 2, 5]; - const depositAmount = 85; - await expect( - env.fuelERC20Gateway.depositWithData( - toAddress, - tokenAddress, - fuelTokenTarget1, - depositAmount, - depositData - ) - ).to.not.be.reverted; - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal( - gatewayBalance.add(depositAmount) - ); - - // Verify MessageSent event to l2contract - const messageData = computeMessageData( - fuelTokenTarget1, - tokenAddress.split('0x').join('0x000000000000000000000000'), - env.addresses[0].split('0x').join('0x000000000000000000000000'), - toAddress, - depositAmount, - depositData - ); - const filter = { - address: env.fuelMessagePortal.address, - }; - const logs = await provider.getLogs(filter); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal(gatewayAddress); - expect(messageSentEvent.args.data).to.equal(messageData); - expect(messageSentEvent.args.amount).to.equal(0); - }); - - it('Should be able to deposit tokens with empty data', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - expect( - (await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget1)).add( - await env.fuelERC20Gateway.tokensDeposited(tokenAddress, fuelTokenTarget2) - ) - ).to.be.equal(gatewayBalance); - - // Deposit to fuelTokenTarget2 - const toAddress = randomBytes32(); - const depositAmount = 320; - await expect( - env.fuelERC20Gateway.depositWithData(toAddress, tokenAddress, fuelTokenTarget2, depositAmount, []) - ).to.not.be.reverted; - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal( - gatewayBalance.add(depositAmount) - ); - - // Verify MessageSent event to l2contract - const messageData = computeMessageData( - fuelTokenTarget2, - tokenAddress.split('0x').join('0x000000000000000000000000'), - env.addresses[0].split('0x').join('0x000000000000000000000000'), - toAddress, - depositAmount, - [] - ); - const filter = { - address: env.fuelMessagePortal.address, - }; - const logs = await provider.getLogs(filter); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal(gatewayAddress); - expect(messageSentEvent.args.data).to.equal(messageData); - expect(messageSentEvent.args.amount).to.equal(0); - }); - }); - - describe('Make both valid and invalid ERC20 withdrawals', async () => { - it('Should not be able to directly call finalize', async () => { - await expect( - env.fuelERC20Gateway.finalizeWithdrawal(env.addresses[2], tokenAddress, BN.from(100)) - ).to.be.revertedWith('Caller is not the portal'); - }); - - it('Should be able to finalize valid withdrawal through portal', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[2]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageWithdrawal1, 23); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageWithdrawal1, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance.sub(100)); - expect(await env.token.balanceOf(env.addresses[2])).to.be.equal(recipientBalance.add(100)); - }); - - it('Should be able to finalize valid withdrawal through portal again', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageWithdrawal2, 73); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageWithdrawal2, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance.sub(75)); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance.add(75)); - }); - - it('Should not be able to finalize withdrawal with more than deposited', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageTooLarge, 38); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageTooLarge, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance); - }); - - it('Should not be able to finalize withdrawal of zero tokens', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageTooSmall, 47); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageTooSmall, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance); - }); - - it('Should not be able to finalize withdrawal with bad L2 token', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageBadL2Token, 85); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageBadL2Token, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance); - }); - - it('Should not be able to finalize withdrawal with bad L1 token', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageBadL1Token, 19); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageBadL1Token, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance); - }); - - it('Should not be able to finalize withdrawal with bad sender', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageBadSender, 26); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageBadSender, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance); - }); - }); - - describe('Verify pause and unpause', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2])).to.equal(false); - - // Grant pauser role - await expect(env.fuelERC20Gateway.grantRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2])).to.equal(true); - }); - - it('Should not be able to pause as non-pauser', async () => { - expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); - - // Attempt pause - await expect(env.fuelERC20Gateway.connect(env.signers[1]).pause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` - ); - expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); - }); - - it('Should be able to pause as pauser', async () => { - expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); - - // Pause - await expect(env.fuelERC20Gateway.connect(env.signers[2]).pause()).to.not.be.reverted; - expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as pauser (and not admin)', async () => { - expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelERC20Gateway.connect(env.signers[2]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as non-admin', async () => { - expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelERC20Gateway.connect(env.signers[1]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); - }); - - it('Should not be able to finalize withdrawal when paused', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageWithdrawal3, 31); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageWithdrawal3, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should not be able to deposit when paused', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - - // Deposit 175 to fuelTokenTarget1 - await expect( - env.fuelERC20Gateway.deposit(randomBytes32(), tokenAddress, fuelTokenTarget1, 175) - ).to.be.revertedWith('Pausable: paused'); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - }); - - it('Should not be able to deposit with data when paused', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - - // Deposit 205 to fuelTokenTarget1 - await expect( - env.fuelERC20Gateway.depositWithData(randomBytes32(), tokenAddress, fuelTokenTarget1, 205, []) - ).to.be.revertedWith('Pausable: paused'); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance); - }); - - it('Should be able to unpause as admin', async () => { - expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); - - // Unpause - await expect(env.fuelERC20Gateway.unpause()).to.not.be.reverted; - expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); - }); - - it('Should be able to finalize withdrawal when unpaused', async () => { - const gatewayBalance = await env.token.balanceOf(env.fuelERC20Gateway.address); - const recipientBalance = await env.token.balanceOf(env.addresses[3]); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageWithdrawal3, 37); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageWithdrawal3, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await env.token.balanceOf(env.fuelERC20Gateway.address)).to.be.equal(gatewayBalance.sub(250)); - expect(await env.token.balanceOf(env.addresses[3])).to.be.equal(recipientBalance.add(250)); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2])).to.equal(true); - - // Grant pauser role - await expect(env.fuelERC20Gateway.revokeRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2])).to.equal(false); - }); + 175, + [] + ) + ).to.be.revertedWith('ERC20: insufficient allowance'); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + }); + + it('Should be able to deposit tokens', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + expect( + ( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget1 + ) + ).add( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget2 + ) + ) + ).to.be.equal(gatewayBalance); + + // Deposit to fuelTokenTarget1 + const depositAmount1 = 175; + await expect( + env.fuelERC20Gateway.deposit( + randomBytes32(), + tokenAddress, + fuelTokenTarget1, + depositAmount1 + ) + ).to.not.be.reverted; + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.add(depositAmount1)); + + // Deposit to fuelTokenTarget2 + const toAddress = randomBytes32(); + const depositAmount2 = 250; + await expect( + env.fuelERC20Gateway.deposit( + toAddress, + tokenAddress, + fuelTokenTarget2, + depositAmount2 + ) + ).to.not.be.reverted; + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.add(depositAmount1).add(depositAmount2)); + + // Verify MessageSent event to l2contract + const messageData = computeMessageData( + fuelTokenTarget2, + tokenAddress.split('0x').join('0x000000000000000000000000'), + env.addresses[0].split('0x').join('0x000000000000000000000000'), + toAddress, + depositAmount2 + ); + const filter2 = { + address: env.fuelMessagePortal.address, + }; + const logs2 = await provider.getLogs(filter2); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs2[logs2.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal(gatewayAddress); + expect(messageSentEvent.args.data).to.equal(messageData); + expect(messageSentEvent.args.amount).to.equal(0); + }); + + it('Should be able to deposit tokens with data', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + expect( + ( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget1 + ) + ).add( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget2 + ) + ) + ).to.be.equal(gatewayBalance); + + // Deposit to fuelTokenTarget1 + const toAddress = randomBytes32(); + const depositData = [3, 2, 6, 9, 2, 5]; + const depositAmount = 85; + await expect( + env.fuelERC20Gateway.depositWithData( + toAddress, + tokenAddress, + fuelTokenTarget1, + depositAmount, + depositData + ) + ).to.not.be.reverted; + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.add(depositAmount)); + + // Verify MessageSent event to l2contract + const messageData = computeMessageData( + fuelTokenTarget1, + tokenAddress.split('0x').join('0x000000000000000000000000'), + env.addresses[0].split('0x').join('0x000000000000000000000000'), + toAddress, + depositAmount, + depositData + ); + const filter = { + address: env.fuelMessagePortal.address, + }; + const logs = await provider.getLogs(filter); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal(gatewayAddress); + expect(messageSentEvent.args.data).to.equal(messageData); + expect(messageSentEvent.args.amount).to.equal(0); + }); + + it('Should be able to deposit tokens with empty data', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + expect( + ( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget1 + ) + ).add( + await env.fuelERC20Gateway.tokensDeposited( + tokenAddress, + fuelTokenTarget2 + ) + ) + ).to.be.equal(gatewayBalance); + + // Deposit to fuelTokenTarget2 + const toAddress = randomBytes32(); + const depositAmount = 320; + await expect( + env.fuelERC20Gateway.depositWithData( + toAddress, + tokenAddress, + fuelTokenTarget2, + depositAmount, + [] + ) + ).to.not.be.reverted; + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.add(depositAmount)); + + // Verify MessageSent event to l2contract + const messageData = computeMessageData( + fuelTokenTarget2, + tokenAddress.split('0x').join('0x000000000000000000000000'), + env.addresses[0].split('0x').join('0x000000000000000000000000'), + toAddress, + depositAmount, + [] + ); + const filter = { + address: env.fuelMessagePortal.address, + }; + const logs = await provider.getLogs(filter); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal(gatewayAddress); + expect(messageSentEvent.args.data).to.equal(messageData); + expect(messageSentEvent.args.amount).to.equal(0); + }); + }); + + describe('Make both valid and invalid ERC20 withdrawals', async () => { + it('Should not be able to directly call finalize', async () => { + await expect( + env.fuelERC20Gateway.finalizeWithdrawal( + env.addresses[2], + tokenAddress, + BN.from(100) + ) + ).to.be.revertedWith('Caller is not the portal'); + }); + + it('Should be able to finalize valid withdrawal through portal', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[2]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageWithdrawal1, + 23 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageWithdrawal1, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.sub(100)); + expect(await env.token.balanceOf(env.addresses[2])).to.be.equal( + recipientBalance.add(100) + ); + }); + + it('Should be able to finalize valid withdrawal through portal again', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageWithdrawal2, + 73 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageWithdrawal2, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.sub(75)); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance.add(75) + ); + }); + + it('Should not be able to finalize withdrawal with more than deposited', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageTooLarge, + 38 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageTooLarge, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance + ); + }); + + it('Should not be able to finalize withdrawal of zero tokens', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageTooSmall, + 47 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageTooSmall, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance + ); + }); + + it('Should not be able to finalize withdrawal with bad L2 token', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageBadL2Token, + 85 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageBadL2Token, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance + ); + }); + + it('Should not be able to finalize withdrawal with bad L1 token', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageBadL1Token, + 19 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageBadL1Token, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance + ); + }); + + it('Should not be able to finalize withdrawal with bad sender', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageBadSender, + 26 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageBadSender, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance + ); + }); + }); + + describe('Verify pause and unpause', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + + it('Should be able to grant pauser role', async () => { + expect( + await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); + + // Grant pauser role + await expect(env.fuelERC20Gateway.grantRole(pauserRole, env.addresses[2])) + .to.not.be.reverted; + expect( + await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + }); + + it('Should not be able to pause as non-pauser', async () => { + expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); + + // Attempt pause + await expect( + env.fuelERC20Gateway.connect(env.signers[1]).pause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` + ); + expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); + }); + + it('Should be able to pause as pauser', async () => { + expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); + + // Pause + await expect(env.fuelERC20Gateway.connect(env.signers[2]).pause()).to.not + .be.reverted; + expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as pauser (and not admin)', async () => { + expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelERC20Gateway.connect(env.signers[2]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as non-admin', async () => { + expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelERC20Gateway.connect(env.signers[1]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); + }); + + it('Should not be able to finalize withdrawal when paused', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageWithdrawal3, + 31 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageWithdrawal3, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should not be able to deposit when paused', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + + // Deposit 175 to fuelTokenTarget1 + await expect( + env.fuelERC20Gateway.deposit( + randomBytes32(), + tokenAddress, + fuelTokenTarget1, + 175 + ) + ).to.be.revertedWith('Pausable: paused'); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + }); + + it('Should not be able to deposit with data when paused', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + + // Deposit 205 to fuelTokenTarget1 + await expect( + env.fuelERC20Gateway.depositWithData( + randomBytes32(), + tokenAddress, + fuelTokenTarget1, + 205, + [] + ) + ).to.be.revertedWith('Pausable: paused'); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance); + }); + + it('Should be able to unpause as admin', async () => { + expect(await env.fuelERC20Gateway.paused()).to.be.equal(true); + + // Unpause + await expect(env.fuelERC20Gateway.unpause()).to.not.be.reverted; + expect(await env.fuelERC20Gateway.paused()).to.be.equal(false); + }); + + it('Should be able to finalize withdrawal when unpaused', async () => { + const gatewayBalance = await env.token.balanceOf( + env.fuelERC20Gateway.address + ); + const recipientBalance = await env.token.balanceOf(env.addresses[3]); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageWithdrawal3, + 37 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageWithdrawal3, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect( + await env.token.balanceOf(env.fuelERC20Gateway.address) + ).to.be.equal(gatewayBalance.sub(250)); + expect(await env.token.balanceOf(env.addresses[3])).to.be.equal( + recipientBalance.add(250) + ); + }); + + it('Should be able to revoke pauser role', async () => { + expect( + await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + + // Grant pauser role + await expect( + env.fuelERC20Gateway.revokeRole(pauserRole, env.addresses[2]) + ).to.not.be.reverted; + expect( + await env.fuelERC20Gateway.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); }); + }); }); diff --git a/packages/portal-contracts/test/fuelChainState.ts b/packages/portal-contracts/test/fuelChainState.ts index 5e22038e..63143a75 100644 --- a/packages/portal-contracts/test/fuelChainState.ts +++ b/packages/portal-contracts/test/fuelChainState.ts @@ -12,273 +12,375 @@ const { expect } = chai; // Create a simple block function createBlock(height: number): BlockHeader { - const header: BlockHeader = { - prevRoot: EMPTY, - height: BigNumber.from(height).toHexString(), - timestamp: tai64Time(new Date().getTime()), - daHeight: '0', - txCount: '0', - outputMessagesCount: '0', - txRoot: EMPTY, - outputMessagesRoot: EMPTY, - }; - - return header; + const header: BlockHeader = { + prevRoot: EMPTY, + height: BigNumber.from(height).toHexString(), + timestamp: tai64Time(new Date().getTime()), + daHeight: '0', + txCount: '0', + outputMessagesCount: '0', + txRoot: EMPTY, + outputMessagesRoot: EMPTY, + }; + + return header; } describe('Fuel Chain State', async () => { - let env: HarnessObject; + let env: HarnessObject; + + // Contract constants + const TIME_TO_FINALIZE = 10800; + const BLOCKS_PER_COMMIT_INTERVAL = 10800; + + // Committed block headers + let blockHeader: BlockHeader; + let blockId: string; + let blockHeaderUnfinalized: BlockHeader; + let blockIdUnfinalized: string; + + before(async () => { + env = await setupFuel(); + + // Create, commit, finalize a block + blockHeader = createBlock(0); + blockId = computeBlockId(blockHeader); + await env.fuelChainState.commit(blockId, 0); + ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); + + // Create an unfinalized block + blockHeaderUnfinalized = createBlock(BLOCKS_PER_COMMIT_INTERVAL); + blockIdUnfinalized = computeBlockId(blockHeaderUnfinalized); + await env.fuelChainState.commit(blockIdUnfinalized, 1); + }); + + describe('Verify access control', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + let signer0: string; + let signer1: string; + let signer2: string; + before(async () => { + signer0 = env.addresses[0]; + signer1 = env.addresses[1]; + signer2 = env.addresses[2]; + }); + + it('Should be able to grant admin role', async () => { + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + + // Grant admin role + await expect(env.fuelChainState.grantRole(defaultAdminRole, signer1)).to + .not.be.reverted; + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + }); - // Contract constants - const TIME_TO_FINALIZE = 10800; - const BLOCKS_PER_COMMIT_INTERVAL = 10800; + it('Should be able to renounce admin role', async () => { + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke admin role + await expect(env.fuelChainState.renounceRole(defaultAdminRole, signer0)) + .to.not.be.reverted; + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); - // Committed block headers - let blockHeader: BlockHeader; - let blockId: string; - let blockHeaderUnfinalized: BlockHeader; - let blockIdUnfinalized: string; + it('Should not be able to grant admin role as non-admin', async () => { + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + + // Attempt grant admin role + await expect( + env.fuelChainState.grantRole(defaultAdminRole, signer0) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + it('Should be able to grant then revoke admin role', async () => { + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + + // Grant admin role + await expect( + env.fuelChainState + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer0) + ).to.not.be.reverted; + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke previous admin + await expect(env.fuelChainState.revokeRole(defaultAdminRole, signer1)).to + .not.be.reverted; + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + }); + + it('Should be able to grant pauser role', async () => { + expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal( + false + ); + + // Grant pauser role + await expect(env.fuelChainState.grantRole(pauserRole, signer1)).to.not.be + .reverted; + expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal( + true + ); + }); + + it('Should not be able to grant permission as pauser', async () => { + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + expect(await env.fuelChainState.hasRole(pauserRole, signer2)).to.equal( + false + ); + + // Attempt grant admin role + await expect( + env.fuelChainState + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelChainState.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + + // Attempt grant pauser role + await expect( + env.fuelChainState + .connect(env.signers[1]) + .grantRole(pauserRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelChainState.hasRole(pauserRole, signer2)).to.equal( + false + ); + }); + + it('Should be able to revoke pauser role', async () => { + expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal( + true + ); + + // Grant pauser role + await expect(env.fuelChainState.revokeRole(pauserRole, signer1)).to.not.be + .reverted; + expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal( + false + ); + }); + }); + + describe('Verify admin functions', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const committerRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('COMMITTER_ROLE') + ); + let signer1: string; + let signer2: string; before(async () => { - env = await setupFuel(); - - // Create, commit, finalize a block - blockHeader = createBlock(0); - blockId = computeBlockId(blockHeader); - await env.fuelChainState.commit(blockId, 0); - ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); - - // Create an unfinalized block - blockHeaderUnfinalized = createBlock(BLOCKS_PER_COMMIT_INTERVAL); - blockIdUnfinalized = computeBlockId(blockHeaderUnfinalized); - await env.fuelChainState.commit(blockIdUnfinalized, 1); + signer1 = env.addresses[1]; + signer2 = env.addresses[2]; + }); + + it('Should be able to set comitter as admin', async () => { + expect(await env.fuelChainState.hasRole(committerRole, signer1)).to.equal( + false + ); + + // Set comitter + await expect(env.fuelChainState.grantRole(committerRole, signer1)).to.not + .be.reverted; + expect(await env.fuelChainState.hasRole(committerRole, signer1)).to.equal( + true + ); + }); + + it('Should not be able to set committer role as non-admin', async () => { + expect(await env.fuelChainState.hasRole(committerRole, signer2)).to.equal( + false + ); + + // Attempt set comitter + await expect( + env.fuelChainState + .connect(env.signers[1]) + .grantRole(committerRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelChainState.hasRole(committerRole, signer2)).to.equal( + false + ); }); - describe('Verify access control', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - let signer0: string; - let signer1: string; - let signer2: string; - before(async () => { - signer0 = env.addresses[0]; - signer1 = env.addresses[1]; - signer2 = env.addresses[2]; - }); - - it('Should be able to grant admin role', async () => { - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer1)).to.equal(false); - - // Grant admin role - await expect(env.fuelChainState.grantRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer1)).to.equal(true); - }); - - it('Should be able to renounce admin role', async () => { - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke admin role - await expect(env.fuelChainState.renounceRole(defaultAdminRole, signer0)).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should not be able to grant admin role as non-admin', async () => { - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer0)).to.equal(false); - - // Attempt grant admin role - await expect(env.fuelChainState.grantRole(defaultAdminRole, signer0)).to.be.revertedWith( - `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should be able to grant then revoke admin role', async () => { - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer0)).to.equal(false); - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer1)).to.equal(true); - - // Grant admin role - await expect(env.fuelChainState.connect(env.signers[1]).grantRole(defaultAdminRole, signer0)).to.not.be - .reverted; - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke previous admin - await expect(env.fuelChainState.revokeRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer1)).to.equal(false); - }); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal(false); - - // Grant pauser role - await expect(env.fuelChainState.grantRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal(true); - }); - - it('Should not be able to grant permission as pauser', async () => { - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer2)).to.equal(false); - expect(await env.fuelChainState.hasRole(pauserRole, signer2)).to.equal(false); - - // Attempt grant admin role - await expect( - env.fuelChainState.connect(env.signers[1]).grantRole(defaultAdminRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelChainState.hasRole(defaultAdminRole, signer2)).to.equal(false); - - // Attempt grant pauser role - await expect(env.fuelChainState.connect(env.signers[1]).grantRole(pauserRole, signer2)).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelChainState.hasRole(pauserRole, signer2)).to.equal(false); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal(true); - - // Grant pauser role - await expect(env.fuelChainState.revokeRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(pauserRole, signer1)).to.equal(false); - }); + it('Should not be able to make commits as non-comitter', async () => { + const blockHash = await env.fuelChainState.blockHashAtCommit(9); + await expect( + env.fuelChainState.connect(env.signers[2]).commit(randomBytes32(), 9) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${committerRole}` + ); + expect(await env.fuelChainState.blockHashAtCommit(9)).to.equal(blockHash); }); - describe('Verify admin functions', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const committerRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('COMMITTER_ROLE')); - let signer1: string; - let signer2: string; - before(async () => { - signer1 = env.addresses[1]; - signer2 = env.addresses[2]; - }); - - it('Should be able to set comitter as admin', async () => { - expect(await env.fuelChainState.hasRole(committerRole, signer1)).to.equal(false); - - // Set comitter - await expect(env.fuelChainState.grantRole(committerRole, signer1)).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(committerRole, signer1)).to.equal(true); - }); - - it('Should not be able to set committer role as non-admin', async () => { - expect(await env.fuelChainState.hasRole(committerRole, signer2)).to.equal(false); - - // Attempt set comitter - await expect( - env.fuelChainState.connect(env.signers[1]).grantRole(committerRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelChainState.hasRole(committerRole, signer2)).to.equal(false); - }); - - it('Should not be able to make commits as non-comitter', async () => { - const blockHash = await env.fuelChainState.blockHashAtCommit(9); - await expect(env.fuelChainState.connect(env.signers[2]).commit(randomBytes32(), 9)).to.be.revertedWith( - `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${committerRole}` - ); - expect(await env.fuelChainState.blockHashAtCommit(9)).to.equal(blockHash); - }); - - it('Should be able to make commits as comitter', async () => { - const blockHash = randomBytes32(); - await expect(env.fuelChainState.connect(env.signers[1]).commit(blockHash, 9)).to.not.be.reverted; - expect(await env.fuelChainState.blockHashAtCommit(9)).to.equal(blockHash); - }); + it('Should be able to make commits as comitter', async () => { + const blockHash = randomBytes32(); + await expect( + env.fuelChainState.connect(env.signers[1]).commit(blockHash, 9) + ).to.not.be.reverted; + expect(await env.fuelChainState.blockHashAtCommit(9)).to.equal(blockHash); }); + }); - describe('Verify valid blocks', async () => { - it('Should be able to verify valid block', async () => { - expect(await env.fuelChainState.finalized(blockId, 0)).to.be.equal(true); - }); + describe('Verify valid blocks', async () => { + it('Should be able to verify valid block', async () => { + expect(await env.fuelChainState.finalized(blockId, 0)).to.be.equal(true); + }); - it('Should not be able to verify unfinalized block', async () => { - expect(await env.fuelChainState.finalized(blockIdUnfinalized, BLOCKS_PER_COMMIT_INTERVAL)).to.be.equal( - false - ); - }); + it('Should not be able to verify unfinalized block', async () => { + expect( + await env.fuelChainState.finalized( + blockIdUnfinalized, + BLOCKS_PER_COMMIT_INTERVAL + ) + ).to.be.equal(false); + }); + + it('Should not be able to verify invalid block', async () => { + await expect( + env.fuelChainState.finalized(randomBytes32(), 0) + ).to.be.revertedWith('Unknown block'); + }); + }); + + describe('Verify pause and unpause', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + + it('Should be able to grant pauser role', async () => { + expect( + await env.fuelChainState.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); + + // Grant pauser role + await expect(env.fuelChainState.grantRole(pauserRole, env.addresses[2])) + .to.not.be.reverted; + expect( + await env.fuelChainState.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + }); + + it('Should not be able to pause as non-pauser', async () => { + expect(await env.fuelChainState.paused()).to.be.equal(false); + + // Attempt pause + await expect( + env.fuelChainState.connect(env.signers[1]).pause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` + ); + expect(await env.fuelChainState.paused()).to.be.equal(false); + }); + + it('Should be able to pause as pauser', async () => { + expect(await env.fuelChainState.paused()).to.be.equal(false); + + // Pause + await expect(env.fuelChainState.connect(env.signers[2]).pause()).to.not.be + .reverted; + expect(await env.fuelChainState.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as pauser (and not admin)', async () => { + expect(await env.fuelChainState.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelChainState.connect(env.signers[2]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelChainState.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as non-admin', async () => { + expect(await env.fuelChainState.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelChainState.connect(env.signers[1]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelChainState.paused()).to.be.equal(true); + }); + + it('Should not be able to verify blocks when paused', async () => { + await expect(env.fuelChainState.finalized(blockId, 0)).to.be.revertedWith( + 'Pausable: paused' + ); + }); + + it('Should not be able to commit blocks when paused', async () => { + await expect( + env.fuelChainState.commit(randomBytes32(), 9) + ).to.be.revertedWith('Pausable: paused'); + }); + + it('Should be able to unpause as admin', async () => { + expect(await env.fuelChainState.paused()).to.be.equal(true); + + // Unpause + await expect(env.fuelChainState.unpause()).to.not.be.reverted; + expect(await env.fuelChainState.paused()).to.be.equal(false); + }); - it('Should not be able to verify invalid block', async () => { - await expect(env.fuelChainState.finalized(randomBytes32(), 0)).to.be.revertedWith('Unknown block'); - }); + it('Should be able to verify block when unpaused', async () => { + expect(await env.fuelChainState.finalized(blockId, 0)).to.be.equal(true); }); - describe('Verify pause and unpause', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelChainState.hasRole(pauserRole, env.addresses[2])).to.equal(false); - - // Grant pauser role - await expect(env.fuelChainState.grantRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(pauserRole, env.addresses[2])).to.equal(true); - }); - - it('Should not be able to pause as non-pauser', async () => { - expect(await env.fuelChainState.paused()).to.be.equal(false); - - // Attempt pause - await expect(env.fuelChainState.connect(env.signers[1]).pause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` - ); - expect(await env.fuelChainState.paused()).to.be.equal(false); - }); - - it('Should be able to pause as pauser', async () => { - expect(await env.fuelChainState.paused()).to.be.equal(false); - - // Pause - await expect(env.fuelChainState.connect(env.signers[2]).pause()).to.not.be.reverted; - expect(await env.fuelChainState.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as pauser (and not admin)', async () => { - expect(await env.fuelChainState.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelChainState.connect(env.signers[2]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelChainState.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as non-admin', async () => { - expect(await env.fuelChainState.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelChainState.connect(env.signers[1]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelChainState.paused()).to.be.equal(true); - }); - - it('Should not be able to verify blocks when paused', async () => { - await expect(env.fuelChainState.finalized(blockId, 0)).to.be.revertedWith('Pausable: paused'); - }); - - it('Should not be able to commit blocks when paused', async () => { - await expect(env.fuelChainState.commit(randomBytes32(), 9)).to.be.revertedWith('Pausable: paused'); - }); - - it('Should be able to unpause as admin', async () => { - expect(await env.fuelChainState.paused()).to.be.equal(true); - - // Unpause - await expect(env.fuelChainState.unpause()).to.not.be.reverted; - expect(await env.fuelChainState.paused()).to.be.equal(false); - }); - - it('Should be able to verify block when unpaused', async () => { - expect(await env.fuelChainState.finalized(blockId, 0)).to.be.equal(true); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelChainState.hasRole(pauserRole, env.addresses[2])).to.equal(true); - - // Grant pauser role - await expect(env.fuelChainState.revokeRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelChainState.hasRole(pauserRole, env.addresses[2])).to.equal(false); - }); + it('Should be able to revoke pauser role', async () => { + expect( + await env.fuelChainState.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + + // Grant pauser role + await expect(env.fuelChainState.revokeRole(pauserRole, env.addresses[2])) + .to.not.be.reverted; + expect( + await env.fuelChainState.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); }); + }); }); diff --git a/packages/portal-contracts/test/messagesIncoming.ts b/packages/portal-contracts/test/messagesIncoming.ts index 811c3f6b..0f59a5d3 100644 --- a/packages/portal-contracts/test/messagesIncoming.ts +++ b/packages/portal-contracts/test/messagesIncoming.ts @@ -6,7 +6,11 @@ import { Provider } from '@ethersproject/abstract-provider'; import { constructTree, calcRoot, getProof } from '@fuel-ts/merkle'; import { MessageTester } from '../typechain/MessageTester.d'; import { HarnessObject, setupFuel } from '../protocol/harness'; -import BlockHeader, { BlockHeaderLite, computeBlockId, generateBlockHeaderLite } from '../protocol/blockHeader'; +import BlockHeader, { + BlockHeaderLite, + computeBlockId, + generateBlockHeaderLite, +} from '../protocol/blockHeader'; import { EMPTY, ZERO } from '../protocol/constants'; import Message, { computeMessageId } from '../protocol/message'; import { randomBytes32, tai64Time } from '../protocol/utils'; @@ -17,777 +21,1075 @@ const { expect } = chai; // Merkle tree node structure // TODO: should be importable from @fuel-ts/merkle declare class TreeNode { - left: number; - right: number; - parent: number; - hash: string; - data: string; - index: number; + left: number; + right: number; + parent: number; + hash: string; + data: string; + index: number; } // Merkle proof class declare class MerkleProof { - key: number; - proof: string[]; + key: number; + proof: string[]; } // Create a simple block function createBlock( - prevRoot: string, - blockHeight: number, - timestamp?: string, - outputMessagesCount?: string, - outputMessagesRoot?: string + prevRoot: string, + blockHeight: number, + timestamp?: string, + outputMessagesCount?: string, + outputMessagesRoot?: string ): BlockHeader { - const header: BlockHeader = { - prevRoot: prevRoot ? prevRoot : ZERO, - height: blockHeight.toString(), - timestamp: timestamp ? timestamp : '0', - daHeight: '0', - txCount: '0', - outputMessagesCount: outputMessagesCount ? outputMessagesCount : '0', - txRoot: EMPTY, - outputMessagesRoot: outputMessagesRoot ? outputMessagesRoot : ZERO, - }; - return header; + const header: BlockHeader = { + prevRoot: prevRoot ? prevRoot : ZERO, + height: blockHeight.toString(), + timestamp: timestamp ? timestamp : '0', + daHeight: '0', + txCount: '0', + outputMessagesCount: outputMessagesCount ? outputMessagesCount : '0', + txRoot: EMPTY, + outputMessagesRoot: outputMessagesRoot ? outputMessagesRoot : ZERO, + }; + return header; } // Get proof for the leaf function getLeafIndexKey(nodes: TreeNode[], data: string): number { - for (let n = 0; n < nodes.length; n += 1) { - if (nodes[n].data === data) { - return nodes[n].index; - } + for (let n = 0; n < nodes.length; n += 1) { + if (nodes[n].data === data) { + return nodes[n].index; } - return 0; + } + return 0; } describe('Incoming Messages', async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let env: HarnessObject; - let fuelBaseAssetDecimals: number; - let baseAssetConversion: number; - - // Contract constants - const TIME_TO_FINALIZE = 10800; - const BLOCKS_PER_COMMIT_INTERVAL = 10800; - - // Message data - const messageTestData1 = randomBytes32(); - const messageTestData2 = randomBytes32(); - const messageTestData3 = randomBytes32(); - let messageNodes: TreeNode[]; - let trustedSenderAddress: string; - - // Testing contracts - let messageTester: MessageTester; - let messageTesterAddress: string; - let fuelMessagePortalContractAddress: string; - - // Messages - let message1: Message; - let message2: Message; - let messageWithAmount: Message; - let messageBadSender: Message; - let messageBadRecipient: Message; - let messageBadData: Message; - let messageEOA: Message; - let messageEOANoAmount: Message; - - // Arrays of committed block headers and their IDs - const blockHeaders: BlockHeader[] = []; - const blockIds: string[] = []; - let endOfCommitIntervalHeader: BlockHeader; - let endOfCommitIntervalHeaderLite: BlockHeaderLite; - let unflinalizedBlock: BlockHeader; - let prevBlockNodes: TreeNode[]; - - // Helper function to setup test data - function generateProof(message: Message, prevBlockDistance = 1): [string, BlockHeader, MerkleProof, MerkleProof] { - const messageBlockIndex = BLOCKS_PER_COMMIT_INTERVAL - 1 - prevBlockDistance; - const messageBlockHeader = blockHeaders[messageBlockIndex]; - const messageBlockLeafIndexKey = getLeafIndexKey(prevBlockNodes, blockIds[messageBlockIndex]); - const blockInHistoryProof = { - key: messageBlockLeafIndexKey, - proof: getProof(prevBlockNodes, messageBlockLeafIndexKey), - }; - const messageID = computeMessageId(message); - const messageLeafIndexKey = getLeafIndexKey(messageNodes, messageID); - const messageInBlockProof = { - key: messageLeafIndexKey, - proof: getProof(messageNodes, messageLeafIndexKey), - }; - return [messageID, messageBlockHeader, blockInHistoryProof, messageInBlockProof]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let env: HarnessObject; + let fuelBaseAssetDecimals: number; + let baseAssetConversion: number; + + // Contract constants + const TIME_TO_FINALIZE = 10800; + const BLOCKS_PER_COMMIT_INTERVAL = 10800; + + // Message data + const messageTestData1 = randomBytes32(); + const messageTestData2 = randomBytes32(); + const messageTestData3 = randomBytes32(); + let messageNodes: TreeNode[]; + let trustedSenderAddress: string; + + // Testing contracts + let messageTester: MessageTester; + let messageTesterAddress: string; + let fuelMessagePortalContractAddress: string; + + // Messages + let message1: Message; + let message2: Message; + let messageWithAmount: Message; + let messageBadSender: Message; + let messageBadRecipient: Message; + let messageBadData: Message; + let messageEOA: Message; + let messageEOANoAmount: Message; + + // Arrays of committed block headers and their IDs + const blockHeaders: BlockHeader[] = []; + const blockIds: string[] = []; + let endOfCommitIntervalHeader: BlockHeader; + let endOfCommitIntervalHeaderLite: BlockHeaderLite; + let unflinalizedBlock: BlockHeader; + let prevBlockNodes: TreeNode[]; + + // Helper function to setup test data + function generateProof( + message: Message, + prevBlockDistance = 1 + ): [string, BlockHeader, MerkleProof, MerkleProof] { + const messageBlockIndex = + BLOCKS_PER_COMMIT_INTERVAL - 1 - prevBlockDistance; + const messageBlockHeader = blockHeaders[messageBlockIndex]; + const messageBlockLeafIndexKey = getLeafIndexKey( + prevBlockNodes, + blockIds[messageBlockIndex] + ); + const blockInHistoryProof = { + key: messageBlockLeafIndexKey, + proof: getProof(prevBlockNodes, messageBlockLeafIndexKey), + }; + const messageID = computeMessageId(message); + const messageLeafIndexKey = getLeafIndexKey(messageNodes, messageID); + const messageInBlockProof = { + key: messageLeafIndexKey, + proof: getProof(messageNodes, messageLeafIndexKey), + }; + return [ + messageID, + messageBlockHeader, + blockInHistoryProof, + messageInBlockProof, + ]; + } + + before(async () => { + env = await setupFuel(); + fuelBaseAssetDecimals = await env.fuelMessagePortal.fuelBaseAssetDecimals(); + baseAssetConversion = 10 ** (18 - fuelBaseAssetDecimals); + + // Deploy contracts for message testing. + const messageTesterContractFactory = await ethers.getContractFactory( + 'MessageTester' + ); + messageTester = (await messageTesterContractFactory.deploy( + env.fuelMessagePortal.address + )) as MessageTester; + await messageTester.deployed(); + expect(await messageTester.data1()).to.be.equal(0); + expect(await messageTester.data2()).to.be.equal(0); + + // get data for building messages + messageTesterAddress = messageTester.address + .split('0x') + .join('0x000000000000000000000000'); + fuelMessagePortalContractAddress = env.fuelMessagePortal.address + .split('0x') + .join('0x000000000000000000000000'); + trustedSenderAddress = await messageTester.getTrustedSender(); + + // message from trusted sender + message1 = new Message( + trustedSenderAddress, + messageTesterAddress, + BN.from(0), + randomBytes32(), + messageTester.interface.encodeFunctionData('receiveMessage', [ + messageTestData1, + messageTestData2, + ]) + ); + message2 = new Message( + trustedSenderAddress, + messageTesterAddress, + BN.from(0), + randomBytes32(), + messageTester.interface.encodeFunctionData('receiveMessage', [ + messageTestData2, + messageTestData1, + ]) + ); + // message from trusted sender with amount + messageWithAmount = new Message( + trustedSenderAddress, + messageTesterAddress, + ethers.utils.parseEther('0.1').div(baseAssetConversion), + randomBytes32(), + messageTester.interface.encodeFunctionData('receiveMessage', [ + messageTestData2, + messageTestData3, + ]) + ); + // message from untrusted sender + messageBadSender = new Message( + randomBytes32(), + messageTesterAddress, + BN.from(0), + randomBytes32(), + messageTester.interface.encodeFunctionData('receiveMessage', [ + messageTestData3, + messageTestData1, + ]) + ); + // message to bad recipient + messageBadRecipient = new Message( + trustedSenderAddress, + env.fuelMessagePortal.address + .split('0x') + .join('0x000000000000000000000000'), + BN.from(0), + randomBytes32(), + messageTester.interface.encodeFunctionData('receiveMessage', [ + messageTestData2, + messageTestData2, + ]) + ); + // message with bad data + messageBadData = new Message( + trustedSenderAddress, + messageTesterAddress, + BN.from(0), + randomBytes32(), + randomBytes32() + ); + // message to EOA + messageEOA = new Message( + randomBytes32(), + env.addresses[2].split('0x').join('0x000000000000000000000000'), + ethers.utils.parseEther('0.1').div(baseAssetConversion), + randomBytes32(), + '0x' + ); + // message to EOA no amount + messageEOANoAmount = new Message( + randomBytes32(), + env.addresses[3].split('0x').join('0x000000000000000000000000'), + BN.from(0), + randomBytes32(), + '0x' + ); + + // compile all message IDs + const messageIds: string[] = []; + messageIds.push(computeMessageId(message1)); + messageIds.push(computeMessageId(message2)); + messageIds.push(computeMessageId(messageWithAmount)); + messageIds.push(computeMessageId(messageBadSender)); + messageIds.push(computeMessageId(messageBadRecipient)); + messageIds.push(computeMessageId(messageBadData)); + messageIds.push(computeMessageId(messageEOA)); + messageIds.push(computeMessageId(messageEOANoAmount)); + messageNodes = constructTree(messageIds); + + // create blocks + const messageCount = messageIds.length.toString(); + const messagesRoot = calcRoot(messageIds); + for (let i = 0; i < BLOCKS_PER_COMMIT_INTERVAL - 1; i++) { + const blockHeader = createBlock('', i, '', messageCount, messagesRoot); + const blockId = computeBlockId(blockHeader); + + // append block header and Id to arrays + blockHeaders.push(blockHeader); + blockIds.push(blockId); } + // create end of commit interval block + const timestamp = tai64Time(new Date().getTime()); + endOfCommitIntervalHeader = createBlock( + calcRoot(blockIds), + blockIds.length, + timestamp, + messageCount, + messagesRoot + ); + endOfCommitIntervalHeaderLite = generateBlockHeaderLite( + endOfCommitIntervalHeader + ); + prevBlockNodes = constructTree(blockIds); + blockIds.push(computeBlockId(endOfCommitIntervalHeader)); + + // finalize blocks in the state contract + await env.fuelChainState.commit( + computeBlockId(endOfCommitIntervalHeader), + 0 + ); + ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); + + // create an unfinalized block + unflinalizedBlock = createBlock( + calcRoot(blockIds), + BLOCKS_PER_COMMIT_INTERVAL * 11 - 1, + timestamp, + messageCount, + messagesRoot + ); + await env.fuelChainState.commit(computeBlockId(unflinalizedBlock), 10); + + // make sure the portal has eth to relay + await env.fuelMessagePortal.depositETH(EMPTY, { + value: ethers.utils.parseEther('0.2'), + }); + + // Verify contract getters + expect(await env.fuelMessagePortal.fuelChainStateContract()).to.equal( + env.fuelChainState.address + ); + expect(await messageTester.fuelMessagePortal()).to.equal( + env.fuelMessagePortal.address + ); + }); + + describe('Verify access control', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + let signer0: string; + let signer1: string; + let signer2: string; + before(async () => { + signer0 = env.addresses[0]; + signer1 = env.addresses[1]; + signer2 = env.addresses[2]; + }); + + it('Should be able to grant admin role', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + + // Grant admin role + await expect(env.fuelMessagePortal.grantRole(defaultAdminRole, signer1)) + .to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + }); + + it('Should be able to renounce admin role', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke admin role + await expect( + env.fuelMessagePortal.renounceRole(defaultAdminRole, signer0) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + + it('Should not be able to grant admin role as non-admin', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + + // Attempt grant admin role + await expect( + env.fuelMessagePortal.grantRole(defaultAdminRole, signer0) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + + it('Should be able to grant then revoke admin role', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + + // Grant admin role + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer0) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke previous admin + await expect(env.fuelMessagePortal.revokeRole(defaultAdminRole, signer1)) + .to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + }); + + it('Should be able to grant pauser role', async () => { + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + false + ); + + // Grant pauser role + await expect(env.fuelMessagePortal.grantRole(pauserRole, signer1)).to.not + .be.reverted; + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + true + ); + }); + + it('Should not be able to grant permission as pauser', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal( + false + ); + + // Attempt grant admin role + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + + // Attempt grant pauser role + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .grantRole(pauserRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal( + false + ); + }); + + it('Should be able to revoke pauser role', async () => { + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + true + ); + + // Grant pauser role + await expect(env.fuelMessagePortal.revokeRole(pauserRole, signer1)).to.not + .be.reverted; + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + false + ); + }); + }); + + describe('Relay both valid and invalid messages', async () => { + let provider: Provider; before(async () => { - env = await setupFuel(); - fuelBaseAssetDecimals = await env.fuelMessagePortal.fuelBaseAssetDecimals(); - baseAssetConversion = 10 ** (18 - fuelBaseAssetDecimals); - - // Deploy contracts for message testing. - const messageTesterContractFactory = await ethers.getContractFactory('MessageTester'); - messageTester = (await messageTesterContractFactory.deploy(env.fuelMessagePortal.address)) as MessageTester; - await messageTester.deployed(); - expect(await messageTester.data1()).to.be.equal(0); - expect(await messageTester.data2()).to.be.equal(0); - - // get data for building messages - messageTesterAddress = messageTester.address.split('0x').join('0x000000000000000000000000'); - fuelMessagePortalContractAddress = env.fuelMessagePortal.address.split('0x').join('0x000000000000000000000000'); - trustedSenderAddress = await messageTester.getTrustedSender(); - - // message from trusted sender - message1 = new Message( - trustedSenderAddress, - messageTesterAddress, - BN.from(0), - randomBytes32(), - messageTester.interface.encodeFunctionData('receiveMessage', [messageTestData1, messageTestData2]) - ); - message2 = new Message( - trustedSenderAddress, - messageTesterAddress, - BN.from(0), - randomBytes32(), - messageTester.interface.encodeFunctionData('receiveMessage', [messageTestData2, messageTestData1]) - ); - // message from trusted sender with amount - messageWithAmount = new Message( - trustedSenderAddress, - messageTesterAddress, - ethers.utils.parseEther('0.1').div(baseAssetConversion), - randomBytes32(), - messageTester.interface.encodeFunctionData('receiveMessage', [messageTestData2, messageTestData3]) - ); - // message from untrusted sender - messageBadSender = new Message( - randomBytes32(), - messageTesterAddress, - BN.from(0), - randomBytes32(), - messageTester.interface.encodeFunctionData('receiveMessage', [messageTestData3, messageTestData1]) - ); - // message to bad recipient - messageBadRecipient = new Message( - trustedSenderAddress, - env.fuelMessagePortal.address.split('0x').join('0x000000000000000000000000'), - BN.from(0), - randomBytes32(), - messageTester.interface.encodeFunctionData('receiveMessage', [messageTestData2, messageTestData2]) - ); - // message with bad data - messageBadData = new Message( - trustedSenderAddress, - messageTesterAddress, - BN.from(0), - randomBytes32(), - randomBytes32() - ); - // message to EOA - messageEOA = new Message( - randomBytes32(), - env.addresses[2].split('0x').join('0x000000000000000000000000'), - ethers.utils.parseEther('0.1').div(baseAssetConversion), - randomBytes32(), - '0x' - ); - // message to EOA no amount - messageEOANoAmount = new Message( - randomBytes32(), - env.addresses[3].split('0x').join('0x000000000000000000000000'), - BN.from(0), - randomBytes32(), - '0x' - ); - - // compile all message IDs - const messageIds: string[] = []; - messageIds.push(computeMessageId(message1)); - messageIds.push(computeMessageId(message2)); - messageIds.push(computeMessageId(messageWithAmount)); - messageIds.push(computeMessageId(messageBadSender)); - messageIds.push(computeMessageId(messageBadRecipient)); - messageIds.push(computeMessageId(messageBadData)); - messageIds.push(computeMessageId(messageEOA)); - messageIds.push(computeMessageId(messageEOANoAmount)); - messageNodes = constructTree(messageIds); - - // create blocks - const messageCount = messageIds.length.toString(); - const messagesRoot = calcRoot(messageIds); - for (let i = 0; i < BLOCKS_PER_COMMIT_INTERVAL - 1; i++) { - const blockHeader = createBlock('', i, '', messageCount, messagesRoot); - const blockId = computeBlockId(blockHeader); - - // append block header and Id to arrays - blockHeaders.push(blockHeader); - blockIds.push(blockId); - } - - // create end of commit interval block - const timestamp = tai64Time(new Date().getTime()); - endOfCommitIntervalHeader = createBlock( - calcRoot(blockIds), - blockIds.length, - timestamp, - messageCount, - messagesRoot - ); - endOfCommitIntervalHeaderLite = generateBlockHeaderLite(endOfCommitIntervalHeader); - prevBlockNodes = constructTree(blockIds); - blockIds.push(computeBlockId(endOfCommitIntervalHeader)); - - // finalize blocks in the state contract - await env.fuelChainState.commit(computeBlockId(endOfCommitIntervalHeader), 0); - ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); - - // create an unfinalized block - unflinalizedBlock = createBlock( - calcRoot(blockIds), - BLOCKS_PER_COMMIT_INTERVAL * 11 - 1, - timestamp, - messageCount, - messagesRoot - ); - await env.fuelChainState.commit(computeBlockId(unflinalizedBlock), 10); - - // make sure the portal has eth to relay - await env.fuelMessagePortal.depositETH(EMPTY, { - value: ethers.utils.parseEther('0.2'), - }); - - // Verify contract getters - expect(await env.fuelMessagePortal.fuelChainStateContract()).to.equal(env.fuelChainState.address); - expect(await messageTester.fuelMessagePortal()).to.equal(env.fuelMessagePortal.address); - }); - - describe('Verify access control', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - let signer0: string; - let signer1: string; - let signer2: string; - before(async () => { - signer0 = env.addresses[0]; - signer1 = env.addresses[1]; - signer2 = env.addresses[2]; - }); - - it('Should be able to grant admin role', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(false); - - // Grant admin role - await expect(env.fuelMessagePortal.grantRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(true); - }); - - it('Should be able to renounce admin role', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke admin role - await expect(env.fuelMessagePortal.renounceRole(defaultAdminRole, signer0)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should not be able to grant admin role as non-admin', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - - // Attempt grant admin role - await expect(env.fuelMessagePortal.grantRole(defaultAdminRole, signer0)).to.be.revertedWith( - `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should be able to grant then revoke admin role', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(true); - - // Grant admin role - await expect(env.fuelMessagePortal.connect(env.signers[1]).grantRole(defaultAdminRole, signer0)).to.not.be - .reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke previous admin - await expect(env.fuelMessagePortal.revokeRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(false); - }); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(false); - - // Grant pauser role - await expect(env.fuelMessagePortal.grantRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(true); - }); - - it('Should not be able to grant permission as pauser', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2)).to.equal(false); - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal(false); - - // Attempt grant admin role - await expect( - env.fuelMessagePortal.connect(env.signers[1]).grantRole(defaultAdminRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2)).to.equal(false); - - // Attempt grant pauser role - await expect( - env.fuelMessagePortal.connect(env.signers[1]).grantRole(pauserRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal(false); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(true); - - // Grant pauser role - await expect(env.fuelMessagePortal.revokeRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(false); - }); - }); - - describe('Relay both valid and invalid messages', async () => { - let provider: Provider; - before(async () => { - provider = env.fuelMessagePortal.provider; - }); - - it('Should not get a valid message sender outside of relaying', async () => { - await expect(env.fuelMessagePortal.messageSender()).to.be.revertedWith('Current message sender not set'); - }); - - it('Should not be able to call messageable contract directly', async () => { - await expect(messageTester.receiveMessage(messageTestData3, messageTestData3)).to.be.revertedWith( - 'Caller is not the portal' - ); - }); - - it('Should not be able to relay message with bad root block', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message1, 24); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - message1, - generateBlockHeaderLite(createBlock('', BLOCKS_PER_COMMIT_INTERVAL * 20 - 1)), - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Unknown block'); - await expect( - env.fuelMessagePortal.relayMessage( - message1, - generateBlockHeaderLite(unflinalizedBlock), - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Unfinalized root block'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should not be able to relay message with bad proof in root block', async () => { - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const messageTesterBalance = await provider.getBalance(messageTester.address); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message1, 67); - blockInRoot.key = blockInRoot.key + 1; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - message1, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Invalid block in history proof'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal(portalBalance); - expect(await provider.getBalance(messageTester.address)).to.be.equal(messageTesterBalance); - }); - - it('Should be able to relay valid message', async () => { - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const messageTesterBalance = await provider.getBalance(messageTester.address); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message1, 22); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - message1, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await messageTester.data1()).to.be.equal(messageTestData1); - expect(await messageTester.data2()).to.be.equal(messageTestData2); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal(portalBalance); - expect(await provider.getBalance(messageTester.address)).to.be.equal(messageTesterBalance); - }); - - it('Should not be able to relay already relayed message', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message1, 68); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - await expect( - env.fuelMessagePortal.relayMessage( - message1, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Already relayed'); - }); - - it('Should not be able to relay message with low gas', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageWithAmount, 11); - const options = { - gasLimit: 140000, - }; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageWithAmount, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock, - options - ) - ).to.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should be able to relay message with amount', async () => { - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const messageTesterBalance = await provider.getBalance(messageTester.address); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageWithAmount, 33); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageWithAmount, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await messageTester.data1()).to.be.equal(messageTestData2); - expect(await messageTester.data2()).to.be.equal(messageTestData3); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal( - portalBalance.sub(messageWithAmount.amount.mul(baseAssetConversion)) - ); - expect(await provider.getBalance(messageTester.address)).to.be.equal( - messageTesterBalance.add(messageWithAmount.amount.mul(baseAssetConversion)) - ); - }); - - it('Should not be able to relay message from untrusted sender', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageBadSender, 47); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageBadSender, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should not be able to relay message to bad recipient', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageBadRecipient, 69); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageBadRecipient, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should not be able to relay message with bad data', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageBadData, 21); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageBadData, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should be able to relay message to EOA', async () => { - const accountBalance = await provider.getBalance(env.addresses[2]); - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageEOA, 19); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageEOA, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await provider.getBalance(env.addresses[2])).to.be.equal( - accountBalance.add(messageEOA.amount.mul(baseAssetConversion)) - ); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal( - portalBalance.sub(messageEOA.amount.mul(baseAssetConversion)) - ); - }); - - it('Should be able to relay message to EOA with no amount', async () => { - const accountBalance = await provider.getBalance(env.addresses[3]); - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(messageEOANoAmount, 25); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageEOANoAmount, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await provider.getBalance(env.addresses[3])).to.be.equal(accountBalance); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal(portalBalance); - }); - - it('Should not be able to relay valid message with different amount', async () => { - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const messageTesterBalance = await provider.getBalance(messageTester.address); - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message2, 68); - const diffBlock = { - sender: message2.sender, - recipient: message2.recipient, - nonce: message2.nonce, - amount: message2.amount.add(ethers.utils.parseEther('1.0')), - data: message2.data, - }; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - diffBlock, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Invalid message in block proof'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal(portalBalance); - expect(await provider.getBalance(messageTester.address)).to.be.equal(messageTesterBalance); - }); - - it('Should not be able to relay non-existent message', async () => { - const [, msgBlockHeader, blockInRoot] = generateProof(message2, 1); - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - const messageTesterBalance = await provider.getBalance(messageTester.address); - const msgInBlock = { - key: 0, - proof: [], - }; - await expect( - env.fuelMessagePortal.relayMessage( - message2, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Invalid message in block proof'); - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.be.equal(portalBalance); - expect(await provider.getBalance(messageTester.address)).to.be.equal(messageTesterBalance); - }); - - it('Should not be able to relay reentrant messages', async () => { - // create a message that attempts to relay another message - const [, rTestMsgBlockHeader, rTestBlockInRoot, rTestMsgInBlock] = generateProof(message1, 5); - const reentrantTestData = env.fuelMessagePortal.interface.encodeFunctionData('relayMessage', [ - message1, - endOfCommitIntervalHeaderLite, - rTestMsgBlockHeader, - rTestBlockInRoot, - rTestMsgInBlock, - ]); - const messageReentrant = new Message( - trustedSenderAddress, - fuelMessagePortalContractAddress, - BN.from(0), - randomBytes32(), - reentrantTestData - ); - const messageReentrantId = computeMessageId(messageReentrant); - const messageReentrantMessages = [messageReentrantId]; - const messageReentrantMessageNodes = constructTree(messageReentrantMessages); - - // create block that contains this message - const tai64Time = BN.from(Math.floor(new Date().getTime() / 1000)).add('4611686018427387914'); - const reentrantTestMessageBlock = createBlock( - '', - blockIds.length, - tai64Time.toHexString(), - '1', - calcRoot(messageReentrantMessages) - ); - const reentrantTestMessageBlockId = computeBlockId(reentrantTestMessageBlock); - blockIds.push(reentrantTestMessageBlockId); - - // commit and finalize a block that contains the block with the message - const reentrantTestRootBlock = createBlock(calcRoot(blockIds), blockIds.length, tai64Time.toHexString()); - const reentrantTestPrevBlockNodes = constructTree(blockIds); - const reentrantTestRootBlockId = computeBlockId(reentrantTestRootBlock); - await env.fuelChainState.commit(reentrantTestRootBlockId, 1); - ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); - - // generate proof for relaying reentrant message - const messageBlockLeafIndexKey = getLeafIndexKey(reentrantTestPrevBlockNodes, reentrantTestMessageBlockId); - const blockInHistoryProof = { - key: messageBlockLeafIndexKey, - proof: getProof(reentrantTestPrevBlockNodes, messageBlockLeafIndexKey), - }; - const messageLeafIndexKey = getLeafIndexKey(messageReentrantMessageNodes, messageReentrantId); - const messageInBlockProof = { - key: messageLeafIndexKey, - proof: getProof(messageReentrantMessageNodes, messageLeafIndexKey), - }; - - // re-enter via relayMessage - expect(await env.fuelMessagePortal.incomingMessageSuccessful(messageReentrantId)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - messageReentrant, - generateBlockHeaderLite(reentrantTestRootBlock), - reentrantTestMessageBlock, - blockInHistoryProof, - messageInBlockProof - ) - ).to.be.revertedWith('Message relay failed'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(messageReentrantId)).to.be.equal(false); - }); - }); - - describe('Verify pause and unpause', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(false); - - // Grant pauser role - await expect(env.fuelMessagePortal.grantRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(true); - }); - - it('Should not be able to pause as non-pauser', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - - // Attempt pause - await expect(env.fuelMessagePortal.connect(env.signers[1]).pause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` - ); - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - }); - - it('Should be able to pause as pauser', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - - // Pause - await expect(env.fuelMessagePortal.connect(env.signers[2]).pause()).to.not.be.reverted; - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as pauser (and not admin)', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelMessagePortal.connect(env.signers[2]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as non-admin', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelMessagePortal.connect(env.signers[1]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - }); - - it('Should not be able to relay messages when paused', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message2, 51); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - message2, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.be.revertedWith('Pausable: paused'); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - }); - - it('Should be able to unpause as admin', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - - // Unpause - await expect(env.fuelMessagePortal.unpause()).to.not.be.reverted; - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - }); - - it('Should be able to relay message when unpaused', async () => { - const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof(message2, 1); - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(false); - await expect( - env.fuelMessagePortal.relayMessage( - message2, - endOfCommitIntervalHeaderLite, - msgBlockHeader, - blockInRoot, - msgInBlock - ) - ).to.not.be.reverted; - expect(await env.fuelMessagePortal.incomingMessageSuccessful(msgID)).to.be.equal(true); - expect(await messageTester.data1()).to.be.equal(messageTestData2); - expect(await messageTester.data2()).to.be.equal(messageTestData1); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(true); - - // Grant pauser role - await expect(env.fuelMessagePortal.revokeRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(false); - }); + provider = env.fuelMessagePortal.provider; + }); + + it('Should not get a valid message sender outside of relaying', async () => { + await expect(env.fuelMessagePortal.messageSender()).to.be.revertedWith( + 'Current message sender not set' + ); + }); + + it('Should not be able to call messageable contract directly', async () => { + await expect( + messageTester.receiveMessage(messageTestData3, messageTestData3) + ).to.be.revertedWith('Caller is not the portal'); + }); + + it('Should not be able to relay message with bad root block', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message1, + 24 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + message1, + generateBlockHeaderLite( + createBlock('', BLOCKS_PER_COMMIT_INTERVAL * 20 - 1) + ), + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Unknown block'); + await expect( + env.fuelMessagePortal.relayMessage( + message1, + generateBlockHeaderLite(unflinalizedBlock), + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Unfinalized root block'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should not be able to relay message with bad proof in root block', async () => { + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const messageTesterBalance = await provider.getBalance( + messageTester.address + ); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message1, + 67 + ); + blockInRoot.key = blockInRoot.key + 1; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + message1, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Invalid block in history proof'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal(portalBalance); + expect(await provider.getBalance(messageTester.address)).to.be.equal( + messageTesterBalance + ); + }); + + it('Should be able to relay valid message', async () => { + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const messageTesterBalance = await provider.getBalance( + messageTester.address + ); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message1, + 22 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + message1, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect(await messageTester.data1()).to.be.equal(messageTestData1); + expect(await messageTester.data2()).to.be.equal(messageTestData2); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal(portalBalance); + expect(await provider.getBalance(messageTester.address)).to.be.equal( + messageTesterBalance + ); + }); + + it('Should not be able to relay already relayed message', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message1, + 68 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + await expect( + env.fuelMessagePortal.relayMessage( + message1, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Already relayed'); + }); + + it('Should not be able to relay message with low gas', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageWithAmount, + 11 + ); + const options = { + gasLimit: 140000, + }; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageWithAmount, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock, + options + ) + ).to.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should be able to relay message with amount', async () => { + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const messageTesterBalance = await provider.getBalance( + messageTester.address + ); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageWithAmount, + 33 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageWithAmount, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect(await messageTester.data1()).to.be.equal(messageTestData2); + expect(await messageTester.data2()).to.be.equal(messageTestData3); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal( + portalBalance.sub(messageWithAmount.amount.mul(baseAssetConversion)) + ); + expect(await provider.getBalance(messageTester.address)).to.be.equal( + messageTesterBalance.add( + messageWithAmount.amount.mul(baseAssetConversion) + ) + ); + }); + + it('Should not be able to relay message from untrusted sender', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageBadSender, + 47 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageBadSender, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should not be able to relay message to bad recipient', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageBadRecipient, + 69 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageBadRecipient, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should not be able to relay message with bad data', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageBadData, + 21 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageBadData, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should be able to relay message to EOA', async () => { + const accountBalance = await provider.getBalance(env.addresses[2]); + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageEOA, + 19 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageEOA, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect(await provider.getBalance(env.addresses[2])).to.be.equal( + accountBalance.add(messageEOA.amount.mul(baseAssetConversion)) + ); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal( + portalBalance.sub(messageEOA.amount.mul(baseAssetConversion)) + ); + }); + + it('Should be able to relay message to EOA with no amount', async () => { + const accountBalance = await provider.getBalance(env.addresses[3]); + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + messageEOANoAmount, + 25 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageEOANoAmount, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect(await provider.getBalance(env.addresses[3])).to.be.equal( + accountBalance + ); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal(portalBalance); + }); + + it('Should not be able to relay valid message with different amount', async () => { + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const messageTesterBalance = await provider.getBalance( + messageTester.address + ); + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message2, + 68 + ); + const diffBlock = { + sender: message2.sender, + recipient: message2.recipient, + nonce: message2.nonce, + amount: message2.amount.add(ethers.utils.parseEther('1.0')), + data: message2.data, + }; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + diffBlock, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Invalid message in block proof'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal(portalBalance); + expect(await provider.getBalance(messageTester.address)).to.be.equal( + messageTesterBalance + ); + }); + + it('Should not be able to relay non-existent message', async () => { + const [, msgBlockHeader, blockInRoot] = generateProof(message2, 1); + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + const messageTesterBalance = await provider.getBalance( + messageTester.address + ); + const msgInBlock = { + key: 0, + proof: [], + }; + await expect( + env.fuelMessagePortal.relayMessage( + message2, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Invalid message in block proof'); + expect( + await provider.getBalance(env.fuelMessagePortal.address) + ).to.be.equal(portalBalance); + expect(await provider.getBalance(messageTester.address)).to.be.equal( + messageTesterBalance + ); + }); + + it('Should not be able to relay reentrant messages', async () => { + // create a message that attempts to relay another message + const [, rTestMsgBlockHeader, rTestBlockInRoot, rTestMsgInBlock] = + generateProof(message1, 5); + const reentrantTestData = + env.fuelMessagePortal.interface.encodeFunctionData('relayMessage', [ + message1, + endOfCommitIntervalHeaderLite, + rTestMsgBlockHeader, + rTestBlockInRoot, + rTestMsgInBlock, + ]); + const messageReentrant = new Message( + trustedSenderAddress, + fuelMessagePortalContractAddress, + BN.from(0), + randomBytes32(), + reentrantTestData + ); + const messageReentrantId = computeMessageId(messageReentrant); + const messageReentrantMessages = [messageReentrantId]; + const messageReentrantMessageNodes = constructTree( + messageReentrantMessages + ); + + // create block that contains this message + const tai64Time = BN.from(Math.floor(new Date().getTime() / 1000)).add( + '4611686018427387914' + ); + const reentrantTestMessageBlock = createBlock( + '', + blockIds.length, + tai64Time.toHexString(), + '1', + calcRoot(messageReentrantMessages) + ); + const reentrantTestMessageBlockId = computeBlockId( + reentrantTestMessageBlock + ); + blockIds.push(reentrantTestMessageBlockId); + + // commit and finalize a block that contains the block with the message + const reentrantTestRootBlock = createBlock( + calcRoot(blockIds), + blockIds.length, + tai64Time.toHexString() + ); + const reentrantTestPrevBlockNodes = constructTree(blockIds); + const reentrantTestRootBlockId = computeBlockId(reentrantTestRootBlock); + await env.fuelChainState.commit(reentrantTestRootBlockId, 1); + ethers.provider.send('evm_increaseTime', [TIME_TO_FINALIZE]); + + // generate proof for relaying reentrant message + const messageBlockLeafIndexKey = getLeafIndexKey( + reentrantTestPrevBlockNodes, + reentrantTestMessageBlockId + ); + const blockInHistoryProof = { + key: messageBlockLeafIndexKey, + proof: getProof(reentrantTestPrevBlockNodes, messageBlockLeafIndexKey), + }; + const messageLeafIndexKey = getLeafIndexKey( + messageReentrantMessageNodes, + messageReentrantId + ); + const messageInBlockProof = { + key: messageLeafIndexKey, + proof: getProof(messageReentrantMessageNodes, messageLeafIndexKey), + }; + + // re-enter via relayMessage + expect( + await env.fuelMessagePortal.incomingMessageSuccessful( + messageReentrantId + ) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + messageReentrant, + generateBlockHeaderLite(reentrantTestRootBlock), + reentrantTestMessageBlock, + blockInHistoryProof, + messageInBlockProof + ) + ).to.be.revertedWith('Message relay failed'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful( + messageReentrantId + ) + ).to.be.equal(false); + }); + }); + + describe('Verify pause and unpause', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + + it('Should be able to grant pauser role', async () => { + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); + + // Grant pauser role + await expect( + env.fuelMessagePortal.grantRole(pauserRole, env.addresses[2]) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + }); + + it('Should not be able to pause as non-pauser', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + + // Attempt pause + await expect( + env.fuelMessagePortal.connect(env.signers[1]).pause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` + ); + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + }); + + it('Should be able to pause as pauser', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + + // Pause + await expect(env.fuelMessagePortal.connect(env.signers[2]).pause()).to.not + .be.reverted; + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as pauser (and not admin)', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelMessagePortal.connect(env.signers[2]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as non-admin', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelMessagePortal.connect(env.signers[1]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + }); + + it('Should not be able to relay messages when paused', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message2, + 51 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + message2, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.be.revertedWith('Pausable: paused'); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + }); + + it('Should be able to unpause as admin', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + + // Unpause + await expect(env.fuelMessagePortal.unpause()).to.not.be.reverted; + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + }); + + it('Should be able to relay message when unpaused', async () => { + const [msgID, msgBlockHeader, blockInRoot, msgInBlock] = generateProof( + message2, + 1 + ); + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(false); + await expect( + env.fuelMessagePortal.relayMessage( + message2, + endOfCommitIntervalHeaderLite, + msgBlockHeader, + blockInRoot, + msgInBlock + ) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.incomingMessageSuccessful(msgID) + ).to.be.equal(true); + expect(await messageTester.data1()).to.be.equal(messageTestData2); + expect(await messageTester.data2()).to.be.equal(messageTestData1); + }); + + it('Should be able to revoke pauser role', async () => { + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + + // Grant pauser role + await expect( + env.fuelMessagePortal.revokeRole(pauserRole, env.addresses[2]) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); }); + }); }); diff --git a/packages/portal-contracts/test/messagesOutgoing.ts b/packages/portal-contracts/test/messagesOutgoing.ts index ddd7fd63..9eb48c62 100644 --- a/packages/portal-contracts/test/messagesOutgoing.ts +++ b/packages/portal-contracts/test/messagesOutgoing.ts @@ -11,362 +11,500 @@ chai.use(solidity); const { expect } = chai; describe('Outgoing Messages', async () => { - let env: HarnessObject; - const nonceList: string[] = []; + let env: HarnessObject; + const nonceList: string[] = []; + + // Testing contracts + let messageTester: MessageTester; + + before(async () => { + env = await setupFuel(); + + // Deploy contracts for message testing + const messageTesterContractFactory = await ethers.getContractFactory( + 'MessageTester' + ); + messageTester = (await messageTesterContractFactory.deploy( + env.fuelMessagePortal.address + )) as MessageTester; + await messageTester.deployed(); + + // Send eth to contract + const tx = { + to: messageTester.address, + value: ethers.utils.parseEther('2'), + }; + const transaction = await env.signers[0].sendTransaction(tx); + await transaction.wait(); + + // Verify contract getters + expect(await env.fuelMessagePortal.fuelChainStateContract()).to.equal( + env.fuelChainState.address + ); + expect(await messageTester.fuelMessagePortal()).to.equal( + env.fuelMessagePortal.address + ); + }); + + describe('Verify access control', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + let signer0: string; + let signer1: string; + let signer2: string; + before(async () => { + signer0 = env.addresses[0]; + signer1 = env.addresses[1]; + signer2 = env.addresses[2]; + }); + + it('Should be able to grant admin role', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + + // Grant admin role + await expect(env.fuelMessagePortal.grantRole(defaultAdminRole, signer1)) + .to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + }); + + it('Should be able to renounce admin role', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke admin role + await expect( + env.fuelMessagePortal.renounceRole(defaultAdminRole, signer0) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + + it('Should not be able to grant admin role as non-admin', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + + // Attempt grant admin role + await expect( + env.fuelMessagePortal.grantRole(defaultAdminRole, signer0) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + }); + + it('Should be able to grant then revoke admin role', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(false); + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(true); + + // Grant admin role + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer0) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0) + ).to.equal(true); + + // Revoke previous admin + await expect(env.fuelMessagePortal.revokeRole(defaultAdminRole, signer1)) + .to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1) + ).to.equal(false); + }); + + it('Should be able to grant pauser role', async () => { + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + false + ); + + // Grant pauser role + await expect(env.fuelMessagePortal.grantRole(pauserRole, signer1)).to.not + .be.reverted; + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + true + ); + }); - // Testing contracts - let messageTester: MessageTester; + it('Should not be able to grant permission as pauser', async () => { + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal( + false + ); + + // Attempt grant admin role + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .grantRole(defaultAdminRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect( + await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2) + ).to.equal(false); + + // Attempt grant pauser role + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .grantRole(pauserRole, signer2) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal( + false + ); + }); + + it('Should be able to revoke pauser role', async () => { + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + true + ); + + // Grant pauser role + await expect(env.fuelMessagePortal.revokeRole(pauserRole, signer1)).to.not + .be.reverted; + expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal( + false + ); + }); + }); + describe('Send messages', async () => { + let provider: Provider; + let filterAddress: string; + let fuelBaseAssetDecimals: number; + let baseAssetConversion: number; before(async () => { - env = await setupFuel(); - - // Deploy contracts for message testing - const messageTesterContractFactory = await ethers.getContractFactory('MessageTester'); - messageTester = (await messageTesterContractFactory.deploy(env.fuelMessagePortal.address)) as MessageTester; - await messageTester.deployed(); - - // Send eth to contract - const tx = { - to: messageTester.address, - value: ethers.utils.parseEther('2'), - }; - const transaction = await env.signers[0].sendTransaction(tx); - await transaction.wait(); - - // Verify contract getters - expect(await env.fuelMessagePortal.fuelChainStateContract()).to.equal(env.fuelChainState.address); - expect(await messageTester.fuelMessagePortal()).to.equal(env.fuelMessagePortal.address); + provider = env.fuelMessagePortal.provider; + filterAddress = env.fuelMessagePortal.address; + fuelBaseAssetDecimals = + await env.fuelMessagePortal.fuelBaseAssetDecimals(); + baseAssetConversion = 10 ** (18 - fuelBaseAssetDecimals); + }); + + it('Should be able to send message with data', async () => { + const recipient = randomBytes32(); + const data = randomBytes(16); + await expect(messageTester.attemptSendMessage(recipient, data)).to.not.be + .reverted; + + // Check logs for message sent + const logs = await provider.getLogs({ address: filterAddress }); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal( + messageTester.address + .split('0x') + .join('0x000000000000000000000000') + .toLowerCase() + ); + expect(messageSentEvent.args.recipient).to.equal(recipient); + expect(messageSentEvent.args.data).to.equal(data); + expect(messageSentEvent.args.amount).to.equal(0); + + // Check that nonce is unique + expect(nonceList).to.not.include(messageSentEvent.args.nonce); + nonceList.push(messageSentEvent.args.nonce); + }); + + it('Should be able to send message without data', async () => { + const recipient = randomBytes32(); + await expect(messageTester.attemptSendMessage(recipient, [])).to.not.be + .reverted; + + // Check logs for message sent + const logs = await provider.getLogs({ address: filterAddress }); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal( + messageTester.address + .split('0x') + .join('0x000000000000000000000000') + .toLowerCase() + ); + expect(messageSentEvent.args.recipient).to.equal(recipient); + expect(messageSentEvent.args.data).to.equal('0x'); + expect(messageSentEvent.args.amount).to.equal(0); + + // Check that nonce is unique + expect(nonceList).to.not.include(messageSentEvent.args.nonce); + nonceList.push(messageSentEvent.args.nonce); + }); + + it('Should be able to send message with amount and data', async () => { + const recipient = randomBytes32(); + const data = randomBytes(8); + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + await expect( + messageTester.attemptSendMessageWithAmount( + recipient, + ethers.utils.parseEther('0.1'), + data + ) + ).to.not.be.reverted; + + // Check logs for message sent + const logs = await provider.getLogs({ address: filterAddress }); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal( + messageTester.address + .split('0x') + .join('0x000000000000000000000000') + .toLowerCase() + ); + expect(messageSentEvent.args.recipient).to.equal(recipient); + expect(messageSentEvent.args.data).to.equal(data); + expect(messageSentEvent.args.amount).to.equal( + ethers.utils.parseEther('0.1').div(baseAssetConversion) + ); + + // Check that nonce is unique + expect(nonceList).to.not.include(messageSentEvent.args.nonce); + nonceList.push(messageSentEvent.args.nonce); + + // Check that portal balance increased + expect(await provider.getBalance(env.fuelMessagePortal.address)).to.equal( + portalBalance.add(ethers.utils.parseEther('0.1')) + ); }); - describe('Verify access control', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - let signer0: string; - let signer1: string; - let signer2: string; - before(async () => { - signer0 = env.addresses[0]; - signer1 = env.addresses[1]; - signer2 = env.addresses[2]; - }); - - it('Should be able to grant admin role', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(false); - - // Grant admin role - await expect(env.fuelMessagePortal.grantRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(true); - }); - - it('Should be able to renounce admin role', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke admin role - await expect(env.fuelMessagePortal.renounceRole(defaultAdminRole, signer0)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should not be able to grant admin role as non-admin', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - - // Attempt grant admin role - await expect(env.fuelMessagePortal.grantRole(defaultAdminRole, signer0)).to.be.revertedWith( - `AccessControl: account ${env.addresses[0].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - }); - - it('Should be able to grant then revoke admin role', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(false); - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(true); - - // Grant admin role - await expect(env.fuelMessagePortal.connect(env.signers[1]).grantRole(defaultAdminRole, signer0)).to.not.be - .reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer0)).to.equal(true); - - // Revoke previous admin - await expect(env.fuelMessagePortal.revokeRole(defaultAdminRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer1)).to.equal(false); - }); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(false); - - // Grant pauser role - await expect(env.fuelMessagePortal.grantRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(true); - }); - - it('Should not be able to grant permission as pauser', async () => { - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2)).to.equal(false); - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal(false); - - // Attempt grant admin role - await expect( - env.fuelMessagePortal.connect(env.signers[1]).grantRole(defaultAdminRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.hasRole(defaultAdminRole, signer2)).to.equal(false); - - // Attempt grant pauser role - await expect( - env.fuelMessagePortal.connect(env.signers[1]).grantRole(pauserRole, signer2) - ).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer2)).to.equal(false); - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(true); - - // Grant pauser role - await expect(env.fuelMessagePortal.revokeRole(pauserRole, signer1)).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, signer1)).to.equal(false); - }); + it('Should be able to send message with amount and without data', async () => { + const recipient = randomBytes32(); + const portalBalance = await provider.getBalance( + env.fuelMessagePortal.address + ); + await expect( + messageTester.attemptSendMessageWithAmount( + recipient, + ethers.utils.parseEther('0.5'), + [] + ) + ).to.not.be.reverted; + + // Check logs for message sent + const logs = await provider.getLogs({ address: filterAddress }); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal( + messageTester.address + .split('0x') + .join('0x000000000000000000000000') + .toLowerCase() + ); + expect(messageSentEvent.args.recipient).to.equal(recipient); + expect(messageSentEvent.args.data).to.equal('0x'); + expect(messageSentEvent.args.amount).to.equal( + ethers.utils.parseEther('0.5').div(baseAssetConversion) + ); + + // Check that nonce is unique + expect(nonceList).to.not.include(messageSentEvent.args.nonce); + nonceList.push(messageSentEvent.args.nonce); + + // Check that portal balance increased + expect(await provider.getBalance(env.fuelMessagePortal.address)).to.equal( + portalBalance.add(ethers.utils.parseEther('0.5')) + ); + }); + + it('Should not be able to send message with amount too small', async () => { + const recipient = randomBytes32(); + await expect( + env.fuelMessagePortal.sendMessage(recipient, [], { + value: 1, + }) + ).to.be.revertedWith('amount-precision-incompatability'); + }); + + it('Should not be able to send message with amount too big', async () => { + const recipient = randomBytes32(); + await ethers.provider.send('hardhat_setBalance', [ + env.addresses[0], + '0xf00000000000000000000000', + ]); + await expect( + env.fuelMessagePortal.sendMessage(recipient, [], { + value: BN.from('0x3b9aca000000000000000000'), + }) + ).to.be.revertedWith('amount-precision-incompatability'); + }); + + it('Should not be able to send message with too much data', async () => { + const recipient = randomBytes32(); + const data = new Uint8Array(65536 + 1); + await expect( + env.fuelMessagePortal.sendMessage(recipient, data) + ).to.be.revertedWith('message-data-too-large'); + }); + + it('Should be able to send message with only ETH', async () => { + const recipient = randomBytes32(); + await expect( + env.fuelMessagePortal.depositETH(recipient, { + value: ethers.utils.parseEther('1.234'), + }) + ).to.not.be.reverted; + + // Check logs for message sent + const logs = await provider.getLogs({ address: filterAddress }); + const messageSentEvent = env.fuelMessagePortal.interface.parseLog( + logs[logs.length - 1] + ); + expect(messageSentEvent.name).to.equal('MessageSent'); + expect(messageSentEvent.args.sender).to.equal( + env.addresses[0] + .split('0x') + .join('0x000000000000000000000000') + .toLowerCase() + ); + expect(messageSentEvent.args.recipient).to.equal(recipient); + expect(messageSentEvent.args.data).to.equal('0x'); + expect(messageSentEvent.args.amount).to.equal( + ethers.utils.parseEther('1.234').div(baseAssetConversion) + ); + + // Check that nonce is unique + expect(nonceList).to.not.include(messageSentEvent.args.nonce); + nonceList.push(messageSentEvent.args.nonce); + }); + }); + + describe('Verify pause and unpause', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + const pauserRole = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes('PAUSER_ROLE') + ); + const recipient = randomBytes32(); + const data = randomBytes(8); + + it('Should be able to grant pauser role', async () => { + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); + + // Grant pauser role + await expect( + env.fuelMessagePortal.grantRole(pauserRole, env.addresses[2]) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + }); + + it('Should not be able to pause as non-pauser', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + + // Attempt pause + await expect( + env.fuelMessagePortal.connect(env.signers[1]).pause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` + ); + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + }); + + it('Should be able to pause as pauser', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + + // Pause + await expect(env.fuelMessagePortal.connect(env.signers[2]).pause()).to.not + .be.reverted; + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as pauser (and not admin)', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelMessagePortal.connect(env.signers[2]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + }); + + it('Should not be able to unpause as non-admin', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + + // Attempt unpause + await expect( + env.fuelMessagePortal.connect(env.signers[1]).unpause() + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + }); + + it('Should not be able to send messages when paused', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + await expect( + env.fuelMessagePortal.sendMessage(recipient, data) + ).to.be.revertedWith('Pausable: paused'); + await expect( + env.fuelMessagePortal.depositETH(recipient, { value: 1 }) + ).to.be.revertedWith('Pausable: paused'); + }); + + it('Should be able to unpause as admin', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(true); + + // Unpause + await expect(env.fuelMessagePortal.unpause()).to.not.be.reverted; + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); }); - describe('Send messages', async () => { - let provider: Provider; - let filterAddress: string; - let fuelBaseAssetDecimals: number; - let baseAssetConversion: number; - before(async () => { - provider = env.fuelMessagePortal.provider; - filterAddress = env.fuelMessagePortal.address; - fuelBaseAssetDecimals = await env.fuelMessagePortal.fuelBaseAssetDecimals(); - baseAssetConversion = 10 ** (18 - fuelBaseAssetDecimals); - }); - - it('Should be able to send message with data', async () => { - const recipient = randomBytes32(); - const data = randomBytes(16); - await expect(messageTester.attemptSendMessage(recipient, data)).to.not.be.reverted; - - // Check logs for message sent - const logs = await provider.getLogs({ address: filterAddress }); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal( - messageTester.address.split('0x').join('0x000000000000000000000000').toLowerCase() - ); - expect(messageSentEvent.args.recipient).to.equal(recipient); - expect(messageSentEvent.args.data).to.equal(data); - expect(messageSentEvent.args.amount).to.equal(0); - - // Check that nonce is unique - expect(nonceList).to.not.include(messageSentEvent.args.nonce); - nonceList.push(messageSentEvent.args.nonce); - }); - - it('Should be able to send message without data', async () => { - const recipient = randomBytes32(); - await expect(messageTester.attemptSendMessage(recipient, [])).to.not.be.reverted; - - // Check logs for message sent - const logs = await provider.getLogs({ address: filterAddress }); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal( - messageTester.address.split('0x').join('0x000000000000000000000000').toLowerCase() - ); - expect(messageSentEvent.args.recipient).to.equal(recipient); - expect(messageSentEvent.args.data).to.equal('0x'); - expect(messageSentEvent.args.amount).to.equal(0); - - // Check that nonce is unique - expect(nonceList).to.not.include(messageSentEvent.args.nonce); - nonceList.push(messageSentEvent.args.nonce); - }); - - it('Should be able to send message with amount and data', async () => { - const recipient = randomBytes32(); - const data = randomBytes(8); - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - await expect(messageTester.attemptSendMessageWithAmount(recipient, ethers.utils.parseEther('0.1'), data)).to - .not.be.reverted; - - // Check logs for message sent - const logs = await provider.getLogs({ address: filterAddress }); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal( - messageTester.address.split('0x').join('0x000000000000000000000000').toLowerCase() - ); - expect(messageSentEvent.args.recipient).to.equal(recipient); - expect(messageSentEvent.args.data).to.equal(data); - expect(messageSentEvent.args.amount).to.equal(ethers.utils.parseEther('0.1').div(baseAssetConversion)); - - // Check that nonce is unique - expect(nonceList).to.not.include(messageSentEvent.args.nonce); - nonceList.push(messageSentEvent.args.nonce); - - // Check that portal balance increased - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.equal( - portalBalance.add(ethers.utils.parseEther('0.1')) - ); - }); - - it('Should be able to send message with amount and without data', async () => { - const recipient = randomBytes32(); - const portalBalance = await provider.getBalance(env.fuelMessagePortal.address); - await expect(messageTester.attemptSendMessageWithAmount(recipient, ethers.utils.parseEther('0.5'), [])).to - .not.be.reverted; - - // Check logs for message sent - const logs = await provider.getLogs({ address: filterAddress }); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal( - messageTester.address.split('0x').join('0x000000000000000000000000').toLowerCase() - ); - expect(messageSentEvent.args.recipient).to.equal(recipient); - expect(messageSentEvent.args.data).to.equal('0x'); - expect(messageSentEvent.args.amount).to.equal(ethers.utils.parseEther('0.5').div(baseAssetConversion)); - - // Check that nonce is unique - expect(nonceList).to.not.include(messageSentEvent.args.nonce); - nonceList.push(messageSentEvent.args.nonce); - - // Check that portal balance increased - expect(await provider.getBalance(env.fuelMessagePortal.address)).to.equal( - portalBalance.add(ethers.utils.parseEther('0.5')) - ); - }); - - it('Should not be able to send message with amount too small', async () => { - const recipient = randomBytes32(); - await expect( - env.fuelMessagePortal.sendMessage(recipient, [], { - value: 1, - }) - ).to.be.revertedWith('amount-precision-incompatability'); - }); - - it('Should not be able to send message with amount too big', async () => { - const recipient = randomBytes32(); - await ethers.provider.send('hardhat_setBalance', [env.addresses[0], '0xf00000000000000000000000']); - await expect( - env.fuelMessagePortal.sendMessage(recipient, [], { - value: BN.from('0x3b9aca000000000000000000'), - }) - ).to.be.revertedWith('amount-precision-incompatability'); - }); - - it('Should not be able to send message with too much data', async () => { - const recipient = randomBytes32(); - const data = new Uint8Array(65536 + 1); - await expect(env.fuelMessagePortal.sendMessage(recipient, data)).to.be.revertedWith( - 'message-data-too-large' - ); - }); - - it('Should be able to send message with only ETH', async () => { - const recipient = randomBytes32(); - await expect( - env.fuelMessagePortal.depositETH(recipient, { - value: ethers.utils.parseEther('1.234'), - }) - ).to.not.be.reverted; - - // Check logs for message sent - const logs = await provider.getLogs({ address: filterAddress }); - const messageSentEvent = env.fuelMessagePortal.interface.parseLog(logs[logs.length - 1]); - expect(messageSentEvent.name).to.equal('MessageSent'); - expect(messageSentEvent.args.sender).to.equal( - env.addresses[0].split('0x').join('0x000000000000000000000000').toLowerCase() - ); - expect(messageSentEvent.args.recipient).to.equal(recipient); - expect(messageSentEvent.args.data).to.equal('0x'); - expect(messageSentEvent.args.amount).to.equal(ethers.utils.parseEther('1.234').div(baseAssetConversion)); - - // Check that nonce is unique - expect(nonceList).to.not.include(messageSentEvent.args.nonce); - nonceList.push(messageSentEvent.args.nonce); - }); + it('Should be able to send messages when unpaused', async () => { + expect(await env.fuelMessagePortal.paused()).to.be.equal(false); + await expect(env.fuelMessagePortal.sendMessage(recipient, data)).to.not.be + .reverted; }); - describe('Verify pause and unpause', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - const pauserRole = ethers.utils.keccak256(ethers.utils.toUtf8Bytes('PAUSER_ROLE')); - const recipient = randomBytes32(); - const data = randomBytes(8); - - it('Should be able to grant pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(false); - - // Grant pauser role - await expect(env.fuelMessagePortal.grantRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(true); - }); - - it('Should not be able to pause as non-pauser', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - - // Attempt pause - await expect(env.fuelMessagePortal.connect(env.signers[1]).pause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${pauserRole}` - ); - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - }); - - it('Should be able to pause as pauser', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - - // Pause - await expect(env.fuelMessagePortal.connect(env.signers[2]).pause()).to.not.be.reverted; - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as pauser (and not admin)', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelMessagePortal.connect(env.signers[2]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[2].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - }); - - it('Should not be able to unpause as non-admin', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - - // Attempt unpause - await expect(env.fuelMessagePortal.connect(env.signers[1]).unpause()).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - }); - - it('Should not be able to send messages when paused', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - await expect(env.fuelMessagePortal.sendMessage(recipient, data)).to.be.revertedWith('Pausable: paused'); - await expect(env.fuelMessagePortal.depositETH(recipient, { value: 1 })).to.be.revertedWith( - 'Pausable: paused' - ); - }); - - it('Should be able to unpause as admin', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(true); - - // Unpause - await expect(env.fuelMessagePortal.unpause()).to.not.be.reverted; - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - }); - - it('Should be able to send messages when unpaused', async () => { - expect(await env.fuelMessagePortal.paused()).to.be.equal(false); - await expect(env.fuelMessagePortal.sendMessage(recipient, data)).to.not.be.reverted; - }); - - it('Should be able to revoke pauser role', async () => { - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(true); - - // Grant pauser role - await expect(env.fuelMessagePortal.revokeRole(pauserRole, env.addresses[2])).to.not.be.reverted; - expect(await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2])).to.equal(false); - }); + it('Should be able to revoke pauser role', async () => { + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(true); + + // Grant pauser role + await expect( + env.fuelMessagePortal.revokeRole(pauserRole, env.addresses[2]) + ).to.not.be.reverted; + expect( + await env.fuelMessagePortal.hasRole(pauserRole, env.addresses[2]) + ).to.equal(false); }); + }); }); diff --git a/packages/portal-contracts/test/upgrade.ts b/packages/portal-contracts/test/upgrade.ts index a5a813a0..c746ae0a 100644 --- a/packages/portal-contracts/test/upgrade.ts +++ b/packages/portal-contracts/test/upgrade.ts @@ -8,67 +8,89 @@ chai.use(solidity); const { expect } = chai; describe('Contract Upgradability', async () => { - let env: HarnessObject; - let upgradeableTester: UpgradeableTester; + let env: HarnessObject; + let upgradeableTester: UpgradeableTester; - before(async () => { - env = await setupFuel(); + before(async () => { + env = await setupFuel(); - // Deploy contracts for abstract upgradeable contract testing. - const upgradeableTesterContractFactory = await ethers.getContractFactory('UpgradeableTester'); - upgradeableTester = (await upgradeableTesterContractFactory.deploy()) as UpgradeableTester; - await upgradeableTester.deployed(); - }); + // Deploy contracts for abstract upgradeable contract testing. + const upgradeableTesterContractFactory = await ethers.getContractFactory( + 'UpgradeableTester' + ); + upgradeableTester = + (await upgradeableTesterContractFactory.deploy()) as UpgradeableTester; + await upgradeableTester.deployed(); + }); - describe('Upgrade contracts', async () => { - const defaultAdminRole = '0x0000000000000000000000000000000000000000000000000000000000000000'; - it('Should be able to upgrade contracts', async () => { - const contracts = { - FuelChainState: env.fuelChainState.address, - FuelMessagePortal: env.fuelMessagePortal.address, - FuelERC20Gateway: env.fuelERC20Gateway.address, - FuelChainState_impl: '', - FuelMessagePortal_impl: '', - FuelERC20Gateway_impl: '', - }; - const upgradedContracts = await upgradeFuel(contracts); + describe('Upgrade contracts', async () => { + const defaultAdminRole = + '0x0000000000000000000000000000000000000000000000000000000000000000'; + it('Should be able to upgrade contracts', async () => { + const contracts = { + FuelChainState: env.fuelChainState.address, + FuelMessagePortal: env.fuelMessagePortal.address, + FuelERC20Gateway: env.fuelERC20Gateway.address, + FuelChainState_impl: '', + FuelMessagePortal_impl: '', + FuelERC20Gateway_impl: '', + }; + const upgradedContracts = await upgradeFuel(contracts); - expect(upgradedContracts.FuelChainState).to.equal(env.fuelChainState.address); - expect(upgradedContracts.FuelMessagePortal).to.equal(env.fuelMessagePortal.address); - expect(upgradedContracts.FuelERC20Gateway).to.equal(env.fuelERC20Gateway.address); - }); + expect(upgradedContracts.FuelChainState).to.equal( + env.fuelChainState.address + ); + expect(upgradedContracts.FuelMessagePortal).to.equal( + env.fuelMessagePortal.address + ); + expect(upgradedContracts.FuelERC20Gateway).to.equal( + env.fuelERC20Gateway.address + ); + }); - it('Should not be able to call initializers', async () => { - await expect(env.fuelChainState.initialize()).to.be.revertedWith( - 'Initializable: contract is already initialized' - ); - await expect(env.fuelMessagePortal.initialize(env.fuelChainState.address)).to.be.revertedWith( - 'Initializable: contract is already initialized' - ); - await expect(env.fuelERC20Gateway.initialize(env.fuelMessagePortal.address)).to.be.revertedWith( - 'Initializable: contract is already initialized' - ); - }); + it('Should not be able to call initializers', async () => { + await expect(env.fuelChainState.initialize()).to.be.revertedWith( + 'Initializable: contract is already initialized' + ); + await expect( + env.fuelMessagePortal.initialize(env.fuelChainState.address) + ).to.be.revertedWith('Initializable: contract is already initialized'); + await expect( + env.fuelERC20Gateway.initialize(env.fuelMessagePortal.address) + ).to.be.revertedWith('Initializable: contract is already initialized'); + }); - it('Should not be able to call init functions for upgradeable abstract contracts', async () => { - await expect( - upgradeableTester.testFuelMessagesEnabledInit(env.fuelMessagePortal.address) - ).to.be.revertedWith('Initializable: contract is not initializing'); - await expect( - upgradeableTester.testFuelMessagesEnabledInitUnchained(env.fuelMessagePortal.address) - ).to.be.revertedWith('Initializable: contract is not initializing'); - }); + it('Should not be able to call init functions for upgradeable abstract contracts', async () => { + await expect( + upgradeableTester.testFuelMessagesEnabledInit( + env.fuelMessagePortal.address + ) + ).to.be.revertedWith('Initializable: contract is not initializing'); + await expect( + upgradeableTester.testFuelMessagesEnabledInitUnchained( + env.fuelMessagePortal.address + ) + ).to.be.revertedWith('Initializable: contract is not initializing'); + }); - it('Should not be able to upgrade contracts as non-admin', async () => { - await expect(env.fuelChainState.connect(env.signers[1]).upgradeTo(env.addresses[1])).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - await expect(env.fuelMessagePortal.connect(env.signers[1]).upgradeTo(env.addresses[1])).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - await expect(env.fuelERC20Gateway.connect(env.signers[1]).upgradeTo(env.addresses[1])).to.be.revertedWith( - `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` - ); - }); + it('Should not be able to upgrade contracts as non-admin', async () => { + await expect( + env.fuelChainState.connect(env.signers[1]).upgradeTo(env.addresses[1]) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + await expect( + env.fuelMessagePortal + .connect(env.signers[1]) + .upgradeTo(env.addresses[1]) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); + await expect( + env.fuelERC20Gateway.connect(env.signers[1]).upgradeTo(env.addresses[1]) + ).to.be.revertedWith( + `AccessControl: account ${env.addresses[1].toLowerCase()} is missing role ${defaultAdminRole}` + ); }); + }); }); diff --git a/packages/portal-contracts/tsconfig.json b/packages/portal-contracts/tsconfig.json index 493ed6a6..25ef1a06 100644 --- a/packages/portal-contracts/tsconfig.json +++ b/packages/portal-contracts/tsconfig.json @@ -1,13 +1,9 @@ { - "compilerOptions": { - "target": "es5", - "module": "commonjs", - "strict": true, - "esModuleInterop": true, - "outDir": "dist", - "resolveJsonModule": true - }, - "include": ["./scripts", "./protocol", "./test", "./typechain"], - "exclude": ["node_modules"], - "files": ["./hardhat.config.ts"] + "extends": "../../tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "dist" + }, + "include": ["./scripts", "./protocol", "./test", "./typechain"], + "files": ["./hardhat.config.ts"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b6f30564..b5380b25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,18 @@ importers: .: dependencies: + '@fuels/prettier-config': + specifier: ^0.0.2 + version: 0.0.2(prettier@2.7.1) + '@fuels/ts-config': + specifier: ^0.0.2 + version: 0.0.2(typescript@5.1.6) + prettier: + specifier: ^2.7.1 + version: 2.7.1 + prettier-plugin-solidity: + specifier: ^v1.0.0-rc.1 + version: 1.0.0-rc.1(prettier@2.7.1) turbo: specifier: ^1.10.7 version: 1.10.7 @@ -58,7 +70,7 @@ importers: version: 16.0.3 ethereum-waffle: specifier: ^4.0.2 - version: 4.0.2(typescript@4.9.3) + version: 4.0.2(typescript@5.1.6) ethers: specifier: ^5.7.2 version: 5.7.2 @@ -68,12 +80,12 @@ importers: mocha: specifier: ^10.0.0 version: 10.0.0 - prettier: - specifier: ^2.7.1 - version: 2.7.1 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.16.19)(typescript@4.9.3) + version: 10.9.1(@types/node@18.16.19)(typescript@5.1.6) + typescript: + specifier: ^5.1.6 + version: 5.1.6 packages/message-predicates: devDependencies: @@ -103,7 +115,7 @@ importers: version: 1.21.0(@nomiclabs/hardhat-ethers@2.2.1)(@nomiclabs/hardhat-etherscan@3.1.3)(ethers@5.7.2)(hardhat@2.12.4) '@typechain/ethers-v5': specifier: ^6.0.5 - version: 6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@4.9.3) + version: 6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@5.1.6) '@types/express': specifier: ^4.17.14 version: 4.17.14 @@ -112,7 +124,7 @@ importers: version: 4.18.2 hardhat: specifier: ^2.12.2 - version: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + version: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) hardhat-typechain: specifier: ^0.3.5 version: 0.3.5(hardhat@2.12.4)(ts-generator@0.1.1)(typechain@4.0.3) @@ -149,10 +161,10 @@ importers: version: 18.11.9 '@typescript-eslint/eslint-plugin': specifier: ^5.43.0 - version: 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@4.9.3) + version: 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@5.1.6) '@typescript-eslint/parser': specifier: ^5.43.0 - version: 5.43.0(eslint@8.27.0)(typescript@4.9.3) + version: 5.43.0(eslint@8.27.0)(typescript@5.1.6) chai: specifier: ^4.3.7 version: 4.3.7 @@ -185,7 +197,7 @@ importers: version: 4.6.0(eslint@8.27.0) ethereum-waffle: specifier: ^3.4.4 - version: 3.4.4(typescript@4.9.3) + version: 3.4.4(typescript@5.1.6) ethers: specifier: ^5.7.2 version: 5.7.2 @@ -218,13 +230,13 @@ importers: version: 0.1.1 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.11.9)(typescript@4.9.3) + version: 10.9.1(@types/node@18.11.9)(typescript@5.1.6) typechain: specifier: ^4.0.3 - version: 4.0.3(typescript@4.9.3) + version: 4.0.3(typescript@5.1.6) typescript: - specifier: ^4.9.3 - version: 4.9.3 + specifier: ^5.1.6 + version: 5.1.6 packages: @@ -334,7 +346,7 @@ packages: - utf-8-validate dev: true - /@ethereum-waffle/compiler@3.4.4(typescript@4.9.3): + /@ethereum-waffle/compiler@3.4.4(typescript@5.1.6): resolution: {integrity: sha512-RUK3axJ8IkD5xpWjWoJgyHclOeEzDLQFga6gKpeGxiS/zBu+HB0W2FvsrrLalTFIaPw/CGYACRBSIxqiCqwqTQ==} engines: {node: '>=10.0'} dependencies: @@ -348,7 +360,7 @@ packages: node-fetch: 2.6.6 solc: 0.6.12 ts-generator: 0.1.1 - typechain: 3.0.0(typescript@4.9.3) + typechain: 3.0.0(typescript@5.1.6) transitivePeerDependencies: - bufferutil - supports-color @@ -356,7 +368,7 @@ packages: - utf-8-validate dev: true - /@ethereum-waffle/compiler@4.0.1(ethers@5.6.2)(solc@0.6.12)(typechain@8.2.0)(typescript@4.9.3): + /@ethereum-waffle/compiler@4.0.1(ethers@5.6.2)(solc@0.6.12)(typechain@8.2.0)(typescript@5.1.6): resolution: {integrity: sha512-Ux2ytQ8e2r5FKaareU4VGExh9lTFswHXh1ylBNyUtdpv3nqui0rwnrMyRczC7F70UYfhzhWznnRLxLKfRAVx/g==} engines: {node: '>=10.0'} peerDependencies: @@ -369,14 +381,14 @@ packages: '@ethersproject/providers': 5.7.2 '@resolver-engine/imports': 0.3.3 '@resolver-engine/imports-fs': 0.3.3 - '@typechain/ethers-v5': 10.2.1(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.6.2)(typechain@8.2.0)(typescript@4.9.3) + '@typechain/ethers-v5': 10.2.1(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.6.2)(typechain@8.2.0)(typescript@5.1.6) '@types/mkdirp': 0.5.2 '@types/node-fetch': 2.6.4 ethers: 5.6.2 mkdirp: 0.5.6 node-fetch: 2.6.12 solc: 0.6.12 - typechain: 8.2.0(typescript@4.9.3) + typechain: 8.2.0(typescript@5.1.6) transitivePeerDependencies: - bufferutil - encoding @@ -1445,6 +1457,22 @@ packages: /@fuel-ts/wordlists@0.0.0-next-20230707184416: resolution: {integrity: sha512-UTWhEbUhq6YShFK8PLWL8i5Cx0197uR7UH1zfHwdZTW4PeaJWZSF5ndBjs/qoZZ30hLwVaoutw20+VzRUTyfIw==} + /@fuels/prettier-config@0.0.2(prettier@2.7.1): + resolution: {integrity: sha512-C1s11x3H2IY6TJvrV1zbkK2cN/5MXib3L/GcAm+CXQmurAGON8ek9IbKpne54otyX1tethTTVaVaPef6gQwQFA==} + peerDependencies: + prettier: ^3.0.0 + dependencies: + prettier: 2.7.1 + dev: false + + /@fuels/ts-config@0.0.2(typescript@5.1.6): + resolution: {integrity: sha512-6WPfvfooGpBdvnqrMeHOrhcLSoXLq/scwFFVvi41AnlTEpNt1yrtVtGEyjR+QNcQTSNcwIWCo9wLXSX2TjT2og==} + peerDependencies: + typescript: 5.1.6 + dependencies: + typescript: 5.1.6 + dev: false + /@ganache/ethereum-address@0.1.4: resolution: {integrity: sha512-sTkU0M9z2nZUzDeHRzzGlW724xhMLXo2LeX1hixbnjHWY1Zg1hkqORywVfl+g5uOO8ht8T0v+34IxNxAhmWlbw==} dependencies: @@ -1823,7 +1851,7 @@ packages: hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) /@nomiclabs/hardhat-etherscan@3.1.3(hardhat@2.12.4): resolution: {integrity: sha512-UeNO97j0lwOHqX7mrH6SfQQBdXq1Ng6eFr7uJKuQOrq2UVTWGD70lE5QO4fAFVPz9ao+xlNpMyIqSR7+OaDR+Q==} @@ -1836,7 +1864,7 @@ packages: chalk: 2.4.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 7.0.1 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) lodash: 4.17.21 semver: 6.3.0 table: 6.8.1 @@ -1856,9 +1884,9 @@ packages: '@nomiclabs/hardhat-ethers': 2.2.1(ethers@5.7.2)(hardhat@2.12.4) '@types/sinon-chai': 3.2.9 '@types/web3': 1.0.19 - ethereum-waffle: 3.4.4(typescript@4.9.3) + ethereum-waffle: 3.4.4(typescript@5.1.6) ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) dev: true /@openzeppelin/contracts-upgradeable@4.8.0: @@ -1888,7 +1916,7 @@ packages: chalk: 4.1.2 debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) proper-lockfile: 4.1.2 transitivePeerDependencies: - supports-color @@ -2050,7 +2078,6 @@ packages: resolution: {integrity: sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==} dependencies: antlr4ts: 0.5.0-alpha.4 - dev: true /@szmarczak/http-timer@1.1.2: resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} @@ -2088,7 +2115,7 @@ packages: /@tsconfig/node16@1.0.4: resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - /@typechain/ethers-v5@10.2.1(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.6.2)(typechain@8.2.0)(typescript@4.9.3): + /@typechain/ethers-v5@10.2.1(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.6.2)(typechain@8.2.0)(typescript@5.1.6): resolution: {integrity: sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A==} peerDependencies: '@ethersproject/abi': ^5.0.0 @@ -2101,9 +2128,9 @@ packages: '@ethersproject/providers': 5.7.2 ethers: 5.6.2 lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@4.9.3) - typechain: 8.2.0(typescript@4.9.3) - typescript: 4.9.3 + ts-essentials: 7.0.3(typescript@5.1.6) + typechain: 8.2.0(typescript@5.1.6) + typescript: 5.1.6 dev: true /@typechain/ethers-v5@2.0.0(ethers@5.7.2)(typechain@3.0.0): @@ -2113,10 +2140,10 @@ packages: typechain: ^3.0.0 dependencies: ethers: 5.7.2 - typechain: 3.0.0(typescript@4.9.3) + typechain: 3.0.0(typescript@5.1.6) dev: true - /@typechain/ethers-v5@6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@4.9.3): + /@typechain/ethers-v5@6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@5.1.6): resolution: {integrity: sha512-KJh+EWuxmX1a17fQWS1ba8DCYcqK7UpdbqMZZwyfiv9FQfn8ZQJX17anbkCMOSU8TV3EvRuJ/vFEKGzKnpkO8g==} peerDependencies: ethers: ^5.0.0 @@ -2124,8 +2151,8 @@ packages: typescript: '>=4.0.0' dependencies: ethers: 5.7.2 - typechain: 4.0.3(typescript@4.9.3) - typescript: 4.9.3 + typechain: 4.0.3(typescript@5.1.6) + typescript: 5.1.6 dev: false /@types/abstract-leveldown@7.2.1: @@ -2382,7 +2409,7 @@ packages: '@types/underscore': 1.11.5 dev: true - /@typescript-eslint/eslint-plugin@5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@4.9.3): + /@typescript-eslint/eslint-plugin@5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@5.1.6): resolution: {integrity: sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2393,23 +2420,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) '@typescript-eslint/scope-manager': 5.43.0 - '@typescript-eslint/type-utils': 5.43.0(eslint@8.27.0)(typescript@4.9.3) - '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/type-utils': 5.43.0(eslint@8.27.0)(typescript@5.1.6) + '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@5.1.6) debug: 4.3.4(supports-color@8.1.1) eslint: 8.27.0 ignore: 5.2.4 natural-compare-lite: 1.4.0 regexpp: 3.2.0 semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.3) - typescript: 4.9.3 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.43.0(eslint@8.27.0)(typescript@4.9.3): + /@typescript-eslint/parser@5.43.0(eslint@8.27.0)(typescript@5.1.6): resolution: {integrity: sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2421,10 +2448,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.43.0 '@typescript-eslint/types': 5.43.0 - '@typescript-eslint/typescript-estree': 5.43.0(typescript@4.9.3) + '@typescript-eslint/typescript-estree': 5.43.0(typescript@5.1.6) debug: 4.3.4(supports-color@8.1.1) eslint: 8.27.0 - typescript: 4.9.3 + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true @@ -2437,7 +2464,7 @@ packages: '@typescript-eslint/visitor-keys': 5.43.0 dev: true - /@typescript-eslint/type-utils@5.43.0(eslint@8.27.0)(typescript@4.9.3): + /@typescript-eslint/type-utils@5.43.0(eslint@8.27.0)(typescript@5.1.6): resolution: {integrity: sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2447,12 +2474,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.43.0(typescript@4.9.3) - '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/typescript-estree': 5.43.0(typescript@5.1.6) + '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@5.1.6) debug: 4.3.4(supports-color@8.1.1) eslint: 8.27.0 - tsutils: 3.21.0(typescript@4.9.3) - typescript: 4.9.3 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true @@ -2462,7 +2489,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.43.0(typescript@4.9.3): + /@typescript-eslint/typescript-estree@5.43.0(typescript@5.1.6): resolution: {integrity: sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2477,13 +2504,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@4.9.3) - typescript: 4.9.3 + tsutils: 3.21.0(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.43.0(eslint@8.27.0)(typescript@4.9.3): + /@typescript-eslint/utils@5.43.0(eslint@8.27.0)(typescript@5.1.6): resolution: {integrity: sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2493,7 +2520,7 @@ packages: '@types/semver': 7.5.0 '@typescript-eslint/scope-manager': 5.43.0 '@typescript-eslint/types': 5.43.0 - '@typescript-eslint/typescript-estree': 5.43.0(typescript@4.9.3) + '@typescript-eslint/typescript-estree': 5.43.0(typescript@5.1.6) eslint: 8.27.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0(eslint@8.27.0) @@ -2769,7 +2796,6 @@ packages: /antlr4ts@0.5.0-alpha.4: resolution: {integrity: sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==} - dev: true /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -4817,7 +4843,6 @@ packages: /emoji-regex@10.2.1: resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} - dev: true /emoji-regex@7.0.3: resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} @@ -5038,8 +5063,8 @@ packages: eslint: ^7.32.0 || ^8.2.0 eslint-plugin-import: ^2.25.3 dependencies: - '@typescript-eslint/eslint-plugin': 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@4.9.3) - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/eslint-plugin': 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) eslint: 8.27.0 eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.26.0)(eslint@8.27.0) eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0) @@ -5085,7 +5110,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) debug: 3.2.7 eslint: 8.27.0 eslint-import-resolver-node: 0.3.7 @@ -5103,7 +5128,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) array-includes: 3.1.6 array.prototype.flat: 1.3.1 debug: 2.6.9 @@ -5608,13 +5633,13 @@ packages: '@scure/bip32': 1.1.5 '@scure/bip39': 1.1.1 - /ethereum-waffle@3.4.4(typescript@4.9.3): + /ethereum-waffle@3.4.4(typescript@5.1.6): resolution: {integrity: sha512-PA9+jCjw4WC3Oc5ocSMBj5sXvueWQeAbvCA+hUlb6oFgwwKyq5ka3bWQ7QZcjzIX+TdFkxP4IbFmoY2D8Dkj9Q==} engines: {node: '>=10.0'} hasBin: true dependencies: '@ethereum-waffle/chai': 3.4.4 - '@ethereum-waffle/compiler': 3.4.4(typescript@4.9.3) + '@ethereum-waffle/compiler': 3.4.4(typescript@5.1.6) '@ethereum-waffle/mock-contract': 3.4.4 '@ethereum-waffle/provider': 3.4.4 ethers: 5.7.2 @@ -5626,18 +5651,18 @@ packages: - utf-8-validate dev: true - /ethereum-waffle@4.0.2(typescript@4.9.3): + /ethereum-waffle@4.0.2(typescript@5.1.6): resolution: {integrity: sha512-E6B8pYQRtBpQSQLJZ94vNEv2vTBtjaJk0oRF9LocdyJEWwO4kMYyu48T1AJlwXsbHz30ooOaI1QKjk6AIwoiww==} engines: {node: '>=10.0'} hasBin: true dependencies: '@ethereum-waffle/chai': 4.0.2 - '@ethereum-waffle/compiler': 4.0.1(ethers@5.6.2)(solc@0.6.12)(typechain@8.2.0)(typescript@4.9.3) + '@ethereum-waffle/compiler': 4.0.1(ethers@5.6.2)(solc@0.6.12)(typechain@8.2.0)(typescript@5.1.6) '@ethereum-waffle/mock-contract': 4.0.1 '@ethereum-waffle/provider': 4.0.2 ethers: 5.6.2 solc: 0.6.12 - typechain: 8.2.0(typescript@4.9.3) + typechain: 8.2.0(typescript@5.1.6) transitivePeerDependencies: - bufferutil - encoding @@ -6897,7 +6922,7 @@ packages: dependencies: array-uniq: 1.0.3 eth-gas-reporter: 0.2.25 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' @@ -6910,12 +6935,12 @@ packages: ts-generator: ^0.1.1 typechain: ^4.0.1 dependencies: - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) ts-generator: 0.1.1 - typechain: 4.0.3(typescript@4.9.3) + typechain: 4.0.3(typescript@5.1.6) dev: false - /hardhat@2.12.4(ts-node@10.9.1)(typescript@4.9.3): + /hardhat@2.12.4(ts-node@10.9.1)(typescript@5.1.6): resolution: {integrity: sha512-rc9S2U/4M+77LxW1Kg7oqMMmjl81tzn5rNFARhbXKUA1am/nhfMJEujOjuKvt+ZGMiZ11PYSe8gyIpB/aRNDgw==} engines: {node: ^14.0.0 || ^16.0.0 || ^18.0.0} hasBin: true @@ -6974,9 +6999,9 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@18.11.9)(typescript@4.9.3) + ts-node: 10.9.1(@types/node@18.11.9)(typescript@5.1.6) tsort: 0.0.1 - typescript: 4.9.3 + typescript: 5.1.6 undici: 5.22.1 uuid: 8.3.2 ws: 7.5.9 @@ -9522,7 +9547,6 @@ packages: semver: 7.5.4 solidity-comments-extractor: 0.0.7 string-width: 4.2.3 - dev: true /prettier@1.19.1: resolution: {integrity: sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==} @@ -10611,7 +10635,6 @@ packages: /solidity-comments-extractor@0.0.7: resolution: {integrity: sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==} - dev: true /solidity-coverage@0.8.2(hardhat@2.12.4): resolution: {integrity: sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==} @@ -10629,7 +10652,7 @@ packages: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) jsonschema: 1.4.1 lodash: 4.17.21 mocha: 7.1.2 @@ -11226,20 +11249,20 @@ packages: /ts-essentials@1.0.4: resolution: {integrity: sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==} - /ts-essentials@6.0.7(typescript@4.9.3): + /ts-essentials@6.0.7(typescript@5.1.6): resolution: {integrity: sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 4.9.3 + typescript: 5.1.6 dev: true - /ts-essentials@7.0.3(typescript@4.9.3): + /ts-essentials@7.0.3(typescript@5.1.6): resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 4.9.3 + typescript: 5.1.6 /ts-generator@0.1.1: resolution: {integrity: sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ==} @@ -11255,7 +11278,7 @@ packages: resolve: 1.22.2 ts-essentials: 1.0.4 - /ts-node@10.9.1(@types/node@18.11.9)(typescript@4.9.3): + /ts-node@10.9.1(@types/node@18.11.9)(typescript@5.1.6): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -11281,11 +11304,11 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.9.3 + typescript: 5.1.6 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - /ts-node@10.9.1(@types/node@18.16.19)(typescript@4.9.3): + /ts-node@10.9.1(@types/node@18.16.19)(typescript@5.1.6): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -11311,7 +11334,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.9.3 + typescript: 5.1.6 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -11334,14 +11357,14 @@ packages: /tsort@0.0.1: resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} - /tsutils@3.21.0(typescript@4.9.3): + /tsutils@3.21.0(typescript@5.1.6): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.9.3 + typescript: 5.1.6 dev: true /tunnel-agent@0.6.0: @@ -11472,7 +11495,7 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true - /typechain@3.0.0(typescript@4.9.3): + /typechain@3.0.0(typescript@5.1.6): resolution: {integrity: sha512-ft4KVmiN3zH4JUFu2WJBrwfHeDf772Tt2d8bssDTo/YcckKW2D+OwFrHXRC6hJvO3mHjFQTihoMV6fJOi0Hngg==} hasBin: true dependencies: @@ -11481,14 +11504,14 @@ packages: fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 - ts-essentials: 6.0.7(typescript@4.9.3) + ts-essentials: 6.0.7(typescript@5.1.6) ts-generator: 0.1.1 transitivePeerDependencies: - supports-color - typescript dev: true - /typechain@4.0.3(typescript@4.9.3): + /typechain@4.0.3(typescript@5.1.6): resolution: {integrity: sha512-tmoHQeXZWHxIdeLK+i6dU0CU0vOd9Cndr3jFTZIMzak5/YpFZ8XoiYpTZcngygGBqZo+Z1EUmttLbW9KkFZLgQ==} hasBin: true dependencies: @@ -11497,13 +11520,13 @@ packages: fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@4.9.3) + ts-essentials: 7.0.3(typescript@5.1.6) ts-generator: 0.1.1 transitivePeerDependencies: - supports-color - typescript - /typechain@8.2.0(typescript@4.9.3): + /typechain@8.2.0(typescript@5.1.6): resolution: {integrity: sha512-tZqhqjxJ9xAS/Lh32jccTjMkpx7sTdUVVHAy5Bf0TIer5QFNYXotiX74oCvoVYjyxUKDK3MXHtMFzMyD3kE+jg==} hasBin: true peerDependencies: @@ -11518,8 +11541,8 @@ packages: mkdirp: 1.0.4 prettier: 2.7.1 ts-command-line-args: 2.5.1 - ts-essentials: 7.0.3(typescript@4.9.3) - typescript: 4.9.3 + ts-essentials: 7.0.3(typescript@5.1.6) + typescript: 5.1.6 transitivePeerDependencies: - supports-color dev: true @@ -11542,9 +11565,9 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true - /typescript@4.9.3: - resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} - engines: {node: '>=4.2.0'} + /typescript@5.1.6: + resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} + engines: {node: '>=14.17'} hasBin: true /typewise-core@1.2.0: diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..97b9feca --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@fuels/ts-config/base.json" +} diff --git a/turbo.json b/turbo.json index 48508cb8..d93279e6 100644 --- a/turbo.json +++ b/turbo.json @@ -7,6 +7,13 @@ }, "test": { "cache": false + }, + "check": { + "dependsOn": ["build"], + "cache": false + }, + "format": { + "cache": false } } } From 1268591eda5fb5d3aca5f056110a5415ad69d853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 01:41:20 +0200 Subject: [PATCH 2/9] ci: add checkout action --- .github/actions/setup-rust/{action.yaml => action.yml} | 3 --- .github/workflows/pr.yml | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) rename .github/actions/setup-rust/{action.yaml => action.yml} (90%) diff --git a/.github/actions/setup-rust/action.yaml b/.github/actions/setup-rust/action.yml similarity index 90% rename from .github/actions/setup-rust/action.yaml rename to .github/actions/setup-rust/action.yml index 69ada9c9..44fa438f 100644 --- a/.github/actions/setup-rust/action.yaml +++ b/.github/actions/setup-rust/action.yml @@ -11,9 +11,6 @@ inputs: runs: using: 'composite' steps: - - name: Checkout repository - uses: actions/checkout@v3 - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@master with: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index e9d42a5f..f2d09912 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -16,6 +16,7 @@ jobs: vaidate: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 - uses: FuelLabs/github-actions/setups/node@master - uses: FuelLabs/github-actions/setups/docker@master with: From e08f06d6caf19f9381fedf20faa1d423badd3c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 01:43:51 +0200 Subject: [PATCH 3/9] fix: rust version --- .github/actions/setup-rust/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index 44fa438f..207f8f6d 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -2,7 +2,7 @@ name: 'Rust & Forc Setup' inputs: rust-version: - default: 0.17.0 + default: 1.70.0 forc-toolchain: default: latest forc-date: From 73e02ab521980a8a6baf2f0bdc2adda3d3c2120e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 02:09:19 +0200 Subject: [PATCH 4/9] fix: typescript version on portal-contracts --- .nvmrc | 1 + packages/portal-contracts/package.json | 13 +- packages/portal-contracts/tsconfig.json | 17 +- pnpm-lock.yaml | 551 ++++-------------------- tsconfig.json | 2 +- turbo.json | 2 +- 6 files changed, 89 insertions(+), 497 deletions(-) diff --git a/.nvmrc b/.nvmrc index e69de29b..617bcf91 100644 --- a/.nvmrc +++ b/.nvmrc @@ -0,0 +1 @@ +18.14.1 diff --git a/packages/portal-contracts/package.json b/packages/portal-contracts/package.json index 8077da25..98ff162d 100644 --- a/packages/portal-contracts/package.json +++ b/packages/portal-contracts/package.json @@ -18,7 +18,7 @@ "script-upgrade": "pnpm hardhat run --network custom scripts/upgradeAll.ts", "script-verify-source": "pnpm hardhat run --network custom scripts/verifySource.ts", "script-verify-address": "pnpm hardhat run --network custom scripts/verifyAddress.ts", - "serve-deployments": "pnpx ts-node scripts/serveDeployments.ts", + "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", "test": "pnpm hardhat test", "test-no-compile": "pnpm hardhat test --no-compile", "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register" @@ -52,26 +52,17 @@ "chai": "^4.3.7", "dotenv": "^16.0.3", "eslint": "^8.27.0", - "eslint-config-airbnb-typescript": "^17.0.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-react": "^7.31.10", - "eslint-plugin-react-hooks": "^4.6.0", "ethereum-waffle": "^3.4.4", "ethers": "^5.7.2", "hardhat-gas-reporter": "^1.0.9", "markdownlint": "^0.26.2", "markdownlint-cli": "^0.32.2", - "prettier": "^2.7.1", - "prettier-plugin-solidity": "^v1.0.0-rc.1", "solc": "^0.8.17", "solhint": "3.3.7", "solidity-coverage": "^0.8.2", "ts-generator": "^0.1.1", "ts-node": "^10.9.1", "typechain": "^4.0.3", - "typescript": "^5.1.6" + "typescript": "^4.9.3" } } diff --git a/packages/portal-contracts/tsconfig.json b/packages/portal-contracts/tsconfig.json index 25ef1a06..25e9d1ad 100644 --- a/packages/portal-contracts/tsconfig.json +++ b/packages/portal-contracts/tsconfig.json @@ -1,9 +1,12 @@ { - "extends": "../../tsconfig.json", - "compilerOptions": { - "module": "commonjs", - "outDir": "dist" - }, - "include": ["./scripts", "./protocol", "./test", "./typechain"], - "files": ["./hardhat.config.ts"] + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "outDir": "dist", + "resolveJsonModule": true + }, + "include": ["./scripts", "./protocol", "./test", "./typechain"], + "files": ["./hardhat.config.ts"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5380b25..2cd75d3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,7 +115,7 @@ importers: version: 1.21.0(@nomiclabs/hardhat-ethers@2.2.1)(@nomiclabs/hardhat-etherscan@3.1.3)(ethers@5.7.2)(hardhat@2.12.4) '@typechain/ethers-v5': specifier: ^6.0.5 - version: 6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@5.1.6) + version: 6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@4.9.3) '@types/express': specifier: ^4.17.14 version: 4.17.14 @@ -124,7 +124,7 @@ importers: version: 4.18.2 hardhat: specifier: ^2.12.2 - version: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + version: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) hardhat-typechain: specifier: ^0.3.5 version: 0.3.5(hardhat@2.12.4)(ts-generator@0.1.1)(typechain@4.0.3) @@ -161,10 +161,10 @@ importers: version: 18.11.9 '@typescript-eslint/eslint-plugin': specifier: ^5.43.0 - version: 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@5.1.6) + version: 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@4.9.3) '@typescript-eslint/parser': specifier: ^5.43.0 - version: 5.43.0(eslint@8.27.0)(typescript@5.1.6) + version: 5.43.0(eslint@8.27.0)(typescript@4.9.3) chai: specifier: ^4.3.7 version: 4.3.7 @@ -174,30 +174,9 @@ importers: eslint: specifier: ^8.27.0 version: 8.27.0 - eslint-config-airbnb-typescript: - specifier: ^17.0.0 - version: 17.0.0(@typescript-eslint/eslint-plugin@5.43.0)(@typescript-eslint/parser@5.43.0)(eslint-plugin-import@2.26.0)(eslint@8.27.0) - eslint-config-prettier: - specifier: ^8.5.0 - version: 8.5.0(eslint@8.27.0) - eslint-plugin-import: - specifier: ^2.26.0 - version: 2.26.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0) - eslint-plugin-jsx-a11y: - specifier: ^6.6.1 - version: 6.6.1(eslint@8.27.0) - eslint-plugin-prettier: - specifier: ^4.2.1 - version: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.27.0)(prettier@2.7.1) - eslint-plugin-react: - specifier: ^7.31.10 - version: 7.31.10(eslint@8.27.0) - eslint-plugin-react-hooks: - specifier: ^4.6.0 - version: 4.6.0(eslint@8.27.0) ethereum-waffle: specifier: ^3.4.4 - version: 3.4.4(typescript@5.1.6) + version: 3.4.4(typescript@4.9.3) ethers: specifier: ^5.7.2 version: 5.7.2 @@ -210,12 +189,6 @@ importers: markdownlint-cli: specifier: ^0.32.2 version: 0.32.2 - prettier: - specifier: ^2.7.1 - version: 2.7.1 - prettier-plugin-solidity: - specifier: ^v1.0.0-rc.1 - version: 1.0.0-rc.1(prettier@2.7.1) solc: specifier: ^0.8.17 version: 0.8.17 @@ -230,13 +203,13 @@ importers: version: 0.1.1 ts-node: specifier: ^10.9.1 - version: 10.9.1(@types/node@18.11.9)(typescript@5.1.6) + version: 10.9.1(@types/node@18.11.9)(typescript@4.9.3) typechain: specifier: ^4.0.3 - version: 4.0.3(typescript@5.1.6) + version: 4.0.3(typescript@4.9.3) typescript: - specifier: ^5.1.6 - version: 5.1.6 + specifier: ^4.9.3 + version: 4.9.3 packages: @@ -266,21 +239,6 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/runtime-corejs3@7.22.6: - resolution: {integrity: sha512-M+37LLIRBTEVjktoJjbw4KVhupF0U/3PYUCbBwgAd9k17hoKhRu1n935QiG7Tuxv0LJOMrb2vuKEeYUlv0iyiw==} - engines: {node: '>=6.9.0'} - dependencies: - core-js-pure: 3.31.1 - regenerator-runtime: 0.13.11 - dev: true - - /@babel/runtime@7.22.6: - resolution: {integrity: sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==} - engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.13.11 - dev: true - /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -346,7 +304,7 @@ packages: - utf-8-validate dev: true - /@ethereum-waffle/compiler@3.4.4(typescript@5.1.6): + /@ethereum-waffle/compiler@3.4.4(typescript@4.9.3): resolution: {integrity: sha512-RUK3axJ8IkD5xpWjWoJgyHclOeEzDLQFga6gKpeGxiS/zBu+HB0W2FvsrrLalTFIaPw/CGYACRBSIxqiCqwqTQ==} engines: {node: '>=10.0'} dependencies: @@ -360,7 +318,7 @@ packages: node-fetch: 2.6.6 solc: 0.6.12 ts-generator: 0.1.1 - typechain: 3.0.0(typescript@5.1.6) + typechain: 3.0.0(typescript@4.9.3) transitivePeerDependencies: - bufferutil - supports-color @@ -1851,7 +1809,7 @@ packages: hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) /@nomiclabs/hardhat-etherscan@3.1.3(hardhat@2.12.4): resolution: {integrity: sha512-UeNO97j0lwOHqX7mrH6SfQQBdXq1Ng6eFr7uJKuQOrq2UVTWGD70lE5QO4fAFVPz9ao+xlNpMyIqSR7+OaDR+Q==} @@ -1864,7 +1822,7 @@ packages: chalk: 2.4.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 7.0.1 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) lodash: 4.17.21 semver: 6.3.0 table: 6.8.1 @@ -1884,9 +1842,9 @@ packages: '@nomiclabs/hardhat-ethers': 2.2.1(ethers@5.7.2)(hardhat@2.12.4) '@types/sinon-chai': 3.2.9 '@types/web3': 1.0.19 - ethereum-waffle: 3.4.4(typescript@5.1.6) + ethereum-waffle: 3.4.4(typescript@4.9.3) ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) dev: true /@openzeppelin/contracts-upgradeable@4.8.0: @@ -1916,7 +1874,7 @@ packages: chalk: 4.1.2 debug: 4.3.4(supports-color@8.1.1) ethers: 5.7.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) proper-lockfile: 4.1.2 transitivePeerDependencies: - supports-color @@ -2140,10 +2098,10 @@ packages: typechain: ^3.0.0 dependencies: ethers: 5.7.2 - typechain: 3.0.0(typescript@5.1.6) + typechain: 3.0.0(typescript@4.9.3) dev: true - /@typechain/ethers-v5@6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@5.1.6): + /@typechain/ethers-v5@6.0.5(ethers@5.7.2)(typechain@4.0.3)(typescript@4.9.3): resolution: {integrity: sha512-KJh+EWuxmX1a17fQWS1ba8DCYcqK7UpdbqMZZwyfiv9FQfn8ZQJX17anbkCMOSU8TV3EvRuJ/vFEKGzKnpkO8g==} peerDependencies: ethers: ^5.0.0 @@ -2151,8 +2109,8 @@ packages: typescript: '>=4.0.0' dependencies: ethers: 5.7.2 - typechain: 4.0.3(typescript@5.1.6) - typescript: 5.1.6 + typechain: 4.0.3(typescript@4.9.3) + typescript: 4.9.3 dev: false /@types/abstract-leveldown@7.2.1: @@ -2249,10 +2207,6 @@ packages: resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} dev: true - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: true - /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: @@ -2409,7 +2363,7 @@ packages: '@types/underscore': 1.11.5 dev: true - /@typescript-eslint/eslint-plugin@5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@5.1.6): + /@typescript-eslint/eslint-plugin@5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@4.9.3): resolution: {integrity: sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2420,23 +2374,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) + '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@4.9.3) '@typescript-eslint/scope-manager': 5.43.0 - '@typescript-eslint/type-utils': 5.43.0(eslint@8.27.0)(typescript@5.1.6) - '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@5.1.6) + '@typescript-eslint/type-utils': 5.43.0(eslint@8.27.0)(typescript@4.9.3) + '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@4.9.3) debug: 4.3.4(supports-color@8.1.1) eslint: 8.27.0 ignore: 5.2.4 natural-compare-lite: 1.4.0 regexpp: 3.2.0 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.1.6) - typescript: 5.1.6 + tsutils: 3.21.0(typescript@4.9.3) + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@5.43.0(eslint@8.27.0)(typescript@5.1.6): + /@typescript-eslint/parser@5.43.0(eslint@8.27.0)(typescript@4.9.3): resolution: {integrity: sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2448,10 +2402,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.43.0 '@typescript-eslint/types': 5.43.0 - '@typescript-eslint/typescript-estree': 5.43.0(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 5.43.0(typescript@4.9.3) debug: 4.3.4(supports-color@8.1.1) eslint: 8.27.0 - typescript: 5.1.6 + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true @@ -2464,7 +2418,7 @@ packages: '@typescript-eslint/visitor-keys': 5.43.0 dev: true - /@typescript-eslint/type-utils@5.43.0(eslint@8.27.0)(typescript@5.1.6): + /@typescript-eslint/type-utils@5.43.0(eslint@8.27.0)(typescript@4.9.3): resolution: {integrity: sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2474,12 +2428,12 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 5.43.0(typescript@5.1.6) - '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 5.43.0(typescript@4.9.3) + '@typescript-eslint/utils': 5.43.0(eslint@8.27.0)(typescript@4.9.3) debug: 4.3.4(supports-color@8.1.1) eslint: 8.27.0 - tsutils: 3.21.0(typescript@5.1.6) - typescript: 5.1.6 + tsutils: 3.21.0(typescript@4.9.3) + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true @@ -2489,7 +2443,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree@5.43.0(typescript@5.1.6): + /@typescript-eslint/typescript-estree@5.43.0(typescript@4.9.3): resolution: {integrity: sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2504,13 +2458,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 - tsutils: 3.21.0(typescript@5.1.6) - typescript: 5.1.6 + tsutils: 3.21.0(typescript@4.9.3) + typescript: 4.9.3 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils@5.43.0(eslint@8.27.0)(typescript@5.1.6): + /@typescript-eslint/utils@5.43.0(eslint@8.27.0)(typescript@4.9.3): resolution: {integrity: sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2520,7 +2474,7 @@ packages: '@types/semver': 7.5.0 '@typescript-eslint/scope-manager': 5.43.0 '@typescript-eslint/types': 5.43.0 - '@typescript-eslint/typescript-estree': 5.43.0(typescript@5.1.6) + '@typescript-eslint/typescript-estree': 5.43.0(typescript@4.9.3) eslint: 8.27.0 eslint-scope: 5.1.1 eslint-utils: 3.0.0(eslint@8.27.0) @@ -2816,14 +2770,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - /aria-query@4.2.2: - resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} - engines: {node: '>=6.0'} - dependencies: - '@babel/runtime': 7.22.6 - '@babel/runtime-corejs3': 7.22.6 - dev: true - /arr-diff@4.0.0: resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} engines: {node: '>=0.10.0'} @@ -2871,17 +2817,6 @@ packages: /array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.1 - is-string: 1.0.7 - dev: true - /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -2897,26 +2832,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /array.prototype.flat@1.3.1: - resolution: {integrity: sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - dev: true - - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - es-shim-unscopables: 1.0.0 - dev: true - /array.prototype.reduce@1.0.5: resolution: {integrity: sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==} engines: {node: '>= 0.4'} @@ -2966,10 +2881,6 @@ packages: resolution: {integrity: sha512-XHusKxKz3zoYk1ic8Un640joHbFMhbqneyoZfoKnEGtf2ey9Uh/IdpcQplODdO/kENaMIWsD0nJm4+wX3UNLHA==} dev: true - /ast-types-flow@0.0.7: - resolution: {integrity: sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==} - dev: true - /astral-regex@1.0.0: resolution: {integrity: sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==} engines: {node: '>=4'} @@ -3031,11 +2942,6 @@ packages: resolution: {integrity: sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==} dev: true - /axe-core@4.7.2: - resolution: {integrity: sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==} - engines: {node: '>=4'} - dev: true - /axios@0.27.2: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: @@ -3045,10 +2951,6 @@ packages: - debug dev: true - /axobject-query@2.2.0: - resolution: {integrity: sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==} - dev: true - /babel-code-frame@6.26.0: resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} dependencies: @@ -4332,10 +4234,6 @@ packages: typedarray: 0.0.6 dev: true - /confusing-browser-globals@1.0.11: - resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} - dev: true - /content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -4516,10 +4414,6 @@ packages: type: 1.2.0 dev: true - /damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - dev: true - /dashdash@1.14.1: resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} engines: {node: '>=0.10'} @@ -4773,13 +4667,6 @@ packages: path-type: 4.0.0 dev: true - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: true - /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -4843,6 +4730,7 @@ packages: /emoji-regex@10.2.1: resolution: {integrity: sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA==} + dev: false /emoji-regex@7.0.3: resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} @@ -4972,12 +4860,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} - dependencies: - has: 1.0.3 - dev: true - /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} @@ -5040,186 +4922,6 @@ packages: source-map: 0.2.0 dev: true - /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.26.0)(eslint@8.27.0): - resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.2 - dependencies: - confusing-browser-globals: 1.0.11 - eslint: 8.27.0 - eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0) - object.assign: 4.1.4 - object.entries: 1.1.6 - semver: 6.3.0 - dev: true - - /eslint-config-airbnb-typescript@17.0.0(@typescript-eslint/eslint-plugin@5.43.0)(@typescript-eslint/parser@5.43.0)(eslint-plugin-import@2.26.0)(eslint@8.27.0): - resolution: {integrity: sha512-elNiuzD0kPAPTXjFWg+lE24nMdHMtuxgYoD30OyMD6yrW1AhFZPAg27VX7d3tzOErw+dgJTNWfRSDqEcXb4V0g==} - peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.13.0 - '@typescript-eslint/parser': ^5.0.0 - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.3 - dependencies: - '@typescript-eslint/eslint-plugin': 5.43.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0)(typescript@5.1.6) - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) - eslint: 8.27.0 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.26.0)(eslint@8.27.0) - eslint-plugin-import: 2.26.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0) - dev: true - - /eslint-config-prettier@8.5.0(eslint@8.27.0): - resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - dependencies: - eslint: 8.27.0 - dev: true - - /eslint-import-resolver-node@0.3.7: - resolution: {integrity: sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==} - dependencies: - debug: 3.2.7 - is-core-module: 2.12.1 - resolve: 1.22.2 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.43.0)(eslint-import-resolver-node@0.3.7)(eslint@8.27.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - dependencies: - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) - debug: 3.2.7 - eslint: 8.27.0 - eslint-import-resolver-node: 0.3.7 - transitivePeerDependencies: - - supports-color - dev: true - - /eslint-plugin-import@2.26.0(@typescript-eslint/parser@5.43.0)(eslint@8.27.0): - resolution: {integrity: sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - dependencies: - '@typescript-eslint/parser': 5.43.0(eslint@8.27.0)(typescript@5.1.6) - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 - debug: 2.6.9 - doctrine: 2.1.0 - eslint: 8.27.0 - eslint-import-resolver-node: 0.3.7 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.43.0)(eslint-import-resolver-node@0.3.7)(eslint@8.27.0) - has: 1.0.3 - is-core-module: 2.12.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.values: 1.1.6 - resolve: 1.22.2 - tsconfig-paths: 3.14.2 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - dev: true - - /eslint-plugin-jsx-a11y@6.6.1(eslint@8.27.0): - resolution: {integrity: sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - '@babel/runtime': 7.22.6 - aria-query: 4.2.2 - array-includes: 3.1.6 - ast-types-flow: 0.0.7 - axe-core: 4.7.2 - axobject-query: 2.2.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 8.27.0 - has: 1.0.3 - jsx-ast-utils: 3.3.4 - language-tags: 1.0.8 - minimatch: 3.1.2 - semver: 6.3.0 - dev: true - - /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.27.0)(prettier@2.7.1): - resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - eslint: '>=7.28.0' - eslint-config-prettier: '*' - prettier: '>=2.0.0' - peerDependenciesMeta: - eslint-config-prettier: - optional: true - dependencies: - eslint: 8.27.0 - eslint-config-prettier: 8.5.0(eslint@8.27.0) - prettier: 2.7.1 - prettier-linter-helpers: 1.0.0 - dev: true - - /eslint-plugin-react-hooks@4.6.0(eslint@8.27.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - dependencies: - eslint: 8.27.0 - dev: true - - /eslint-plugin-react@7.31.10(eslint@8.27.0): - resolution: {integrity: sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - doctrine: 2.1.0 - eslint: 8.27.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.4 - minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 - prop-types: 15.8.1 - resolve: 2.0.0-next.4 - semver: 6.3.0 - string.prototype.matchall: 4.0.8 - dev: true - /eslint-scope@4.0.3: resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==} engines: {node: '>=4.0.0'} @@ -5633,13 +5335,13 @@ packages: '@scure/bip32': 1.1.5 '@scure/bip39': 1.1.1 - /ethereum-waffle@3.4.4(typescript@5.1.6): + /ethereum-waffle@3.4.4(typescript@4.9.3): resolution: {integrity: sha512-PA9+jCjw4WC3Oc5ocSMBj5sXvueWQeAbvCA+hUlb6oFgwwKyq5ka3bWQ7QZcjzIX+TdFkxP4IbFmoY2D8Dkj9Q==} engines: {node: '>=10.0'} hasBin: true dependencies: '@ethereum-waffle/chai': 3.4.4 - '@ethereum-waffle/compiler': 3.4.4(typescript@5.1.6) + '@ethereum-waffle/compiler': 3.4.4(typescript@4.9.3) '@ethereum-waffle/mock-contract': 3.4.4 '@ethereum-waffle/provider': 3.4.4 ethers: 5.7.2 @@ -6922,7 +6624,7 @@ packages: dependencies: array-uniq: 1.0.3 eth-gas-reporter: 0.2.25 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) sha1: 1.1.1 transitivePeerDependencies: - '@codechecks/client' @@ -6935,12 +6637,12 @@ packages: ts-generator: ^0.1.1 typechain: ^4.0.1 dependencies: - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) ts-generator: 0.1.1 - typechain: 4.0.3(typescript@5.1.6) + typechain: 4.0.3(typescript@4.9.3) dev: false - /hardhat@2.12.4(ts-node@10.9.1)(typescript@5.1.6): + /hardhat@2.12.4(ts-node@10.9.1)(typescript@4.9.3): resolution: {integrity: sha512-rc9S2U/4M+77LxW1Kg7oqMMmjl81tzn5rNFARhbXKUA1am/nhfMJEujOjuKvt+ZGMiZ11PYSe8gyIpB/aRNDgw==} engines: {node: ^14.0.0 || ^16.0.0 || ^18.0.0} hasBin: true @@ -6999,9 +6701,9 @@ packages: solc: 0.7.3(debug@4.3.4) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 - ts-node: 10.9.1(@types/node@18.11.9)(typescript@5.1.6) + ts-node: 10.9.1(@types/node@18.11.9)(typescript@4.9.3) tsort: 0.0.1 - typescript: 5.1.6 + typescript: 4.9.3 undici: 5.22.1 uuid: 8.3.2 ws: 7.5.9 @@ -7811,13 +7513,6 @@ packages: hasBin: true dev: true - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - dependencies: - minimist: 1.2.8 - dev: true - /jsonc-parser@3.1.0: resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==} dev: true @@ -7858,16 +7553,6 @@ packages: verror: 1.10.0 dev: true - /jsx-ast-utils@3.3.4: - resolution: {integrity: sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.6 - array.prototype.flat: 1.3.1 - object.assign: 4.1.4 - object.values: 1.1.6 - dev: true - /keccak@3.0.1: resolution: {integrity: sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==} engines: {node: '>=10.0.0'} @@ -7935,16 +7620,6 @@ packages: optionalDependencies: graceful-fs: 4.2.11 - /language-subtag-registry@0.3.22: - resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==} - dev: true - - /language-tags@1.0.8: - resolution: {integrity: sha512-aWAZwgPLS8hJ20lNPm9HNVs4inexz6S2sQa3wx/+ycuutMNE5/IfYxiWYBbi+9UWCQVaXYCOPUl6gFrPR7+jGg==} - dependencies: - language-subtag-registry: 0.3.22 - dev: true - /lcid@1.0.0: resolution: {integrity: sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==} engines: {node: '>=0.10.0'} @@ -9104,24 +8779,6 @@ packages: object-keys: 1.1.1 dev: true - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - /object.getownpropertydescriptors@2.1.6: resolution: {integrity: sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==} engines: {node: '>= 0.8'} @@ -9133,13 +8790,6 @@ packages: safe-array-concat: 1.0.0 dev: true - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} - dependencies: - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - /object.pick@1.3.0: resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} engines: {node: '>=0.10.0'} @@ -9147,15 +8797,6 @@ packages: isobject: 3.0.1 dev: true - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - dev: true - /obliterator@2.0.4: resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} @@ -9527,13 +9168,6 @@ packages: dev: true optional: true - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - dependencies: - fast-diff: 1.3.0 - dev: true - /prettier-plugin-solidity@1.0.0-rc.1(prettier@2.7.1): resolution: {integrity: sha512-horUGyCBbfNHWvJ44UVEcsfVySEoG2gxGs7TcBfTZWNvD4VU6rjzwAkrUtKV6VvRZWn9dh01XZ2UhhB3eVnMXQ==} engines: {node: '>=12'} @@ -9547,6 +9181,7 @@ packages: semver: 7.5.4 solidity-comments-extractor: 0.0.7 string-width: 4.2.3 + dev: false /prettier@1.19.1: resolution: {integrity: sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==} @@ -9594,14 +9229,6 @@ packages: asap: 2.0.6 dev: true - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - dev: true - /proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} dependencies: @@ -9774,10 +9401,6 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true - /read-pkg-up@1.0.1: resolution: {integrity: sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==} engines: {node: '>=0.10.0'} @@ -9873,10 +9496,6 @@ packages: resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} dev: true - /regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - dev: true - /regenerator-transform@0.10.1: resolution: {integrity: sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==} dependencies: @@ -10070,15 +9689,6 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} - hasBin: true - dependencies: - is-core-module: 2.12.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - /responselike@1.0.2: resolution: {integrity: sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==} dependencies: @@ -10635,6 +10245,7 @@ packages: /solidity-comments-extractor@0.0.7: resolution: {integrity: sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==} + dev: false /solidity-coverage@0.8.2(hardhat@2.12.4): resolution: {integrity: sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==} @@ -10652,7 +10263,7 @@ packages: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.12.4(ts-node@10.9.1)(typescript@5.1.6) + hardhat: 2.12.4(ts-node@10.9.1)(typescript@4.9.3) jsonschema: 1.4.1 lodash: 4.17.21 mocha: 7.1.2 @@ -10855,19 +10466,6 @@ packages: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.0 - es-abstract: 1.21.2 - get-intrinsic: 1.2.1 - has-symbols: 1.0.3 - internal-slot: 1.0.5 - regexp.prototype.flags: 1.5.0 - side-channel: 1.0.4 - dev: true - /string.prototype.trim@1.2.7: resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} engines: {node: '>= 0.4'} @@ -10948,11 +10546,6 @@ packages: is-utf8: 0.2.1 dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true - /strip-hex-prefix@1.0.0: resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} engines: {node: '>=6.5.0', npm: '>=3'} @@ -11249,20 +10842,28 @@ packages: /ts-essentials@1.0.4: resolution: {integrity: sha512-q3N1xS4vZpRouhYHDPwO0bDW3EZ6SK9CrrDHxi/D6BPReSjpVgWIOpLS2o0gSBZm+7q/wyKp6RVM1AeeW7uyfQ==} - /ts-essentials@6.0.7(typescript@5.1.6): + /ts-essentials@6.0.7(typescript@4.9.3): resolution: {integrity: sha512-2E4HIIj4tQJlIHuATRHayv0EfMGK3ris/GRk1E3CFnsZzeNV+hUmelbaTZHLtXaZppM5oLhHRtO04gINC4Jusw==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 5.1.6 + typescript: 4.9.3 dev: true + /ts-essentials@7.0.3(typescript@4.9.3): + resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} + peerDependencies: + typescript: '>=3.7.0' + dependencies: + typescript: 4.9.3 + /ts-essentials@7.0.3(typescript@5.1.6): resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} peerDependencies: typescript: '>=3.7.0' dependencies: typescript: 5.1.6 + dev: true /ts-generator@0.1.1: resolution: {integrity: sha512-N+ahhZxTLYu1HNTQetwWcx3so8hcYbkKBHTr4b4/YgObFTIKkOSSsaa+nal12w8mfrJAyzJfETXawbNjSfP2gQ==} @@ -11278,7 +10879,7 @@ packages: resolve: 1.22.2 ts-essentials: 1.0.4 - /ts-node@10.9.1(@types/node@18.11.9)(typescript@5.1.6): + /ts-node@10.9.1(@types/node@18.11.9)(typescript@4.9.3): resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -11304,7 +10905,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.1.6 + typescript: 4.9.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 @@ -11339,15 +10940,6 @@ packages: yn: 3.1.1 dev: true - /tsconfig-paths@3.14.2: - resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - dev: true - /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -11357,14 +10949,14 @@ packages: /tsort@0.0.1: resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} - /tsutils@3.21.0(typescript@5.1.6): + /tsutils@3.21.0(typescript@4.9.3): resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 5.1.6 + typescript: 4.9.3 dev: true /tunnel-agent@0.6.0: @@ -11495,7 +11087,7 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true - /typechain@3.0.0(typescript@5.1.6): + /typechain@3.0.0(typescript@4.9.3): resolution: {integrity: sha512-ft4KVmiN3zH4JUFu2WJBrwfHeDf772Tt2d8bssDTo/YcckKW2D+OwFrHXRC6hJvO3mHjFQTihoMV6fJOi0Hngg==} hasBin: true dependencies: @@ -11504,14 +11096,14 @@ packages: fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 - ts-essentials: 6.0.7(typescript@5.1.6) + ts-essentials: 6.0.7(typescript@4.9.3) ts-generator: 0.1.1 transitivePeerDependencies: - supports-color - typescript dev: true - /typechain@4.0.3(typescript@5.1.6): + /typechain@4.0.3(typescript@4.9.3): resolution: {integrity: sha512-tmoHQeXZWHxIdeLK+i6dU0CU0vOd9Cndr3jFTZIMzak5/YpFZ8XoiYpTZcngygGBqZo+Z1EUmttLbW9KkFZLgQ==} hasBin: true dependencies: @@ -11520,7 +11112,7 @@ packages: fs-extra: 7.0.1 js-sha3: 0.8.0 lodash: 4.17.21 - ts-essentials: 7.0.3(typescript@5.1.6) + ts-essentials: 7.0.3(typescript@4.9.3) ts-generator: 0.1.1 transitivePeerDependencies: - supports-color @@ -11565,6 +11157,11 @@ packages: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} dev: true + /typescript@4.9.3: + resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==} + engines: {node: '>=4.2.0'} + hasBin: true + /typescript@5.1.6: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} diff --git a/tsconfig.json b/tsconfig.json index 97b9feca..83c8cce7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,3 +1,3 @@ { - "extends": "@fuels/ts-config/base.json" + "extends": ["@fuels/ts-config/base.json"] } diff --git a/turbo.json b/turbo.json index d93279e6..465bc9cf 100644 --- a/turbo.json +++ b/turbo.json @@ -2,7 +2,7 @@ "$schema": "https://turborepo.org/schema.json", "pipeline": { "build": { - "cache": true, + "cache": false, "outputs": ["dist/**", "typechain/**", "artifacts/**", "out/**"] }, "test": { From 1342a130d45cd36ca8ae08a57ac8ee112e2fb31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 02:11:30 +0200 Subject: [PATCH 5/9] fix: eslint --- .eslintrc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.eslintrc b/.eslintrc index 80ca566f..5fa390eb 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,8 +1,8 @@ { - "extends": "./packages/eslint-config" - // "rules": { - // "no-await-in-loop": 0, - // "prefer-destructuring": 0, - // "no-bitwise": 0 - // } + "plugins": ["@fuels/eslint-config"], + "rules": { + "no-await-in-loop": 0, + "prefer-destructuring": 0, + "no-bitwise": 0 + } } From 81e0c8b1031a5627e8b67706187c44117e30d7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 02:29:27 +0200 Subject: [PATCH 6/9] chore: add timeout on test script --- scripts/test.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index c43bc301..2214a858 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -8,14 +8,24 @@ pnpm run build echo "\n\nStarting docker..." pnpm run node:up +RUN_TESTS_TRY=0 +MAX_TRIES=50 + runTests() { NODE_URL="http://localhost:4000/playground"; + if [ $RUN_TESTS_TRY -gt $MAX_TRIES ]; then + echo "\n\nTests failed" + exit 1 + fi + if curl --silent --head --request GET $NODE_URL | grep "200 OK" > /dev/null; then - echo "\Run tests..." - pnpm turbo run test + echo "\nRun tests..." + # pnpm turbo run test else - sleep .5 + # Sleep for 6 seconds before retrying + sleep 6 + RUN_TESTS_TRY=$((RUN_TESTS_TRY+1)) runTests fi } From eab8d9334d3906b291fa4122eccfc0afa1a515db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 13:23:20 +0200 Subject: [PATCH 7/9] chore: add build command --- packages/portal-contracts/package.json | 4 ++-- scripts/test.sh | 9 ++++++--- turbo.json | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/portal-contracts/package.json b/packages/portal-contracts/package.json index 98ff162d..5e254eda 100644 --- a/packages/portal-contracts/package.json +++ b/packages/portal-contracts/package.json @@ -11,8 +11,8 @@ "compile": "pnpm hardhat compile --show-stack-traces", "coverage": "pnpm run build && pnpm hardhat coverage --temp artifacts --network hardhat", "check": "pnpm solhint \"contracts/**/*.sol\"", - "node:start": "pnpm hardhat node --network hardhat", - "contract:deploy": "QUICK_DEPLOY=true pnpm hardhat run --network localhost scripts/deployAll.ts", + "node": "pnpm hardhat node --network hardhat", + "node-deploy": "QUICK_DEPLOY=true pnpm hardhat run --network localhost scripts/deployAll.ts", "script-deploy": "pnpm hardhat run --network custom scripts/deployAll.ts", "script-deploy-impl": "pnpm hardhat run --network custom scripts/deployImplementation.ts", "script-upgrade": "pnpm hardhat run --network custom scripts/upgradeAll.ts", diff --git a/scripts/test.sh b/scripts/test.sh index 2214a858..81c5c71c 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -2,7 +2,7 @@ set -e -echo "\n\Building packages..." +echo "\n\nBuild projects..." pnpm run build echo "\n\nStarting docker..." @@ -10,10 +10,13 @@ pnpm run node:up RUN_TESTS_TRY=0 MAX_TRIES=50 +OUTPUT="" runTests() { NODE_URL="http://localhost:4000/playground"; + printf "\rWaiting for node.${OUTPUT}" + if [ $RUN_TESTS_TRY -gt $MAX_TRIES ]; then echo "\n\nTests failed" exit 1 @@ -21,14 +24,14 @@ runTests() { if curl --silent --head --request GET $NODE_URL | grep "200 OK" > /dev/null; then echo "\nRun tests..." - # pnpm turbo run test + pnpm turbo run test else # Sleep for 6 seconds before retrying sleep 6 RUN_TESTS_TRY=$((RUN_TESTS_TRY+1)) + OUTPUT="${OUTPUT}." runTests fi } -echo "\n\nWaiting for node..." runTests diff --git a/turbo.json b/turbo.json index 465bc9cf..d93279e6 100644 --- a/turbo.json +++ b/turbo.json @@ -2,7 +2,7 @@ "$schema": "https://turborepo.org/schema.json", "pipeline": { "build": { - "cache": false, + "cache": true, "outputs": ["dist/**", "typechain/**", "artifacts/**", "out/**"] }, "test": { From 8a9ebf1c12eb9f05947f188b015f6cb0f7164d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 14:59:41 +0200 Subject: [PATCH 8/9] ci: add cargo clippy flags and fix issues --- .../bridge-fungible-token/tests/utils/builder.rs | 8 ++------ packages/fungible-token/package.json | 2 +- packages/message-predicates/package.json | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs b/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs index ecc712c6..0d1ef91e 100644 --- a/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs +++ b/packages/fungible-token/bridge-fungible-token/tests/utils/builder.rs @@ -43,14 +43,10 @@ pub async fn build_contract_message_tx( if !gas_coins.is_empty() { match gas_coins[0].clone() { Input::CoinSigned(coin) => { - tx_outputs.push(Output::change(coin.owner.into(), 0, AssetId::default())); + tx_outputs.push(Output::change(coin.owner, 0, AssetId::default())); } Input::CoinPredicate(predicate) => { - tx_outputs.push(Output::change( - predicate.owner.into(), - 0, - AssetId::default(), - )); + tx_outputs.push(Output::change(predicate.owner, 0, AssetId::default())); } _ => { // do nothing diff --git a/packages/fungible-token/package.json b/packages/fungible-token/package.json index 6c87573d..982a0dbb 100644 --- a/packages/fungible-token/package.json +++ b/packages/fungible-token/package.json @@ -4,7 +4,7 @@ "main": "dist/index.ts", "scripts": { "format": "forc fmt && cargo fmt", - "check": "forc fmt --check && cargo fmt --check && cargo clippy", + "check": "forc fmt --check && cargo fmt --check && cargo clippy --all-features --all-targets -- -D warnings", "build": "forc build && node ./scripts/build.js", "test": "cargo test" }, diff --git a/packages/message-predicates/package.json b/packages/message-predicates/package.json index 2f675236..6b8df000 100644 --- a/packages/message-predicates/package.json +++ b/packages/message-predicates/package.json @@ -4,7 +4,7 @@ "main": "./dist/index.ts", "scripts": { "test": "cargo test", - "check": "forc fmt --check && cargo fmt --check && cargo clippy", + "check": "forc fmt --check && cargo fmt --check && cargo clippy --all-features --all-targets -- -D warnings", "format": "forc fmt && cargo fmt", "build": "forc build && cargo run && node ./scripts/build.js" }, From 03a806cff5bb640004ef41c525916d2e81c0f558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luiz=20Est=C3=A1cio?= Date: Thu, 13 Jul 2023 15:14:00 +0200 Subject: [PATCH 9/9] chore: improve script redability --- scripts/test.sh | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 81c5c71c..0b09f8a3 100644 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,37 +1,57 @@ #!/bin/bash +# +# This script is used to run tests on the fuel node. +# it will start the node and wait for it to be ready +# before running the tests. +# +# By ready it means; +# - The L1 node is started +# - The portal-contracts are deployed to the L1 node; +# - The Fuel Node is started +# - The Fuel Node is connected to the L1 node and syncing blocks +# +# If the node is not ready after 5 minutes (50 checks with interval of 6 seconds), +# the script will fail. +# set -e +# Build the project to collect the artifacts echo "\n\nBuild projects..." pnpm run build +# Start the docker compose file with L1 and Fuel Node echo "\n\nStarting docker..." pnpm run node:up -RUN_TESTS_TRY=0 -MAX_TRIES=50 -OUTPUT="" +# Wait for the nodes to be ready and run the tests +HEALTH_CHECK_COUNTER=0 +HELTH_CHECK_OUTPUT="" +MAX_CHECK_ATTEMPTS=50 -runTests() { +waitForNodesToBeReady() { NODE_URL="http://localhost:4000/playground"; - printf "\rWaiting for node.${OUTPUT}" + printf "\rWaiting for node.${HELTH_CHECK_OUTPUT}" - if [ $RUN_TESTS_TRY -gt $MAX_TRIES ]; then + if [ $HEALTH_CHECK_COUNTER -gt $MAX_CHECK_ATTEMPTS ]; then echo "\n\nTests failed" exit 1 fi if curl --silent --head --request GET $NODE_URL | grep "200 OK" > /dev/null; then + # If the node responds with 200, it is ready + # to run the tests. echo "\nRun tests..." pnpm turbo run test else - # Sleep for 6 seconds before retrying + # If the request not returns 200 the node is not ready yet + # sleep for 6 seconds before and try again. + HEALTH_CHECK_COUNTER=$((HEALTH_CHECK_COUNTER+1)) + HELTH_CHECK_OUTPUT="${HELTH_CHECK_OUTPUT}." sleep 6 - RUN_TESTS_TRY=$((RUN_TESTS_TRY+1)) - OUTPUT="${OUTPUT}." - runTests + waitForNodesToBeReady fi } -runTests +waitForNodesToBeReady