From d4c266d3322d1e49c88ffe9da374d61298c5352c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:14:42 +0530 Subject: [PATCH 01/81] chore: add workspace in package.json --- docker/l1-chain/package.json | 85 ++++++++++++++++++++++++ packages/solidity-contracts/package.json | 10 ++- pnpm-lock.yaml | 10 +++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 docker/l1-chain/package.json diff --git a/docker/l1-chain/package.json b/docker/l1-chain/package.json new file mode 100644 index 00000000..24b1ad6f --- /dev/null +++ b/docker/l1-chain/package.json @@ -0,0 +1,85 @@ +{ + "name": "@fuel-bridge/solidity-contracts", + "version": "1.0.0", + "description": "The Fuel v2 Solidity smart contracts.", + "license": "APACHE-2.0", + "files": [ + "typechain", + "contracts", + "artifacts", + "dist" + ], + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "typings": "dist/index.d.ts", + "main": "dist/index.js", + "scripts": { + "build": "run-s clean compile build:exports", + "build:exports": "tsup", + "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": "pnpm hardhat node --network hardhat --hostname 0.0.0.0", + "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", + "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", + "test": "pnpm hardhat test", + "integration-test": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", + "node:up": "sh ./scripts/node:up.sh", + "hardhat:test:integration": "DISABLE_GAS_REPORTER=true pnpm hardhat --network mainnetFork test fork-integration-tests/**/*.ts --bail", + "test-no-compile": "pnpm hardhat test --no-compile", + "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register", + "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", + "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" + }, + "devDependencies": { + "@fuel-ts/merkle": "^0.21.2", + "@inquirer/prompts": "^5.3.8", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.4", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-network-helpers": "^1.0.10", + "@nomicfoundation/hardhat-verify": "1.1.1", + "@openzeppelin/contracts": "^4.8.3", + "@openzeppelin/contracts-upgradeable": "^4.8.3", + "@openzeppelin/hardhat-upgrades": "^3.0.4", + "@safe-global/api-kit": "^2.4.6", + "@safe-global/protocol-kit": "^4.1.1", + "@safe-global/safe-core-sdk-types": "^5.1.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", + "@types/chai": "^4.3.4", + "@types/cors": "2.8.17", + "mocha": "^10.0.0", + "@types/express": "^4.17.14", + "@types/lodash": "^4.14.202", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.9", + "@typescript-eslint/eslint-plugin": "^5.43.0", + "@typescript-eslint/parser": "^5.43.0", + "axios": "^1.7.7", + "chai": "^4.3.7", + "cors": "2.8.5", + "dotenv": "^16.0.3", + "ethers": "6.13.1", + "express": "^4.18.2", + "fuels": "0.96.1", + "hardhat": "^2.20.1", + "hardhat-deploy": "^0.12.4", + "inquirer": "^10.1.8", + "lodash": "^4.17.21", + "markdownlint": "^0.26.2", + "markdownlint-cli": "^0.32.2", + "node-fetch": "^2.6.6", + "npm-run-all": "^4.1.5", + "prettier-plugin-solidity": "^1.1.3", + "solc": "^0.8.17", + "solhint": "3.3.7", + "solidity-coverage": "^0.8.5", + "ts-generator": "^0.1.1", + "ts-node": "^10.9.1", + "tsup": "^7.2.0", + "typechain": "^8.3.2", + "typescript": "^4.9.3" + } + } + \ No newline at end of file diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index 70087cf7..41874449 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -21,14 +21,21 @@ "coverage": "pnpm run build && pnpm hardhat coverage --temp artifacts --network hardhat", "check": "pnpm solhint \"contracts/**/*.sol\"", "node": "pnpm hardhat node --network hardhat", + "test:integration": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", + "node:up": "sh ./scripts/node:up.sh", "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", "test": "pnpm hardhat test", + "upgrade:test:integration": "sh ./scripts/test.sh", "test-no-compile": "pnpm hardhat test --no-compile", "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register", "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" }, + "peerDependencies": { + "@fuel-bridge/test-utils": "workspace:*", + "@fuel-bridge/fungible-token": "workspace:*" + }, "devDependencies": { "@fuel-ts/merkle": "^0.21.2", "@inquirer/prompts": "^5.3.8", @@ -54,6 +61,7 @@ "@typescript-eslint/parser": "^5.43.0", "chai": "^4.3.7", "cors": "2.8.5", + "mocha": "^10.0.0", "dotenv": "^16.0.3", "ethers": "6.13.1", "express": "^4.18.2", @@ -76,4 +84,4 @@ "typechain": "^8.3.2", "typescript": "^4.9.3" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc55af5d..94419ef0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,13 @@ importers: version: link:../solidity-contracts packages/solidity-contracts: + dependencies: + '@fuel-bridge/fungible-token': + specifier: workspace:* + version: link:../fungible-token + '@fuel-bridge/test-utils': + specifier: workspace:* + version: link:../test-utils devDependencies: '@fuel-ts/merkle': specifier: ^0.21.2 @@ -230,6 +237,9 @@ importers: markdownlint-cli: specifier: ^0.32.2 version: 0.32.2 + mocha: + specifier: ^10.0.0 + version: 10.4.0 node-fetch: specifier: ^2.6.6 version: 2.7.0 From 61e3417200a4225b024b1049cc2faf9bdee933b9 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:17:15 +0530 Subject: [PATCH 02/81] feat: add docker setup in contracts package --- docker/l1-chain/Dockerfile | 2 +- .../docker/block-committer/Dockerfile | 21 ++ .../docker/block-committer/block-committer.sh | 54 ++++ .../docker/docker-compose.yml | 99 ++++++ .../docker/envs/fuel_core.env | 1 + .../docker/envs/l1_chain.env | 1 + .../docker/fuel-core/Dockerfile | 55 ++++ .../docker/fuel-core/fuel_core.sh | 71 +++++ .../docker/fuel-core/genesis_coins.json | 299 ++++++++++++++++++ .../docker/l1-chain/Dockerfile | 43 +++ .../docker/l1-chain/l1_chain.sh | 39 +++ .../docker/l1-chain/package.json | 84 +++++ 12 files changed, 768 insertions(+), 1 deletion(-) create mode 100644 packages/solidity-contracts/docker/block-committer/Dockerfile create mode 100644 packages/solidity-contracts/docker/block-committer/block-committer.sh create mode 100644 packages/solidity-contracts/docker/docker-compose.yml create mode 100644 packages/solidity-contracts/docker/envs/fuel_core.env create mode 100644 packages/solidity-contracts/docker/envs/l1_chain.env create mode 100644 packages/solidity-contracts/docker/fuel-core/Dockerfile create mode 100644 packages/solidity-contracts/docker/fuel-core/fuel_core.sh create mode 100644 packages/solidity-contracts/docker/fuel-core/genesis_coins.json create mode 100644 packages/solidity-contracts/docker/l1-chain/Dockerfile create mode 100644 packages/solidity-contracts/docker/l1-chain/l1_chain.sh create mode 100644 packages/solidity-contracts/docker/l1-chain/package.json diff --git a/docker/l1-chain/Dockerfile b/docker/l1-chain/Dockerfile index 7970a8b3..a4828892 100644 --- a/docker/l1-chain/Dockerfile +++ b/docker/l1-chain/Dockerfile @@ -10,7 +10,7 @@ RUN apk --no-cache add git curl RUN npm i -g pnpm # clone the contracts repo -ADD ./packages/solidity-contracts/package.json /l1chain/fuel-v2-contracts/ +ADD docker/l1-chain/package.json /l1chain/fuel-v2-contracts/ # copy over the fuel chain and replace consts values WORKDIR /l1chain/fuel-v2-contracts diff --git a/packages/solidity-contracts/docker/block-committer/Dockerfile b/packages/solidity-contracts/docker/block-committer/Dockerfile new file mode 100644 index 00000000..b9399970 --- /dev/null +++ b/packages/solidity-contracts/docker/block-committer/Dockerfile @@ -0,0 +1,21 @@ +FROM ghcr.io/fuellabs/fuel-block-committer:v0.10.4 + +ARG COMMITER_IP=0.0.0.0 +ARG COMMITER_PORT=8888 + +# dependencies +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && apt install -y curl jq && rm -rf /var/lib/apt/lists/* + +# copy chain config +WORKDIR /block-committer + +# expose fuel node port +ENV HOST="${COMMITER_IP}" +ENV PORT="${COMMITER_PORT}" + +EXPOSE ${PORT} + +# copy over script and run +COPY ./block-committer.sh . +ENTRYPOINT ["sh", "./block-committer.sh"] diff --git a/packages/solidity-contracts/docker/block-committer/block-committer.sh b/packages/solidity-contracts/docker/block-committer/block-committer.sh new file mode 100644 index 00000000..f2be6988 --- /dev/null +++ b/packages/solidity-contracts/docker/block-committer/block-committer.sh @@ -0,0 +1,54 @@ +#!/bin/sh +set -euo + +RETRIES=${RETRIES:-60} +DELAY=${DELAY:-10} +JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}' +HEALTH_URL=${HEALTH_URL:-"http://fuel_core:4001/v1/health"} + +if [ -z "$COMMITTER__ETH__RPC" ]; then + echo "Must specify \$ETHEREUM_RPC." + exit 1 +fi +if [ -z "$COMMITTER__FUEL__GRAPHQL_ENDPOINT" ]; then + echo "Must specify \$FUEL_GRAPHQL_ENDPOINT." + exit 1 +fi + +echo $COMMITTER__FUEL__GRAPHQL_ENDPOINT/health + +# wait for the base layer to be up +echo "Waiting for Fuel Core chain." +curl \ + --fail \ + --show-error \ + --silent \ + --retry-connrefused \ + --retry $RETRIES \ + --retry-delay $DELAY \ + $HEALTH_URL > /dev/null +echo "Connected to Fuel Core chain." + +# get the deployments file from the deployer +echo "Waiting for l1 chain deployment data." +curl \ + --fail \ + --show-error \ + --silent \ + --retry-connrefused \ + --retry-all-errors \ + --retry $RETRIES \ + --retry-delay 5 \ + $DEPLOYMENTS_HTTP \ + -o addresses.json +echo "Got l1 chain deployment data." + +# pull data from deployer dump +export COMMITTER__ETH__STATE_CONTRACT_ADDRESS=$(cat "./addresses.json" | jq -r .FuelChainState) +echo "COMMITTER__ETH__STATE_CONTRACT_ADDRESS: $COMMITTER__ETH__STATE_CONTRACT_ADDRESS" +echo "ETHEREUM_RPC: $COMMITTER__ETH__RPC" +echo "FUEL_GRAPHQL_ENDPOINT: $COMMITTER__FUEL__GRAPHQL_ENDPOINT" + +# start the Block Commiter +echo "Starting block commiter" +exec /root/fuel-block-committer diff --git a/packages/solidity-contracts/docker/docker-compose.yml b/packages/solidity-contracts/docker/docker-compose.yml new file mode 100644 index 00000000..b23c6721 --- /dev/null +++ b/packages/solidity-contracts/docker/docker-compose.yml @@ -0,0 +1,99 @@ +version: '3.4' + +services: + db: + image: postgres:14 + environment: + POSTGRES_USER: username + POSTGRES_PASSWORD: password + POSTGRES_DB: committer_db + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U username -d committer_db'] + interval: 5s + timeout: 5s + retries: 5 + + l1_chain: + image: fueldev/l1chain:${DOCKER_TAG_L1_CHAIN:-latest} + build: + dockerfile: ./docker/l1-chain/Dockerfile + # Use build context of the root directory + # to allow copying solidity-contracts on Dockerfile + context: ../ + env_file: + - ./envs/l1_chain.env + ports: + # expose the service to the host for integration testing + - ${L1_CHAIN_HTTP_PORT:-8545}:9545 + - ${DEPLOYMENTS_PORT:-8080}:8081 + stop_grace_period: 1s + + fuel_core: + image: fueldev/fuelcore:${DOCKER_TAG_FUEL_CORE:-latest} + depends_on: + l1_chain: + condition: service_started + platform: linux/amd64 + build: + context: ./fuel-core/ + env_file: + - ./envs/fuel_core.env + environment: + L1_CHAIN_HTTP: http://l1_chain:9545 + DEPLOYMENTS_HTTP: http://l1_chain:8081/deployments.local.json + RUST_LOG: debug + DEBUG: true + DB_PATH: /db + ports: + # expose the service to the host for integration testing + - ${FUEL_CORE_HTTP_PORT:-4000}:4001 + stop_grace_period: 1s + restart: always + + fuel_block_commiter: + image: ghcr.io/fuellabs/fuel-block-committer:v0.10.4 + platform: linux/amd64 + build: + context: ./block-committer/ + environment: + COMMITTER__ETH__RPC: 'ws://l1_chain:9545/' + COMMITTER__ETH__L1_KEYS__MAIN: 'Private(8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba)' + COMMITTER__ETH__L1_KEYS__BLOB: 'Private(59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d)' + COMMITTER__FUEL__GRAPHQL_ENDPOINT: 'http://fuel_core:4001/graphql' + COMMITTER__FUEL__NUM_BUFFERED_REQUESTS: '5' + COMMITTER__APP__DB__PORT: '5432' + COMMITTER__APP__DB__HOST: 'db' + COMMITTER__APP__DB__USERNAME: 'username' + COMMITTER__APP__DB__PASSWORD: 'password' + COMMITTER__APP__DB__MAX_CONNECTIONS: '10' + COMMITTER__APP__DB__USE_SSL: 'false' + COMMITTER__APP__DB__DATABASE: 'committer_db' + COMMITTER__APP__PORT: '8080' + COMMITTER__APP__HOST: '0.0.0.0' + COMMITTER__APP__BLOCK_CHECK_INTERVAL: '5s' + COMMITTER__APP__TX_FINALIZATION_CHECK_INTERVAL: '5s' + COMMITTER__APP__NUM_BLOCKS_TO_FINALIZE_TX: '3' + COMMITTER__APP__GAS_BUMP_TIMEOUT: '300s' + COMMITTER__APP__TX_MAX_FEE: '4000000000000000' + COMMITTER__APP__SEND_TX_REQUEST_TIMEOUT: '10s' + COMMITTER__APP__BUNDLE__ACCUMULATION_TIMEOUT: '3600s' + COMMITTER__APP__BUNDLE__BLOCKS_TO_ACCUMULATE: '400' + COMMITTER__APP__BUNDLE__OPTIMIZATION_TIMEOUT: '60s' + COMMITTER__APP__BUNDLE__BLOCK_HEIGHT_LOOKBACK: '8500' + COMMITTER__APP__BUNDLE__COMPRESSION_LEVEL: 'level6' + COMMITTER__APP__BUNDLE__OPTIMIZATION_STEP: '100' + COMMITTER__APP__BUNDLE__FRAGMENTS_TO_ACCUMULATE: '3' + COMMITTER__APP__BUNDLE__FRAGMENT_ACCUMULATION_TIMEOUT: '10m' + COMMITTER__APP__BUNDLE__NEW_BUNDLE_CHECK_INTERVAL: '3s' + DEPLOYMENTS_HTTP: http://l1_chain:8081/deployments.local.json + ports: + # expose the service to the host for integration testing + - ${COMMITTER_HTTP_PORT:-8888}:8888 + depends_on: + db: + condition: service_healthy + l1_chain: + condition: service_started + fuel_core: + condition: service_started + restart: always diff --git a/packages/solidity-contracts/docker/envs/fuel_core.env b/packages/solidity-contracts/docker/envs/fuel_core.env new file mode 100644 index 00000000..7b0bf02d --- /dev/null +++ b/packages/solidity-contracts/docker/envs/fuel_core.env @@ -0,0 +1 @@ +CONSENSUS_KEY_SECRET="0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298" \ No newline at end of file diff --git a/packages/solidity-contracts/docker/envs/l1_chain.env b/packages/solidity-contracts/docker/envs/l1_chain.env new file mode 100644 index 00000000..efddf6ab --- /dev/null +++ b/packages/solidity-contracts/docker/envs/l1_chain.env @@ -0,0 +1 @@ +TENDERLY_RPC_URL= \ No newline at end of file diff --git a/packages/solidity-contracts/docker/fuel-core/Dockerfile b/packages/solidity-contracts/docker/fuel-core/Dockerfile new file mode 100644 index 00000000..3d8b69db --- /dev/null +++ b/packages/solidity-contracts/docker/fuel-core/Dockerfile @@ -0,0 +1,55 @@ +# IMPORTANT! +# Make sure to check: +# https://github.com/FuelLabs/chain-configuration/tree/master/upgradelog/ignition-testnet +# and apply the latest state_transition_function and consensus_parameter +# when upgrading fuel-core +FROM ghcr.io/fuellabs/fuel-core:v0.40.0 + +ARG FUEL_IP=0.0.0.0 +ARG FUEL_PORT=4001 +ARG CONSENSUS_KEY_SECRET="" + +# dependencies +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && apt install -y git curl jq && rm -rf /var/lib/apt/lists/* + +# copy chain config +WORKDIR /fuel + +COPY ./genesis_coins.json . + +RUN git clone \ + https://github.com/FuelLabs/chain-configuration.git \ + /chain-configuration + +# Anchor the chain configuration to a specific commit to avoid CI breaking +RUN cd /chain-configuration && git checkout 8e4f7b52d7112f929a7cd95b988dfebfd10e87ec + +# Copy the base local configuration +RUN cp -R /chain-configuration/local/* ./ + +# Copy the devnet consensus parameters and state transition bytecode +RUN cp /chain-configuration/upgradelog/ignition-devnet/consensus_parameters/14.json \ + ./latest_consensus_parameters.json +RUN cp /chain-configuration/upgradelog/ignition-devnet/state_transition_function/16.wasm \ + ./state_transition_bytecode.wasm + +# update local state_config with custom genesis coins config +RUN jq '.coins = input' \ + state_config.json genesis_coins.json > tmp.json \ + && mv tmp.json state_config.json + +# update local state_config with testnet consensus parameters +RUN jq '.consensus_parameters = input' \ + state_config.json latest_consensus_parameters.json > tmp.json \ + && mv tmp.json state_config.json + +# expose fuel node port +ENV FUEL_IP="${FUEL_IP}" +ENV FUEL_PORT="${FUEL_PORT}" +ENV CONSENSUS_KEY_SECRET="${CONSENSUS_KEY_SECRET}" +EXPOSE ${FUEL_PORT} + +# copy over script and run +COPY ./fuel_core.sh . +CMD ["sh", "./fuel_core.sh"] \ No newline at end of file diff --git a/packages/solidity-contracts/docker/fuel-core/fuel_core.sh b/packages/solidity-contracts/docker/fuel-core/fuel_core.sh new file mode 100644 index 00000000..a1c2c3a3 --- /dev/null +++ b/packages/solidity-contracts/docker/fuel-core/fuel_core.sh @@ -0,0 +1,71 @@ +#!/bin/sh +set -euo + +#!/bin/sh +set -euo + +RETRIES=${RETRIES:-90} +DA_COMPRESSION=${DA_COMPRESSION:-"3600sec"} +GRAPHQL_COMPLEXITY=${GRAPHQL_COMPLEXITY:-500000} +JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}' +# FUEL_DB_PATH=./mnt/db/ + +if [ -z "$L1_CHAIN_HTTP" ]; then + echo "Must specify \$L1_CHAIN_HTTP." + exit 1 +fi + +# wait for the base layer to be up +echo "Waiting for l1 chain." +curl \ + --fail \ + --show-error \ + --silent \ + -H "Content-Type: application/json" \ + --retry-connrefused \ + --retry $RETRIES \ + --retry-delay 1 \ + -d $JSON \ + $L1_CHAIN_HTTP > /dev/null +echo "Connected to l1 chain." + +# get the deployments file from the deployer +echo "Waiting for l1 chain deployment data." +curl \ + --fail \ + --show-error \ + --silent \ + --retry-connrefused \ + --retry-all-errors \ + --retry $RETRIES \ + --retry-delay 5 \ + $DEPLOYMENTS_HTTP \ + -o addresses.json +echo "Got l1 chain deployment data." + +# pull data from deployer dump +export FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS=$(cat "./addresses.json" | jq -r .FuelMessagePortal) +# export FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS=$(jq -r '.address' /l1chain/fuel-v2-contracts/deployments/localhost/FuelMessagePortal.json) +echo "FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS: $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS" +echo "L1_CHAIN_HTTP: $L1_CHAIN_HTTP" + +# start the Fuel client +#--db-path ${FUEL_DB_PATH} +echo "Starting fuel node." +exec /root/fuel-core run \ + --ip $FUEL_IP \ + --port $FUEL_PORT \ + --utxo-validation \ + --vm-backtrace \ + --enable-relayer \ + --relayer $L1_CHAIN_HTTP \ + --relayer-v2-listening-contracts $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS \ + --poa-interval-period=1s \ + --relayer-da-deploy-height=21371952 \ + --debug \ + --da-compression $DA_COMPRESSION \ + --graphql-max-complexity $GRAPHQL_COMPLEXITY \ + --min-gas-price 0 \ + --tx-ttl-check-interval 10sec \ + --tx-pool-ttl 30sec \ + --snapshot ./ diff --git a/packages/solidity-contracts/docker/fuel-core/genesis_coins.json b/packages/solidity-contracts/docker/fuel-core/genesis_coins.json new file mode 100644 index 00000000..fd78763e --- /dev/null +++ b/packages/solidity-contracts/docker/fuel-core/genesis_coins.json @@ -0,0 +1,299 @@ +[ + { + "owner": "0xc8e615a4089466174459ef19cfd257d2e17adfabff3b8f219dbb5fb4eca87c50", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000001", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x92dffc873b56f219329ed03bb69bebe8c3d8b041088574882f7a6404f02e2f28", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000002", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x456bdaffaf74fe03521754ac445d148033c0c6acf7d593132c43f92fdbc7324c", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000003", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x639880ece7753a32e09164d14dad7436c57737e567f18b98f6ee30fec6b247ec", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000004", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0xfd8c520ef8caff0ad3289aa64acecd4ef86ac8f643fd9b76bf2d163a86a66716", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000005", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x8247104854dd733cb475901d55047f57cb3c8cafe3a9f7233de3325b8bf56a5c", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000006", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x53de37ae51fcfecb17ee3589f68904ac75bf5ec109edeb1065ccb63145287da6", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000007", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x17f4bef51f63f0c28af20d4223a3bf5cf1735a3b7ec52b4fcfbdbb5f30582a6b", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000008", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x95725e9083d8ed1cb52dcf6429d0cfce00cc375eeac5b620b5c36f5b1e734b31", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000009", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x08792a75d5714165aa117fd75450f9efcfb7124d034ef271f2919e4cc287046c", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000a", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x6a16fba49dbdf7689c52b7a22951a54dc164076d27bdc6042b5d8377d68ca10b", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000b", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x6494a55c0e3da212fdd0515507d00ae99151c7966e1448079c76bc447b577254", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000c", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0xe0e1c94a9f9e02454772813ba6a6261b5228db1fabde3a68b23c0e9744ce22fc", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000d", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x425f3e91aedff36e72ae60a8a1d328e625d66d39fcc98d5fcd1ba7df65a9f878", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000e", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0xe2242f2e4971c34bc6fe5e1c0043b1aba717cb6a51f31f0dc0708cca73df905a", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000f", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x6aa67cb316f329111dc708bb766360f5026a614edb11882e14d4cc04f26e0a08", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000010", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0xef5c2b712c4f3a10a37d6371cab2b03a6afd12e4ffcc9567d45d8c4b6e217e5c", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000011", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0xa82f66642de54993b32036eef7914f2dbaa217aa3209707b64d0b90187456a1f", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000012", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x9c9c1f3346b54fe6cb379fa27f338464592515fd865656089c4a23ac34390e6f", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000013", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "owner": "0x8a332bc33f4c10ad36392a9ca958a5ddf56081cc764f61613ea119e42ced6ac5", + "amount": 1000000000000, + "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", + "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000014", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0 + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000015", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "6b63804cfbf9856e68e5b6e7aef238dc8311ec55bec04df774003a2c96e0418e", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000016", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "54944e5b8189827e470e5a8bacfc6c3667397dc4e1eef7ef3519d16d6d6c6610", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000017", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "e10f526b192593793b7a1559a391445faba82a1d669e3eb2dcd17f9c121b24b1", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000018", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "577e424ee53a16e6a85291feabc8443862495f74ac39a706d2dd0b9fc16955eb", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000019", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "c36be0e14d3eaf5d8d233e0f4a40b3b4e48427d25f84c460d2b03b242a38479e", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "000000000000000000000000000000000000000000000000000000000000002a", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "a1184d77d0d08a064e03b2bd9f50863e88faddea4693a05ca1ee9b1732ea99b7", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "000000000000000000000000000000000000000000000000000000000000002b", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "b5566df884bee4e458151c2fe4082c8af38095cc442c61e0dc83a371d70d88fd", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "000000000000000000000000000000000000000000000000000000000000002c", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "9da7247e1d63d30d69f136f0f8654ee8340362c785b50f0a60513c7edbf5bb7c", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "000000000000000000000000000000000000000000000000000000000000002d", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "4b2ca966aad1a9d66994731db5138933cf61679107c3cde2a10d9594e47c084e", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "000000000000000000000000000000000000000000000000000000000000002e", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "26183fbe7375045250865947695dfc12500dcc43efb9102b4e8c4d3c20009dcb", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "000000000000000000000000000000000000000000000000000000000000002f", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "81f3a10b61828580d06cc4c7b0ed8f59b9fb618be856c55d33decd95489a1e23", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000030", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "587aa0482482efea0234752d1ad9a9c438d1f34d2859b8bef2d56a432cb68e33", + "amount": 1152921504606846976, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + }, + { + "tx_id": "0000000000000000000000000000000000000000000000000000000000000031", + "output_index": 0, + "tx_pointer_block_height": 0, + "tx_pointer_tx_idx": 0, + "owner": "53a9c6a74bee79c5e04115a007984f4bddaafed75f512f68766c6ed59d0aedec", + "amount": 1125899906842624, + "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" + } +] diff --git a/packages/solidity-contracts/docker/l1-chain/Dockerfile b/packages/solidity-contracts/docker/l1-chain/Dockerfile new file mode 100644 index 00000000..5e2f20f5 --- /dev/null +++ b/packages/solidity-contracts/docker/l1-chain/Dockerfile @@ -0,0 +1,43 @@ +FROM node:20-alpine AS BUILD_IMAGE + +# dependencies +RUN apk --no-cache add git curl +RUN npm i -g pnpm + +ARG L1_IP=0.0.0.0 +ARG L1_PORT=9545 +ARG SERVE_PORT=8081 + +WORKDIR /l1chain/fuel-v2-contracts + +# clone the contracts repo +COPY docker/l1-chain/package.json /l1chain/fuel-v2-contracts/ + +# build the ethereum contracts and environment +RUN pnpm install +COPY contracts/test/PlaceHolder.sol /l1chain/fuel-v2-contracts/contracts/test/PlaceHolder.sol +COPY hardhat.config.ts /l1chain/fuel-v2-contracts/ +COPY scripts/ /l1chain/fuel-v2-contracts/scripts/ +RUN pnpm compile + +# replace the fuel chain consts values and change contract code +COPY contracts/ /l1chain/fuel-v2-contracts/contracts/ +COPY deploy/ /l1chain/fuel-v2-contracts/deploy/ +COPY deployments/ /l1chain/fuel-v2-contracts/deployments/ +COPY protocol/ /l1chain/fuel-v2-contracts/protocol/ + + +# remove build dependencies +# RUN pnpm prune --prod +RUN pnpm compile + + +ENV L1_IP="${L1_IP}" +ENV L1_PORT="${L1_PORT}" +ENV SERVE_PORT="${SERVE_PORT}" +EXPOSE ${L1_PORT} +EXPOSE ${SERVE_PORT} + +# copy over script and run +COPY ./docker/l1-chain/l1_chain.sh /l1chain/l1_chain.sh +CMD ["sh", "/l1chain/l1_chain.sh"] diff --git a/packages/solidity-contracts/docker/l1-chain/l1_chain.sh b/packages/solidity-contracts/docker/l1-chain/l1_chain.sh new file mode 100644 index 00000000..e0df10bc --- /dev/null +++ b/packages/solidity-contracts/docker/l1-chain/l1_chain.sh @@ -0,0 +1,39 @@ +#!/bin/sh +set -euo + +RETRIES=${RETRIES:-120} +JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}' + +L1_CHAIN_HTTP="http://127.0.0.1:$L1_PORT" + +echo "Starting l1 chain." +pnpm hardhat node --network hardhat --port $L1_PORT --hostname $L1_IP & + +# wait for the base layer to be up +echo "Waiting for l1 chain." +curl \ + --fail \ + --show-error \ + --silent \ + -H "Content-Type: application/json" \ + --retry-connrefused \ + --retry $RETRIES \ + --retry-delay 1 \ + -d $JSON \ + $L1_CHAIN_HTTP > /dev/null + +echo "Connected to l1 chain." + +export LOCALHOST_HTTP=$L1_CHAIN_HTTP + +# Start auto mining +# We use a separate process to start auto mining because +# the deployment of contracts can fail if the chain is +# mining at the same time. +RPC_URL=$L1_CHAIN_HTTP pnpm run start-mining + +# serve contract deployment data +echo "Starting deployment data server." +pnpm run serve-deployments + + diff --git a/packages/solidity-contracts/docker/l1-chain/package.json b/packages/solidity-contracts/docker/l1-chain/package.json new file mode 100644 index 00000000..8d7842f8 --- /dev/null +++ b/packages/solidity-contracts/docker/l1-chain/package.json @@ -0,0 +1,84 @@ +{ + "name": "@fuel-bridge/solidity-contracts", + "version": "1.0.0", + "description": "The Fuel v2 Solidity smart contracts.", + "license": "APACHE-2.0", + "files": [ + "typechain", + "contracts", + "artifacts", + "dist" + ], + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "typings": "dist/index.d.ts", + "main": "dist/index.js", + "scripts": { + "build": "run-s clean compile build:exports", + "build:exports": "tsup", + "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": "pnpm hardhat node --network hardhat --hostname 0.0.0.0", + "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", + "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", + "test": "pnpm hardhat test", + "integration-test": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", + "node:up": "sh ./scripts/node:up.sh", + "hardhat:test:integration": "DISABLE_GAS_REPORTER=true pnpm hardhat --network mainnetFork test fork-integration-tests/**/*.ts --bail", + "test-no-compile": "pnpm hardhat test --no-compile", + "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register", + "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", + "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" + }, + "devDependencies": { + "@fuel-ts/merkle": "^0.21.2", + "@inquirer/prompts": "^5.3.8", + "@nomicfoundation/hardhat-chai-matchers": "^2.0.4", + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-network-helpers": "^1.0.10", + "@nomicfoundation/hardhat-verify": "1.1.1", + "@openzeppelin/contracts": "^4.8.3", + "@openzeppelin/contracts-upgradeable": "^4.8.3", + "@openzeppelin/hardhat-upgrades": "^3.0.4", + "@safe-global/api-kit": "^2.4.6", + "@safe-global/protocol-kit": "^4.1.1", + "@safe-global/safe-core-sdk-types": "^5.1.0", + "@typechain/ethers-v6": "^0.5.1", + "@typechain/hardhat": "^9.1.0", + "@types/chai": "^4.3.4", + "@types/cors": "2.8.17", + "mocha": "^10.0.0", + "@types/express": "^4.17.14", + "@types/lodash": "^4.14.202", + "@types/mocha": "^10.0.0", + "@types/node": "^18.11.9", + "@typescript-eslint/eslint-plugin": "^5.43.0", + "@typescript-eslint/parser": "^5.43.0", + "axios": "^1.7.7", + "chai": "^4.3.7", + "cors": "2.8.5", + "dotenv": "^16.0.3", + "ethers": "6.13.1", + "express": "^4.18.2", + "fuels": "0.96.1", + "hardhat": "^2.20.1", + "hardhat-deploy": "^0.12.4", + "inquirer": "^10.1.8", + "lodash": "^4.17.21", + "markdownlint": "^0.26.2", + "markdownlint-cli": "^0.32.2", + "node-fetch": "^2.6.6", + "npm-run-all": "^4.1.5", + "prettier-plugin-solidity": "^1.1.3", + "solc": "^0.8.17", + "solhint": "3.3.7", + "solidity-coverage": "^0.8.5", + "ts-generator": "^0.1.1", + "ts-node": "^10.9.1", + "tsup": "^7.2.0", + "typechain": "^8.3.2", + "typescript": "^4.9.3" + } +} From a51dc48b0df0479060746cc054e41ebfbde53de5 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:18:09 +0530 Subject: [PATCH 03/81] chore: add test placeholder contract --- packages/solidity-contracts/contracts/test/PlaceHolder.sol | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/solidity-contracts/contracts/test/PlaceHolder.sol diff --git a/packages/solidity-contracts/contracts/test/PlaceHolder.sol b/packages/solidity-contracts/contracts/test/PlaceHolder.sol new file mode 100644 index 00000000..e18ceb0f --- /dev/null +++ b/packages/solidity-contracts/contracts/test/PlaceHolder.sol @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; +/// @dev The only purpose of this contract is to be copied during the Dockerfile build +/// so that hardhat downloads the compiler and it gets cached +contract PlaceHolder {} \ No newline at end of file From a32de3fb3079b00f41e1869aff9790d7f72a1d41 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:19:33 +0530 Subject: [PATCH 04/81] feat: add upgrade scripts --- .../deploy/hardhat/001.chain_state.ts | 4 + .../hardhat/002.fuel_message_portal_v3.ts | 4 + .../deploy/hardhat/003.erc20_gateway_v4.ts | 4 + .../hardhat/005.register_block_committer.ts | 17 +-- .../deploy/hardhat/006._erc721_gateway_v3.ts | 26 ++++- .../deploy/hardhat/007.set_asset_issuer_id.ts | 4 + .../008.set_canonical_token_bytecode.ts | 1 - .../deploy/hardhat/009.chain_state_upgrade.ts | 101 ++++++++++++++++++ .../deploy/hardhat/010.portal_upgrade.ts | 86 +++++++++++++++ .../deploy/hardhat/011.gateway_upgrade.ts | 97 +++++++++++++++++ .../hardhat/999.serve_deployment_file.ts | 79 ++++++++++++-- 11 files changed, 406 insertions(+), 17 deletions(-) create mode 100644 packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts create mode 100644 packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts create mode 100644 packages/solidity-contracts/deploy/hardhat/011.gateway_upgrade.ts diff --git a/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts b/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts index 1d9f0e2d..30b4b275 100644 --- a/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts +++ b/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts @@ -13,6 +13,10 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { upgrades: { deployProxy, erc1967 }, deployments: { save }, } = hre; + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + if (isForking) return; + const [deployer] = await ethers.getSigners(); const contract = await deployProxy(new FuelChainState(deployer), [], { diff --git a/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts b/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts index 225aaf25..5468571c 100644 --- a/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts +++ b/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts @@ -14,6 +14,10 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { upgrades: { deployProxy, erc1967 }, deployments: { get, save }, } = hre; + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + if (isForking) return; + const [deployer] = await ethers.getSigners(); const { address: fuelChainState } = await get('FuelChainState'); diff --git a/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts b/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts index bef7f203..2f117733 100644 --- a/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts +++ b/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts @@ -9,6 +9,10 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { upgrades: { deployProxy, erc1967 }, deployments: { get, save }, } = hre; + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + if (isForking) return; + const [deployer] = await ethers.getSigners(); const fuelMessagePortal = await get('FuelMessagePortal'); diff --git a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts index f0fa19ab..9acd6bbe 100644 --- a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts +++ b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts @@ -7,16 +7,21 @@ const COMMITTER_ADDRESS = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { ethers, deployments } = hre; + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + const [deployer] = await ethers.getSigners(); - const { address } = await deployments.get('FuelChainState'); + if (!isForking) { + const { address } = await deployments.get('FuelChainState'); - const fuelChainState = FuelChainState__factory.connect(address, deployer); - const COMMITTER_ROLE = await fuelChainState.COMMITTER_ROLE(); + const fuelChainState = FuelChainState__factory.connect(address, deployer); + const COMMITTER_ROLE = await fuelChainState.COMMITTER_ROLE(); - await fuelChainState - .grantRole(COMMITTER_ROLE, COMMITTER_ADDRESS) - .then((tx) => tx.wait()); + await fuelChainState + .grantRole(COMMITTER_ROLE, COMMITTER_ADDRESS) + .then((tx) => tx.wait()); + } console.log('Granted role COMMITTER_ROLE to', COMMITTER_ADDRESS); }; diff --git a/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts b/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts index 3f4734bf..69129a74 100644 --- a/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts +++ b/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts @@ -1,5 +1,7 @@ +import fs from 'fs'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; +import path from 'path'; import { FuelERC721GatewayV2__factory as FuelERC721GatewayV2 } from '../../typechain'; @@ -9,13 +11,33 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { upgrades: { deployProxy, erc1967 }, deployments: { get, save }, } = hre; + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + const [deployer] = await ethers.getSigners(); - const fuelMessagePortal = await get('FuelMessagePortal'); + let portlAddress; + if (isForking) { + const deploymentPath = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet', + 'FuelMessagePortal.json' + ); + + const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8')); + portlAddress = deployment.address; + } else { + const fuelMessagePortal = await get('FuelMessagePortal'); + portlAddress = fuelMessagePortal.address; + } const contract = await deployProxy( new FuelERC721GatewayV2(deployer), - [fuelMessagePortal.address], + [portlAddress], { initializer: 'initialize', } diff --git a/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts b/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts index 025d7345..f689131a 100644 --- a/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts +++ b/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts @@ -16,6 +16,10 @@ const ASSET_ISSUER_ID = const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { ethers, deployments } = hre; + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + if (isForking) return; + const [deployer] = await ethers.getSigners(); await deployments.execute( diff --git a/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts b/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts index f7ec8ccb..698cf33d 100644 --- a/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts +++ b/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts @@ -1,6 +1,5 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; - import { USDT_ADDRESS, USDC_ADDRESS, diff --git a/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts b/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts new file mode 100644 index 00000000..48fdf219 --- /dev/null +++ b/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts @@ -0,0 +1,101 @@ +import fs from 'fs'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { DeployFunction } from 'hardhat-deploy/dist/types'; +import path from 'path'; + +import { FuelChainState__factory } from '../../typechain'; + +const BLOCKS_PER_COMMIT_INTERVAL = 30; +const TIME_TO_FINALIZE = 5; +const COMMIT_COOLDOWN = TIME_TO_FINALIZE; + +const ADMIN = '0x32da601374b38154f05904B16F44A1911Aa6f314'; +let COMMITTER_ADDRESS = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { + ethers, + upgrades: { erc1967 }, + } = hre; + + const [deployer] = await ethers.getSigners(); + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + let address; + + if (isForking) { + const deploymentPath = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet', + 'FuelChainState.json' + ); + + const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8')); + address = deployment.address; + + const chainState = FuelChainState__factory.connect(address, deployer); + + const factory = await hre.ethers.getContractFactory('FuelChainState'); + + const newImplementation = await factory.deploy( + TIME_TO_FINALIZE, + BLOCKS_PER_COMMIT_INTERVAL, + COMMIT_COOLDOWN + ); + + const newImplementationAddress = await newImplementation.getAddress(); + + let txData = chainState.interface.encodeFunctionData('upgradeTo', [ + newImplementationAddress, + ]); + + await deployer.sendTransaction({ + to: ADMIN, + value: ethers.parseEther('100'), + }); + + const impersonatedSigner = await ethers.getImpersonatedSigner(ADMIN); + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + const COMMITTER_ROLE = await chainState.COMMITTER_ROLE(); + + txData = await chainState.interface.encodeFunctionData('grantRole', [ + COMMITTER_ROLE, + COMMITTER_ADDRESS, + ]); + + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + COMMITTER_ADDRESS = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65'; + + txData = await chainState.interface.encodeFunctionData('grantRole', [ + COMMITTER_ROLE, + COMMITTER_ADDRESS, + ]); + + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + const implementation = await erc1967.getImplementationAddress(address); + + console.log('Upgraded FuelChainState to', implementation); + + return true; + } +}; + +func.tags = ['upgrade_chain_state']; +func.id = 'upgrade_chain_state'; +export default func; diff --git a/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts b/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts new file mode 100644 index 00000000..14682a6a --- /dev/null +++ b/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts @@ -0,0 +1,86 @@ +import { MaxUint256 } from 'ethers'; +import fs from 'fs'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { DeployFunction } from 'hardhat-deploy/dist/types'; +import path from 'path'; + +import { FuelMessagePortalV3__factory as FuelMessagePortal } from '../../typechain'; + +const RATE_LIMIT_DURATION = 3600 * 24 * 7; + +const ADMIN = '0x32da601374b38154f05904B16F44A1911Aa6f314'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { + ethers, + upgrades: { erc1967 }, + } = hre; + + const [deployer] = await ethers.getSigners(); + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + let address; + + if (isForking) { + const deploymentPath = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet', + 'FuelMessagePortal.json' + ); + + const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8')); + address = deployment.address; + + const portal = FuelMessagePortal.connect(address, deployer); + + const SET_RATE_LIMITER_ROLE = await portal.SET_RATE_LIMITER_ROLE(); + + const factory = await hre.ethers.getContractFactory('FuelMessagePortalV3'); + + const newImplementation = await factory.deploy( + MaxUint256, + RATE_LIMIT_DURATION + ); + + const newImplementationAddress = await newImplementation.getAddress(); + + let txData = portal.interface.encodeFunctionData('upgradeTo', [ + newImplementationAddress, + ]); + + await deployer.sendTransaction({ + to: ADMIN, + value: ethers.parseEther('100'), + }); + + const impersonatedSigner = await ethers.getImpersonatedSigner(ADMIN); + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + txData = await portal.interface.encodeFunctionData('grantRole', [ + SET_RATE_LIMITER_ROLE, + await deployer.getAddress(), + ]); + + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + const implementation = await erc1967.getImplementationAddress(address); + + console.log('Upgraded FuelMessagePortal to', implementation); + + return true; + } +}; + +func.tags = ['upgrade_portal']; +func.id = 'upgrade_portal'; +export default func; diff --git a/packages/solidity-contracts/deploy/hardhat/011.gateway_upgrade.ts b/packages/solidity-contracts/deploy/hardhat/011.gateway_upgrade.ts new file mode 100644 index 00000000..9b6e0d82 --- /dev/null +++ b/packages/solidity-contracts/deploy/hardhat/011.gateway_upgrade.ts @@ -0,0 +1,97 @@ +import fs from 'fs'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { DeployFunction } from 'hardhat-deploy/dist/types'; +import path from 'path'; + +import { FuelERC20GatewayV4__factory } from '../../typechain'; + +const ADMIN = '0x32da601374b38154f05904B16F44A1911Aa6f314'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { + ethers, + upgrades: { erc1967 }, + } = hre; + + const [deployer] = await ethers.getSigners(); + + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + let address; + + if (isForking) { + const deploymentPath = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet', + 'FuelERC20GatewayV4.json' + ); + + const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8')); + address = deployment.address; + + const portal = FuelERC20GatewayV4__factory.connect(address, deployer); + + const ADMIN_ROLE = await portal.DEFAULT_ADMIN_ROLE(); + const SET_RATE_LIMITER_ROLE = await portal.SET_RATE_LIMITER_ROLE(); + + const factory = await hre.ethers.getContractFactory('FuelERC20GatewayV4'); + + const newImplementation = await factory.deploy(); + + const newImplementationAddress = await newImplementation.getAddress(); + + let txData = portal.interface.encodeFunctionData('upgradeTo', [ + newImplementationAddress, + ]); + + await deployer.sendTransaction({ + to: ADMIN, + value: ethers.parseEther('100'), + }); + + const impersonatedSigner = await ethers.getImpersonatedSigner(ADMIN); + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + txData = await portal.interface.encodeFunctionData('grantRole', [ + ADMIN_ROLE, + await deployer.getAddress(), + ]); + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + txData = await portal.interface.encodeFunctionData('grantRole', [ + SET_RATE_LIMITER_ROLE, + await deployer.getAddress(), + ]); + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + txData = await portal.interface.encodeFunctionData('requireWhitelist', [ + false, + ]); + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + const implementation = await erc1967.getImplementationAddress(address); + + console.log('Upgraded FuelGateway to', implementation); + + return true; + } +}; + +func.tags = ['upgrade_gateway']; +func.id = 'upgrade_gateway'; +export default func; diff --git a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts index 9900c7e0..79b4a47b 100644 --- a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts +++ b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts @@ -1,18 +1,79 @@ -import { promises as fs } from 'fs'; +import fs from 'fs'; +import { writeFile } from 'fs'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; +import path from 'path'; +import { promisify } from 'util'; import { FuelChainState__factory } from '../../typechain'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployments, ethers } = hre; - const allDeployments = await deployments.all(); + + let address: any; + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; const deploymentsFile: { [name: string]: string } = {}; - for (const key of Object.keys(allDeployments)) { - deploymentsFile[key] = allDeployments[key].address; + + if (isForking) { + let deploymentDir = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet' + ); + + fs.readdirSync(deploymentDir) + .filter((file) => path.extname(file) === '.json') + .forEach((file) => { + const filePath = path.join(deploymentDir, file); + try { + const deployment = JSON.parse(fs.readFileSync(filePath, 'utf8')); + // Use filename (without .json) as the key + const contractName = path.basename(file, '.json'); + deploymentsFile[contractName] = deployment.address; + } catch (error) { + console.error(`Error reading deployment file ${file}:`, error); + } + }); + + deploymentDir = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'localhost', + 'FuelERC721Gateway.json' + ); + + const deployment = JSON.parse(fs.readFileSync(deploymentDir, 'utf8')); + deploymentsFile['FuelERC721Gateway'] = deployment.address; + + deploymentDir = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet', + 'FuelChainState.json' + ); + + const chainStateDeployment = JSON.parse( + fs.readFileSync(deploymentDir, 'utf8') + ); + address = chainStateDeployment.address; + } else { + const allDeployments = await deployments.all(); + for (const key of Object.keys(allDeployments)) { + deploymentsFile[key] = allDeployments[key].address; + } + + ({ address } = await deployments.get('FuelChainState')); } - const { address } = await deployments.get('FuelChainState'); const state = FuelChainState__factory.connect(address, ethers.provider); deploymentsFile['BLOCKS_PER_COMMIT_INTERVAL'] = ( @@ -25,10 +86,12 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { await state.TIME_TO_FINALIZE() ).toString(); - await fs.writeFile( + const writeFileAsync = promisify(writeFile); + + await writeFileAsync( 'deployments/deployments.local.json', - JSON.stringify(deploymentsFile, null, ' '), - 'utf-8' + JSON.stringify(deploymentsFile, null, 2), + 'utf8' ); }; From fab154b4a69ab7a15a71eec31d1892a3e493906b Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:19:52 +0530 Subject: [PATCH 05/81] chore: enable forking in hardhat config --- packages/solidity-contracts/hardhat.config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/solidity-contracts/hardhat.config.ts b/packages/solidity-contracts/hardhat.config.ts index 2815dc62..887110fb 100644 --- a/packages/solidity-contracts/hardhat.config.ts +++ b/packages/solidity-contracts/hardhat.config.ts @@ -41,6 +41,10 @@ const config: HardhatUserConfig = { accounts: { count: 128, }, + forking: { + enabled: !!process.env.TENDERLY_RPC_URL, + url: process.env.TENDERLY_RPC_URL ? process.env.TENDERLY_RPC_URL : '', + }, deploy: ['deploy/hardhat'], }, localhost: { From 8647dbcdb00bb4440fa463c18a6162f5fb607781 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:20:25 +0530 Subject: [PATCH 06/81] chore: add shell scripts --- .../solidity-contracts/scripts/node:up.sh | 1 + packages/solidity-contracts/scripts/test.sh | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 packages/solidity-contracts/scripts/node:up.sh create mode 100644 packages/solidity-contracts/scripts/test.sh diff --git a/packages/solidity-contracts/scripts/node:up.sh b/packages/solidity-contracts/scripts/node:up.sh new file mode 100644 index 00000000..71620aaf --- /dev/null +++ b/packages/solidity-contracts/scripts/node:up.sh @@ -0,0 +1 @@ +docker compose -f ./docker/docker-compose.yml up -d --build \ No newline at end of file diff --git a/packages/solidity-contracts/scripts/test.sh b/packages/solidity-contracts/scripts/test.sh new file mode 100644 index 00000000..b57d53fd --- /dev/null +++ b/packages/solidity-contracts/scripts/test.sh @@ -0,0 +1,35 @@ +# Start the docker compose file with L1 and Fuel Node +echo "\n\nStarting docker..." +pnpm run node:up + +# Wait for the nodes to be ready and run the tests +HEALTH_CHECK_COUNTER=0 +HELTH_CHECK_OUTPUT="" +MAX_CHECK_ATTEMPTS=50 + +waitForNodesToBeReady() { + NODE_URL="http://localhost:4000/v1/playground"; + + printf "\rWaiting for node.${HELTH_CHECK_OUTPUT}" + + 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 pnpm run test:integration + else + # 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 + waitForNodesToBeReady + fi +} + +waitForNodesToBeReady \ No newline at end of file From e684961c84d360b06a3d045548dc028509886afe Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:20:51 +0530 Subject: [PATCH 07/81] chore: add additional committer signer in test setup --- packages/test-utils/src/utils/setup.ts | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/test-utils/src/utils/setup.ts b/packages/test-utils/src/utils/setup.ts index 35640936..3882ef7e 100644 --- a/packages/test-utils/src/utils/setup.ts +++ b/packages/test-utils/src/utils/setup.ts @@ -13,7 +13,7 @@ import { FuelERC721Gateway__factory, } from '@fuel-bridge/solidity-contracts/typechain'; import * as dotenv from 'dotenv'; -import type { Signer as EthSigner } from 'ethers'; +import type { Signer as EthSigner, Provider as EthProvider } from 'ethers'; import { JsonRpcProvider, ethers, @@ -50,6 +50,7 @@ const eth_private_keys: string[] = [ const def_pk_eth_deployer: string = eth_private_keys[0]; const def_pk_eth_signer1: string = eth_private_keys[3]; const def_pk_eth_signer2: string = eth_private_keys[4]; +const def_pk_eth_signer3: string = eth_private_keys[5]; const def_pk_fuel_deployer: string = '0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c'; @@ -66,6 +67,7 @@ export interface SetupOptions { pk_eth_deployer?: string; pk_eth_signer1?: string; pk_eth_signer2?: string; + pk_eth_signer3?: string; pk_fuel_deployer?: string; pk_fuel_signer1?: string; pk_fuel_signer2?: string; @@ -109,6 +111,8 @@ export async function setupEnvironment( 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_eth_signer3: string = + opts.pk_eth_signer3 || process.env.PK_ETH_SIGNER3 || def_pk_eth_signer3; const pk_fuel_deployer: string = opts.pk_fuel_deployer || process.env.PK_FUEL_DEPLOYER || @@ -213,6 +217,18 @@ export async function setupEnvironment( await tx.wait(); } + const eth_signer3 = new NonceManager( + new ethers.Wallet(pk_eth_signer3, eth_provider) + ); + const eth_signer3Balance = await eth_provider.getBalance(eth_signer3); + if (eth_signer3Balance < parseEther('1') && skip_deployer_balance) { + const tx = await eth_deployer.sendTransaction({ + to: eth_signer3, + value: parseEther('1'), + }); + await tx.wait(); + } + // Get contract addresses let eth_fuelChainStateAddress: string = fuel_chain_consensus_addr; let eth_fuelMessagePortalAddress: string = fuel_message_portal_addr; @@ -302,7 +318,7 @@ export async function setupEnvironment( fuelERC20Gateway: eth_fuelERC20Gateway, fuelERC721Gateway: eth_fuelERC721Gateway, deployer: eth_deployer, - signers: [eth_signer1, eth_signer2], + signers: [eth_signer1, eth_signer2, eth_signer3], }, fuel: { provider: fuel_provider, @@ -310,4 +326,4 @@ export async function setupEnvironment( signers: [fuel_signer1, fuel_signer2], }, }; -} +} \ No newline at end of file From c21c645ca79cb8e262ba06b22f3c30ad7b57fce9 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:21:18 +0530 Subject: [PATCH 08/81] feat: add fork integration tests --- .../integration-tests/bridge_erc20.ts | 726 ++++++++++++++++++ .../integration-tests/transfer_eth.ts | 509 ++++++++++++ 2 files changed, 1235 insertions(+) create mode 100644 packages/solidity-contracts/integration-tests/bridge_erc20.ts create mode 100644 packages/solidity-contracts/integration-tests/transfer_eth.ts diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts new file mode 100644 index 00000000..65954400 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/bridge_erc20.ts @@ -0,0 +1,726 @@ +import type { BridgeFungibleToken } from '@fuel-bridge/fungible-token'; +import type { TestEnvironment } from '@fuel-bridge/test-utils'; +import { + setupEnvironment, + relayCommonMessage, + waitForMessage, + createRelayMessageParams, + getOrDeployECR20Contract, + getOrDeployL2Bridge, + FUEL_TX_PARAMS, + getMessageOutReceipt, + fuel_to_eth_address, + waitForBlockFinalization, + getTokenId, + getBlock, + FUEL_CALL_TX_PARAMS, + hardhatSkipTime, +} from '@fuel-bridge/test-utils'; +import chai from 'chai'; +import { toBeHex, parseEther } from 'ethers'; +import type { JsonRpcProvider, Signer } from 'ethers'; +import { Address, BN } from 'fuels'; +import type { + AbstractAddress, + WalletUnlocked as FuelWallet, + MessageProof, + Provider, +} from 'fuels'; + +import { RATE_LIMIT_AMOUNT, RATE_LIMIT_DURATION } from '../protocol/constants'; +import type { Token } from '../typechain'; + +const { expect } = chai; + +describe('Bridging ERC20 tokens', async function () { + // Timeout 6 minutes + const DEFAULT_TIMEOUT_MS: number = 400_000; + const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000; + const DECIMAL_DIFF = 1_000_000_000n; + + let env: TestEnvironment; + let eth_testToken: Token; + let eth_testTokenAddress: string; + let eth_erc20GatewayAddress: string; + let fuel_bridge: BridgeFungibleToken; + let fuel_bridgeImpl: BridgeFungibleToken; + let fuel_bridgeContractId: string; + let fuel_testAssetId: string; + + // override the default test timeout from 2000ms + this.timeout(DEFAULT_TIMEOUT_MS); + + async function forwardFuelChain(provider: Provider, blocksToForward: string) { + await provider.produceBlocks(Number(blocksToForward)).catch(console.error); + } + + async function getBlockWithHeight(env: any, height: string): Promise { + const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { + block(height: $height) { + id + } + }`; + const BLOCK_BY_HEIGHT_ARGS = { + height: height, + }; + + return fetch(env.fuel.provider.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: BLOCK_BY_HEIGHT_QUERY, + variables: BLOCK_BY_HEIGHT_ARGS, + }), + }) + .then((res: any) => res.json()) + .then(async (res) => { + if (!res.data.block) { + throw new Error(`Could not fetch block with height ${height}`); + } + + return res.data.block; + }); + } + + async function generateWithdrawalMessageProof( + fuel_bridge: BridgeFungibleToken, + fuelTokenSender: FuelWallet, + ethereumTokenReceiverAddress: string, + NUM_TOKENS: bigint, + DECIMAL_DIFF: bigint + ): Promise { + // withdraw tokens back to the base chain + fuel_bridge.account = fuelTokenSender; + const paddedAddress = + '0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0'); + const fuelTokenSenderBalance = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + const transactionRequest = await fuel_bridge.functions + .withdraw(paddedAddress) + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .txParams({ + tip: 0, + maxFee: 1, + }) + .callParams({ + forward: { + amount: new BN(NUM_TOKENS.toString()).div( + new BN(DECIMAL_DIFF.toString()) + ), + assetId: fuel_testAssetId, + }, + }) + .fundWithRequiredCoins(); + + const tx = await fuelTokenSender.sendTransaction(transactionRequest); + const fWithdrawTxResult = await tx.waitForResult(); + expect(fWithdrawTxResult.status).to.equal('success'); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await fuelTokenSender.getBalance(fuel_testAssetId); + + expect( + newSenderBalance.eq( + fuelTokenSenderBalance.sub(toBeHex(NUM_TOKENS / DECIMAL_DIFF)) + ) + ).to.be.true; + + // Wait for the commited block + const withdrawBlock = await getBlock( + env.fuel.provider.url, + fWithdrawTxResult.blockId! + ); + + const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); + + const blocksPerCommitInterval = ( + await env.eth.fuelChainState.BLOCKS_PER_COMMIT_INTERVAL() + ).toString(); + + // Add + 1 to the block height to wait the next block + // that enable to proof the message + const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); + const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); + + let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit( + '0x0000000000000000000000000000000000000000000000000000000000000000', + commitHeight.toString() + ); + + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + + await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); + + const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit(block.id, commitHeight.toString()); + + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); + return await fuelTokenSender.provider.getMessageProof( + tx.id, + messageOutReceipt.nonce, + block.id + ); + } + + async function relayMessageFromFuel( + env: TestEnvironment, + withdrawMessageProof: MessageProof + ) { + // wait for block finalization + await waitForBlockFinalization(env, withdrawMessageProof); + + // construct relay message proof data + const relayMessageParams = createRelayMessageParams(withdrawMessageProof); + + // relay message + await env.eth.fuelMessagePortal.relayMessage( + relayMessageParams.message, + relayMessageParams.rootBlockHeader, + relayMessageParams.blockHeader, + relayMessageParams.blockInHistoryProof, + relayMessageParams.messageInBlockProof + ); + } + + async function relayMessageFromEthereum( + env: TestEnvironment, + fuelTokenMessageReceiver: AbstractAddress, + fuelTokenMessageNonce: BN, + fuel_AssetId: string, + amount: bigint + ) { + // relay the message ourselves + const message = await waitForMessage( + env.fuel.provider, + fuelTokenMessageReceiver, + fuelTokenMessageNonce, + FUEL_MESSAGE_TIMEOUT_MS + ); + expect(message).to.not.be.null; + + const tx = await relayCommonMessage(env.fuel.deployer, message, { + maturity: undefined, + contractIds: [fuel_bridgeImpl.id.toHexString()], + }); + + const txResult = await tx.waitForResult(); + + expect(txResult.status).to.equal('success'); + expect(txResult.mintedAssets.length).to.equal(1); + + const [mintedAsset] = txResult.mintedAssets; + + expect(mintedAsset.assetId).to.equal(fuel_AssetId); + expect(mintedAsset.amount.toString()).to.equal( + (amount / DECIMAL_DIFF).toString() + ); + } + + before(async () => { + env = await setupEnvironment({}); + eth_erc20GatewayAddress = ( + await env.eth.fuelERC20Gateway.getAddress() + ).toLowerCase(); + + eth_testToken = await getOrDeployECR20Contract(env); + eth_testTokenAddress = (await eth_testToken.getAddress()).toLowerCase(); + + const { contract, implementation } = await getOrDeployL2Bridge( + env, + env.eth.fuelERC20Gateway + ); + + fuel_bridge = contract; + fuel_bridgeImpl = implementation; + + fuel_bridgeContractId = fuel_bridge.id.toHexString(); + + await env.eth.fuelERC20Gateway + .connect(env.eth.deployer) + .setAssetIssuerId(fuel_bridgeContractId); + fuel_testAssetId = getTokenId(fuel_bridge, eth_testTokenAddress); + + // initializing rate limit params for the token + await env.eth.fuelERC20Gateway + .connect(env.eth.deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + RATE_LIMIT_AMOUNT.toString(), + RATE_LIMIT_DURATION + ); + + await env.eth.fuelERC20Gateway + .connect(env.eth.deployer) + .updateRateLimitStatus(eth_testTokenAddress, true); + + const { value: expectedGatewayContractId } = await fuel_bridge.functions + .bridged_token_gateway() + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .txParams(FUEL_CALL_TX_PARAMS) + .dryRun(); + + // 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(expectedGatewayContractId)).to.equal( + eth_erc20GatewayAddress + ); + expect(await eth_testToken.decimals()).to.equal(18n); + + // mint tokens as starting balances + await eth_testToken + .mint(await env.eth.deployer.getAddress(), 10_000) + .then((tx) => tx.wait()); + + await eth_testToken + .mint(await env.eth.signers[0].getAddress(), 10_000) + .then((tx) => tx.wait()); + + await eth_testToken + .mint(await env.eth.signers[1].getAddress(), 10_000) + .then((tx) => tx.wait()); + }); + + describe('Bridge ERC20 to Fuel', async () => { + const NUM_TOKENS = 100000000000000000000n; + let ethereumTokenSender: Signer; + let ethereumTokenSenderAddress: string; + let ethereumTokenSenderBalance: bigint; + let fuelTokenReceiver: FuelWallet; + let fuelTokenReceiverAddress: string; + let fuelTokenReceiverBalance: BN; + let fuelTokenMessageNonce: BN; + let fuelTokenMessageReceiver: AbstractAddress; + + before(async () => { + ethereumTokenSender = env.eth.signers[0]; + ethereumTokenSenderAddress = await ethereumTokenSender.getAddress(); + + await eth_testToken + .mint(ethereumTokenSenderAddress, NUM_TOKENS) + .then((tx) => tx.wait()); + + ethereumTokenSenderBalance = await eth_testToken.balanceOf( + ethereumTokenSenderAddress + ); + + fuelTokenReceiver = env.fuel.signers[0]; + fuelTokenReceiverAddress = fuelTokenReceiver.address.toHexString(); + fuelTokenReceiverBalance = await fuelTokenReceiver.getBalance( + fuel_testAssetId + ); + }); + + it('Bridge ERC20 via FuelERC20Gateway', async () => { + // approve FuelERC20Gateway to spend the tokens + await eth_testToken + .connect(ethereumTokenSender) + .approve(eth_erc20GatewayAddress, NUM_TOKENS) + .then((tx) => tx.wait()); + + // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel + const receipt = await env.eth.fuelERC20Gateway + .connect(ethereumTokenSender) + .deposit(fuelTokenReceiverAddress, eth_testTokenAddress, NUM_TOKENS) + .then((tx) => tx.wait()); + + expect(receipt!.status).to.equal(1); + // parse events from logs + const [event, ...restOfEvents] = + await env.eth.fuelMessagePortal.queryFilter( + env.eth.fuelMessagePortal.filters.MessageSent, + receipt!.blockNumber, + receipt!.blockNumber + ); + expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event + + fuelTokenMessageNonce = new BN(event.args.nonce.toString()); + fuelTokenMessageReceiver = Address.fromB256(event.args.recipient); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await eth_testToken.balanceOf( + ethereumTokenSenderAddress + ); + expect(newSenderBalance === ethereumTokenSenderBalance - NUM_TOKENS).to.be + .true; + }); + + it('Relay messages from Ethereum on Fuel', async () => { + // override the default test timeout from 2000ms + this.timeout(FUEL_MESSAGE_TIMEOUT_MS); + // relay the standard erc20 deposit + await relayMessageFromEthereum( + env, + fuelTokenMessageReceiver, + fuelTokenMessageNonce, + fuel_testAssetId, + NUM_TOKENS + ); + + // override the default test timeout from 2000ms + this.timeout(FUEL_MESSAGE_TIMEOUT_MS); + }); + + it('Check metadata was registered', async () => { + await fuel_bridge.functions + .asset_to_l1_address({ bits: fuel_testAssetId }) + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .call(); + + const { value: l2_decimals } = await fuel_bridge.functions + .decimals({ bits: fuel_testAssetId }) + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .get(); + + expect(l2_decimals).to.be.equal(9); + }); + + it('Check ERC20 arrived on Fuel', async () => { + // check that the recipient balance has increased by the expected amount + const newReceiverBalance = await fuelTokenReceiver.getBalance( + fuel_testAssetId + ); + + expect( + newReceiverBalance.eq( + fuelTokenReceiverBalance.add(toBeHex(NUM_TOKENS / DECIMAL_DIFF)) + ) + ).to.be.true; + }); + + it('Bridge metadata', async () => { + // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel + const receipt = await env.eth.fuelERC20Gateway + .connect(ethereumTokenSender) + .sendMetadata(eth_testTokenAddress) + .then((tx) => tx.wait()); + + // parse events from logs + const [event, ...restOfEvents] = + await env.eth.fuelMessagePortal.queryFilter( + env.eth.fuelMessagePortal.filters.MessageSent, + receipt!.blockNumber, + receipt!.blockNumber + ); + expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event + + const nonce = new BN(event.args.nonce.toString()); + const fuelReceiver = Address.fromB256(event.args.recipient); + + // relay the message ourselves + const message = await waitForMessage( + env.fuel.provider, + fuelReceiver, + nonce, + FUEL_MESSAGE_TIMEOUT_MS + ); + expect(message).to.not.be.null; + + const tx = await relayCommonMessage(env.fuel.deployer, message, { + ...FUEL_TX_PARAMS, + maturity: undefined, + contractIds: [fuel_bridgeImpl.id.toHexString()], + }); + + const txResult = await tx.waitForResult(); + expect(txResult.status).to.equal('success'); + + const fuel_name = ( + await fuel_bridge.functions.name({ bits: fuel_testAssetId }).dryRun() + ).value; + const fuel_symbol = ( + await fuel_bridge.functions.symbol({ bits: fuel_testAssetId }).dryRun() + ).value; + + const eth_name = await eth_testToken.name(); + const eth_symbol = await eth_testToken.symbol(); + + expect(fuel_name).to.equal(eth_name); + expect(fuel_symbol).to.equal(eth_symbol); + }); + }); + + describe('Bridge ERC20 from Fuel', async () => { + const NUM_TOKENS = 10000000000000000000n; + const largeRateLimit = `30`; + let fuelTokenSender: FuelWallet; + let ethereumTokenReceiver: Signer; + let ethereumTokenReceiverAddress: string; + let ethereumTokenReceiverBalance: bigint; + let withdrawMessageProof: MessageProof | null; + let tokenBalanceBeforeWithdrawingOnFuel: BN; + + before(async () => { + fuelTokenSender = env.fuel.signers[0]; + ethereumTokenReceiver = env.eth.signers[0]; + ethereumTokenReceiverAddress = await ethereumTokenReceiver.getAddress(); + ethereumTokenReceiverBalance = await eth_testToken.balanceOf( + ethereumTokenReceiverAddress + ); + + tokenBalanceBeforeWithdrawingOnFuel = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + }); + + it('Bridge ERC20 via Fuel token contract', async () => { + // withdraw tokens back to the base chain + withdrawMessageProof = await generateWithdrawalMessageProof( + fuel_bridge, + fuelTokenSender, + ethereumTokenReceiverAddress, + NUM_TOKENS, + DECIMAL_DIFF + ); + }); + + it('Relay Message from Fuel on Ethereum', async () => { + const withdrawnAmountBeforeRelay = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + const rateLimitEndDuratioBeforeRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + // relay message + await relayMessageFromFuel(env, withdrawMessageProof!); + + // check rate limit params + const withdrawnAmountAfterRelay = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + const rateLimitEndDuratioAfterRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + expect(rateLimitEndDuratioAfterRelay === rateLimitEndDuratioBeforeRelay) + .to.be.true; + + expect( + withdrawnAmountAfterRelay === NUM_TOKENS + withdrawnAmountBeforeRelay + ).to.be.true; + }); + + it('Check the remaining token balance on Fuel after the first withdrawal', async () => { + // fetch the remaining token balance + const currentTokenBalance = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + + // currentTokenBalance has BN type by default hence the use of BN for conversion here + const expectedRemainingTokenBalanceOnFuel = + tokenBalanceBeforeWithdrawingOnFuel.sub( + new BN((NUM_TOKENS / DECIMAL_DIFF).toString()) + ); + + expect(currentTokenBalance.eq(expectedRemainingTokenBalanceOnFuel)).to.be + .true; + }); + + it('Rate limit parameters are updated when current withdrawn amount is more than the new limit & set a new higher limit', async () => { + const deployer = await env.eth.deployer; + const newRateLimit = '5'; + + let withdrawnAmountBeforeReset = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(newRateLimit), + RATE_LIMIT_DURATION + ); + + let currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + // current withdrawn amount doesn't change when rate limit is updated + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + + withdrawnAmountBeforeReset = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(largeRateLimit), + RATE_LIMIT_DURATION + ); + + currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + }); + + it('Rate limit parameters are updated when the initial duration is over', async () => { + const deployer = await env.eth.deployer; + + const rateLimitDuration = + await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); + + // fast forward time + await hardhatSkipTime( + env.eth.provider as JsonRpcProvider, + rateLimitDuration * 2n + ); + const currentPeriodEndBeforeRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(largeRateLimit), + RATE_LIMIT_DURATION + ); + + const currentWitdrawnAmountAfterReset = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect(currentWitdrawnAmountAfterReset == 0n).to.be.true; + + // withdraw tokens back to the base chain + withdrawMessageProof = await generateWithdrawalMessageProof( + fuel_bridge, + fuelTokenSender, + ethereumTokenReceiverAddress, + NUM_TOKENS, + DECIMAL_DIFF + ); + + // relay message + await relayMessageFromFuel(env, withdrawMessageProof!); + + const currentPeriodEndAfterRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + .true; + + const currentPeriodAmount = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect(currentPeriodAmount === NUM_TOKENS).to.be.true; + }); + + it('Check the remaining token balance on Fuel after the second withdrawal', async () => { + // fetch the remaining token balance + const currentTokenBalance = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + + // currentTokenBalance has BN type by default hence the use of BN for conversion here + const expectedRemainingTokenBalanceOnFuel = + tokenBalanceBeforeWithdrawingOnFuel.sub( + new BN(((NUM_TOKENS * 2n) / DECIMAL_DIFF).toString()) + ); + + expect(currentTokenBalance.eq(expectedRemainingTokenBalanceOnFuel)).to.be + .true; + }); + + it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { + const rateLimitDuration = + await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); + + const deployer = await env.eth.deployer; + const newRateLimit = `40`; + + // fast forward time + await hardhatSkipTime( + env.eth.provider as JsonRpcProvider, + rateLimitDuration * 2n + ); + + const currentWithdrawnAmountBeforeSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + const currentPeriodEndBeforeSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(newRateLimit), + RATE_LIMIT_DURATION + ); + + const currentPeriodEndAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + const currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect( + currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit + ).to.be.true; + + expect( + currentWithdrawnAmountBeforeSettingLimit > + currentWithdrawnAmountAfterSettingLimit + ).to.be.true; + + expect(currentWithdrawnAmountAfterSettingLimit === 0n).to.be.true; + }); + + it('Check ERC20 arrived on Ethereum', async () => { + // check that the recipient balance has increased by the expected amount + const newReceiverBalance = await eth_testToken.balanceOf( + ethereumTokenReceiverAddress + ); + expect( + newReceiverBalance === ethereumTokenReceiverBalance + NUM_TOKENS * 2n + ).to.be.true; + }); + }); +}); diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts new file mode 100644 index 00000000..9d5d1a4c --- /dev/null +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -0,0 +1,509 @@ +import type { TestEnvironment } from '@fuel-bridge/test-utils'; +import { + setupEnvironment, + fuels_parseEther, + createRelayMessageParams, + getMessageOutReceipt, + waitForMessage, + waitForBlockFinalization, + getBlock, + FUEL_CALL_TX_PARAMS, +} from '@fuel-bridge/test-utils'; +import chai from 'chai'; +import { parseEther } from 'ethers'; +import type { Signer } from 'ethers'; +import { Address, BN, padFirst12BytesOfEvmAddress } from 'fuels'; +import type { + AbstractAddress, + WalletUnlocked as FuelWallet, + MessageProof, + Provider, +} from 'fuels'; + +const { expect } = chai; + +describe('Transferring ETH', async function () { + // Timeout 6 minutes + const DEFAULT_TIMEOUT_MS: number = 400_000; + const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000; + let BASE_ASSET_ID: string; + + let env: TestEnvironment; + + // override the default test timeout of 2000ms + this.timeout(DEFAULT_TIMEOUT_MS); + + async function forwardFuelChain(provider: Provider, blocksToForward: string) { + await provider.produceBlocks(Number(blocksToForward)).catch(console.error); + } + + async function getBlockWithHeight(env: any, height: string): Promise { + const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { + block(height: $height) { + id + } + }`; + const BLOCK_BY_HEIGHT_ARGS = { + height: height, + }; + + return fetch(env.fuel.provider.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: BLOCK_BY_HEIGHT_QUERY, + variables: BLOCK_BY_HEIGHT_ARGS, + }), + }) + .then((res: any) => res.json()) + .then(async (res) => { + if (!res.data.block) { + throw new Error(`Could not fetch block with height ${height}`); + } + + return res.data.block; + }); + } + + async function generateWithdrawalMessageProof( + fuelETHSender: FuelWallet, + ethereumETHReceiverAddress: string, + NUM_ETH: string + ): Promise { + // withdraw ETH back to the base chain + const fWithdrawTx = await fuelETHSender.withdrawToBaseLayer( + Address.fromString( + padFirst12BytesOfEvmAddress(ethereumETHReceiverAddress) + ), + fuels_parseEther(NUM_ETH), + FUEL_CALL_TX_PARAMS + ); + const fWithdrawTxResult = await fWithdrawTx.waitForResult(); + expect(fWithdrawTxResult.status).to.equal('success'); + + // Wait for the commited block + const withdrawBlock = await getBlock( + env.fuel.provider.url, + fWithdrawTxResult.blockId! + ); + + const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); + + const blocksPerCommitInterval = ( + await env.eth.fuelChainState.BLOCKS_PER_COMMIT_INTERVAL() + ).toString(); + + // Add + 1 to the block height to wait the next block + // that enable to proof the message + const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); + const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); + + let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit( + '0x0000000000000000000000000000000000000000000000000000000000000000', + commitHeight.toString() + ); + + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + + await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); + + const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit(block.id, commitHeight.toString()); + + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // get message proof + const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); + + return await fuelETHSender.provider.getMessageProof( + fWithdrawTx.id, + messageOutReceipt.nonce, + block.id + ); + } + + async function relayMessage( + env: TestEnvironment, + withdrawMessageProof: MessageProof + ) { + // wait for block finalization + await waitForBlockFinalization(env, withdrawMessageProof); + + // construct relay message proof data + const relayMessageParams = createRelayMessageParams(withdrawMessageProof); + + // relay message + await env.eth.fuelMessagePortal.relayMessage( + relayMessageParams.message, + relayMessageParams.rootBlockHeader, + relayMessageParams.blockHeader, + relayMessageParams.blockInHistoryProof, + relayMessageParams.messageInBlockProof + ); + } + + before(async () => { + env = await setupEnvironment({}); + BASE_ASSET_ID = env.fuel.provider.getBaseAssetId(); + }); + + describe('Send ETH to Fuel', async () => { + const NUM_ETH = '30'; + let ethereumETHSender: Signer; + let ethereumETHSenderAddress: string; + let fuelETHReceiver: AbstractAddress; + let fuelETHReceiverAddress: string; + let fuelETHReceiverBalance: BN; + let fuelETHMessageNonce: BN; + + before(async () => { + ethereumETHSender = env.eth.signers[0]; + ethereumETHSenderAddress = await ethereumETHSender.getAddress(); + fuelETHReceiver = env.fuel.signers[0].address; + fuelETHReceiverAddress = fuelETHReceiver.toHexString(); + + fuelETHReceiverBalance = await env.fuel.provider.getBalance( + fuelETHReceiver, + BASE_ASSET_ID + ); + }); + + it('Send ETH via MessagePortal', async () => { + // use the FuelMessagePortal to directly send ETH which should be immediately spendable + const tx = await env.eth.fuelMessagePortal + .connect(ethereumETHSender) + .depositETH(fuelETHReceiverAddress, { + value: parseEther(NUM_ETH), + }); + const receipt = await tx.wait(); + expect(receipt!.status).to.equal(1); + + // parse events from logs + const filter = env.eth.fuelMessagePortal.filters.MessageSent( + undefined, // Args set to null since there should be just 1 event for MessageSent + undefined, + undefined, + undefined, + undefined + ); + + const [event, ...restOfEvents] = + await env.eth.fuelMessagePortal.queryFilter( + filter, + receipt!.blockNumber, + receipt!.blockNumber + ); + expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event + + fuelETHMessageNonce = new BN(event.args.nonce.toString()); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await env.eth.provider.getBalance( + ethereumETHSenderAddress, + receipt!.blockNumber + ); + + const txCost = receipt!.fee; + + const expectedSenderBalance = + (await env.eth.provider.getBalance( + ethereumETHSender, + receipt!.blockNumber - 1 + )) - + txCost - + parseEther(NUM_ETH); + + expect(newSenderBalance).to.be.eq(expectedSenderBalance); + }); + + it('Wait for ETH to arrive on Fuel', async function () { + // wait for message to appear in fuel client + 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 + const newReceiverBalance = await env.fuel.provider.getBalance( + fuelETHReceiver, + BASE_ASSET_ID + ); + expect( + newReceiverBalance.eq( + fuelETHReceiverBalance.add(fuels_parseEther(NUM_ETH)) + ) + ).to.be.true; + }); + }); + + describe('Send ETH from Fuel', async () => { + const NUM_ETH = '0.001'; + let fuelETHSender: FuelWallet; + let fuelETHSenderBalance: BN; + let ethereumETHReceiver: Signer; + let ethereumETHReceiverAddress: string; + let ethereumETHReceiverBalance: bigint; + let withdrawMessageProof: MessageProof | null; + + before(async () => { + fuelETHSender = env.fuel.signers[1]; + fuelETHSenderBalance = await fuelETHSender.getBalance(BASE_ASSET_ID); + ethereumETHReceiver = env.eth.signers[1]; + ethereumETHReceiverAddress = await ethereumETHReceiver.getAddress(); + ethereumETHReceiverBalance = await env.eth.provider.getBalance( + ethereumETHReceiver + ); + }); + + it('Send ETH via OutputMessage', async () => { + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await fuelETHSender.getBalance(BASE_ASSET_ID); + + // 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(); + expect(diffOnSenderBalance.startsWith(NUM_ETH)).to.be.true; + }); + + it('Relay Message from Fuel on Ethereum', async () => { + await relayMessage(env, withdrawMessageProof!); + }); + + it('Check ETH arrived on Ethereum', async () => { + // check that the recipient balance has increased by the expected amount + const newReceiverBalance = await env.eth.provider.getBalance( + ethereumETHReceiver + ); + + expect( + newReceiverBalance <= ethereumETHReceiverBalance + parseEther(NUM_ETH) + ).to.be.true; + }); + }); + + describe('ETH Withdrawls based on rate limit updates', async () => { + const NUM_ETH = '9'; + const largeRateLimit = `30`; + let fuelETHSender: FuelWallet; + let ethereumETHReceiver: Signer; + let ethereumETHReceiverAddress: string; + let withdrawMessageProof: MessageProof | null; + let rateLimitDuration: bigint; + + before(async () => { + fuelETHSender = env.fuel.signers[1]; + ethereumETHReceiver = env.eth.signers[1]; + ethereumETHReceiverAddress = await ethereumETHReceiver.getAddress(); + + await env.eth.fuelMessagePortal + .connect(env.eth.deployer) + .updateRateLimitStatus(true); + rateLimitDuration = await env.eth.fuelMessagePortal.RATE_LIMIT_DURATION(); + }); + + it('Checks rate limit params after relaying', async () => { + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + const withdrawnAmountBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + await relayMessage(env, withdrawMessageProof!); + + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect( + currentPeriodAmount === parseEther(NUM_ETH) + withdrawnAmountBeforeRelay + ).to.be.true; + }); + + it('Relays ETH after the rate limit is updated', async () => { + const deployer = env.eth.deployer; + const newRateLimit = `30`; + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(newRateLimit)); + + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + const withdrawnAmountBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + let currentWIthdrawnAmountReset = false; + + if (withdrawnAmountBeforeRelay > parseEther(newRateLimit)) { + currentWIthdrawnAmountReset = true; + + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + } + + await relayMessage(env, withdrawMessageProof!); + + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + if (currentWIthdrawnAmountReset) + expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + else { + expect( + currentPeriodAmount === + parseEther(NUM_ETH) + withdrawnAmountBeforeRelay + ).to.be.true; + } + }); + + it('Rate limit parameters are updated when current withdrawn amount is more than the new limit & set a new higher limit', async () => { + const deployer = env.eth.deployer; + const newRateLimit = `10`; + + let withdrawnAmountBeforeReset = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(newRateLimit)); + + let currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + // current withdrawn amount doesn't change when rate limit is updated + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + + withdrawnAmountBeforeReset = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(largeRateLimit)); + + currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + }); + + it('Rate limit parameters are updated when the initial duration is over', async () => { + const deployer = env.eth.deployer; + + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + const currentPeriodEndBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(largeRateLimit)); + + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + await relayMessage(env, withdrawMessageProof!); + + const currentPeriodEndAfterRelay = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + + expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + .true; + + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + }); + + it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { + const deployer = await env.eth.deployer; + const newRateLimit = `40`; + + const currentWithdrawnAmountBeforeSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + const currentPeriodEndBeforeSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(newRateLimit)); + + const currentPeriodEndAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + const currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect( + currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit + ).to.be.true; + + expect( + currentWithdrawnAmountBeforeSettingLimit > + currentWithdrawnAmountAfterSettingLimit + ).to.be.true; + + expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; + }); + }); +}); From f36aef9a9c845c77ade0475bc79e57db3959d312 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 15:21:59 +0530 Subject: [PATCH 09/81] feat: add github action for fork integration tests --- .github/workflows/upgrade-test-suite.yml | 36 ++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/upgrade-test-suite.yml diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml new file mode 100644 index 00000000..002d0178 --- /dev/null +++ b/.github/workflows/upgrade-test-suite.yml @@ -0,0 +1,36 @@ +name: Upgrade Test Suite + +on: + push: + branches: + - main + pull_request: + branches: + - main # Target branch for the PR + release: + types: [published] + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + upgrade-test-suite: + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@v3 + - uses: FuelLabs/github-actions/setups/node@master + with: + node-version: 20.16.0 + pnpm-version: 9.0.6 + - name: Sets the tenderly rpc endpoint in the L1 docker container env + run: | + cat << EOF > l1_chain.env + TENDERLY_RPC_URL=${{ secrets.TENDERLY_RPC_URL }} + EOF + working-directory: ./packages/solidity-contracts/docker/envs + - name: Run integration tests on a L1 fork after upgrading contracts + run: | + pnpm upgrade:test:integration + working-directory: ./packages/solidity-contracts From 7b932fbe6be8de3393851fc383c26ccd21baa141 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 27 Dec 2024 18:06:02 +0530 Subject: [PATCH 10/81] chore: add build command in workflow --- .github/workflows/upgrade-test-suite.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index 002d0178..7ca7c424 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -24,6 +24,8 @@ jobs: with: node-version: 20.16.0 pnpm-version: 9.0.6 + - name: Build project + run: pnpm build - name: Sets the tenderly rpc endpoint in the L1 docker container env run: | cat << EOF > l1_chain.env From 2ebc8b7e1bc83417a37fa01764d479cd673c6c03 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 30 Dec 2024 10:55:27 +0530 Subject: [PATCH 11/81] chore: add docker and rust setup in ci --- .github/workflows/upgrade-test-suite.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index 7ca7c424..82ec5a43 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -24,6 +24,11 @@ jobs: with: node-version: 20.16.0 pnpm-version: 9.0.6 + - uses: FuelLabs/github-actions/setups/docker@master + with: + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: ./.github/actions/setup-rust - name: Build project run: pnpm build - name: Sets the tenderly rpc endpoint in the L1 docker container env From 22dafb85c359bb6c894367cdba08a930778da1cb Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 30 Dec 2024 15:28:08 +0530 Subject: [PATCH 12/81] chore: debug ci failure --- .../integration-tests/bridge_erc20.ts | 14 +++++++++++--- .../integration-tests/transfer_eth.ts | 12 ++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts index 65954400..fa6dc64f 100644 --- a/packages/solidity-contracts/integration-tests/bridge_erc20.ts +++ b/packages/solidity-contracts/integration-tests/bridge_erc20.ts @@ -146,16 +146,20 @@ describe('Bridging ERC20 tokens', async function () { const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - + + // fast forward post the commit cooldown period await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour await env.eth.provider.send('evm_mine', []); // Mine a new block + + // override the commit hash in a existing block await env.eth.fuelChainState .connect(env.eth.signers[1]) .commit( '0x0000000000000000000000000000000000000000000000000000000000000000', commitHeight.toString() ); - + + // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ Number(TIME_TO_FINALIZE) * 2, ]); @@ -163,17 +167,21 @@ describe('Bridging ERC20 tokens', async function () { cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + // fast forward post the commit cooldown period await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour await env.eth.provider.send('evm_mine', []); // Mine a new block - + + // produce more blocks to fetch the block height await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + // reset the commit hash in the local L2 network await env.eth.fuelChainState .connect(env.eth.signers[1]) .commit(block.id, commitHeight.toString()); + // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ Number(TIME_TO_FINALIZE) * 2, ]); diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 9d5d1a4c..5df1477f 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -102,8 +102,11 @@ describe('Transferring ETH', async function () { let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + // fast forward post the commit cooldown period await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour await env.eth.provider.send('evm_mine', []); // Mine a new block + + // override the commit hash in a existing block await env.eth.fuelChainState .connect(env.eth.signers[1]) .commit( @@ -111,6 +114,7 @@ describe('Transferring ETH', async function () { commitHeight.toString() ); + // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ Number(TIME_TO_FINALIZE) * 2, ]); @@ -118,17 +122,21 @@ describe('Transferring ETH', async function () { cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + // fast forward post the commit cooldown period await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour await env.eth.provider.send('evm_mine', []); // Mine a new block + // produce more blocks to fetch the block height await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + // reset the commit hash in the local L2 network await env.eth.fuelChainState .connect(env.eth.signers[1]) .commit(block.id, commitHeight.toString()); + // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ Number(TIME_TO_FINALIZE) * 2, ]); @@ -446,9 +454,13 @@ describe('Transferring ETH', async function () { const currentPeriodEndBeforeRelay = await env.eth.fuelMessagePortal.currentPeriodEnd(); + console.log("reset limit") + await env.eth.fuelMessagePortal .connect(deployer) .resetRateLimitAmount(parseEther(largeRateLimit)); + + console.log("generateWithdrawalMessageProof") withdrawMessageProof = await generateWithdrawalMessageProof( fuelETHSender, From 092e1db464e56b9b2e9f9935f7b64ac06c71162a Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 30 Dec 2024 16:53:18 +0530 Subject: [PATCH 13/81] chore: fast forward finalization time before relay --- .../solidity-contracts/integration-tests/transfer_eth.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 5df1477f..3dca9ae6 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -162,6 +162,14 @@ describe('Transferring ETH', async function () { // construct relay message proof data const relayMessageParams = createRelayMessageParams(withdrawMessageProof); + const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); + + // fast forward to the block finalization time + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 100, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + // relay message await env.eth.fuelMessagePortal.relayMessage( relayMessageParams.message, From 8768fbb838aff7456965d477ce1b035a2d8ed7d6 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 31 Dec 2024 12:06:44 +0530 Subject: [PATCH 14/81] chore: debug ci failure --- .../integration-tests/transfer_eth.ts | 77 ++++++++++--------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 3dca9ae6..d20d388d 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -450,45 +450,45 @@ describe('Transferring ETH', async function () { ).to.be.true; }); - it('Rate limit parameters are updated when the initial duration is over', async () => { - const deployer = env.eth.deployer; + // it('Rate limit parameters are updated when the initial duration is over', async () => { + // const deployer = env.eth.deployer; - // fast forward time - await env.eth.provider.send('evm_increaseTime', [ - Number(rateLimitDuration) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block + // // fast forward time + // await env.eth.provider.send('evm_increaseTime', [ + // Number(rateLimitDuration) * 2, + // ]); + // await env.eth.provider.send('evm_mine', []); // Mine a new block - const currentPeriodEndBeforeRelay = - await env.eth.fuelMessagePortal.currentPeriodEnd(); + // const currentPeriodEndBeforeRelay = + // await env.eth.fuelMessagePortal.currentPeriodEnd(); - console.log("reset limit") + // console.log("reset limit") - await env.eth.fuelMessagePortal - .connect(deployer) - .resetRateLimitAmount(parseEther(largeRateLimit)); + // await env.eth.fuelMessagePortal + // .connect(deployer) + // .resetRateLimitAmount(parseEther(largeRateLimit)); - console.log("generateWithdrawalMessageProof") + // console.log("generateWithdrawalMessageProof") - withdrawMessageProof = await generateWithdrawalMessageProof( - fuelETHSender, - ethereumETHReceiverAddress, - NUM_ETH - ); + // withdrawMessageProof = await generateWithdrawalMessageProof( + // fuelETHSender, + // ethereumETHReceiverAddress, + // NUM_ETH + // ); - await relayMessage(env, withdrawMessageProof!); + // await relayMessage(env, withdrawMessageProof!); - const currentPeriodEndAfterRelay = - await env.eth.fuelMessagePortal.currentPeriodEnd(); + // const currentPeriodEndAfterRelay = + // await env.eth.fuelMessagePortal.currentPeriodEnd(); - expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be - .true; + // expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + // .true; - const currentPeriodAmount = - await env.eth.fuelMessagePortal.currentPeriodAmount(); + // const currentPeriodAmount = + // await env.eth.fuelMessagePortal.currentPeriodAmount(); - expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; - }); + // expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + // }); it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { const deployer = await env.eth.deployer; @@ -505,25 +505,30 @@ describe('Transferring ETH', async function () { ]); await env.eth.provider.send('evm_mine', []); // Mine a new block + console.log("reset limit") + await env.eth.fuelMessagePortal .connect(deployer) .resetRateLimitAmount(parseEther(newRateLimit)); + console.log("reset done") + + const currentPeriodEndAfterSettingLimit = await env.eth.fuelMessagePortal.currentPeriodEnd(); const currentWithdrawnAmountAfterSettingLimit = await env.eth.fuelMessagePortal.currentPeriodAmount(); - expect( - currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit - ).to.be.true; + // expect( + // currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit + // ).to.be.true; - expect( - currentWithdrawnAmountBeforeSettingLimit > - currentWithdrawnAmountAfterSettingLimit - ).to.be.true; + // expect( + // currentWithdrawnAmountBeforeSettingLimit > + // currentWithdrawnAmountAfterSettingLimit + // ).to.be.true; - expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; + // expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; }); }); }); From 4a8ac6e7ccf92f53eeeb6da1b64a50b14e9401da Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 31 Dec 2024 16:43:27 +0530 Subject: [PATCH 15/81] chore: debug ci failure --- .../integration-tests/transfer_eth.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index d20d388d..f3cfda10 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -450,45 +450,45 @@ describe('Transferring ETH', async function () { ).to.be.true; }); - // it('Rate limit parameters are updated when the initial duration is over', async () => { - // const deployer = env.eth.deployer; + it('Rate limit parameters are updated when the initial duration is over', async () => { + const deployer = env.eth.deployer; - // // fast forward time - // await env.eth.provider.send('evm_increaseTime', [ - // Number(rateLimitDuration) * 2, - // ]); - // await env.eth.provider.send('evm_mine', []); // Mine a new block + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block - // const currentPeriodEndBeforeRelay = - // await env.eth.fuelMessagePortal.currentPeriodEnd(); + const currentPeriodEndBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodEnd(); - // console.log("reset limit") + console.log("reset limit") - // await env.eth.fuelMessagePortal - // .connect(deployer) - // .resetRateLimitAmount(parseEther(largeRateLimit)); + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(largeRateLimit)); - // console.log("generateWithdrawalMessageProof") + console.log("generateWithdrawalMessageProof") - // withdrawMessageProof = await generateWithdrawalMessageProof( - // fuelETHSender, - // ethereumETHReceiverAddress, - // NUM_ETH - // ); + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); - // await relayMessage(env, withdrawMessageProof!); + await relayMessage(env, withdrawMessageProof!); - // const currentPeriodEndAfterRelay = - // await env.eth.fuelMessagePortal.currentPeriodEnd(); + // const currentPeriodEndAfterRelay = + // await env.eth.fuelMessagePortal.currentPeriodEnd(); - // expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be - // .true; + // expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + // .true; - // const currentPeriodAmount = - // await env.eth.fuelMessagePortal.currentPeriodAmount(); + // const currentPeriodAmount = + // await env.eth.fuelMessagePortal.currentPeriodAmount(); - // expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; - // }); + // expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + }); it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { const deployer = await env.eth.deployer; From 3f56a0d707143f0bc654b0a4fe224b35ea85b353 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 31 Dec 2024 17:44:46 +0530 Subject: [PATCH 16/81] chore: update portal upgrade script --- .../deploy/hardhat/010.portal_upgrade.ts | 13 ++++++++++++- .../integration-tests/transfer_eth.ts | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts b/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts index 14682a6a..0b33feb9 100644 --- a/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts +++ b/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts @@ -16,7 +16,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { upgrades: { erc1967 }, } = hre; - const [deployer] = await ethers.getSigners(); + const [deployer, , ,secondaryUser] = await ethers.getSigners(); const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; let address; @@ -73,6 +73,17 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { data: txData, }); + txData = await portal.interface.encodeFunctionData('grantRole', [ + SET_RATE_LIMITER_ROLE, + await secondaryUser.getAddress(), + ]); + + await impersonatedSigner.sendTransaction({ + to: address, + data: txData, + }); + + const implementation = await erc1967.getImplementationAddress(address); console.log('Upgraded FuelMessagePortal to', implementation); diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index f3cfda10..3063f771 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -451,7 +451,7 @@ describe('Transferring ETH', async function () { }); it('Rate limit parameters are updated when the initial duration is over', async () => { - const deployer = env.eth.deployer; + const deployer = await env.eth.signers[0]; // fast forward time await env.eth.provider.send('evm_increaseTime', [ From b0e7f059c22b5470d3db91ce64817cc1e2910b81 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 31 Dec 2024 20:18:35 +0530 Subject: [PATCH 17/81] chore: uncomment code --- .../deploy/hardhat/010.portal_upgrade.ts | 13 +----- .../integration-tests/transfer_eth.ts | 44 +++++++------------ 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts b/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts index 0b33feb9..14682a6a 100644 --- a/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts +++ b/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts @@ -16,7 +16,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { upgrades: { erc1967 }, } = hre; - const [deployer, , ,secondaryUser] = await ethers.getSigners(); + const [deployer] = await ethers.getSigners(); const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; let address; @@ -73,17 +73,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { data: txData, }); - txData = await portal.interface.encodeFunctionData('grantRole', [ - SET_RATE_LIMITER_ROLE, - await secondaryUser.getAddress(), - ]); - - await impersonatedSigner.sendTransaction({ - to: address, - data: txData, - }); - - const implementation = await erc1967.getImplementationAddress(address); console.log('Upgraded FuelMessagePortal to', implementation); diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 3063f771..73d03caf 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -430,7 +430,6 @@ describe('Transferring ETH', async function () { await env.eth.fuelMessagePortal.currentPeriodAmount(); // current withdrawn amount doesn't change when rate limit is updated - expect( currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset ).to.be.true; @@ -462,14 +461,6 @@ describe('Transferring ETH', async function () { const currentPeriodEndBeforeRelay = await env.eth.fuelMessagePortal.currentPeriodEnd(); - console.log("reset limit") - - await env.eth.fuelMessagePortal - .connect(deployer) - .resetRateLimitAmount(parseEther(largeRateLimit)); - - console.log("generateWithdrawalMessageProof") - withdrawMessageProof = await generateWithdrawalMessageProof( fuelETHSender, ethereumETHReceiverAddress, @@ -478,16 +469,16 @@ describe('Transferring ETH', async function () { await relayMessage(env, withdrawMessageProof!); - // const currentPeriodEndAfterRelay = - // await env.eth.fuelMessagePortal.currentPeriodEnd(); + const currentPeriodEndAfterRelay = + await env.eth.fuelMessagePortal.currentPeriodEnd(); - // expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be - // .true; + expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + .true; - // const currentPeriodAmount = - // await env.eth.fuelMessagePortal.currentPeriodAmount(); + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); - // expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; }); it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { @@ -505,30 +496,25 @@ describe('Transferring ETH', async function () { ]); await env.eth.provider.send('evm_mine', []); // Mine a new block - console.log("reset limit") - await env.eth.fuelMessagePortal .connect(deployer) .resetRateLimitAmount(parseEther(newRateLimit)); - console.log("reset done") - - const currentPeriodEndAfterSettingLimit = await env.eth.fuelMessagePortal.currentPeriodEnd(); const currentWithdrawnAmountAfterSettingLimit = await env.eth.fuelMessagePortal.currentPeriodAmount(); - // expect( - // currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit - // ).to.be.true; + expect( + currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit + ).to.be.true; - // expect( - // currentWithdrawnAmountBeforeSettingLimit > - // currentWithdrawnAmountAfterSettingLimit - // ).to.be.true; + expect( + currentWithdrawnAmountBeforeSettingLimit > + currentWithdrawnAmountAfterSettingLimit + ).to.be.true; - // expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; + expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; }); }); }); From 98afdf2a341e977410e235c1a9af79a23f27686f Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 31 Dec 2024 21:45:48 +0530 Subject: [PATCH 18/81] chore: add changeset --- .changeset/thin-ways-dress.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/thin-ways-dress.md diff --git a/.changeset/thin-ways-dress.md b/.changeset/thin-ways-dress.md new file mode 100644 index 00000000..33a25e4b --- /dev/null +++ b/.changeset/thin-ways-dress.md @@ -0,0 +1,6 @@ +--- +'@fuel-bridge/solidity-contracts': minor +'@fuel-bridge/test-utils': minor +--- + +ci for contract upgrade test suite From 666ccc6a523b41847c12d3184031d40bbd383b83 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 1 Jan 2025 11:53:32 +0530 Subject: [PATCH 19/81] chore: formatting --- .../solidity-contracts/integration-tests/bridge_erc20.ts | 6 +++--- .../solidity-contracts/integration-tests/transfer_eth.ts | 2 +- packages/test-utils/src/utils/setup.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts index fa6dc64f..c1fe6d66 100644 --- a/packages/solidity-contracts/integration-tests/bridge_erc20.ts +++ b/packages/solidity-contracts/integration-tests/bridge_erc20.ts @@ -146,7 +146,7 @@ describe('Bridging ERC20 tokens', async function () { const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - + // fast forward post the commit cooldown period await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour await env.eth.provider.send('evm_mine', []); // Mine a new block @@ -158,7 +158,7 @@ describe('Bridging ERC20 tokens', async function () { '0x0000000000000000000000000000000000000000000000000000000000000000', commitHeight.toString() ); - + // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ Number(TIME_TO_FINALIZE) * 2, @@ -170,7 +170,7 @@ describe('Bridging ERC20 tokens', async function () { // fast forward post the commit cooldown period await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour await env.eth.provider.send('evm_mine', []); // Mine a new block - + // produce more blocks to fetch the block height await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 73d03caf..786a2e56 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -163,7 +163,7 @@ describe('Transferring ETH', async function () { const relayMessageParams = createRelayMessageParams(withdrawMessageProof); const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); - + // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ Number(TIME_TO_FINALIZE) * 100, diff --git a/packages/test-utils/src/utils/setup.ts b/packages/test-utils/src/utils/setup.ts index 3882ef7e..6a1c2adc 100644 --- a/packages/test-utils/src/utils/setup.ts +++ b/packages/test-utils/src/utils/setup.ts @@ -326,4 +326,4 @@ export async function setupEnvironment( signers: [fuel_signer1, fuel_signer2], }, }; -} \ No newline at end of file +} From 51f7a9f9968597173ed6cc2f59ab4f1b5832d85b Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 12:45:04 +0530 Subject: [PATCH 20/81] chore: update forc 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 7fdbe222..87736a64 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -4,7 +4,7 @@ inputs: rust-version: default: 1.81.0 forc-components: - default: 'forc@0.63.4, fuel-core@0.36.0' + default: 'forc@0.66.5, fuel-core@0.36.0' runs: using: 'composite' From 1d537929b25a9f3032b48e17e0feebaafd7917fe Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 13:08:09 +0530 Subject: [PATCH 21/81] chore: debug format check in ci --- .github/actions/setup-rust/action.yml | 2 +- scripts/check.sh | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index 87736a64..7fdbe222 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -4,7 +4,7 @@ inputs: rust-version: default: 1.81.0 forc-components: - default: 'forc@0.66.5, fuel-core@0.36.0' + default: 'forc@0.63.4, fuel-core@0.36.0' runs: using: 'composite' diff --git a/scripts/check.sh b/scripts/check.sh index f2f4be9b..e3690eef 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,6 +1,5 @@ #!/bin/bash -pnpm forc fmt --check cargo fmt --check pnpm forc build --release cargo clippy --all-features --all-targets -- -D warnings From a13443550e8d534c858abce7e5404fb3fb4ce76c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 13:31:31 +0530 Subject: [PATCH 22/81] chore: ts lint fixes --- .../deploy/hardhat/008.set_canonical_token_bytecode.ts | 1 + .../deploy/hardhat/999.serve_deployment_file.ts | 3 +-- packages/solidity-contracts/integration-tests/transfer_eth.ts | 2 -- packages/test-utils/src/utils/setup.ts | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts b/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts index 698cf33d..f7ec8ccb 100644 --- a/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts +++ b/packages/solidity-contracts/deploy/hardhat/008.set_canonical_token_bytecode.ts @@ -1,5 +1,6 @@ import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; + import { USDT_ADDRESS, USDC_ADDRESS, diff --git a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts index 79b4a47b..4a2fbc64 100644 --- a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts +++ b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts @@ -1,5 +1,4 @@ -import fs from 'fs'; -import { writeFile } from 'fs'; +import fs, { writeFile } from 'fs'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; import path from 'path'; diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 786a2e56..eed3f7b8 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -450,8 +450,6 @@ describe('Transferring ETH', async function () { }); it('Rate limit parameters are updated when the initial duration is over', async () => { - const deployer = await env.eth.signers[0]; - // fast forward time await env.eth.provider.send('evm_increaseTime', [ Number(rateLimitDuration) * 2, diff --git a/packages/test-utils/src/utils/setup.ts b/packages/test-utils/src/utils/setup.ts index 6a1c2adc..e05d08f8 100644 --- a/packages/test-utils/src/utils/setup.ts +++ b/packages/test-utils/src/utils/setup.ts @@ -13,7 +13,7 @@ import { FuelERC721Gateway__factory, } from '@fuel-bridge/solidity-contracts/typechain'; import * as dotenv from 'dotenv'; -import type { Signer as EthSigner, Provider as EthProvider } from 'ethers'; +import type { Signer as EthSigner } from 'ethers'; import { JsonRpcProvider, ethers, From 417733cbe93e14afc15053882b9cf4461a3a6e6d Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 13:56:08 +0530 Subject: [PATCH 23/81] chore: update check shell script --- scripts/check.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check.sh b/scripts/check.sh index e3690eef..f2f4be9b 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,5 +1,6 @@ #!/bin/bash +pnpm forc fmt --check cargo fmt --check pnpm forc build --release cargo clippy --all-features --all-targets -- -D warnings From 2ada6f56864baa9965299b7fa805218fc95a3ed8 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 15:45:42 +0530 Subject: [PATCH 24/81] chore: update order of commands in check script --- scripts/check.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check.sh b/scripts/check.sh index f2f4be9b..fdc25a02 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,8 +1,8 @@ #!/bin/bash -pnpm forc fmt --check cargo fmt --check pnpm forc build --release +pnpm forc fmt --check cargo clippy --all-features --all-targets -- -D warnings pnpm prettier:check pnpm lint:check From 430bce2c8d35effdfa26602ce238678dac226293 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 16:29:23 +0530 Subject: [PATCH 25/81] chore: debug forc formatting --- .github/workflows/pr.yml | 3 +++ scripts/check.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 7d0bdfd1..21d05c6e 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -73,6 +73,9 @@ jobs: - name: Build projects run: pnpm build + - name: Forc formatting + run: forc fmt --check + - name: Check projects formatting run: pnpm check diff --git a/scripts/check.sh b/scripts/check.sh index fdc25a02..f2f4be9b 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -1,8 +1,8 @@ #!/bin/bash +pnpm forc fmt --check cargo fmt --check pnpm forc build --release -pnpm forc fmt --check cargo clippy --all-features --all-targets -- -D warnings pnpm prettier:check pnpm lint:check From da63952f20b2b588ce1be8e39a19ac0e37a24b2b Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 17:31:02 +0530 Subject: [PATCH 26/81] chore: remove --check --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 21d05c6e..4d42e03f 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -74,7 +74,7 @@ jobs: run: pnpm build - name: Forc formatting - run: forc fmt --check + run: forc fmt - name: Check projects formatting run: pnpm check From e933a2e81f0696f89b809710ec6bf25d36d48265 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 17:48:07 +0530 Subject: [PATCH 27/81] chore: update forc command --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 4d42e03f..fc4e8516 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -74,7 +74,7 @@ jobs: run: pnpm build - name: Forc formatting - run: forc fmt + run: forc-fmt --check - name: Check projects formatting run: pnpm check From 08917b8c072dfb66f4b142c1a625d8e920891da2 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 19:48:29 +0530 Subject: [PATCH 28/81] chore: add build command --- .github/workflows/pr.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index fc4e8516..1fe74699 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -69,9 +69,12 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/setup-rust + + - name: Forc Build + run: forc build --release - name: Build projects - run: pnpm build + run: pnpm build - name: Forc formatting run: forc-fmt --check From 6348e3861e4350e19c9f0e46ba0b97f4cc2ddfd6 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 2 Jan 2025 21:49:18 +0530 Subject: [PATCH 29/81] chore: remove forc lock files in ci --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 1fe74699..d56559f2 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -77,7 +77,7 @@ jobs: run: pnpm build - name: Forc formatting - run: forc-fmt --check + run: rm -rf ~/.forc/.lsp-locks/ && forc-fmt --check - name: Check projects formatting run: pnpm check From c4d176b2d42a5563233563219080a3741ede5c20 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 11:21:23 +0530 Subject: [PATCH 30/81] chore: update installation for forc-fmt --- .github/actions/setup-rust/action.yml | 10 +++++----- .github/workflows/pr.yml | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index 7fdbe222..472338e8 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -19,8 +19,8 @@ runs: - name: Init cache uses: Swatinem/rust-cache@v2 - - name: Install Fuel toolchain - uses: FuelLabs/action-fuel-toolchain@v0.6.0 - with: - name: fuel-bridge - components: ${{ inputs.forc-components }} + # - name: Install Fuel toolchain + # uses: FuelLabs/action-fuel-toolchain@v0.6.0 + # with: + # name: fuel-bridge + # components: ${{ inputs.forc-components }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d56559f2..de5bd644 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -69,9 +69,21 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/setup-rust + + - name: Forc Build + run: | + curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz + tar -xvf forc.tar.gz + chmod +x forc-binaries/forc + mv forc-binaries/forc /usr/local/bin/forc + mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt + forc-fmt --check + fi + - name: Forc Build run: forc build --release + - name: Build projects run: pnpm build From 8ff85d20a3ad904225c2337b75ba38350bd676f7 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 11:34:06 +0530 Subject: [PATCH 31/81] chore: test ci --- .github/workflows/upgrade-test-suite.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index 82ec5a43..dc320a82 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -29,6 +29,15 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/setup-rust + - name: Forc Build + run: | + curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz + tar -xvf forc.tar.gz + chmod +x forc-binaries/forc + mv forc-binaries/forc /usr/local/bin/forc + mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt + forc-fmt --check + fi - name: Build project run: pnpm build - name: Sets the tenderly rpc endpoint in the L1 docker container env From e31d697d2d1b1caf5e3d8f4e77fe582d5ba5ea91 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 11:52:31 +0530 Subject: [PATCH 32/81] chore: test ci --- .github/actions/setup-rust/action.yml | 8 +++++++- .github/workflows/upgrade-test-suite.yml | 11 ++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index 472338e8..f983c781 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -19,7 +19,13 @@ runs: - name: Init cache uses: Swatinem/rust-cache@v2 - # - name: Install Fuel toolchain + - name: Install Fuel toolchain + run: | + curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz + tar -xvf forc.tar.gz + chmod +x forc-binaries/forc + mv forc-binaries/forc /usr/local/bin/forc + mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt # uses: FuelLabs/action-fuel-toolchain@v0.6.0 # with: # name: fuel-bridge diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index dc320a82..74cff881 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -29,15 +29,8 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/setup-rust - - name: Forc Build - run: | - curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz - tar -xvf forc.tar.gz - chmod +x forc-binaries/forc - mv forc-binaries/forc /usr/local/bin/forc - mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt - forc-fmt --check - fi + - name: project + run: forc-fmt --check - name: Build project run: pnpm build - name: Sets the tenderly rpc endpoint in the L1 docker container env From 396622d172e8d8112507b072aaa0417e6db16de4 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 12:06:30 +0530 Subject: [PATCH 33/81] chore: minor update --- .github/actions/setup-rust/action.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index f983c781..da808252 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -16,8 +16,16 @@ runs: components: clippy, rustfmt targets: wasm32-unknown-unknown - - name: Init cache - uses: Swatinem/rust-cache@v2 + # - name: Init cache + # uses: Swatinem/rust-cache@v2 + + - uses: buildjet/cache@v3 + with: + prefix-key: "v1-rust" + + - name: Set git config + run: | + git config --global core.bigfilethreshold 100m - name: Install Fuel toolchain run: | From 537f38a83ae69c540405fadc2ea19c0cd3440de7 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 12:10:35 +0530 Subject: [PATCH 34/81] chore: small touchup in action.yml --- .github/actions/setup-rust/action.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index da808252..f494d981 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -26,14 +26,16 @@ runs: - name: Set git config run: | git config --global core.bigfilethreshold 100m + shell: bash - name: Install Fuel toolchain run: | - curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz - tar -xvf forc.tar.gz - chmod +x forc-binaries/forc - mv forc-binaries/forc /usr/local/bin/forc - mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt + curl -sSLf https://github.com/FuelLabs/sway/releases/download/v0.66.5/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz + tar -xvf forc.tar.gz + chmod +x forc-binaries/forc + mv forc-binaries/forc /usr/local/bin/forc + mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt + shell: bash # uses: FuelLabs/action-fuel-toolchain@v0.6.0 # with: # name: fuel-bridge From 861d437a2199b293ac636a2902e194818a975815 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 12:21:26 +0530 Subject: [PATCH 35/81] chore: revert workflow changes --- .github/actions/setup-rust/action.yml | 28 +++++------------------- .github/workflows/pr.yml | 22 ++----------------- .github/workflows/upgrade-test-suite.yml | 2 -- scripts/build.sh | 1 + 4 files changed, 9 insertions(+), 44 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index f494d981..8e2bae1a 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -16,27 +16,11 @@ runs: components: clippy, rustfmt targets: wasm32-unknown-unknown - # - name: Init cache - # uses: Swatinem/rust-cache@v2 - - - uses: buildjet/cache@v3 - with: - prefix-key: "v1-rust" - - - name: Set git config - run: | - git config --global core.bigfilethreshold 100m - shell: bash + - name: Init cache + uses: Swatinem/rust-cache@v2 - name: Install Fuel toolchain - run: | - curl -sSLf https://github.com/FuelLabs/sway/releases/download/v0.66.5/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz - tar -xvf forc.tar.gz - chmod +x forc-binaries/forc - mv forc-binaries/forc /usr/local/bin/forc - mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt - shell: bash - # uses: FuelLabs/action-fuel-toolchain@v0.6.0 - # with: - # name: fuel-bridge - # components: ${{ inputs.forc-components }} + uses: FuelLabs/action-fuel-toolchain@v0.6.0 + with: + name: fuel-bridge + components: ${{ inputs.forc-components }} \ No newline at end of file diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index de5bd644..fa5667e6 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -70,26 +70,8 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/setup-rust - - name: Forc Build - run: | - curl -sSLf https://github.com/FuelLabs/sway/releases/download/v${{ env.FORC_VERSION }}/forc-binaries-linux_amd64.tar.gz -L -o forc.tar.gz - tar -xvf forc.tar.gz - chmod +x forc-binaries/forc - mv forc-binaries/forc /usr/local/bin/forc - mv forc-binaries/forc-fmt /usr/local/bin/forc-fmt - forc-fmt --check - fi - - - - name: Forc Build - run: forc build --release - - - name: Build projects - run: pnpm build - - - name: Forc formatting - run: rm -rf ~/.forc/.lsp-locks/ && forc-fmt --check + run: pnpm build - name: Check projects formatting run: pnpm check @@ -100,4 +82,4 @@ jobs: # TODO issue 115: https://github.com/FuelLabs/fuel-bridge/issues/115 # Run test again to ensure it works with node that already has transactions/messages # - name: Test projects Again - # run: pnpm --filter @fuel-bridge/integration-tests test + # run: pnpm --filter @fuel-bridge/integration-tests test \ No newline at end of file diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index 74cff881..82ec5a43 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -29,8 +29,6 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/setup-rust - - name: project - run: forc-fmt --check - name: Build project run: pnpm build - name: Sets the tenderly rpc endpoint in the L1 docker container env diff --git a/scripts/build.sh b/scripts/build.sh index 4e93b7d2..2642c39a 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -3,3 +3,4 @@ forc build --release cargo run --bin fuel-contract-message-predicate turbo run build +pnpm forc fmt --check From 38ba169230bbc2fbc24fe3799fff8cced13975bb Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 13:04:59 +0530 Subject: [PATCH 36/81] chore: update fmt command --- scripts/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 2642c39a..7fcb21b0 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,6 +1,6 @@ #!/bin/bash forc build --release +pnpm forc fmt --path packages/message-predicates --check cargo run --bin fuel-contract-message-predicate -turbo run build -pnpm forc fmt --check +turbo run build \ No newline at end of file From 37ad5bd3f8d9803fe11580e3a929202ddf4ce242 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 17:04:47 +0530 Subject: [PATCH 37/81] chore: add package.json replacement logic --- .github/workflows/pr.yml | 5 +++++ scripts/check.sh | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index fa5667e6..25a16e57 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -60,6 +60,11 @@ jobs: needs: audit-deps steps: - uses: actions/checkout@v3 + - name: Replace package.json + run: | + echo "Replacing package.json in ./target-directory with one from ./source-directory" + cp ./docker/l1-chain/package.json ./packages/solidity-contracts/package.json + echo "Replacement successful." - uses: FuelLabs/github-actions/setups/node@master with: node-version: 20.16.0 diff --git a/scripts/check.sh b/scripts/check.sh index f2f4be9b..f1b28c1d 100644 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -5,5 +5,4 @@ cargo fmt --check pnpm forc build --release cargo clippy --all-features --all-targets -- -D warnings pnpm prettier:check -pnpm lint:check - +pnpm lint:check \ No newline at end of file From 76aa9702a02fa2e8f2e1282c65a8e257b678ba2e Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 17:25:11 +0530 Subject: [PATCH 38/81] chore: revert changes in build script --- scripts/build.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/build.sh b/scripts/build.sh index 7fcb21b0..dac65f6a 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,6 +1,5 @@ #!/bin/bash forc build --release -pnpm forc fmt --path packages/message-predicates --check cargo run --bin fuel-contract-message-predicate turbo run build \ No newline at end of file From cf1e1e40de4f34d2f604c281ab8f8a85b3e39f5f Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 21:57:26 +0530 Subject: [PATCH 39/81] chore: revert peer dependencies --- packages/solidity-contracts/package.json | 4 ---- pnpm-lock.yaml | 7 ------- 2 files changed, 11 deletions(-) diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index 41874449..9920ace0 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -32,10 +32,6 @@ "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" }, - "peerDependencies": { - "@fuel-bridge/test-utils": "workspace:*", - "@fuel-bridge/fungible-token": "workspace:*" - }, "devDependencies": { "@fuel-ts/merkle": "^0.21.2", "@inquirer/prompts": "^5.3.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94419ef0..e68d59a5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,13 +127,6 @@ importers: version: link:../solidity-contracts packages/solidity-contracts: - dependencies: - '@fuel-bridge/fungible-token': - specifier: workspace:* - version: link:../fungible-token - '@fuel-bridge/test-utils': - specifier: workspace:* - version: link:../test-utils devDependencies: '@fuel-ts/merkle': specifier: ^0.21.2 From e3c3e335d9a183b32e345710494d0e504624a7b0 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Fri, 3 Jan 2025 21:58:06 +0530 Subject: [PATCH 40/81] chore: add setup for fork integration tests --- .../integration-tests/setup/setup.ts | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 packages/solidity-contracts/integration-tests/setup/setup.ts diff --git a/packages/solidity-contracts/integration-tests/setup/setup.ts b/packages/solidity-contracts/integration-tests/setup/setup.ts new file mode 100644 index 00000000..3a5111f4 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/setup/setup.ts @@ -0,0 +1,155 @@ +import type { Signer as EthSigner } from 'ethers'; +import type { WalletUnlocked as FuelWallet } from 'fuels'; +import { Wallet, Provider as FuelProvider, BN } from 'fuels'; +import hre from 'hardhat'; + +import { + FuelChainState__factory, + FuelMessagePortalV3__factory as FuelMessagePortal__factory, + FuelERC20GatewayV4__factory as FuelERC20Gateway__factory, +} from '../../typechain'; +import type { + FuelChainState, + FuelMessagePortalV3 as FuelMessagePortal, + FuelERC20GatewayV4 as FuelERC20Gateway, +} from '../../typechain'; + +export interface TestEnvironment { + eth: { + fuelChainState: FuelChainState; + fuelMessagePortal: FuelMessagePortal; + fuelERC20Gateway: FuelERC20Gateway; + deployer: EthSigner; + signers: EthSigner[]; + }; + fuel: { + provider: FuelProvider; + deployer: FuelWallet; + signers: FuelWallet[]; + }; +} + +const ETHEREUM_ETH_DECIMALS = 18n; +const FUEL_ETH_DECIMALS = 9n; + +const def_pk_fuel_deployer: string = + '0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c'; +const def_pk_fuel_signer1: string = + '0xa349d39f614a3085b7f7f8cef63fd5189136924fc1238e6d25ccdaa43a901cd0'; +const def_pk_fuel_signer2: string = + '0x139f2cd8db62a9d64c3ed4cdc804f1fb53be98d750cd1432a308b34a42d8dcc7'; + +// Parse ETH value as a string +export function fuels_parseEther(ether: string): BN { + let val = hre.ethers.parseEther(ether); + val = val / 10n ** (ETHEREUM_ETH_DECIMALS - FUEL_ETH_DECIMALS); + return new BN(hre.ethers.toBeHex(val)); +} + +// Format ETH value to a string +export function fuels_formatEther(ether: BN): string { + let val = BigInt(ether.toHex()); + val = val * 10n ** (ETHEREUM_ETH_DECIMALS - FUEL_ETH_DECIMALS); + return hre.ethers.formatEther(val); +} + +export async function setupEnvironment(): Promise { + // Default config values + const def_http_deployer: string = 'http://127.0.0.1:8080'; + const def_http_fuel: string = 'http://127.0.0.1:4000/v1/graphql'; + + let deployerAddresses: any = null; + try { + deployerAddresses = await fetch( + def_http_deployer + '/deployments.local.json' + ).then((resp) => resp.json()); + } catch (e) { + console.error(e); + throw new Error( + 'Failed to connect to the deployer at (' + + def_http_deployer + + "). Are you sure it's running?" + ); + } + + const signers = await hre.ethers.getSigners(); + + const eth_fuelChainState: FuelChainState = FuelChainState__factory.connect( + deployerAddresses.FuelChainState, + signers[0] + ); + const eth_fuelMessagePortal: FuelMessagePortal = + FuelMessagePortal__factory.connect( + deployerAddresses.FuelMessagePortal, + signers[0] + ); + const eth_fuelERC20Gateway: FuelERC20Gateway = + FuelERC20Gateway__factory.connect( + deployerAddresses.FuelERC20GatewayV4, + signers[0] + ); + + // Create provider from http_fuel_client + const fuel_provider = await FuelProvider.create(def_http_fuel); + try { + await fuel_provider.getBlockNumber(); + } catch (e) { + throw new Error( + 'Failed to connect to the Fuel client at (' + + def_http_fuel + + "). Are you sure it's running?" + ); + } + + const fuel_deployer = Wallet.fromPrivateKey( + def_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)' + ); + } + const fuel_signer1 = Wallet.fromPrivateKey( + def_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() + ); + await tx.wait(); + } + const fuel_signer2 = Wallet.fromPrivateKey( + def_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() + ); + await tx.wait(); + } + + return { + eth: { + fuelChainState: eth_fuelChainState, + fuelMessagePortal: eth_fuelMessagePortal, + fuelERC20Gateway: eth_fuelERC20Gateway, + deployer: signers[0], + signers: [signers[3], signers[4], signers[5]], + }, + fuel: { + provider: fuel_provider, + deployer: fuel_deployer, + signers: [fuel_signer1, fuel_signer2], + }, + }; +} From 70da6869afd2ea9241b2cd22b81b2fce4c5b5bd1 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 17:06:57 +0530 Subject: [PATCH 41/81] chore: revert workflow changes --- .github/actions/setup-rust/action.yml | 2 +- .github/workflows/pr.yml | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index 8e2bae1a..7fdbe222 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -23,4 +23,4 @@ runs: uses: FuelLabs/action-fuel-toolchain@v0.6.0 with: name: fuel-bridge - components: ${{ inputs.forc-components }} \ No newline at end of file + components: ${{ inputs.forc-components }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 25a16e57..7d0bdfd1 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -60,11 +60,6 @@ jobs: needs: audit-deps steps: - uses: actions/checkout@v3 - - name: Replace package.json - run: | - echo "Replacing package.json in ./target-directory with one from ./source-directory" - cp ./docker/l1-chain/package.json ./packages/solidity-contracts/package.json - echo "Replacement successful." - uses: FuelLabs/github-actions/setups/node@master with: node-version: 20.16.0 @@ -87,4 +82,4 @@ jobs: # TODO issue 115: https://github.com/FuelLabs/fuel-bridge/issues/115 # Run test again to ensure it works with node that already has transactions/messages # - name: Test projects Again - # run: pnpm --filter @fuel-bridge/integration-tests test \ No newline at end of file + # run: pnpm --filter @fuel-bridge/integration-tests test From f2a469fb52b27fafa993ff7f4071d01a288a535c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 17:08:22 +0530 Subject: [PATCH 42/81] chore: remove unused workspace package --- packages/fungible-token/package.json | 3 +-- packages/message-predicates/package.json | 3 +-- packages/solidity-contracts/package.json | 2 ++ pnpm-lock.yaml | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/fungible-token/package.json b/packages/fungible-token/package.json index 4f179bd1..d1125a70 100644 --- a/packages/fungible-token/package.json +++ b/packages/fungible-token/package.json @@ -10,7 +10,6 @@ }, "devDependencies": { "fuels": "0.96.1", - "@fuel-bridge/esbuild-bin-loader": "workspace:../esbuild-bin-loader", - "@fuel-bridge/solidity-contracts": "workspace:*" + "@fuel-bridge/esbuild-bin-loader": "workspace:../esbuild-bin-loader" } } diff --git a/packages/message-predicates/package.json b/packages/message-predicates/package.json index 6fc7d683..39babf10 100644 --- a/packages/message-predicates/package.json +++ b/packages/message-predicates/package.json @@ -12,7 +12,6 @@ "build": "tsup" }, "devDependencies": { - "@fuel-bridge/esbuild-bin-loader": "workspace:../esbuild-bin-loader", - "@fuel-bridge/solidity-contracts": "workspace:*" + "@fuel-bridge/esbuild-bin-loader": "workspace:../esbuild-bin-loader" } } diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index 9920ace0..fe58d6cb 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -33,6 +33,8 @@ "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" }, "devDependencies": { + "@fuel-bridge/fungible-token": "workspace:*", + "@fuel-bridge/message-predicates": "workspace:*", "@fuel-ts/merkle": "^0.21.2", "@inquirer/prompts": "^5.3.8", "@nomicfoundation/hardhat-chai-matchers": "^2.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e68d59a5..ef434741 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,9 +68,6 @@ importers: '@fuel-bridge/esbuild-bin-loader': specifier: workspace:../esbuild-bin-loader version: link:../esbuild-bin-loader - '@fuel-bridge/solidity-contracts': - specifier: workspace:* - version: link:../solidity-contracts fuels: specifier: 0.96.1 version: 0.96.1 @@ -122,12 +119,15 @@ importers: '@fuel-bridge/esbuild-bin-loader': specifier: workspace:../esbuild-bin-loader version: link:../esbuild-bin-loader - '@fuel-bridge/solidity-contracts': - specifier: workspace:* - version: link:../solidity-contracts packages/solidity-contracts: devDependencies: + '@fuel-bridge/fungible-token': + specifier: workspace:* + version: link:../fungible-token + '@fuel-bridge/message-predicates': + specifier: workspace:* + version: link:../message-predicates '@fuel-ts/merkle': specifier: ^0.21.2 version: 0.21.2 From 9c9382da658961491afd69b1c9eb816274bfde6f Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 17:08:56 +0530 Subject: [PATCH 43/81] chore: add integration test in tsconfig import --- packages/solidity-contracts/tsconfig.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/solidity-contracts/tsconfig.json b/packages/solidity-contracts/tsconfig.json index 3dacae81..a44acc62 100644 --- a/packages/solidity-contracts/tsconfig.json +++ b/packages/solidity-contracts/tsconfig.json @@ -8,6 +8,13 @@ "lib": ["ES2021"], "strict": true }, - "include": ["./scripts", "./protocol", "./test", "./typechain", "./deploy"], + "include": [ + "./scripts", + "./protocol", + "./test", + "./typechain", + "./deploy", + "./integration-tests" + ], "files": ["./hardhat.config.ts"] } From 2510749f59bb1bfc3455733cce1e5634e45df853 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 17:10:57 +0530 Subject: [PATCH 44/81] refactor: use custom integration test helpers --- .../integration-tests/bridge_erc20.ts | 45 ++- .../integration-tests/constants.ts | 13 + .../integration-tests/debug.ts | 5 + .../ethers/createRelayParams.ts | 96 +++++ .../ethers/getOrDeployECR20Contract.ts | 81 ++++ .../ethers/hardhatSkipTime.ts | 23 ++ .../integration-tests/ethers/index.ts | 2 + .../ethers/waitForBlockFinalization.ts | 83 +++++ .../fuels/fungibleToken/factory.ts | 352 ++++++++++++++++++ .../integration-tests/fuels/getBlock.ts | 62 +++ .../fuels/getMessageOutReceipt.ts | 14 + .../fuels/getOrDeployL2Bridge.ts | 91 +++++ .../integration-tests/fuels/getTokenId.ts | 24 ++ .../integration-tests/fuels/index.ts | 3 + .../fuels/relayCommonMessage.ts | 248 ++++++++++++ .../integration-tests/fuels/transaction.ts | 17 + .../integration-tests/fuels/waitForMessage.ts | 49 +++ .../integration-tests/parsers.ts | 7 + .../integration-tests/setup/setup.ts | 95 ++++- .../integration-tests/transfer_eth.ts | 19 +- 20 files changed, 1283 insertions(+), 46 deletions(-) create mode 100644 packages/solidity-contracts/integration-tests/constants.ts create mode 100644 packages/solidity-contracts/integration-tests/debug.ts create mode 100644 packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts create mode 100644 packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts create mode 100644 packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts create mode 100644 packages/solidity-contracts/integration-tests/ethers/index.ts create mode 100644 packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/getBlock.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/getTokenId.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/index.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/transaction.ts create mode 100644 packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts create mode 100644 packages/solidity-contracts/integration-tests/parsers.ts diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts index c1fe6d66..59700f9e 100644 --- a/packages/solidity-contracts/integration-tests/bridge_erc20.ts +++ b/packages/solidity-contracts/integration-tests/bridge_erc20.ts @@ -1,21 +1,3 @@ -import type { BridgeFungibleToken } from '@fuel-bridge/fungible-token'; -import type { TestEnvironment } from '@fuel-bridge/test-utils'; -import { - setupEnvironment, - relayCommonMessage, - waitForMessage, - createRelayMessageParams, - getOrDeployECR20Contract, - getOrDeployL2Bridge, - FUEL_TX_PARAMS, - getMessageOutReceipt, - fuel_to_eth_address, - waitForBlockFinalization, - getTokenId, - getBlock, - FUEL_CALL_TX_PARAMS, - hardhatSkipTime, -} from '@fuel-bridge/test-utils'; import chai from 'chai'; import { toBeHex, parseEther } from 'ethers'; import type { JsonRpcProvider, Signer } from 'ethers'; @@ -30,6 +12,17 @@ import type { import { RATE_LIMIT_AMOUNT, RATE_LIMIT_DURATION } from '../protocol/constants'; import type { Token } from '../typechain'; +import { FUEL_CALL_TX_PARAMS, FUEL_TX_PARAMS } from './constants'; +import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; +import { getOrDeployECR20Contract } from './ethers/getOrDeployECR20Contract'; +import { hardhatSkipTime } from './ethers/hardhatSkipTime'; +import { waitForMessage, getMessageOutReceipt, getBlock } from './fuels'; +import { getOrDeployL2Bridge } from './fuels/getOrDeployL2Bridge'; +import { getTokenId } from './fuels/getTokenId'; +import { relayCommonMessage } from './fuels/relayCommonMessage'; +import type { TestEnvironment } from './setup/setup'; +import { setupEnvironment } from './setup/setup'; + const { expect } = chai; describe('Bridging ERC20 tokens', async function () { @@ -42,14 +35,18 @@ describe('Bridging ERC20 tokens', async function () { let eth_testToken: Token; let eth_testTokenAddress: string; let eth_erc20GatewayAddress: string; - let fuel_bridge: BridgeFungibleToken; - let fuel_bridgeImpl: BridgeFungibleToken; + let fuel_bridge: any; + let fuel_bridgeImpl: any; let fuel_bridgeContractId: string; let fuel_testAssetId: string; // override the default test timeout from 2000ms this.timeout(DEFAULT_TIMEOUT_MS); + function fuel_to_eth_address(address: string): string { + return `0x${address.substring(26)}`.toLowerCase(); + } + async function forwardFuelChain(provider: Provider, blocksToForward: string) { await provider.produceBlocks(Number(blocksToForward)).catch(console.error); } @@ -85,7 +82,7 @@ describe('Bridging ERC20 tokens', async function () { } async function generateWithdrawalMessageProof( - fuel_bridge: BridgeFungibleToken, + fuel_bridge: any, fuelTokenSender: FuelWallet, ethereumTokenReceiverAddress: string, NUM_TOKENS: bigint, @@ -223,7 +220,7 @@ describe('Bridging ERC20 tokens', async function () { amount: bigint ) { // relay the message ourselves - const message = await waitForMessage( + const message: any = await waitForMessage( env.fuel.provider, fuelTokenMessageReceiver, fuelTokenMessageNonce, @@ -250,7 +247,7 @@ describe('Bridging ERC20 tokens', async function () { } before(async () => { - env = await setupEnvironment({}); + env = await setupEnvironment(); eth_erc20GatewayAddress = ( await env.eth.fuelERC20Gateway.getAddress() ).toLowerCase(); @@ -441,7 +438,7 @@ describe('Bridging ERC20 tokens', async function () { const fuelReceiver = Address.fromB256(event.args.recipient); // relay the message ourselves - const message = await waitForMessage( + const message: any = await waitForMessage( env.fuel.provider, fuelReceiver, nonce, diff --git a/packages/solidity-contracts/integration-tests/constants.ts b/packages/solidity-contracts/integration-tests/constants.ts new file mode 100644 index 00000000..ce453eed --- /dev/null +++ b/packages/solidity-contracts/integration-tests/constants.ts @@ -0,0 +1,13 @@ +import type { TxParams } from 'fuels'; +import { bn } from 'fuels'; + +const FUEL_GAS_LIMIT = 100000000; +const FUEL_MAX_FEE = 1; +export const FUEL_TX_PARAMS: TxParams = { + gasLimit: process.env.FUEL_GAS_LIMIT || FUEL_GAS_LIMIT, + maxFee: process.env.FUEL_MAX_FEE || FUEL_MAX_FEE, +}; +export const FUEL_CALL_TX_PARAMS = { + gasLimit: bn(10_000_000), + maxFee: FUEL_TX_PARAMS.maxFee, +}; diff --git a/packages/solidity-contracts/integration-tests/debug.ts b/packages/solidity-contracts/integration-tests/debug.ts new file mode 100644 index 00000000..99abd2cd --- /dev/null +++ b/packages/solidity-contracts/integration-tests/debug.ts @@ -0,0 +1,5 @@ +export function debug(...args: any) { + if (process.env.DEBUG) { + console.log(...args); + } +} diff --git a/packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts b/packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts new file mode 100644 index 00000000..849e621c --- /dev/null +++ b/packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts @@ -0,0 +1,96 @@ +import type { MessageProof } from 'fuels'; +import { arrayify } from 'fuels'; + +// The BlockHeader structure. +type MessageBlockHeader = { + prevRoot: string; + height: string; + timestamp: string; + daHeight: string; + txCount: string; + outputMessagesCount: string; + txRoot: string; + outputMessagesRoot: string; + consensusParametersVersion: bigint; + stateTransitionBytecodeVersion: bigint; + eventInboxRoot: string; +}; + +// The BlockHeader structure. +type CommitBlockHeader = { + prevRoot: string; + height: string; + timestamp: string; + applicationHash: string; +}; + +// The MessageOut structure. +type Message = { + sender: string; + recipient: string; + amount: string; + nonce: string; + data: string; +}; + +type Proof = { + key: string; + proof: Array; +}; + +export function createRelayMessageParams(withdrawMessageProof: MessageProof) { + // construct data objects for relaying message on L1 + const message: Message = { + sender: withdrawMessageProof.sender.toHexString(), + recipient: withdrawMessageProof.recipient.toHexString(), + amount: withdrawMessageProof.amount.toHex(), + nonce: withdrawMessageProof.nonce, + data: withdrawMessageProof.data, + }; + const header = withdrawMessageProof.messageBlockHeader; + + const blockHeader: MessageBlockHeader = { + prevRoot: header.prevRoot, + height: header.height.toString(), + timestamp: header.time, + daHeight: header.daHeight.toString(), + txCount: header.transactionsCount.toString(), + txRoot: header.transactionsRoot, + outputMessagesRoot: header.messageOutboxRoot, + outputMessagesCount: header.messageReceiptCount.toString(), + consensusParametersVersion: BigInt(header.consensusParametersVersion), + stateTransitionBytecodeVersion: BigInt( + header.stateTransitionBytecodeVersion + ), + eventInboxRoot: header.eventInboxRoot, + }; + const messageProof = withdrawMessageProof.messageProof; + // Create the message proof object + const messageInBlockProof: Proof = { + key: messageProof.proofIndex.toString(), + proof: messageProof.proofSet.map((p) => arrayify(p)), + }; + + // construct data objects for relaying message on L1 (cont) + const rootHeader = withdrawMessageProof.commitBlockHeader; + const rootBlockHeader: CommitBlockHeader = { + prevRoot: rootHeader.prevRoot, + height: rootHeader.height.toString(), + timestamp: rootHeader.time, + applicationHash: rootHeader.applicationHash, + }; + const blockProof = withdrawMessageProof.blockProof; + // Create the block proof object + const blockInHistoryProof: Proof = { + key: blockProof.proofIndex.toString(), + proof: blockProof.proofSet.map((p) => arrayify(p)), + }; + + return { + message, + rootBlockHeader, + blockHeader, + blockInHistoryProof, + messageInBlockProof, + }; +} diff --git a/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts b/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts new file mode 100644 index 00000000..973e6375 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts @@ -0,0 +1,81 @@ +import { parseEther } from 'ethers'; + +import type { Token } from '../../typechain'; +import { Token__factory } from '../../typechain'; +import { debug } from '../debug'; +import type { TestEnvironment } from '../setup/setup'; + +const { ETH_ERC20_TOKEN_ADDRESS } = process.env; +const ETHEREUM_ETH_DECIMALS = 18n; + +function ethers_parseToken( + value: string, + decimals: bigint | number = 18n +): bigint { + const val = parseEther(value); + if (typeof decimals === 'number') decimals = BigInt(decimals); + return val / 10n ** (ETHEREUM_ETH_DECIMALS - decimals); +} + +export async function getOrDeployECR20Contract(env: TestEnvironment) { + debug('Setting up environment...'); + const ethDeployer = env.eth.signers[0]; + const ethDeployerAddr = await ethDeployer.getAddress(); + const ethAcct = env.eth.signers[0]; + + // load ERC20 contract + let ethTestToken; + if (ETH_ERC20_TOKEN_ADDRESS) { + try { + ethTestToken = Token__factory.connect( + ETH_ERC20_TOKEN_ADDRESS, + ethDeployer + ); + const tokenOwner = await ethTestToken._owner(); + if (tokenOwner.toLowerCase() != ethDeployerAddr.toLowerCase()) { + debug( + `The Ethereum ERC-20 token at ${ETH_ERC20_TOKEN_ADDRESS} is not owned by the Ethereum deployer ${ethDeployerAddr}.` + ); + } + } catch (e) { + debug( + `The Ethereum ERC-20 token could not be found at the provided address ${ETH_ERC20_TOKEN_ADDRESS}.` + ); + } + } + if (!ethTestToken) { + debug(`Creating ERC-20 token contract to test with...`); + const eth_tokenFactory = new Token__factory(ethDeployer); + ethTestToken = await eth_tokenFactory + .deploy() + .then((tx) => tx.waitForDeployment()); + debug( + `Ethereum ERC-20 token contract created at address ${await ethTestToken.getAddress()}.` + ); + } + ethTestToken = ethTestToken?.connect(ethAcct); + const ethTestTokenAddress = await ethTestToken.getAddress(); + debug( + `Testing with Ethereum ERC-20 token contract at ${ethTestTokenAddress}.` + ); + + return ethTestToken; +} + +export async function mintECR20( + env: TestEnvironment, + ethTestToken: Token, + ethAcctAddr: string, + amount: string +) { + if ( + (await ethTestToken.balanceOf(ethAcctAddr)) <= + ethers_parseToken(amount, 18n) * 2n + ) { + debug(`Minting ERC-20 tokens to test with...`); + const tokenMintTx1 = await ethTestToken + .connect(env.eth.deployer) + .mint(ethAcctAddr, ethers_parseToken('100', 18n)); + await tokenMintTx1.wait(); + } +} diff --git a/packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts b/packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts new file mode 100644 index 00000000..9a10a882 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts @@ -0,0 +1,23 @@ +import { toBeHex, type BigNumberish, type JsonRpcProvider } from 'ethers'; + +/** + * @description jumps time in the blockchain by the specified amount of time + * @param provider A provider that exposes hardhat_ methods + */ +export async function hardhatSkipTime( + provider: JsonRpcProvider, + time: BigNumberish +) { + const startingBlockNumber = await provider.getBlockNumber(); + const hexTime = toBeHex(time).replace(/^0x0+/, '0x'); + + await provider.send('evm_increaseTime', [hexTime]); + const success = await provider.send('hardhat_mine', ['0x1']); + + while (success) { + if ((await provider.getBlockNumber()) > startingBlockNumber) break; + await new Promise((resolve) => setTimeout(() => resolve(null), 100)); + } + + return success; +} diff --git a/packages/solidity-contracts/integration-tests/ethers/index.ts b/packages/solidity-contracts/integration-tests/ethers/index.ts new file mode 100644 index 00000000..e5a5bf55 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/ethers/index.ts @@ -0,0 +1,2 @@ +export * from './createRelayParams'; +export * from './waitForBlockFinalization'; diff --git a/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts b/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts new file mode 100644 index 00000000..51b1f558 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts @@ -0,0 +1,83 @@ +import type { JsonRpcProvider, Provider, BigNumberish } from 'ethers'; +import { toBeHex } from 'ethers'; +import type { MessageProof } from 'fuels'; +import { arrayify } from 'fuels'; + +import { debug } from '../debug'; +import type { TestEnvironment } from '../setup/setup'; + +/** + * @description jumps time in the blockchain by the specified amount of time + * @param provider A provider that exposes hardhat_ methods + */ +async function hardhatSkipTime(provider: any, time: BigNumberish) { + const startingBlockNumber = await provider.getBlockNumber(); + const hexTime = toBeHex(time).replace(/^0x0+/, '0x'); + + await provider.send('evm_increaseTime', [hexTime]); + const success = await provider.send('hardhat_mine', ['0x1']); + + while (success) { + if ((await provider.getBlockNumber()) > startingBlockNumber) break; + await new Promise((resolve) => setTimeout(() => resolve(null), 100)); + } + + return success; +} + +async function isHardhatProvider(provider: Provider) { + if (!('send' in provider)) return false; + + try { + const result = await (provider as JsonRpcProvider).send( + 'hardhat_metadata', + [] + ); + + return !!result?.clientVersion; + } catch (e) { + return null; + } +} + +export async function waitForBlockFinalization( + env: TestEnvironment, + messageProof: MessageProof +) { + // connect to FuelChainState contract as the permissioned block comitter + const fuelChainState = env.eth.fuelChainState.connect(env.eth.provider); + + // If we are connecting to a hardhat instance, we can speed up the wait + if (await isHardhatProvider(env.eth.provider)) { + const time = await fuelChainState.TIME_TO_FINALIZE(); + + await hardhatSkipTime(env.eth.provider, time); + + const isFinalized = await fuelChainState.finalized( + arrayify(messageProof.commitBlockHeader.id), + messageProof.commitBlockHeader.height.toString() + ); + + if (isFinalized) return; + } + + return new Promise((resolve) => { + debug('Waiting for block to be finalized on L1...'); + function onBlock() { + fuelChainState + .finalized( + arrayify(messageProof.commitBlockHeader.id), + messageProof.commitBlockHeader.height.toString() + ) + .then((isFinalized) => { + if (isFinalized) { + env.eth.provider.removeListener('block', onBlock); + debug('Block is finalized on L1'); + resolve(true); + } + }) + .catch(() => {}); + } + env.eth.provider.addListener('block', onBlock); + }); +} diff --git a/packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts b/packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts new file mode 100644 index 00000000..ec5b4eef --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts @@ -0,0 +1,352 @@ +import type { + StorageSlot, + InvokeFunction, + BigNumberish, + AbstractAddress, + Account, + Provider, + DeployContractOptions, + DeployContractResult, + StdString, + BN, + Bytes, + FunctionFragment, +} from 'fuels'; +import { Contract, Interface, ContractFactory } from 'fuels'; + +type Option = T | undefined; + +type Enum = { + [K in keyof T]: Pick & { [P in Exclude]?: never }; +}[keyof T]; + +type MetadataOutput = Enum<{ + B256: string; + Bytes: Bytes; + Int: BN; + String: StdString; +}>; +type AssetIdInput = { + bits: string; +}; + +type IdentityInput = Enum<{ + Address: AddressInput; + ContractId: ContractIdInput; +}>; +type IdentityOutput = Enum<{ + Address: AddressOutput; + ContractId: ContractIdOutput; +}>; +type StateOutput = Enum<{ + Uninitialized: void; + Initialized: IdentityOutput; + Revoked: void; +}>; +type AddressInput = { + bits: string; +}; +type AddressOutput = AddressInput; +type ContractIdInput = { + bits: string; +}; +type ContractIdOutput = ContractIdInput; + +declare class ProxyInterface extends Interface { + constructor(); + functions: { + proxy_target: FunctionFragment; + set_proxy_target: FunctionFragment; + _proxy_change_owner: FunctionFragment; + _proxy_owner: FunctionFragment; + _proxy_revoke_ownership: FunctionFragment; + }; +} +declare class Proxy extends Contract { + static readonly abi: { + programType: string; + specVersion: string; + encodingVersion: string; + concreteTypes: ( + | { + type: string; + concreteTypeId: string; + metadataTypeId?: undefined; + typeArguments?: undefined; + } + | { + type: string; + concreteTypeId: string; + metadataTypeId: number; + typeArguments?: undefined; + } + | { + type: string; + concreteTypeId: string; + metadataTypeId: number; + typeArguments: string[]; + } + )[]; + metadataTypes: ( + | { + type: string; + metadataTypeId: number; + components?: undefined; + typeParameters?: undefined; + } + | { + type: string; + metadataTypeId: number; + components: ( + | { + name: string; + typeId: string; + } + | { + name: string; + typeId: number; + } + )[]; + typeParameters?: undefined; + } + | { + type: string; + metadataTypeId: number; + components: ( + | { + name: string; + typeId: string; + } + | { + name: string; + typeId: number; + } + )[]; + typeParameters: number[]; + } + )[]; + functions: { + inputs: { + name: string; + concreteTypeId: string; + }[]; + name: string; + output: string; + attributes: { + name: string; + arguments: string[]; + }[]; + }[]; + loggedTypes: { + logId: string; + concreteTypeId: string; + }[]; + messagesTypes: never[]; + configurables: { + name: string; + concreteTypeId: string; + offset: number; + }[]; + }; + + static readonly storageSlots: StorageSlot[]; + interface: ProxyInterface; + functions: { + proxy_target: InvokeFunction<[], Option>; + set_proxy_target: InvokeFunction<[new_target: ContractIdInput], void>; + _proxy_change_owner: InvokeFunction<[new_owner: IdentityInput], void>; + _proxy_owner: InvokeFunction<[], StateOutput>; + _proxy_revoke_ownership: InvokeFunction<[], void>; + }; + constructor( + id: string | AbstractAddress, + accountOrProvider: Account | Provider + ); +} + +declare class ProxyFactory extends ContractFactory { + static readonly bytecode: Uint8Array; + constructor(accountOrProvider: Account | Provider); + deploy( + deployOptions?: DeployContractOptions + ): Promise>; + static deploy( + wallet: Account, + options?: DeployContractOptions + ): Promise>; +} + +declare class BridgeFungibleTokenInterface extends Interface { + constructor(); + functions: { + process_message: FunctionFragment; + asset_to_l1_address: FunctionFragment; + asset_to_sub_id: FunctionFragment; + bridged_token_gateway: FunctionFragment; + claim_refund: FunctionFragment; + withdraw: FunctionFragment; + decimals: FunctionFragment; + name: FunctionFragment; + symbol: FunctionFragment; + total_assets: FunctionFragment; + total_supply: FunctionFragment; + metadata: FunctionFragment; + }; +} + +declare class BridgeFungibleToken extends Contract { + static readonly abi: { + programType: string; + specVersion: string; + encodingVersion: string; + concreteTypes: ( + | { + type: string; + concreteTypeId: string; + metadataTypeId?: undefined; + typeArguments?: undefined; + } + | { + type: string; + concreteTypeId: string; + metadataTypeId: number; + typeArguments?: undefined; + } + | { + type: string; + concreteTypeId: string; + metadataTypeId: number; + typeArguments: string[]; + } + )[]; + metadataTypes: ( + | { + type: string; + metadataTypeId: number; + components: ( + | { + name: string; + typeId: string; + } + | { + name: string; + typeId: number; + } + )[]; + typeParameters?: undefined; + } + | { + type: string; + metadataTypeId: number; + components: ( + | { + name: string; + typeId: string; + } + | { + name: string; + typeId: number; + } + )[]; + typeParameters: number[]; + } + | { + type: string; + metadataTypeId: number; + components?: undefined; + typeParameters?: undefined; + } + | { + type: string; + metadataTypeId: number; + components: ( + | { + name: string; + typeId: number; + typeArguments?: undefined; + } + | { + name: string; + typeId: number; + typeArguments: { + name: string; + typeId: number; + }[]; + } + )[]; + typeParameters?: undefined; + } + )[]; + functions: ( + | { + inputs: { + name: string; + concreteTypeId: string; + }[]; + name: string; + output: string; + attributes: { + name: string; + arguments: string[]; + }[]; + } + | { + inputs: never[]; + name: string; + output: string; + attributes: null; + } + )[]; + loggedTypes: { + logId: string; + concreteTypeId: string; + }[]; + messagesTypes: never[]; + configurables: { + name: string; + concreteTypeId: string; + offset: number; + }[]; + }; + + static readonly storageSlots: StorageSlot[]; + interface: BridgeFungibleTokenInterface; + functions: { + process_message: InvokeFunction<[msg_idx: BigNumberish], void>; + asset_to_l1_address: InvokeFunction<[asset_id: AssetIdInput], string>; + asset_to_sub_id: InvokeFunction<[asset_id: AssetIdInput], string>; + bridged_token_gateway: InvokeFunction<[], string>; + claim_refund: InvokeFunction< + [from: string, token_address: string, token_id: string], + void + >; + withdraw: InvokeFunction<[to: string], void>; + decimals: InvokeFunction<[asset: AssetIdInput], Option>; + name: InvokeFunction<[asset: AssetIdInput], Option>; + symbol: InvokeFunction<[asset: AssetIdInput], Option>; + total_assets: InvokeFunction<[], BN>; + total_supply: InvokeFunction<[asset: AssetIdInput], Option>; + metadata: InvokeFunction< + [asset: AssetIdInput, key: StdString], + Option + >; + }; + constructor( + id: string | AbstractAddress, + accountOrProvider: Account | Provider + ); +} + +declare class BridgeFungibleTokenFactory extends ContractFactory { + static readonly bytecode: Uint8Array; + constructor(accountOrProvider: Account | Provider); + deploy( + deployOptions?: DeployContractOptions + ): Promise>; + static deploy( + wallet: Account, + options?: DeployContractOptions + ): Promise>; +} + +export { BridgeFungibleToken, BridgeFungibleTokenFactory, Proxy, ProxyFactory }; diff --git a/packages/solidity-contracts/integration-tests/fuels/getBlock.ts b/packages/solidity-contracts/integration-tests/fuels/getBlock.ts new file mode 100644 index 00000000..4ad12c53 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/getBlock.ts @@ -0,0 +1,62 @@ +import { debug } from '../debug'; + +const query = ` + query Block($id: BlockId!) { + block(id: $id) { + id + header { + prevRoot + transactionsCount + applicationHash + transactionsRoot + height + daHeight + transactionsCount + messageOutboxRoot + messageReceiptCount + time + id + } + } + } +`; + +export interface Block { + id: string; + header: Header; +} + +export interface Header { + prevRoot: string; + transactionsCount: string; + applicationHash: string; + transactionsRoot: string; + height: string; + daHeight: string; + messageOutboxRoot: string; + messageReceiptCount: string; + time: string; + id: string; +} + +export function getBlock(providerUrl: string, blockId: string): Promise { + debug(`Fetching block with id ${blockId}`); + return fetch(providerUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query, + variables: { + id: blockId, + }, + }), + }) + .then((res) => res.json()) + .then((res: any) => { + if (!res.data.block) + throw new Error(`Could not fetch block with id ${blockId}`); + return res.data.block; + }); +} diff --git a/packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts b/packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts new file mode 100644 index 00000000..4368ab46 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts @@ -0,0 +1,14 @@ +import type { Receipt, TransactionResultMessageOutReceipt } from 'fuels'; +import { ReceiptType } from 'fuels'; + +export function getMessageOutReceipt(receipts: Array) { + const messageOutReceipt = receipts.find( + (r) => r.type === ReceiptType.MessageOut + ) as TransactionResultMessageOutReceipt; + + if (!messageOutReceipt) { + throw new Error('Failed to get message out receipt'); + } + + return messageOutReceipt; +} diff --git a/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts b/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts new file mode 100644 index 00000000..9d5fa82c --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts @@ -0,0 +1,91 @@ +import { + BridgeFungibleToken, + BridgeFungibleTokenFactory, + Proxy, + ProxyFactory, +} from '@fuel-bridge/fungible-token'; +import { resolveAddress, type AddressLike } from 'ethers'; + +// import { BridgeFungibleToken, BridgeFungibleTokenFactory, Proxy, ProxyFactory } from '../../../fungible-token/exports/types/index' +import { debug } from '../debug'; +import { eth_address_to_b256 } from '../parsers'; +import type { TestEnvironment } from '../setup/setup'; + +const { FUEL_FUNGIBLE_TOKEN_ADDRESS } = process.env; + +export async function getOrDeployL2Bridge( + env: TestEnvironment, + ethTokenGateway: AddressLike +) { + ethTokenGateway = await resolveAddress(ethTokenGateway); + + const tokenGateway = ethTokenGateway.replace('0x', ''); + const fuelAcct = env.fuel.signers[1]; + + let l2Bridge: any; + let proxy: Proxy; + let implementation: BridgeFungibleToken; + + if (FUEL_FUNGIBLE_TOKEN_ADDRESS) { + try { + proxy = new Proxy(FUEL_FUNGIBLE_TOKEN_ADDRESS, fuelAcct); + + const { value: implementationContractId }: any = await proxy.functions + .proxy_target() + .dryRun(); + + implementation = new BridgeFungibleToken( + implementationContractId.bits, + fuelAcct + ); + + l2Bridge = new BridgeFungibleToken(FUEL_FUNGIBLE_TOKEN_ADDRESS, fuelAcct); + + return { contract: l2Bridge, proxy, implementation }; + } catch (e) { + l2Bridge = null; + debug( + `The Fuel bridge contract could not be found at the provided address ${FUEL_FUNGIBLE_TOKEN_ADDRESS}.` + ); + } + } + + debug(`Creating Fuel bridge contract to test with...`); + const implConfigurables: any = { + BRIDGED_TOKEN_GATEWAY: eth_address_to_b256(tokenGateway), + }; + + implementation = await BridgeFungibleTokenFactory.deploy(fuelAcct, { + configurableConstants: implConfigurables, + }) + .then((tx: any) => tx.waitForResult()) + .then(({ contract }: any) => contract); + + debug('Creating proxy contract'); + const proxyConfigurables: any = { + INITIAL_TARGET: { bits: implementation.id.toB256() }, + INITIAL_OWNER: { + Initialized: { + Address: { bits: env.fuel.deployer.address.toHexString() }, + }, + }, + }; + + proxy = await ProxyFactory.deploy(fuelAcct, { + configurableConstants: proxyConfigurables, + }) + .then((tx: any) => tx.waitForResult()) + .then(({ contract }: any) => contract); + + // create contract instance + l2Bridge = new BridgeFungibleToken(proxy.id.toB256(), fuelAcct); + + const [fuelSigner] = env.fuel.signers; + l2Bridge.account = fuelSigner; + + debug('Finished setting up bridge'); + + l2Bridge.account = fuelAcct; + + return { contract: l2Bridge, proxy, implementation }; +} diff --git a/packages/solidity-contracts/integration-tests/fuels/getTokenId.ts b/packages/solidity-contracts/integration-tests/fuels/getTokenId.ts new file mode 100644 index 00000000..c2b64d24 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/getTokenId.ts @@ -0,0 +1,24 @@ +import type { BytesLike } from 'ethers'; +import { concat, dataLength, sha256, zeroPadValue, toUtf8Bytes } from 'ethers'; +import type { Contract } from 'fuels'; +import { ZeroBytes32 } from 'fuels'; + +export function getTokenId( + contract: Contract | string, + tokenAddress: BytesLike, + tokenId: BytesLike = ZeroBytes32, + chainId: string = '1' +) { + if (dataLength(tokenAddress) < 32) + tokenAddress = zeroPadValue(tokenAddress, 32); + + if (dataLength(tokenId) < 32) tokenId = zeroPadValue(tokenId, 32); + + const id = + typeof contract === 'object' ? contract.id.toHexString() : contract; + + const subId = sha256(concat([toUtf8Bytes(chainId), tokenAddress, tokenId])); + const assetId = sha256(concat([id, subId])); + + return assetId; +} diff --git a/packages/solidity-contracts/integration-tests/fuels/index.ts b/packages/solidity-contracts/integration-tests/fuels/index.ts new file mode 100644 index 00000000..31425fa8 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/index.ts @@ -0,0 +1,3 @@ +export * from './getBlock'; +export * from './getMessageOutReceipt'; +export * from './waitForMessage'; diff --git a/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts b/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts new file mode 100644 index 00000000..58a13cea --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts @@ -0,0 +1,248 @@ +/// @dev The Fuel testing utils. +/// A set of useful helper methods for the integration test environment. +import { + contractMessagePredicate, + contractMessageScript, +} from '@fuel-bridge/message-predicates'; +import type { + Message, + WalletUnlocked as FuelWallet, + TransactionResponse, + Provider, + ScriptTransactionRequestLike, + BytesLike, +} from 'fuels'; +import { + ZeroBytes32, + ScriptTransactionRequest, + arrayify, + InputType, + hexlify, + OutputType, + Predicate, + bn, + BN, +} from 'fuels'; + +import { debug } from '../debug'; + +import { resourcesToInputs } from './transaction'; + +type RelayMessageOptions = Pick< + ScriptTransactionRequestLike, + 'gasLimit' | 'maturity' | 'maxFee' +> & { + contractIds?: BytesLike[]; +}; + +type CommonMessageDetails = { + name: string; + predicateRoot: string; + predicate: string; + script: string; + buildTx: ( + relayer: FuelWallet, + message: Message, + details: CommonMessageDetails, + opts?: RelayMessageOptions + ) => Promise; +}; + +// const contractMessagePredicate = _contractMessagePredicate; +// const contractMessageScript = _contractMessageScript; + +// Update for mainnet gas costs +const PREDICATE_GAS_LIMIT = 10000000; + +// Details for relaying common messages with certain predicate roots +function getCommonRelayableMessages(provider: Provider) { + // Create a predicate for common messages + const predicate = new Predicate({ + bytecode: contractMessagePredicate, + provider, + }); + + const assetId = provider.getBaseAssetId(); + + // Details for relaying common messages with certain predicate roots + const relayableMessages: CommonMessageDetails[] = [ + { + name: 'Message To Contract v1.3', + predicateRoot: predicate.address.toHexString(), + predicate: contractMessagePredicate, + script: contractMessageScript, + buildTx: async ( + relayer: FuelWallet, + message: Message, + details: CommonMessageDetails, + opts?: RelayMessageOptions + ): Promise => { + const script = arrayify(details.script); + const predicateBytecode = arrayify(details.predicate); + + // get resources to fund the transaction + // these are initial values that enable calling + // `provider.estimatePredicates()` later + const resources = await relayer.getResourcesToSpend([ + { + amount: new BN(1), + assetId, + }, + ]); + + if (resources.length === 0) { + throw new Error('Could not find resources to fund the transaction'); + } + + // convert resources to inputs + const spendableInputs = resourcesToInputs(resources); + + // get contract id + const data = arrayify(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, + }); + transaction.inputs.push({ + type: InputType.Message, + amount: message.amount, + sender: message.sender.toHexString(), + recipient: message.recipient.toHexString(), + witnessIndex: 0, + data: message.data, + nonce: message.nonce, + predicate: predicateBytecode, + }); + + transaction.inputs.push({ + type: InputType.Contract, + txPointer: ZeroBytes32, + contractId, + }); + + for (const additionalContractId of opts?.contractIds || []) { + transaction.inputs.push({ + type: InputType.Contract, + txPointer: ZeroBytes32, + contractId: additionalContractId, + }); + } + + transaction.inputs.push(...spendableInputs); + + transaction.outputs.push({ + type: OutputType.Contract, + inputIndex: 1, + }); + + for (const [index] of (opts?.contractIds || []).entries()) { + transaction.outputs.push({ + type: OutputType.Contract, + inputIndex: 2 + index, + }); + } + + transaction.outputs.push({ + type: OutputType.Change, + to: relayer.address.toB256(), + assetId, + }); + transaction.outputs.push({ + type: OutputType.Variable, + }); + transaction.witnesses.push(ZeroBytes32); + + transaction.gasLimit = bn(PREDICATE_GAS_LIMIT); + + debug( + '-------------------------------------------------------------------' + ); + debug(transaction.inputs); + debug( + '-------------------------------------------------------------------' + ); + debug(transaction.outputs); + debug( + '-------------------------------------------------------------------' + ); + + return transaction; + }, + }, + ]; + + return relayableMessages; +} + +// Relay commonly used messages with predicates spendable by anyone +export async function relayCommonMessage( + relayer: FuelWallet, + message: Message, + opts?: RelayMessageOptions +): Promise { + // find the relay details for the specified message + let messageRelayDetails: CommonMessageDetails | null = null; + const predicateRoot = message.recipient.toHexString(); + + for (const details of getCommonRelayableMessages(relayer.provider)) { + if (details.predicateRoot == predicateRoot) { + messageRelayDetails = details; + break; + } + } + if (messageRelayDetails == null) + throw new Error('message is not a common relayable message'); + + // build and send transaction + const transaction = await messageRelayDetails.buildTx( + relayer, + message, + messageRelayDetails, + opts || {} + ); + + const estimated_tx = await relayer.provider.estimatePredicates(transaction); + + const fees = await relayer.provider.estimateTxGasAndFee({ + transactionRequest: estimated_tx, + }); + const [feeInput] = await relayer + .getResourcesToSpend([ + { + amount: fees.maxFee, + assetId: relayer.provider.getBaseAssetId(), + }, + ]) + .then(resourcesToInputs); + + if (!feeInput) { + throw new Error('Could not find resources to fund the transaction'); + } + + // Find the coins that are being used to pay for the tx + const feeInputIndex = estimated_tx.inputs.findIndex( + (input) => input.type === InputType.Coin + ); + + if (feeInputIndex === -1) { + estimated_tx.inputs.push(feeInput); + } else { + estimated_tx.inputs[feeInputIndex] = feeInput; + } + + estimated_tx.maxFee = fees.maxFee; + + const simulation = await relayer.simulateTransaction(estimated_tx); + debug(simulation); + if (simulation.dryRunStatus?.type === 'DryRunFailureStatus') { + throw new Error( + `Transaction simulation failure: ${JSON.stringify(simulation)}` + ); + } + + return relayer.sendTransaction(estimated_tx); +} diff --git a/packages/solidity-contracts/integration-tests/fuels/transaction.ts b/packages/solidity-contracts/integration-tests/fuels/transaction.ts new file mode 100644 index 00000000..350c4ee9 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/transaction.ts @@ -0,0 +1,17 @@ +import type { Resource, TransactionRequestInput } from 'fuels'; +import { InputType, ZeroBytes32, isCoin } from 'fuels'; + +export function resourcesToInputs(resources: Array) { + const inputs: Array = resources + .filter((r) => isCoin(r)) + .map((r: any) => ({ + type: InputType.Coin, + id: r.id, + owner: r.owner.toB256(), + amount: r.amount.toHex(), + assetId: r.assetId, + txPointer: ZeroBytes32, + witnessIndex: 0, + })); + return inputs; +} diff --git a/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts b/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts new file mode 100644 index 00000000..c5ac1814 --- /dev/null +++ b/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts @@ -0,0 +1,49 @@ +/// @dev The Fuel testing utils. +/// A set of useful helper methods for the integration test environment. +import type { BN, AbstractAddress } from 'fuels'; +import { type Provider as FuelProvider, type Message, hexlify } from 'fuels'; + +import { debug } from '../debug'; + +// Simple async delay function +function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +// Wait until a message is present in the fuel client +export async function waitForMessage( + provider: FuelProvider, + recipient: AbstractAddress, + nonce: BN, + timeout: number +): Promise { + const FUEL_MESSAGE_POLL_MS: number = 300; + const startTime = new Date().getTime(); + while (new Date().getTime() - startTime < timeout) { + const gqlMessage = await provider.getMessageByNonce( + hexlify(nonce.toBytes()) + ); + + if (gqlMessage) { + if (gqlMessage.recipient.toB256() !== recipient.toB256()) { + return null; + } + + const message: Message = { + messageId: gqlMessage.messageId, + sender: gqlMessage.sender, + recipient: gqlMessage.recipient, + nonce: hexlify(nonce.toBytes(32)), + amount: gqlMessage.amount, + data: gqlMessage.data, + daHeight: gqlMessage.daHeight, + }; + + return message; + } + + debug(`Waiting for message with nonce ${nonce}`); + await delay(FUEL_MESSAGE_POLL_MS); + } + return null; +} diff --git a/packages/solidity-contracts/integration-tests/parsers.ts b/packages/solidity-contracts/integration-tests/parsers.ts new file mode 100644 index 00000000..49518a8a --- /dev/null +++ b/packages/solidity-contracts/integration-tests/parsers.ts @@ -0,0 +1,7 @@ +export function fuel_to_eth_address(address: string): string { + return `0x${address.substring(26)}`.toLowerCase(); +} + +export function eth_address_to_b256(address: string): string { + return `0x000000000000000000000000${address.toLowerCase()}`.toLowerCase(); +} diff --git a/packages/solidity-contracts/integration-tests/setup/setup.ts b/packages/solidity-contracts/integration-tests/setup/setup.ts index 3a5111f4..67867c00 100644 --- a/packages/solidity-contracts/integration-tests/setup/setup.ts +++ b/packages/solidity-contracts/integration-tests/setup/setup.ts @@ -1,7 +1,13 @@ import type { Signer as EthSigner } from 'ethers'; +import { + JsonRpcProvider, + parseEther, + formatEther, + ethers, + NonceManager, +} from 'ethers'; import type { WalletUnlocked as FuelWallet } from 'fuels'; import { Wallet, Provider as FuelProvider, BN } from 'fuels'; -import hre from 'hardhat'; import { FuelChainState__factory, @@ -16,6 +22,7 @@ import type { export interface TestEnvironment { eth: { + provider: JsonRpcProvider; fuelChainState: FuelChainState; fuelMessagePortal: FuelMessagePortal; fuelERC20Gateway: FuelERC20Gateway; @@ -41,23 +48,43 @@ const def_pk_fuel_signer2: string = // Parse ETH value as a string export function fuels_parseEther(ether: string): BN { - let val = hre.ethers.parseEther(ether); + let val = ethers.parseEther(ether); val = val / 10n ** (ETHEREUM_ETH_DECIMALS - FUEL_ETH_DECIMALS); - return new BN(hre.ethers.toBeHex(val)); + return new BN(ethers.toBeHex(val)); } // Format ETH value to a string export function fuels_formatEther(ether: BN): string { let val = BigInt(ether.toHex()); val = val * 10n ** (ETHEREUM_ETH_DECIMALS - FUEL_ETH_DECIMALS); - return hre.ethers.formatEther(val); + return ethers.formatEther(val); } export async function setupEnvironment(): Promise { // Default config values + 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/v1/graphql'; + // Default private keys of the developer mnemonic + const eth_private_keys: string[] = [ + '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', + '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', + '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a', + '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6', + '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a', + '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba', + '0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e', + '0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356', + '0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97', + '0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6', + ]; + + const def_pk_eth_deployer: string = eth_private_keys[0]; + const def_pk_eth_signer1: string = eth_private_keys[3]; + const def_pk_eth_signer2: string = eth_private_keys[4]; + const def_pk_eth_signer3: string = eth_private_keys[5]; + let deployerAddresses: any = null; try { deployerAddresses = await fetch( @@ -72,21 +99,68 @@ export async function setupEnvironment(): Promise { ); } - const signers = await hre.ethers.getSigners(); + // Create provider and signers from http_ethereum_client + const eth_provider = new JsonRpcProvider(def_http_eth); + + const eth_deployer = new NonceManager( + new ethers.Wallet(def_pk_eth_deployer, eth_provider) + ); + const eth_deployerBalance = await eth_provider.getBalance(eth_deployer); + if (eth_deployerBalance < parseEther('5')) { + throw new Error( + 'Ethereum deployer balance is very low (' + + formatEther(eth_deployerBalance) + + 'ETH)' + ); + } + const eth_signer1 = new NonceManager( + new ethers.Wallet(def_pk_eth_signer1, eth_provider) + ); + const eth_signer1Balance = await eth_provider.getBalance(eth_signer1); + if (eth_signer1Balance < parseEther('1')) { + const tx = await eth_deployer.sendTransaction({ + to: eth_signer1, + value: parseEther('1'), + }); + await tx.wait(); + } + const eth_signer2 = new NonceManager( + new ethers.Wallet(def_pk_eth_signer2, eth_provider) + ); + const eth_signer2Balance = await eth_provider.getBalance(eth_signer2); + if (eth_signer2Balance < parseEther('1')) { + const tx = await eth_deployer.sendTransaction({ + to: eth_signer2, + value: parseEther('1'), + }); + await tx.wait(); + } + + const eth_signer3 = new NonceManager( + new ethers.Wallet(def_pk_eth_signer3, eth_provider) + ); + const eth_signer3Balance = await eth_provider.getBalance(eth_signer3); + if (eth_signer3Balance < parseEther('1')) { + const tx = await eth_deployer.sendTransaction({ + to: eth_signer3, + value: parseEther('1'), + }); + await tx.wait(); + } const eth_fuelChainState: FuelChainState = FuelChainState__factory.connect( deployerAddresses.FuelChainState, - signers[0] + eth_deployer ); const eth_fuelMessagePortal: FuelMessagePortal = FuelMessagePortal__factory.connect( deployerAddresses.FuelMessagePortal, - signers[0] + eth_deployer ); const eth_fuelERC20Gateway: FuelERC20Gateway = FuelERC20Gateway__factory.connect( deployerAddresses.FuelERC20GatewayV4, - signers[0] + eth_deployer ); // Create provider from http_fuel_client @@ -140,11 +214,12 @@ export async function setupEnvironment(): Promise { return { eth: { + provider: eth_provider, fuelChainState: eth_fuelChainState, fuelMessagePortal: eth_fuelMessagePortal, fuelERC20Gateway: eth_fuelERC20Gateway, - deployer: signers[0], - signers: [signers[3], signers[4], signers[5]], + deployer: eth_deployer, + signers: [eth_signer1, eth_signer2, eth_signer3], }, fuel: { provider: fuel_provider, diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index eed3f7b8..449e55a2 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -1,14 +1,3 @@ -import type { TestEnvironment } from '@fuel-bridge/test-utils'; -import { - setupEnvironment, - fuels_parseEther, - createRelayMessageParams, - getMessageOutReceipt, - waitForMessage, - waitForBlockFinalization, - getBlock, - FUEL_CALL_TX_PARAMS, -} from '@fuel-bridge/test-utils'; import chai from 'chai'; import { parseEther } from 'ethers'; import type { Signer } from 'ethers'; @@ -20,6 +9,12 @@ import type { Provider, } from 'fuels'; +import { FUEL_CALL_TX_PARAMS } from './constants'; +import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; +import { waitForMessage, getMessageOutReceipt, getBlock } from './fuels'; +import { setupEnvironment, fuels_parseEther } from './setup/setup'; +import type { TestEnvironment } from './setup/setup'; + const { expect } = chai; describe('Transferring ETH', async function () { @@ -181,7 +176,7 @@ describe('Transferring ETH', async function () { } before(async () => { - env = await setupEnvironment({}); + env = await setupEnvironment(); BASE_ASSET_ID = env.fuel.provider.getBaseAssetId(); }); From 3925774a766a69327e8324f05fdd06f8af47ea2b Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 17:38:08 +0530 Subject: [PATCH 45/81] chore: remove unused test helper --- .../fuels/fungibleToken/factory.ts | 352 ------------------ 1 file changed, 352 deletions(-) delete mode 100644 packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts diff --git a/packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts b/packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts deleted file mode 100644 index ec5b4eef..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/fungibleToken/factory.ts +++ /dev/null @@ -1,352 +0,0 @@ -import type { - StorageSlot, - InvokeFunction, - BigNumberish, - AbstractAddress, - Account, - Provider, - DeployContractOptions, - DeployContractResult, - StdString, - BN, - Bytes, - FunctionFragment, -} from 'fuels'; -import { Contract, Interface, ContractFactory } from 'fuels'; - -type Option = T | undefined; - -type Enum = { - [K in keyof T]: Pick & { [P in Exclude]?: never }; -}[keyof T]; - -type MetadataOutput = Enum<{ - B256: string; - Bytes: Bytes; - Int: BN; - String: StdString; -}>; -type AssetIdInput = { - bits: string; -}; - -type IdentityInput = Enum<{ - Address: AddressInput; - ContractId: ContractIdInput; -}>; -type IdentityOutput = Enum<{ - Address: AddressOutput; - ContractId: ContractIdOutput; -}>; -type StateOutput = Enum<{ - Uninitialized: void; - Initialized: IdentityOutput; - Revoked: void; -}>; -type AddressInput = { - bits: string; -}; -type AddressOutput = AddressInput; -type ContractIdInput = { - bits: string; -}; -type ContractIdOutput = ContractIdInput; - -declare class ProxyInterface extends Interface { - constructor(); - functions: { - proxy_target: FunctionFragment; - set_proxy_target: FunctionFragment; - _proxy_change_owner: FunctionFragment; - _proxy_owner: FunctionFragment; - _proxy_revoke_ownership: FunctionFragment; - }; -} -declare class Proxy extends Contract { - static readonly abi: { - programType: string; - specVersion: string; - encodingVersion: string; - concreteTypes: ( - | { - type: string; - concreteTypeId: string; - metadataTypeId?: undefined; - typeArguments?: undefined; - } - | { - type: string; - concreteTypeId: string; - metadataTypeId: number; - typeArguments?: undefined; - } - | { - type: string; - concreteTypeId: string; - metadataTypeId: number; - typeArguments: string[]; - } - )[]; - metadataTypes: ( - | { - type: string; - metadataTypeId: number; - components?: undefined; - typeParameters?: undefined; - } - | { - type: string; - metadataTypeId: number; - components: ( - | { - name: string; - typeId: string; - } - | { - name: string; - typeId: number; - } - )[]; - typeParameters?: undefined; - } - | { - type: string; - metadataTypeId: number; - components: ( - | { - name: string; - typeId: string; - } - | { - name: string; - typeId: number; - } - )[]; - typeParameters: number[]; - } - )[]; - functions: { - inputs: { - name: string; - concreteTypeId: string; - }[]; - name: string; - output: string; - attributes: { - name: string; - arguments: string[]; - }[]; - }[]; - loggedTypes: { - logId: string; - concreteTypeId: string; - }[]; - messagesTypes: never[]; - configurables: { - name: string; - concreteTypeId: string; - offset: number; - }[]; - }; - - static readonly storageSlots: StorageSlot[]; - interface: ProxyInterface; - functions: { - proxy_target: InvokeFunction<[], Option>; - set_proxy_target: InvokeFunction<[new_target: ContractIdInput], void>; - _proxy_change_owner: InvokeFunction<[new_owner: IdentityInput], void>; - _proxy_owner: InvokeFunction<[], StateOutput>; - _proxy_revoke_ownership: InvokeFunction<[], void>; - }; - constructor( - id: string | AbstractAddress, - accountOrProvider: Account | Provider - ); -} - -declare class ProxyFactory extends ContractFactory { - static readonly bytecode: Uint8Array; - constructor(accountOrProvider: Account | Provider); - deploy( - deployOptions?: DeployContractOptions - ): Promise>; - static deploy( - wallet: Account, - options?: DeployContractOptions - ): Promise>; -} - -declare class BridgeFungibleTokenInterface extends Interface { - constructor(); - functions: { - process_message: FunctionFragment; - asset_to_l1_address: FunctionFragment; - asset_to_sub_id: FunctionFragment; - bridged_token_gateway: FunctionFragment; - claim_refund: FunctionFragment; - withdraw: FunctionFragment; - decimals: FunctionFragment; - name: FunctionFragment; - symbol: FunctionFragment; - total_assets: FunctionFragment; - total_supply: FunctionFragment; - metadata: FunctionFragment; - }; -} - -declare class BridgeFungibleToken extends Contract { - static readonly abi: { - programType: string; - specVersion: string; - encodingVersion: string; - concreteTypes: ( - | { - type: string; - concreteTypeId: string; - metadataTypeId?: undefined; - typeArguments?: undefined; - } - | { - type: string; - concreteTypeId: string; - metadataTypeId: number; - typeArguments?: undefined; - } - | { - type: string; - concreteTypeId: string; - metadataTypeId: number; - typeArguments: string[]; - } - )[]; - metadataTypes: ( - | { - type: string; - metadataTypeId: number; - components: ( - | { - name: string; - typeId: string; - } - | { - name: string; - typeId: number; - } - )[]; - typeParameters?: undefined; - } - | { - type: string; - metadataTypeId: number; - components: ( - | { - name: string; - typeId: string; - } - | { - name: string; - typeId: number; - } - )[]; - typeParameters: number[]; - } - | { - type: string; - metadataTypeId: number; - components?: undefined; - typeParameters?: undefined; - } - | { - type: string; - metadataTypeId: number; - components: ( - | { - name: string; - typeId: number; - typeArguments?: undefined; - } - | { - name: string; - typeId: number; - typeArguments: { - name: string; - typeId: number; - }[]; - } - )[]; - typeParameters?: undefined; - } - )[]; - functions: ( - | { - inputs: { - name: string; - concreteTypeId: string; - }[]; - name: string; - output: string; - attributes: { - name: string; - arguments: string[]; - }[]; - } - | { - inputs: never[]; - name: string; - output: string; - attributes: null; - } - )[]; - loggedTypes: { - logId: string; - concreteTypeId: string; - }[]; - messagesTypes: never[]; - configurables: { - name: string; - concreteTypeId: string; - offset: number; - }[]; - }; - - static readonly storageSlots: StorageSlot[]; - interface: BridgeFungibleTokenInterface; - functions: { - process_message: InvokeFunction<[msg_idx: BigNumberish], void>; - asset_to_l1_address: InvokeFunction<[asset_id: AssetIdInput], string>; - asset_to_sub_id: InvokeFunction<[asset_id: AssetIdInput], string>; - bridged_token_gateway: InvokeFunction<[], string>; - claim_refund: InvokeFunction< - [from: string, token_address: string, token_id: string], - void - >; - withdraw: InvokeFunction<[to: string], void>; - decimals: InvokeFunction<[asset: AssetIdInput], Option>; - name: InvokeFunction<[asset: AssetIdInput], Option>; - symbol: InvokeFunction<[asset: AssetIdInput], Option>; - total_assets: InvokeFunction<[], BN>; - total_supply: InvokeFunction<[asset: AssetIdInput], Option>; - metadata: InvokeFunction< - [asset: AssetIdInput, key: StdString], - Option - >; - }; - constructor( - id: string | AbstractAddress, - accountOrProvider: Account | Provider - ); -} - -declare class BridgeFungibleTokenFactory extends ContractFactory { - static readonly bytecode: Uint8Array; - constructor(accountOrProvider: Account | Provider); - deploy( - deployOptions?: DeployContractOptions - ): Promise>; - static deploy( - wallet: Account, - options?: DeployContractOptions - ): Promise>; -} - -export { BridgeFungibleToken, BridgeFungibleTokenFactory, Proxy, ProxyFactory }; From 2d667592e41c309364879826a10efb4022da02de Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 18:50:29 +0530 Subject: [PATCH 46/81] chore: add block committer container env vars --- .../docker/block-committer/block-committer.sh | 4 ++++ packages/solidity-contracts/docker/docker-compose.yml | 6 ++---- .../solidity-contracts/docker/envs/block_committer.env | 9 +++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 packages/solidity-contracts/docker/envs/block_committer.env diff --git a/packages/solidity-contracts/docker/block-committer/block-committer.sh b/packages/solidity-contracts/docker/block-committer/block-committer.sh index f2be6988..61a5eadb 100644 --- a/packages/solidity-contracts/docker/block-committer/block-committer.sh +++ b/packages/solidity-contracts/docker/block-committer/block-committer.sh @@ -45,6 +45,10 @@ echo "Got l1 chain deployment data." # pull data from deployer dump export COMMITTER__ETH__STATE_CONTRACT_ADDRESS=$(cat "./addresses.json" | jq -r .FuelChainState) +export COMMITTER__APP__DB__USERNAME=${DB__USERNAME} +export COMMITTER__APP__DB__PASSWORD=${DB__PASSWORD} +export COMMITTER__ETH__L1_KEYS__MAIN=${COMMITTER__ETH__L1_KEYS__MAIN} +export COMMITTER__ETH__L1_KEYS__BLOB=${COMMITTER__ETH__L1_KEYS__BLOB} echo "COMMITTER__ETH__STATE_CONTRACT_ADDRESS: $COMMITTER__ETH__STATE_CONTRACT_ADDRESS" echo "ETHEREUM_RPC: $COMMITTER__ETH__RPC" echo "FUEL_GRAPHQL_ENDPOINT: $COMMITTER__FUEL__GRAPHQL_ENDPOINT" diff --git a/packages/solidity-contracts/docker/docker-compose.yml b/packages/solidity-contracts/docker/docker-compose.yml index b23c6721..5e09668f 100644 --- a/packages/solidity-contracts/docker/docker-compose.yml +++ b/packages/solidity-contracts/docker/docker-compose.yml @@ -55,16 +55,14 @@ services: platform: linux/amd64 build: context: ./block-committer/ + env_file: + - ./envs/block_committer.env environment: COMMITTER__ETH__RPC: 'ws://l1_chain:9545/' - COMMITTER__ETH__L1_KEYS__MAIN: 'Private(8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba)' - COMMITTER__ETH__L1_KEYS__BLOB: 'Private(59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d)' COMMITTER__FUEL__GRAPHQL_ENDPOINT: 'http://fuel_core:4001/graphql' COMMITTER__FUEL__NUM_BUFFERED_REQUESTS: '5' COMMITTER__APP__DB__PORT: '5432' COMMITTER__APP__DB__HOST: 'db' - COMMITTER__APP__DB__USERNAME: 'username' - COMMITTER__APP__DB__PASSWORD: 'password' COMMITTER__APP__DB__MAX_CONNECTIONS: '10' COMMITTER__APP__DB__USE_SSL: 'false' COMMITTER__APP__DB__DATABASE: 'committer_db' diff --git a/packages/solidity-contracts/docker/envs/block_committer.env b/packages/solidity-contracts/docker/envs/block_committer.env new file mode 100644 index 00000000..7cabadc5 --- /dev/null +++ b/packages/solidity-contracts/docker/envs/block_committer.env @@ -0,0 +1,9 @@ +ETHEREUM_WALLET_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d +COMMIT_INTERVAL=30 +DB__USERNAME=username +DB__PASSWORD=password +COMMITTER__ETH__L1_KEYS__MAIN=Private(8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba) +COMMITTER__ETH__L1_KEYS__BLOB=Private(59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d) +# HOST= +# PORT= +# ETHEREUM_CHAIN= \ No newline at end of file From 6572facde4edbd3807e4a2c60379ab6b7698cec7 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 18:50:56 +0530 Subject: [PATCH 47/81] chore: add comments --- packages/solidity-contracts/integration-tests/bridge_erc20.ts | 2 ++ packages/solidity-contracts/integration-tests/transfer_eth.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts index 59700f9e..b3a56526 100644 --- a/packages/solidity-contracts/integration-tests/bridge_erc20.ts +++ b/packages/solidity-contracts/integration-tests/bridge_erc20.ts @@ -12,6 +12,8 @@ import type { import { RATE_LIMIT_AMOUNT, RATE_LIMIT_DURATION } from '../protocol/constants'; import type { Token } from '../typechain'; +// due to cyclic workspace dependencies with test-utils package, the CI which runs `pnpm forc fmt --check` was getting timed out +// so hence had to re-use some test helpers in the test-utils package import { FUEL_CALL_TX_PARAMS, FUEL_TX_PARAMS } from './constants'; import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; import { getOrDeployECR20Contract } from './ethers/getOrDeployECR20Contract'; diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index 449e55a2..a2639faf 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -9,6 +9,8 @@ import type { Provider, } from 'fuels'; +// due to cyclic workspace dependencies with test-utils package, the CI which runs `pnpm forc fmt --check` was getting timed out +// so hence had to re-use some test helpers in the test-utils package import { FUEL_CALL_TX_PARAMS } from './constants'; import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; import { waitForMessage, getMessageOutReceipt, getBlock } from './fuels'; From 5208aa6946fda4f2caabce284382d69d3de3ef9a Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 18:53:23 +0530 Subject: [PATCH 48/81] refactor: add a utils section for integration tests --- packages/solidity-contracts/integration-tests/bridge_erc20.ts | 2 +- .../integration-tests/ethers/getOrDeployECR20Contract.ts | 2 +- .../integration-tests/fuels/getOrDeployL2Bridge.ts | 4 ++-- .../integration-tests/fuels/relayCommonMessage.ts | 2 +- packages/solidity-contracts/integration-tests/transfer_eth.ts | 2 +- .../integration-tests/{ => utils}/constants.ts | 0 .../solidity-contracts/integration-tests/{ => utils}/debug.ts | 0 .../integration-tests/{ => utils}/parsers.ts | 0 8 files changed, 6 insertions(+), 6 deletions(-) rename packages/solidity-contracts/integration-tests/{ => utils}/constants.ts (100%) rename packages/solidity-contracts/integration-tests/{ => utils}/debug.ts (100%) rename packages/solidity-contracts/integration-tests/{ => utils}/parsers.ts (100%) diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts index b3a56526..73fa844d 100644 --- a/packages/solidity-contracts/integration-tests/bridge_erc20.ts +++ b/packages/solidity-contracts/integration-tests/bridge_erc20.ts @@ -14,7 +14,6 @@ import type { Token } from '../typechain'; // due to cyclic workspace dependencies with test-utils package, the CI which runs `pnpm forc fmt --check` was getting timed out // so hence had to re-use some test helpers in the test-utils package -import { FUEL_CALL_TX_PARAMS, FUEL_TX_PARAMS } from './constants'; import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; import { getOrDeployECR20Contract } from './ethers/getOrDeployECR20Contract'; import { hardhatSkipTime } from './ethers/hardhatSkipTime'; @@ -24,6 +23,7 @@ import { getTokenId } from './fuels/getTokenId'; import { relayCommonMessage } from './fuels/relayCommonMessage'; import type { TestEnvironment } from './setup/setup'; import { setupEnvironment } from './setup/setup'; +import { FUEL_CALL_TX_PARAMS, FUEL_TX_PARAMS } from './utils/constants'; const { expect } = chai; diff --git a/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts b/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts index 973e6375..f9cc1f94 100644 --- a/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts +++ b/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts @@ -2,8 +2,8 @@ import { parseEther } from 'ethers'; import type { Token } from '../../typechain'; import { Token__factory } from '../../typechain'; -import { debug } from '../debug'; import type { TestEnvironment } from '../setup/setup'; +import { debug } from '../utils/debug'; const { ETH_ERC20_TOKEN_ADDRESS } = process.env; const ETHEREUM_ETH_DECIMALS = 18n; diff --git a/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts b/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts index 9d5fa82c..4374c565 100644 --- a/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts +++ b/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts @@ -7,9 +7,9 @@ import { import { resolveAddress, type AddressLike } from 'ethers'; // import { BridgeFungibleToken, BridgeFungibleTokenFactory, Proxy, ProxyFactory } from '../../../fungible-token/exports/types/index' -import { debug } from '../debug'; -import { eth_address_to_b256 } from '../parsers'; import type { TestEnvironment } from '../setup/setup'; +import { debug } from '../utils/debug'; +import { eth_address_to_b256 } from '../utils/parsers'; const { FUEL_FUNGIBLE_TOKEN_ADDRESS } = process.env; diff --git a/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts b/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts index 58a13cea..033de52f 100644 --- a/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts +++ b/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts @@ -24,7 +24,7 @@ import { BN, } from 'fuels'; -import { debug } from '../debug'; +import { debug } from '../utils/debug'; import { resourcesToInputs } from './transaction'; diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts index a2639faf..d30a59c4 100644 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ b/packages/solidity-contracts/integration-tests/transfer_eth.ts @@ -11,11 +11,11 @@ import type { // due to cyclic workspace dependencies with test-utils package, the CI which runs `pnpm forc fmt --check` was getting timed out // so hence had to re-use some test helpers in the test-utils package -import { FUEL_CALL_TX_PARAMS } from './constants'; import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; import { waitForMessage, getMessageOutReceipt, getBlock } from './fuels'; import { setupEnvironment, fuels_parseEther } from './setup/setup'; import type { TestEnvironment } from './setup/setup'; +import { FUEL_CALL_TX_PARAMS } from './utils/constants'; const { expect } = chai; diff --git a/packages/solidity-contracts/integration-tests/constants.ts b/packages/solidity-contracts/integration-tests/utils/constants.ts similarity index 100% rename from packages/solidity-contracts/integration-tests/constants.ts rename to packages/solidity-contracts/integration-tests/utils/constants.ts diff --git a/packages/solidity-contracts/integration-tests/debug.ts b/packages/solidity-contracts/integration-tests/utils/debug.ts similarity index 100% rename from packages/solidity-contracts/integration-tests/debug.ts rename to packages/solidity-contracts/integration-tests/utils/debug.ts diff --git a/packages/solidity-contracts/integration-tests/parsers.ts b/packages/solidity-contracts/integration-tests/utils/parsers.ts similarity index 100% rename from packages/solidity-contracts/integration-tests/parsers.ts rename to packages/solidity-contracts/integration-tests/utils/parsers.ts From d7ce105bca4e2b15f1b0bd60c97a93ff3b73bd5a Mon Sep 17 00:00:00 2001 From: Viraz Malhotra Date: Mon, 6 Jan 2025 19:05:39 +0530 Subject: [PATCH 49/81] fix --- .../ethers/waitForBlockFinalization.ts | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts b/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts index 51b1f558..6972b898 100644 --- a/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts +++ b/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts @@ -1,29 +1,12 @@ -import type { JsonRpcProvider, Provider, BigNumberish } from 'ethers'; -import { toBeHex } from 'ethers'; +import type { JsonRpcProvider, Provider } from 'ethers'; import type { MessageProof } from 'fuels'; import { arrayify } from 'fuels'; -import { debug } from '../debug'; import type { TestEnvironment } from '../setup/setup'; +import {debug} from '../utils/debug'; -/** - * @description jumps time in the blockchain by the specified amount of time - * @param provider A provider that exposes hardhat_ methods - */ -async function hardhatSkipTime(provider: any, time: BigNumberish) { - const startingBlockNumber = await provider.getBlockNumber(); - const hexTime = toBeHex(time).replace(/^0x0+/, '0x'); +import { hardhatSkipTime } from './hardhatSkipTime'; - await provider.send('evm_increaseTime', [hexTime]); - const success = await provider.send('hardhat_mine', ['0x1']); - - while (success) { - if ((await provider.getBlockNumber()) > startingBlockNumber) break; - await new Promise((resolve) => setTimeout(() => resolve(null), 100)); - } - - return success; -} async function isHardhatProvider(provider: Provider) { if (!('send' in provider)) return false; From 29163144e33892f68cc14ba43b09f6fb01eca85b Mon Sep 17 00:00:00 2001 From: Viraz Malhotra Date: Mon, 6 Jan 2025 19:22:44 +0530 Subject: [PATCH 50/81] fix: update import --- .../integration-tests/fuels/waitForMessage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts b/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts index c5ac1814..c1f68bf9 100644 --- a/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts +++ b/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts @@ -3,7 +3,7 @@ import type { BN, AbstractAddress } from 'fuels'; import { type Provider as FuelProvider, type Message, hexlify } from 'fuels'; -import { debug } from '../debug'; +import {debug} from '../utils/debug'; // Simple async delay function function delay(ms: number) { From 0c972378fd015b9b7b9dbd21854223b2e0fa87b5 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Mon, 6 Jan 2025 19:23:51 +0530 Subject: [PATCH 51/81] chore: small touchup --- packages/solidity-contracts/integration-tests/fuels/getBlock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/solidity-contracts/integration-tests/fuels/getBlock.ts b/packages/solidity-contracts/integration-tests/fuels/getBlock.ts index 4ad12c53..f9108c03 100644 --- a/packages/solidity-contracts/integration-tests/fuels/getBlock.ts +++ b/packages/solidity-contracts/integration-tests/fuels/getBlock.ts @@ -1,4 +1,4 @@ -import { debug } from '../debug'; +import { debug } from '../utils/debug'; const query = ` query Block($id: BlockId!) { From a666ed93f409e9c5f5522ee6df4edb81c368b7ae Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 19:34:08 +0530 Subject: [PATCH 52/81] chore: simplify commiter role grant script --- .../hardhat/005.register_block_committer.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts index 9acd6bbe..dad1bb30 100644 --- a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts +++ b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts @@ -12,16 +12,16 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const [deployer] = await ethers.getSigners(); - if (!isForking) { - const { address } = await deployments.get('FuelChainState'); + if (isForking) return; - const fuelChainState = FuelChainState__factory.connect(address, deployer); - const COMMITTER_ROLE = await fuelChainState.COMMITTER_ROLE(); + const { address } = await deployments.get('FuelChainState'); - await fuelChainState - .grantRole(COMMITTER_ROLE, COMMITTER_ADDRESS) - .then((tx) => tx.wait()); - } + const fuelChainState = FuelChainState__factory.connect(address, deployer); + const COMMITTER_ROLE = await fuelChainState.COMMITTER_ROLE(); + + await fuelChainState + .grantRole(COMMITTER_ROLE, COMMITTER_ADDRESS) + .then((tx) => tx.wait()); console.log('Granted role COMMITTER_ROLE to', COMMITTER_ADDRESS); }; From 4d954d514bdd66941cd67603e8da298acb850e71 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 19:38:16 +0530 Subject: [PATCH 53/81] chore: revert erc721 deployment script updates --- .../hardhat/005.register_block_committer.ts | 3 +-- .../deploy/hardhat/006._erc721_gateway_v3.ts | 24 +++---------------- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts index dad1bb30..521d68ab 100644 --- a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts +++ b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts @@ -9,11 +9,10 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { ethers, deployments } = hre; const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + if (isForking) return; const [deployer] = await ethers.getSigners(); - if (isForking) return; - const { address } = await deployments.get('FuelChainState'); const fuelChainState = FuelChainState__factory.connect(address, deployer); diff --git a/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts b/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts index 69129a74..3bafb3cc 100644 --- a/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts +++ b/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts @@ -1,7 +1,5 @@ -import fs from 'fs'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; -import path from 'path'; import { FuelERC721GatewayV2__factory as FuelERC721GatewayV2 } from '../../typechain'; @@ -13,31 +11,15 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { } = hre; const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + if (isForking) return; const [deployer] = await ethers.getSigners(); - let portlAddress; - if (isForking) { - const deploymentPath = path.join( - __dirname, - '..', - '..', - '/', - 'deployments', - 'mainnet', - 'FuelMessagePortal.json' - ); - - const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8')); - portlAddress = deployment.address; - } else { - const fuelMessagePortal = await get('FuelMessagePortal'); - portlAddress = fuelMessagePortal.address; - } + const fuelMessagePortal = await get('FuelMessagePortal'); const contract = await deployProxy( new FuelERC721GatewayV2(deployer), - [portlAddress], + [fuelMessagePortal.address], { initializer: 'initialize', } From 79575c94480f8477f1e2e86e8f6ed930ab590905 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 19:57:31 +0530 Subject: [PATCH 54/81] chore: remove erc721 gateway from artifacts in docker container --- .../deploy/hardhat/999.serve_deployment_file.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts index 4a2fbc64..54c4f6b1 100644 --- a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts +++ b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts @@ -37,19 +37,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { } }); - deploymentDir = path.join( - __dirname, - '..', - '..', - '/', - 'deployments', - 'localhost', - 'FuelERC721Gateway.json' - ); - - const deployment = JSON.parse(fs.readFileSync(deploymentDir, 'utf8')); - deploymentsFile['FuelERC721Gateway'] = deployment.address; - deploymentDir = path.join( __dirname, '..', From 5d038a7e4b33bd7fb5be20fc679d3e3cb3c5dac7 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:14:02 +0530 Subject: [PATCH 55/81] refactor: handle original docker setup to handle both fork and local env --- docker/envs/fuel_core.env | 2 ++ docker/envs/l1_chain.env | 2 ++ docker/fuel-core/fuel_core.sh | 51 ++++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/docker/envs/fuel_core.env b/docker/envs/fuel_core.env index 5afd14ac..8e4eaf8a 100644 --- a/docker/envs/fuel_core.env +++ b/docker/envs/fuel_core.env @@ -3,3 +3,5 @@ CONSENSUS_KEY_SECRET="0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc24 # Uncommend to use the variables by removing # # FUEL_IP= # FUEL_PORT= +# set true when running integration tests over forked environment +FORKING=false diff --git a/docker/envs/l1_chain.env b/docker/envs/l1_chain.env index 2334abdb..1218fb96 100644 --- a/docker/envs/l1_chain.env +++ b/docker/envs/l1_chain.env @@ -5,3 +5,5 @@ # L1_PORT= # SERVE_PORT= DEPLOYER_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d +# set tenderly rpc for spinning up forked network +TENDERLY_RPC_URL= \ No newline at end of file diff --git a/docker/fuel-core/fuel_core.sh b/docker/fuel-core/fuel_core.sh index 3f9d2b65..34b579d7 100644 --- a/docker/fuel-core/fuel_core.sh +++ b/docker/fuel-core/fuel_core.sh @@ -49,19 +49,40 @@ export FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS=$(cat "./addresses.json" | jq -r .Fu echo "FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS: $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS" echo "L1_CHAIN_HTTP: $L1_CHAIN_HTTP" +export FORKING=${FORKING} + # start the Fuel client -echo "Starting fuel node." -exec /root/fuel-core run \ - --ip $FUEL_IP \ - --port $FUEL_PORT \ - --utxo-validation \ - --vm-backtrace \ - --enable-relayer \ - --relayer $L1_CHAIN_HTTP \ - --relayer-v2-listening-contracts $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS \ - --poa-interval-period 1sec \ - --debug \ - --da-compression $DA_COMPRESSION \ - --graphql-max-complexity $GRAPHQL_COMPLEXITY \ - --min-gas-price 0 \ - --snapshot ./ \ No newline at end of file +if [ "$FORKING" = "true" ]; then + echo "FORKING is enabled. Running with da deploy height" + exec /root/fuel-core run \ + --ip $FUEL_IP \ + --port $FUEL_PORT \ + --utxo-validation \ + --vm-backtrace \ + --enable-relayer \ + --relayer $L1_CHAIN_HTTP \ + --relayer-v2-listening-contracts $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS \ + --poa-interval-period 1sec \ + --relayer-da-deploy-height=21371952 \ + --debug \ + --da-compression $DA_COMPRESSION \ + --graphql-max-complexity $GRAPHQL_COMPLEXITY \ + --min-gas-price 0 \ + --snapshot ./ +else + echo "FORKING is disabled. Running without da deploy height" + exec /root/fuel-core run \ + --ip $FUEL_IP \ + --port $FUEL_PORT \ + --utxo-validation \ + --vm-backtrace \ + --enable-relayer \ + --relayer $L1_CHAIN_HTTP \ + --relayer-v2-listening-contracts $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS \ + --poa-interval-period 1sec \ + --debug \ + --da-compression $DA_COMPRESSION \ + --graphql-max-complexity $GRAPHQL_COMPLEXITY \ + --min-gas-price 0 \ + --snapshot ./ +fi \ No newline at end of file From ab878759ea34ac8466383e97a87541e0edc71b3b Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:14:50 +0530 Subject: [PATCH 56/81] chore: L1 container setup updates --- docker/l1-chain/Dockerfile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/docker/l1-chain/Dockerfile b/docker/l1-chain/Dockerfile index a4828892..361e11b3 100644 --- a/docker/l1-chain/Dockerfile +++ b/docker/l1-chain/Dockerfile @@ -11,6 +11,7 @@ RUN npm i -g pnpm # clone the contracts repo ADD docker/l1-chain/package.json /l1chain/fuel-v2-contracts/ + # copy over the fuel chain and replace consts values WORKDIR /l1chain/fuel-v2-contracts @@ -27,16 +28,11 @@ RUN pnpm compile ADD ./docker/l1-chain/.fuelChainConsts.env /l1chain/fuel-v2-contracts/.fuelChainConsts.env ADD ./packages/solidity-contracts/contracts /l1chain/fuel-v2-contracts/contracts ADD ./packages/solidity-contracts/deploy /l1chain/fuel-v2-contracts/deploy +ADD ./packages/solidity-contracts/deployments/ /l1chain/fuel-v2-contracts/deployments/ ADD ./packages/solidity-contracts/protocol /l1chain/fuel-v2-contracts/protocol - -# remove build dependencies -# RUN pnpm prune --prod RUN pnpm compile -# Create deployments dir -RUN mkdir deployments - # expose node and server port ENV L1_IP="${L1_IP}" ENV L1_PORT="${L1_PORT}" From 88a98b99855db50d321fe4857caf163c1b189e0c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:41:52 +0530 Subject: [PATCH 57/81] chore: remove docker startup script inside solidity-contracts --- packages/solidity-contracts/package.json | 1 - packages/solidity-contracts/scripts/node:up.sh | 1 - 2 files changed, 2 deletions(-) delete mode 100644 packages/solidity-contracts/scripts/node:up.sh diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index fe58d6cb..c5eb72f4 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -22,7 +22,6 @@ "check": "pnpm solhint \"contracts/**/*.sol\"", "node": "pnpm hardhat node --network hardhat", "test:integration": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", - "node:up": "sh ./scripts/node:up.sh", "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", "test": "pnpm hardhat test", diff --git a/packages/solidity-contracts/scripts/node:up.sh b/packages/solidity-contracts/scripts/node:up.sh deleted file mode 100644 index 71620aaf..00000000 --- a/packages/solidity-contracts/scripts/node:up.sh +++ /dev/null @@ -1 +0,0 @@ -docker compose -f ./docker/docker-compose.yml up -d --build \ No newline at end of file From 81b7cf0d10c4fd2132d30fd66aa2ed813415fcae Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:42:53 +0530 Subject: [PATCH 58/81] chore: set consensus secret in docker file --- docker/envs/fuel_core.env | 1 - docker/fuel-core/Dockerfile | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/envs/fuel_core.env b/docker/envs/fuel_core.env index 8e4eaf8a..1f6ad282 100644 --- a/docker/envs/fuel_core.env +++ b/docker/envs/fuel_core.env @@ -1,5 +1,4 @@ RUST_BACKTRACE=1 -CONSENSUS_KEY_SECRET="0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298" # Uncommend to use the variables by removing # # FUEL_IP= # FUEL_PORT= diff --git a/docker/fuel-core/Dockerfile b/docker/fuel-core/Dockerfile index 3d8b69db..012142f4 100644 --- a/docker/fuel-core/Dockerfile +++ b/docker/fuel-core/Dockerfile @@ -7,7 +7,8 @@ FROM ghcr.io/fuellabs/fuel-core:v0.40.0 ARG FUEL_IP=0.0.0.0 ARG FUEL_PORT=4001 -ARG CONSENSUS_KEY_SECRET="" +# since we need to set the FORKING var in the upgrade-test-suite ci so setting it here +ARG CONSENSUS_KEY_SECRET="0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298" # dependencies ENV DEBIAN_FRONTEND=noninteractive From f3c2438c6904b2b995f19aa4e0ea40a886406d1d Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:44:19 +0530 Subject: [PATCH 59/81] refactor: remove docker setup in solidity-contracts package --- .../docker/block-committer/Dockerfile | 21 -- .../docker/block-committer/block-committer.sh | 58 ---- .../docker/docker-compose.yml | 97 ------ .../docker/envs/block_committer.env | 9 - .../docker/envs/fuel_core.env | 1 - .../docker/envs/l1_chain.env | 1 - .../docker/fuel-core/Dockerfile | 55 ---- .../docker/fuel-core/fuel_core.sh | 71 ----- .../docker/fuel-core/genesis_coins.json | 299 ------------------ .../docker/l1-chain/Dockerfile | 43 --- .../docker/l1-chain/l1_chain.sh | 39 --- .../docker/l1-chain/package.json | 84 ----- packages/solidity-contracts/scripts/test.sh | 4 - 13 files changed, 782 deletions(-) delete mode 100644 packages/solidity-contracts/docker/block-committer/Dockerfile delete mode 100644 packages/solidity-contracts/docker/block-committer/block-committer.sh delete mode 100644 packages/solidity-contracts/docker/docker-compose.yml delete mode 100644 packages/solidity-contracts/docker/envs/block_committer.env delete mode 100644 packages/solidity-contracts/docker/envs/fuel_core.env delete mode 100644 packages/solidity-contracts/docker/envs/l1_chain.env delete mode 100644 packages/solidity-contracts/docker/fuel-core/Dockerfile delete mode 100644 packages/solidity-contracts/docker/fuel-core/fuel_core.sh delete mode 100644 packages/solidity-contracts/docker/fuel-core/genesis_coins.json delete mode 100644 packages/solidity-contracts/docker/l1-chain/Dockerfile delete mode 100644 packages/solidity-contracts/docker/l1-chain/l1_chain.sh delete mode 100644 packages/solidity-contracts/docker/l1-chain/package.json diff --git a/packages/solidity-contracts/docker/block-committer/Dockerfile b/packages/solidity-contracts/docker/block-committer/Dockerfile deleted file mode 100644 index b9399970..00000000 --- a/packages/solidity-contracts/docker/block-committer/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM ghcr.io/fuellabs/fuel-block-committer:v0.10.4 - -ARG COMMITER_IP=0.0.0.0 -ARG COMMITER_PORT=8888 - -# dependencies -ENV DEBIAN_FRONTEND=noninteractive -RUN apt update && apt install -y curl jq && rm -rf /var/lib/apt/lists/* - -# copy chain config -WORKDIR /block-committer - -# expose fuel node port -ENV HOST="${COMMITER_IP}" -ENV PORT="${COMMITER_PORT}" - -EXPOSE ${PORT} - -# copy over script and run -COPY ./block-committer.sh . -ENTRYPOINT ["sh", "./block-committer.sh"] diff --git a/packages/solidity-contracts/docker/block-committer/block-committer.sh b/packages/solidity-contracts/docker/block-committer/block-committer.sh deleted file mode 100644 index 61a5eadb..00000000 --- a/packages/solidity-contracts/docker/block-committer/block-committer.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/sh -set -euo - -RETRIES=${RETRIES:-60} -DELAY=${DELAY:-10} -JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}' -HEALTH_URL=${HEALTH_URL:-"http://fuel_core:4001/v1/health"} - -if [ -z "$COMMITTER__ETH__RPC" ]; then - echo "Must specify \$ETHEREUM_RPC." - exit 1 -fi -if [ -z "$COMMITTER__FUEL__GRAPHQL_ENDPOINT" ]; then - echo "Must specify \$FUEL_GRAPHQL_ENDPOINT." - exit 1 -fi - -echo $COMMITTER__FUEL__GRAPHQL_ENDPOINT/health - -# wait for the base layer to be up -echo "Waiting for Fuel Core chain." -curl \ - --fail \ - --show-error \ - --silent \ - --retry-connrefused \ - --retry $RETRIES \ - --retry-delay $DELAY \ - $HEALTH_URL > /dev/null -echo "Connected to Fuel Core chain." - -# get the deployments file from the deployer -echo "Waiting for l1 chain deployment data." -curl \ - --fail \ - --show-error \ - --silent \ - --retry-connrefused \ - --retry-all-errors \ - --retry $RETRIES \ - --retry-delay 5 \ - $DEPLOYMENTS_HTTP \ - -o addresses.json -echo "Got l1 chain deployment data." - -# pull data from deployer dump -export COMMITTER__ETH__STATE_CONTRACT_ADDRESS=$(cat "./addresses.json" | jq -r .FuelChainState) -export COMMITTER__APP__DB__USERNAME=${DB__USERNAME} -export COMMITTER__APP__DB__PASSWORD=${DB__PASSWORD} -export COMMITTER__ETH__L1_KEYS__MAIN=${COMMITTER__ETH__L1_KEYS__MAIN} -export COMMITTER__ETH__L1_KEYS__BLOB=${COMMITTER__ETH__L1_KEYS__BLOB} -echo "COMMITTER__ETH__STATE_CONTRACT_ADDRESS: $COMMITTER__ETH__STATE_CONTRACT_ADDRESS" -echo "ETHEREUM_RPC: $COMMITTER__ETH__RPC" -echo "FUEL_GRAPHQL_ENDPOINT: $COMMITTER__FUEL__GRAPHQL_ENDPOINT" - -# start the Block Commiter -echo "Starting block commiter" -exec /root/fuel-block-committer diff --git a/packages/solidity-contracts/docker/docker-compose.yml b/packages/solidity-contracts/docker/docker-compose.yml deleted file mode 100644 index 5e09668f..00000000 --- a/packages/solidity-contracts/docker/docker-compose.yml +++ /dev/null @@ -1,97 +0,0 @@ -version: '3.4' - -services: - db: - image: postgres:14 - environment: - POSTGRES_USER: username - POSTGRES_PASSWORD: password - POSTGRES_DB: committer_db - healthcheck: - test: ['CMD-SHELL', 'pg_isready -U username -d committer_db'] - interval: 5s - timeout: 5s - retries: 5 - - l1_chain: - image: fueldev/l1chain:${DOCKER_TAG_L1_CHAIN:-latest} - build: - dockerfile: ./docker/l1-chain/Dockerfile - # Use build context of the root directory - # to allow copying solidity-contracts on Dockerfile - context: ../ - env_file: - - ./envs/l1_chain.env - ports: - # expose the service to the host for integration testing - - ${L1_CHAIN_HTTP_PORT:-8545}:9545 - - ${DEPLOYMENTS_PORT:-8080}:8081 - stop_grace_period: 1s - - fuel_core: - image: fueldev/fuelcore:${DOCKER_TAG_FUEL_CORE:-latest} - depends_on: - l1_chain: - condition: service_started - platform: linux/amd64 - build: - context: ./fuel-core/ - env_file: - - ./envs/fuel_core.env - environment: - L1_CHAIN_HTTP: http://l1_chain:9545 - DEPLOYMENTS_HTTP: http://l1_chain:8081/deployments.local.json - RUST_LOG: debug - DEBUG: true - DB_PATH: /db - ports: - # expose the service to the host for integration testing - - ${FUEL_CORE_HTTP_PORT:-4000}:4001 - stop_grace_period: 1s - restart: always - - fuel_block_commiter: - image: ghcr.io/fuellabs/fuel-block-committer:v0.10.4 - platform: linux/amd64 - build: - context: ./block-committer/ - env_file: - - ./envs/block_committer.env - environment: - COMMITTER__ETH__RPC: 'ws://l1_chain:9545/' - COMMITTER__FUEL__GRAPHQL_ENDPOINT: 'http://fuel_core:4001/graphql' - COMMITTER__FUEL__NUM_BUFFERED_REQUESTS: '5' - COMMITTER__APP__DB__PORT: '5432' - COMMITTER__APP__DB__HOST: 'db' - COMMITTER__APP__DB__MAX_CONNECTIONS: '10' - COMMITTER__APP__DB__USE_SSL: 'false' - COMMITTER__APP__DB__DATABASE: 'committer_db' - COMMITTER__APP__PORT: '8080' - COMMITTER__APP__HOST: '0.0.0.0' - COMMITTER__APP__BLOCK_CHECK_INTERVAL: '5s' - COMMITTER__APP__TX_FINALIZATION_CHECK_INTERVAL: '5s' - COMMITTER__APP__NUM_BLOCKS_TO_FINALIZE_TX: '3' - COMMITTER__APP__GAS_BUMP_TIMEOUT: '300s' - COMMITTER__APP__TX_MAX_FEE: '4000000000000000' - COMMITTER__APP__SEND_TX_REQUEST_TIMEOUT: '10s' - COMMITTER__APP__BUNDLE__ACCUMULATION_TIMEOUT: '3600s' - COMMITTER__APP__BUNDLE__BLOCKS_TO_ACCUMULATE: '400' - COMMITTER__APP__BUNDLE__OPTIMIZATION_TIMEOUT: '60s' - COMMITTER__APP__BUNDLE__BLOCK_HEIGHT_LOOKBACK: '8500' - COMMITTER__APP__BUNDLE__COMPRESSION_LEVEL: 'level6' - COMMITTER__APP__BUNDLE__OPTIMIZATION_STEP: '100' - COMMITTER__APP__BUNDLE__FRAGMENTS_TO_ACCUMULATE: '3' - COMMITTER__APP__BUNDLE__FRAGMENT_ACCUMULATION_TIMEOUT: '10m' - COMMITTER__APP__BUNDLE__NEW_BUNDLE_CHECK_INTERVAL: '3s' - DEPLOYMENTS_HTTP: http://l1_chain:8081/deployments.local.json - ports: - # expose the service to the host for integration testing - - ${COMMITTER_HTTP_PORT:-8888}:8888 - depends_on: - db: - condition: service_healthy - l1_chain: - condition: service_started - fuel_core: - condition: service_started - restart: always diff --git a/packages/solidity-contracts/docker/envs/block_committer.env b/packages/solidity-contracts/docker/envs/block_committer.env deleted file mode 100644 index 7cabadc5..00000000 --- a/packages/solidity-contracts/docker/envs/block_committer.env +++ /dev/null @@ -1,9 +0,0 @@ -ETHEREUM_WALLET_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d -COMMIT_INTERVAL=30 -DB__USERNAME=username -DB__PASSWORD=password -COMMITTER__ETH__L1_KEYS__MAIN=Private(8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba) -COMMITTER__ETH__L1_KEYS__BLOB=Private(59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d) -# HOST= -# PORT= -# ETHEREUM_CHAIN= \ No newline at end of file diff --git a/packages/solidity-contracts/docker/envs/fuel_core.env b/packages/solidity-contracts/docker/envs/fuel_core.env deleted file mode 100644 index 7b0bf02d..00000000 --- a/packages/solidity-contracts/docker/envs/fuel_core.env +++ /dev/null @@ -1 +0,0 @@ -CONSENSUS_KEY_SECRET="0xa449b1ffee0e2205fa924c6740cc48b3b473aa28587df6dab12abc245d1f5298" \ No newline at end of file diff --git a/packages/solidity-contracts/docker/envs/l1_chain.env b/packages/solidity-contracts/docker/envs/l1_chain.env deleted file mode 100644 index efddf6ab..00000000 --- a/packages/solidity-contracts/docker/envs/l1_chain.env +++ /dev/null @@ -1 +0,0 @@ -TENDERLY_RPC_URL= \ No newline at end of file diff --git a/packages/solidity-contracts/docker/fuel-core/Dockerfile b/packages/solidity-contracts/docker/fuel-core/Dockerfile deleted file mode 100644 index 3d8b69db..00000000 --- a/packages/solidity-contracts/docker/fuel-core/Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -# IMPORTANT! -# Make sure to check: -# https://github.com/FuelLabs/chain-configuration/tree/master/upgradelog/ignition-testnet -# and apply the latest state_transition_function and consensus_parameter -# when upgrading fuel-core -FROM ghcr.io/fuellabs/fuel-core:v0.40.0 - -ARG FUEL_IP=0.0.0.0 -ARG FUEL_PORT=4001 -ARG CONSENSUS_KEY_SECRET="" - -# dependencies -ENV DEBIAN_FRONTEND=noninteractive -RUN apt update && apt install -y git curl jq && rm -rf /var/lib/apt/lists/* - -# copy chain config -WORKDIR /fuel - -COPY ./genesis_coins.json . - -RUN git clone \ - https://github.com/FuelLabs/chain-configuration.git \ - /chain-configuration - -# Anchor the chain configuration to a specific commit to avoid CI breaking -RUN cd /chain-configuration && git checkout 8e4f7b52d7112f929a7cd95b988dfebfd10e87ec - -# Copy the base local configuration -RUN cp -R /chain-configuration/local/* ./ - -# Copy the devnet consensus parameters and state transition bytecode -RUN cp /chain-configuration/upgradelog/ignition-devnet/consensus_parameters/14.json \ - ./latest_consensus_parameters.json -RUN cp /chain-configuration/upgradelog/ignition-devnet/state_transition_function/16.wasm \ - ./state_transition_bytecode.wasm - -# update local state_config with custom genesis coins config -RUN jq '.coins = input' \ - state_config.json genesis_coins.json > tmp.json \ - && mv tmp.json state_config.json - -# update local state_config with testnet consensus parameters -RUN jq '.consensus_parameters = input' \ - state_config.json latest_consensus_parameters.json > tmp.json \ - && mv tmp.json state_config.json - -# expose fuel node port -ENV FUEL_IP="${FUEL_IP}" -ENV FUEL_PORT="${FUEL_PORT}" -ENV CONSENSUS_KEY_SECRET="${CONSENSUS_KEY_SECRET}" -EXPOSE ${FUEL_PORT} - -# copy over script and run -COPY ./fuel_core.sh . -CMD ["sh", "./fuel_core.sh"] \ No newline at end of file diff --git a/packages/solidity-contracts/docker/fuel-core/fuel_core.sh b/packages/solidity-contracts/docker/fuel-core/fuel_core.sh deleted file mode 100644 index a1c2c3a3..00000000 --- a/packages/solidity-contracts/docker/fuel-core/fuel_core.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/sh -set -euo - -#!/bin/sh -set -euo - -RETRIES=${RETRIES:-90} -DA_COMPRESSION=${DA_COMPRESSION:-"3600sec"} -GRAPHQL_COMPLEXITY=${GRAPHQL_COMPLEXITY:-500000} -JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}' -# FUEL_DB_PATH=./mnt/db/ - -if [ -z "$L1_CHAIN_HTTP" ]; then - echo "Must specify \$L1_CHAIN_HTTP." - exit 1 -fi - -# wait for the base layer to be up -echo "Waiting for l1 chain." -curl \ - --fail \ - --show-error \ - --silent \ - -H "Content-Type: application/json" \ - --retry-connrefused \ - --retry $RETRIES \ - --retry-delay 1 \ - -d $JSON \ - $L1_CHAIN_HTTP > /dev/null -echo "Connected to l1 chain." - -# get the deployments file from the deployer -echo "Waiting for l1 chain deployment data." -curl \ - --fail \ - --show-error \ - --silent \ - --retry-connrefused \ - --retry-all-errors \ - --retry $RETRIES \ - --retry-delay 5 \ - $DEPLOYMENTS_HTTP \ - -o addresses.json -echo "Got l1 chain deployment data." - -# pull data from deployer dump -export FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS=$(cat "./addresses.json" | jq -r .FuelMessagePortal) -# export FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS=$(jq -r '.address' /l1chain/fuel-v2-contracts/deployments/localhost/FuelMessagePortal.json) -echo "FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS: $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS" -echo "L1_CHAIN_HTTP: $L1_CHAIN_HTTP" - -# start the Fuel client -#--db-path ${FUEL_DB_PATH} -echo "Starting fuel node." -exec /root/fuel-core run \ - --ip $FUEL_IP \ - --port $FUEL_PORT \ - --utxo-validation \ - --vm-backtrace \ - --enable-relayer \ - --relayer $L1_CHAIN_HTTP \ - --relayer-v2-listening-contracts $FUEL_MESSAGE_PORTAL_CONTRACT_ADDRESS \ - --poa-interval-period=1s \ - --relayer-da-deploy-height=21371952 \ - --debug \ - --da-compression $DA_COMPRESSION \ - --graphql-max-complexity $GRAPHQL_COMPLEXITY \ - --min-gas-price 0 \ - --tx-ttl-check-interval 10sec \ - --tx-pool-ttl 30sec \ - --snapshot ./ diff --git a/packages/solidity-contracts/docker/fuel-core/genesis_coins.json b/packages/solidity-contracts/docker/fuel-core/genesis_coins.json deleted file mode 100644 index fd78763e..00000000 --- a/packages/solidity-contracts/docker/fuel-core/genesis_coins.json +++ /dev/null @@ -1,299 +0,0 @@ -[ - { - "owner": "0xc8e615a4089466174459ef19cfd257d2e17adfabff3b8f219dbb5fb4eca87c50", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000001", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x92dffc873b56f219329ed03bb69bebe8c3d8b041088574882f7a6404f02e2f28", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000002", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x456bdaffaf74fe03521754ac445d148033c0c6acf7d593132c43f92fdbc7324c", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000003", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x639880ece7753a32e09164d14dad7436c57737e567f18b98f6ee30fec6b247ec", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000004", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0xfd8c520ef8caff0ad3289aa64acecd4ef86ac8f643fd9b76bf2d163a86a66716", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000005", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x8247104854dd733cb475901d55047f57cb3c8cafe3a9f7233de3325b8bf56a5c", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000006", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x53de37ae51fcfecb17ee3589f68904ac75bf5ec109edeb1065ccb63145287da6", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000007", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x17f4bef51f63f0c28af20d4223a3bf5cf1735a3b7ec52b4fcfbdbb5f30582a6b", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000008", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x95725e9083d8ed1cb52dcf6429d0cfce00cc375eeac5b620b5c36f5b1e734b31", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000009", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x08792a75d5714165aa117fd75450f9efcfb7124d034ef271f2919e4cc287046c", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000a", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x6a16fba49dbdf7689c52b7a22951a54dc164076d27bdc6042b5d8377d68ca10b", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000b", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x6494a55c0e3da212fdd0515507d00ae99151c7966e1448079c76bc447b577254", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000c", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0xe0e1c94a9f9e02454772813ba6a6261b5228db1fabde3a68b23c0e9744ce22fc", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000d", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x425f3e91aedff36e72ae60a8a1d328e625d66d39fcc98d5fcd1ba7df65a9f878", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000e", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0xe2242f2e4971c34bc6fe5e1c0043b1aba717cb6a51f31f0dc0708cca73df905a", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x000000000000000000000000000000000000000000000000000000000000000f", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x6aa67cb316f329111dc708bb766360f5026a614edb11882e14d4cc04f26e0a08", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000010", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0xef5c2b712c4f3a10a37d6371cab2b03a6afd12e4ffcc9567d45d8c4b6e217e5c", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000011", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0xa82f66642de54993b32036eef7914f2dbaa217aa3209707b64d0b90187456a1f", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000012", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x9c9c1f3346b54fe6cb379fa27f338464592515fd865656089c4a23ac34390e6f", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000013", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "owner": "0x8a332bc33f4c10ad36392a9ca958a5ddf56081cc764f61613ea119e42ced6ac5", - "amount": 1000000000000, - "asset_id": "0xf8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07", - "tx_id": "0x0000000000000000000000000000000000000000000000000000000000000014", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0 - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000015", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "6b63804cfbf9856e68e5b6e7aef238dc8311ec55bec04df774003a2c96e0418e", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000016", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "54944e5b8189827e470e5a8bacfc6c3667397dc4e1eef7ef3519d16d6d6c6610", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000017", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "e10f526b192593793b7a1559a391445faba82a1d669e3eb2dcd17f9c121b24b1", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000018", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "577e424ee53a16e6a85291feabc8443862495f74ac39a706d2dd0b9fc16955eb", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000019", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "c36be0e14d3eaf5d8d233e0f4a40b3b4e48427d25f84c460d2b03b242a38479e", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "000000000000000000000000000000000000000000000000000000000000002a", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "a1184d77d0d08a064e03b2bd9f50863e88faddea4693a05ca1ee9b1732ea99b7", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "000000000000000000000000000000000000000000000000000000000000002b", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "b5566df884bee4e458151c2fe4082c8af38095cc442c61e0dc83a371d70d88fd", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "000000000000000000000000000000000000000000000000000000000000002c", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "9da7247e1d63d30d69f136f0f8654ee8340362c785b50f0a60513c7edbf5bb7c", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "000000000000000000000000000000000000000000000000000000000000002d", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "4b2ca966aad1a9d66994731db5138933cf61679107c3cde2a10d9594e47c084e", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "000000000000000000000000000000000000000000000000000000000000002e", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "26183fbe7375045250865947695dfc12500dcc43efb9102b4e8c4d3c20009dcb", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "000000000000000000000000000000000000000000000000000000000000002f", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "81f3a10b61828580d06cc4c7b0ed8f59b9fb618be856c55d33decd95489a1e23", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000030", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "587aa0482482efea0234752d1ad9a9c438d1f34d2859b8bef2d56a432cb68e33", - "amount": 1152921504606846976, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - }, - { - "tx_id": "0000000000000000000000000000000000000000000000000000000000000031", - "output_index": 0, - "tx_pointer_block_height": 0, - "tx_pointer_tx_idx": 0, - "owner": "53a9c6a74bee79c5e04115a007984f4bddaafed75f512f68766c6ed59d0aedec", - "amount": 1125899906842624, - "asset_id": "f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07" - } -] diff --git a/packages/solidity-contracts/docker/l1-chain/Dockerfile b/packages/solidity-contracts/docker/l1-chain/Dockerfile deleted file mode 100644 index 5e2f20f5..00000000 --- a/packages/solidity-contracts/docker/l1-chain/Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM node:20-alpine AS BUILD_IMAGE - -# dependencies -RUN apk --no-cache add git curl -RUN npm i -g pnpm - -ARG L1_IP=0.0.0.0 -ARG L1_PORT=9545 -ARG SERVE_PORT=8081 - -WORKDIR /l1chain/fuel-v2-contracts - -# clone the contracts repo -COPY docker/l1-chain/package.json /l1chain/fuel-v2-contracts/ - -# build the ethereum contracts and environment -RUN pnpm install -COPY contracts/test/PlaceHolder.sol /l1chain/fuel-v2-contracts/contracts/test/PlaceHolder.sol -COPY hardhat.config.ts /l1chain/fuel-v2-contracts/ -COPY scripts/ /l1chain/fuel-v2-contracts/scripts/ -RUN pnpm compile - -# replace the fuel chain consts values and change contract code -COPY contracts/ /l1chain/fuel-v2-contracts/contracts/ -COPY deploy/ /l1chain/fuel-v2-contracts/deploy/ -COPY deployments/ /l1chain/fuel-v2-contracts/deployments/ -COPY protocol/ /l1chain/fuel-v2-contracts/protocol/ - - -# remove build dependencies -# RUN pnpm prune --prod -RUN pnpm compile - - -ENV L1_IP="${L1_IP}" -ENV L1_PORT="${L1_PORT}" -ENV SERVE_PORT="${SERVE_PORT}" -EXPOSE ${L1_PORT} -EXPOSE ${SERVE_PORT} - -# copy over script and run -COPY ./docker/l1-chain/l1_chain.sh /l1chain/l1_chain.sh -CMD ["sh", "/l1chain/l1_chain.sh"] diff --git a/packages/solidity-contracts/docker/l1-chain/l1_chain.sh b/packages/solidity-contracts/docker/l1-chain/l1_chain.sh deleted file mode 100644 index e0df10bc..00000000 --- a/packages/solidity-contracts/docker/l1-chain/l1_chain.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -set -euo - -RETRIES=${RETRIES:-120} -JSON='{"jsonrpc":"2.0","id":0,"method":"net_version","params":[]}' - -L1_CHAIN_HTTP="http://127.0.0.1:$L1_PORT" - -echo "Starting l1 chain." -pnpm hardhat node --network hardhat --port $L1_PORT --hostname $L1_IP & - -# wait for the base layer to be up -echo "Waiting for l1 chain." -curl \ - --fail \ - --show-error \ - --silent \ - -H "Content-Type: application/json" \ - --retry-connrefused \ - --retry $RETRIES \ - --retry-delay 1 \ - -d $JSON \ - $L1_CHAIN_HTTP > /dev/null - -echo "Connected to l1 chain." - -export LOCALHOST_HTTP=$L1_CHAIN_HTTP - -# Start auto mining -# We use a separate process to start auto mining because -# the deployment of contracts can fail if the chain is -# mining at the same time. -RPC_URL=$L1_CHAIN_HTTP pnpm run start-mining - -# serve contract deployment data -echo "Starting deployment data server." -pnpm run serve-deployments - - diff --git a/packages/solidity-contracts/docker/l1-chain/package.json b/packages/solidity-contracts/docker/l1-chain/package.json deleted file mode 100644 index 8d7842f8..00000000 --- a/packages/solidity-contracts/docker/l1-chain/package.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "name": "@fuel-bridge/solidity-contracts", - "version": "1.0.0", - "description": "The Fuel v2 Solidity smart contracts.", - "license": "APACHE-2.0", - "files": [ - "typechain", - "contracts", - "artifacts", - "dist" - ], - "module": "dist/index.mjs", - "types": "dist/index.d.ts", - "typings": "dist/index.d.ts", - "main": "dist/index.js", - "scripts": { - "build": "run-s clean compile build:exports", - "build:exports": "tsup", - "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": "pnpm hardhat node --network hardhat --hostname 0.0.0.0", - "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", - "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", - "test": "pnpm hardhat test", - "integration-test": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", - "node:up": "sh ./scripts/node:up.sh", - "hardhat:test:integration": "DISABLE_GAS_REPORTER=true pnpm hardhat --network mainnetFork test fork-integration-tests/**/*.ts --bail", - "test-no-compile": "pnpm hardhat test --no-compile", - "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register", - "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", - "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" - }, - "devDependencies": { - "@fuel-ts/merkle": "^0.21.2", - "@inquirer/prompts": "^5.3.8", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.4", - "@nomicfoundation/hardhat-ethers": "^3.0.5", - "@nomicfoundation/hardhat-network-helpers": "^1.0.10", - "@nomicfoundation/hardhat-verify": "1.1.1", - "@openzeppelin/contracts": "^4.8.3", - "@openzeppelin/contracts-upgradeable": "^4.8.3", - "@openzeppelin/hardhat-upgrades": "^3.0.4", - "@safe-global/api-kit": "^2.4.6", - "@safe-global/protocol-kit": "^4.1.1", - "@safe-global/safe-core-sdk-types": "^5.1.0", - "@typechain/ethers-v6": "^0.5.1", - "@typechain/hardhat": "^9.1.0", - "@types/chai": "^4.3.4", - "@types/cors": "2.8.17", - "mocha": "^10.0.0", - "@types/express": "^4.17.14", - "@types/lodash": "^4.14.202", - "@types/mocha": "^10.0.0", - "@types/node": "^18.11.9", - "@typescript-eslint/eslint-plugin": "^5.43.0", - "@typescript-eslint/parser": "^5.43.0", - "axios": "^1.7.7", - "chai": "^4.3.7", - "cors": "2.8.5", - "dotenv": "^16.0.3", - "ethers": "6.13.1", - "express": "^4.18.2", - "fuels": "0.96.1", - "hardhat": "^2.20.1", - "hardhat-deploy": "^0.12.4", - "inquirer": "^10.1.8", - "lodash": "^4.17.21", - "markdownlint": "^0.26.2", - "markdownlint-cli": "^0.32.2", - "node-fetch": "^2.6.6", - "npm-run-all": "^4.1.5", - "prettier-plugin-solidity": "^1.1.3", - "solc": "^0.8.17", - "solhint": "3.3.7", - "solidity-coverage": "^0.8.5", - "ts-generator": "^0.1.1", - "ts-node": "^10.9.1", - "tsup": "^7.2.0", - "typechain": "^8.3.2", - "typescript": "^4.9.3" - } -} diff --git a/packages/solidity-contracts/scripts/test.sh b/packages/solidity-contracts/scripts/test.sh index b57d53fd..b8dbcdad 100644 --- a/packages/solidity-contracts/scripts/test.sh +++ b/packages/solidity-contracts/scripts/test.sh @@ -1,7 +1,3 @@ -# Start the docker compose file with L1 and Fuel Node -echo "\n\nStarting docker..." -pnpm run node:up - # Wait for the nodes to be ready and run the tests HEALTH_CHECK_COUNTER=0 HELTH_CHECK_OUTPUT="" From 9075b5979db4698640121d351ec4ec2a35abf4c9 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:45:56 +0530 Subject: [PATCH 60/81] chore: add comment --- .../deploy/hardhat/009.chain_state_upgrade.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts b/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts index 48fdf219..eb413e19 100644 --- a/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts +++ b/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts @@ -75,7 +75,8 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { to: address, data: txData, }); - + + // hardhat with forking sometimes throws a `nonce too low error` using only one committer, so added another to be used in tests COMMITTER_ADDRESS = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65'; txData = await chainState.interface.encodeFunctionData('grantRole', [ From f030adfd37c54cd10d07732d4e0653632ab129a8 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Tue, 7 Jan 2025 21:46:43 +0530 Subject: [PATCH 61/81] chore: add docker setup initiation in upgrade test suite workflow --- .github/workflows/upgrade-test-suite.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index 82ec5a43..9771b8ea 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -31,12 +31,18 @@ jobs: - uses: ./.github/actions/setup-rust - name: Build project run: pnpm build - - name: Sets the tenderly rpc endpoint in the L1 docker container env + - name: Sets the tenderly rpc endpoint in the L1 docker container env and sets forking variable for fuel core setup run: | cat << EOF > l1_chain.env TENDERLY_RPC_URL=${{ secrets.TENDERLY_RPC_URL }} EOF - working-directory: ./packages/solidity-contracts/docker/envs + + cat << EOF > fuel_core.env + FORKING=true + EOF + working-directory: docker/envs + - name: Starts docker containers + run: pnpm run node:up - name: Run integration tests on a L1 fork after upgrading contracts run: | pnpm upgrade:test:integration From 9296322dab9761a2674534031c2945d4c2f4e80c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:30:19 +0530 Subject: [PATCH 62/81] chore: remove workspace dependencies in solidity-contracts --- packages/solidity-contracts/package.json | 4 ---- pnpm-lock.yaml | 6 ------ 2 files changed, 10 deletions(-) diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index c5eb72f4..ad754d80 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -21,19 +21,15 @@ "coverage": "pnpm run build && pnpm hardhat coverage --temp artifacts --network hardhat", "check": "pnpm solhint \"contracts/**/*.sol\"", "node": "pnpm hardhat node --network hardhat", - "test:integration": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", "test": "pnpm hardhat test", - "upgrade:test:integration": "sh ./scripts/test.sh", "test-no-compile": "pnpm hardhat test --no-compile", "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register", "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" }, "devDependencies": { - "@fuel-bridge/fungible-token": "workspace:*", - "@fuel-bridge/message-predicates": "workspace:*", "@fuel-ts/merkle": "^0.21.2", "@inquirer/prompts": "^5.3.8", "@nomicfoundation/hardhat-chai-matchers": "^2.0.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef434741..11c155b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,12 +122,6 @@ importers: packages/solidity-contracts: devDependencies: - '@fuel-bridge/fungible-token': - specifier: workspace:* - version: link:../fungible-token - '@fuel-bridge/message-predicates': - specifier: workspace:* - version: link:../message-predicates '@fuel-ts/merkle': specifier: ^0.21.2 version: 0.21.2 From 52bdd4c5c57305c8b5ab635bddb886570fad0c82 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:31:17 +0530 Subject: [PATCH 63/81] chore: remove package.json in the L1 docker setup --- docker/l1-chain/Dockerfile | 2 +- docker/l1-chain/package.json | 85 ------------------------------------ 2 files changed, 1 insertion(+), 86 deletions(-) delete mode 100644 docker/l1-chain/package.json diff --git a/docker/l1-chain/Dockerfile b/docker/l1-chain/Dockerfile index 361e11b3..1f6bbe87 100644 --- a/docker/l1-chain/Dockerfile +++ b/docker/l1-chain/Dockerfile @@ -10,7 +10,7 @@ RUN apk --no-cache add git curl RUN npm i -g pnpm # clone the contracts repo -ADD docker/l1-chain/package.json /l1chain/fuel-v2-contracts/ +ADD ./packages/solidity-contracts/package.json /l1chain/fuel-v2-contracts/ # copy over the fuel chain and replace consts values WORKDIR /l1chain/fuel-v2-contracts diff --git a/docker/l1-chain/package.json b/docker/l1-chain/package.json deleted file mode 100644 index 24b1ad6f..00000000 --- a/docker/l1-chain/package.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "name": "@fuel-bridge/solidity-contracts", - "version": "1.0.0", - "description": "The Fuel v2 Solidity smart contracts.", - "license": "APACHE-2.0", - "files": [ - "typechain", - "contracts", - "artifacts", - "dist" - ], - "module": "dist/index.mjs", - "types": "dist/index.d.ts", - "typings": "dist/index.d.ts", - "main": "dist/index.js", - "scripts": { - "build": "run-s clean compile build:exports", - "build:exports": "tsup", - "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": "pnpm hardhat node --network hardhat --hostname 0.0.0.0", - "start-mining": "pnpm hardhat run --no-compile --network localhost scripts/startAutoMining.ts", - "serve-deployments": "pnpm ts-node scripts/serveDeployments.ts", - "test": "pnpm hardhat test", - "integration-test": "pnpm mocha -b -r ts-node/register 'integration-tests/**/*.ts'", - "node:up": "sh ./scripts/node:up.sh", - "hardhat:test:integration": "DISABLE_GAS_REPORTER=true pnpm hardhat --network mainnetFork test fork-integration-tests/**/*.ts --bail", - "test-no-compile": "pnpm hardhat test --no-compile", - "test-parallel": "pnpx mocha 'test/**/*.ts' --recursive --parallel --require hardhat/register", - "prettier": "prettier --write --plugin=prettier-plugin-solidity 'contracts/**/*.sol'", - "lint": "prettier --list-different --plugin=prettier-plugin-solidity 'contracts/**/*.sol'" - }, - "devDependencies": { - "@fuel-ts/merkle": "^0.21.2", - "@inquirer/prompts": "^5.3.8", - "@nomicfoundation/hardhat-chai-matchers": "^2.0.4", - "@nomicfoundation/hardhat-ethers": "^3.0.5", - "@nomicfoundation/hardhat-network-helpers": "^1.0.10", - "@nomicfoundation/hardhat-verify": "1.1.1", - "@openzeppelin/contracts": "^4.8.3", - "@openzeppelin/contracts-upgradeable": "^4.8.3", - "@openzeppelin/hardhat-upgrades": "^3.0.4", - "@safe-global/api-kit": "^2.4.6", - "@safe-global/protocol-kit": "^4.1.1", - "@safe-global/safe-core-sdk-types": "^5.1.0", - "@typechain/ethers-v6": "^0.5.1", - "@typechain/hardhat": "^9.1.0", - "@types/chai": "^4.3.4", - "@types/cors": "2.8.17", - "mocha": "^10.0.0", - "@types/express": "^4.17.14", - "@types/lodash": "^4.14.202", - "@types/mocha": "^10.0.0", - "@types/node": "^18.11.9", - "@typescript-eslint/eslint-plugin": "^5.43.0", - "@typescript-eslint/parser": "^5.43.0", - "axios": "^1.7.7", - "chai": "^4.3.7", - "cors": "2.8.5", - "dotenv": "^16.0.3", - "ethers": "6.13.1", - "express": "^4.18.2", - "fuels": "0.96.1", - "hardhat": "^2.20.1", - "hardhat-deploy": "^0.12.4", - "inquirer": "^10.1.8", - "lodash": "^4.17.21", - "markdownlint": "^0.26.2", - "markdownlint-cli": "^0.32.2", - "node-fetch": "^2.6.6", - "npm-run-all": "^4.1.5", - "prettier-plugin-solidity": "^1.1.3", - "solc": "^0.8.17", - "solhint": "3.3.7", - "solidity-coverage": "^0.8.5", - "ts-generator": "^0.1.1", - "ts-node": "^10.9.1", - "tsup": "^7.2.0", - "typechain": "^8.3.2", - "typescript": "^4.9.3" - } - } - \ No newline at end of file From 01f21a8219bfb799672659a4110ed63d2ad9ef22 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:32:37 +0530 Subject: [PATCH 64/81] refactor: reloacte fork tests under integration-tests package --- .../fork-tests/bridge_erc20.ts | 739 ++++++++++++++++++ .../fork-tests/transfer_eth.ts | 518 ++++++++++++ packages/integration-tests/package.json | 1 + packages/integration-tests/tsconfig.json | 2 +- 4 files changed, 1259 insertions(+), 1 deletion(-) create mode 100644 packages/integration-tests/fork-tests/bridge_erc20.ts create mode 100644 packages/integration-tests/fork-tests/transfer_eth.ts diff --git a/packages/integration-tests/fork-tests/bridge_erc20.ts b/packages/integration-tests/fork-tests/bridge_erc20.ts new file mode 100644 index 00000000..33c9ebea --- /dev/null +++ b/packages/integration-tests/fork-tests/bridge_erc20.ts @@ -0,0 +1,739 @@ +import type { BridgeFungibleToken } from '@fuel-bridge/fungible-token'; +import { + RATE_LIMIT_AMOUNT, + RATE_LIMIT_DURATION, +} from '@fuel-bridge/solidity-contracts/protocol/constants'; +import type { + Token, +} from '@fuel-bridge/solidity-contracts/typechain'; +import type { TestEnvironment } from '@fuel-bridge/test-utils'; +import { + setupEnvironment, + relayCommonMessage, + waitForMessage, + createRelayMessageParams, + getOrDeployECR20Contract, + getOrDeployL2Bridge, + FUEL_TX_PARAMS, + getMessageOutReceipt, + fuel_to_eth_address, + waitForBlockFinalization, + getTokenId, + getBlock, + FUEL_CALL_TX_PARAMS, + hardhatSkipTime, +} from '@fuel-bridge/test-utils'; +import chai from 'chai'; +import { toBeHex, parseEther, ZeroHash } from 'ethers'; +import type { JsonRpcProvider, Signer } from 'ethers'; +import { Address, BN } from 'fuels'; +import type { + AbstractAddress, + WalletUnlocked as FuelWallet, + MessageProof, + Provider, +} from 'fuels'; + + +const { expect } = chai; + +describe('Bridging ERC20 tokens', async function () { + // Timeout 6 minutes + const DEFAULT_TIMEOUT_MS: number = 400_000; + const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000; + const DECIMAL_DIFF = 1_000_000_000n; + + let env: TestEnvironment; + let eth_testToken: Token; + let eth_testTokenAddress: string; + let eth_erc20GatewayAddress: string; + let fuel_bridge: BridgeFungibleToken; + let fuel_bridgeImpl: BridgeFungibleToken; + let fuel_bridgeContractId: string; + let fuel_testAssetId: string; + + // override the default test timeout from 2000ms + this.timeout(DEFAULT_TIMEOUT_MS); + + async function forwardFuelChain(provider: Provider, blocksToForward: string) { + await provider.produceBlocks(Number(blocksToForward)).catch(console.error); + } + + async function getBlockWithHeight(env: any, height: string): Promise { + const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { + block(height: $height) { + id + } + }`; + const BLOCK_BY_HEIGHT_ARGS = { + height: height, + }; + + return fetch(env.fuel.provider.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: BLOCK_BY_HEIGHT_QUERY, + variables: BLOCK_BY_HEIGHT_ARGS, + }), + }) + .then((res: any) => res.json()) + .then(async (res) => { + if (!res.data.block) { + throw new Error(`Could not fetch block with height ${height}`); + } + + return res.data.block; + }); + } + + async function generateWithdrawalMessageProof( + fuel_bridge: BridgeFungibleToken, + fuelTokenSender: FuelWallet, + ethereumTokenReceiverAddress: string, + NUM_TOKENS: bigint, + DECIMAL_DIFF: bigint + ): Promise { + // withdraw tokens back to the base chain + fuel_bridge.account = fuelTokenSender; + const paddedAddress = + '0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0'); + const fuelTokenSenderBalance = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + const transactionRequest = await fuel_bridge.functions + .withdraw(paddedAddress) + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .txParams({ + tip: 0, + maxFee: 1, + }) + .callParams({ + forward: { + amount: new BN(NUM_TOKENS.toString()).div( + new BN(DECIMAL_DIFF.toString()) + ), + assetId: fuel_testAssetId, + }, + }) + .fundWithRequiredCoins(); + + const tx = await fuelTokenSender.sendTransaction(transactionRequest); + const fWithdrawTxResult = await tx.waitForResult(); + expect(fWithdrawTxResult.status).to.equal('success'); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await fuelTokenSender.getBalance(fuel_testAssetId); + + expect( + newSenderBalance.eq( + fuelTokenSenderBalance.sub(toBeHex(NUM_TOKENS / DECIMAL_DIFF)) + ) + ).to.be.true; + + // Wait for the commited block + const withdrawBlock = await getBlock( + env.fuel.provider.url, + fWithdrawTxResult.blockId! + ); + + const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); + + const blocksPerCommitInterval = ( + await env.eth.fuelChainState.BLOCKS_PER_COMMIT_INTERVAL() + ).toString(); + + // Add + 1 to the block height to wait the next block + // that enable to proof the message + const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); + const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); + + let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + // fast forward post the commit cooldown period + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // override the commit hash in a existing block + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit( + ZeroHash, + commitHeight.toString() + ); + + // fast forward to the block finalization time + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + // fast forward post the commit cooldown period + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // produce more blocks to fetch the block height + await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); + + const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + + // reset the commit hash in the local L2 network + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit(block.id, commitHeight.toString()); + + // fast forward to the block finalization time + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); + return await fuelTokenSender.provider.getMessageProof( + tx.id, + messageOutReceipt.nonce, + block.id + ); + } + + async function relayMessageFromFuel( + env: TestEnvironment, + withdrawMessageProof: MessageProof + ) { + // wait for block finalization + await waitForBlockFinalization(env, withdrawMessageProof); + + // construct relay message proof data + const relayMessageParams = createRelayMessageParams(withdrawMessageProof); + + // relay message + await env.eth.fuelMessagePortal.relayMessage( + relayMessageParams.message, + relayMessageParams.rootBlockHeader, + relayMessageParams.blockHeader, + relayMessageParams.blockInHistoryProof, + relayMessageParams.messageInBlockProof + ); + } + + async function relayMessageFromEthereum( + env: TestEnvironment, + fuelTokenMessageReceiver: AbstractAddress, + fuelTokenMessageNonce: BN, + fuel_AssetId: string, + amount: bigint + ) { + // relay the message ourselves + const message = await waitForMessage( + env.fuel.provider, + fuelTokenMessageReceiver, + fuelTokenMessageNonce, + FUEL_MESSAGE_TIMEOUT_MS + ); + expect(message).to.not.be.null; + + const tx = await relayCommonMessage(env.fuel.deployer, message, { + maturity: undefined, + contractIds: [fuel_bridgeImpl.id.toHexString()], + }); + + const txResult = await tx.waitForResult(); + + expect(txResult.status).to.equal('success'); + expect(txResult.mintedAssets.length).to.equal(1); + + const [mintedAsset] = txResult.mintedAssets; + + expect(mintedAsset.assetId).to.equal(fuel_AssetId); + expect(mintedAsset.amount.toString()).to.equal( + (amount / DECIMAL_DIFF).toString() + ); + } + + before(async () => { + env = await setupEnvironment({}); + eth_erc20GatewayAddress = ( + await env.eth.fuelERC20Gateway.getAddress() + ).toLowerCase(); + + eth_testToken = await getOrDeployECR20Contract(env); + eth_testTokenAddress = (await eth_testToken.getAddress()).toLowerCase(); + + const { contract, implementation } = await getOrDeployL2Bridge( + env, + env.eth.fuelERC20Gateway + ); + + fuel_bridge = contract; + fuel_bridgeImpl = implementation; + + fuel_bridgeContractId = fuel_bridge.id.toHexString(); + + await env.eth.fuelERC20Gateway + .connect(env.eth.deployer) + .setAssetIssuerId(fuel_bridgeContractId); + fuel_testAssetId = getTokenId(fuel_bridge, eth_testTokenAddress); + + // initializing rate limit params for the token + await env.eth.fuelERC20Gateway + .connect(env.eth.deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + RATE_LIMIT_AMOUNT.toString(), + RATE_LIMIT_DURATION + ); + + await env.eth.fuelERC20Gateway + .connect(env.eth.deployer) + .updateRateLimitStatus(eth_testTokenAddress, true); + + const { value: expectedGatewayContractId } = await fuel_bridge.functions + .bridged_token_gateway() + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .txParams(FUEL_CALL_TX_PARAMS) + .dryRun(); + + // 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(expectedGatewayContractId)).to.equal( + eth_erc20GatewayAddress + ); + expect(await eth_testToken.decimals()).to.equal(18n); + + // mint tokens as starting balances + await eth_testToken + .mint(await env.eth.deployer.getAddress(), 10_000) + .then((tx) => tx.wait()); + + await eth_testToken + .mint(await env.eth.signers[0].getAddress(), 10_000) + .then((tx) => tx.wait()); + + await eth_testToken + .mint(await env.eth.signers[1].getAddress(), 10_000) + .then((tx) => tx.wait()); + }); + + describe('Bridge ERC20 to Fuel', async () => { + const NUM_TOKENS = 100000000000000000000n; + let ethereumTokenSender: Signer; + let ethereumTokenSenderAddress: string; + let ethereumTokenSenderBalance: bigint; + let fuelTokenReceiver: FuelWallet; + let fuelTokenReceiverAddress: string; + let fuelTokenReceiverBalance: BN; + let fuelTokenMessageNonce: BN; + let fuelTokenMessageReceiver: AbstractAddress; + + before(async () => { + ethereumTokenSender = env.eth.signers[0]; + ethereumTokenSenderAddress = await ethereumTokenSender.getAddress(); + + await eth_testToken + .mint(ethereumTokenSenderAddress, NUM_TOKENS) + .then((tx) => tx.wait()); + + ethereumTokenSenderBalance = await eth_testToken.balanceOf( + ethereumTokenSenderAddress + ); + + fuelTokenReceiver = env.fuel.signers[0]; + fuelTokenReceiverAddress = fuelTokenReceiver.address.toHexString(); + fuelTokenReceiverBalance = await fuelTokenReceiver.getBalance( + fuel_testAssetId + ); + }); + + it('Bridge ERC20 via FuelERC20Gateway', async () => { + // approve FuelERC20Gateway to spend the tokens + await eth_testToken + .connect(ethereumTokenSender) + .approve(eth_erc20GatewayAddress, NUM_TOKENS) + .then((tx) => tx.wait()); + + // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel + const receipt = await env.eth.fuelERC20Gateway + .connect(ethereumTokenSender) + .deposit(fuelTokenReceiverAddress, eth_testTokenAddress, NUM_TOKENS) + .then((tx) => tx.wait()); + + expect(receipt!.status).to.equal(1); + // parse events from logs + const [event, ...restOfEvents] = + await env.eth.fuelMessagePortal.queryFilter( + env.eth.fuelMessagePortal.filters.MessageSent, + receipt!.blockNumber, + receipt!.blockNumber + ); + expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event + + fuelTokenMessageNonce = new BN(event.args.nonce.toString()); + fuelTokenMessageReceiver = Address.fromB256(event.args.recipient); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await eth_testToken.balanceOf( + ethereumTokenSenderAddress + ); + expect(newSenderBalance === ethereumTokenSenderBalance - NUM_TOKENS).to.be + .true; + }); + + it('Relay messages from Ethereum on Fuel', async () => { + // override the default test timeout from 2000ms + this.timeout(FUEL_MESSAGE_TIMEOUT_MS); + // relay the standard erc20 deposit + await relayMessageFromEthereum( + env, + fuelTokenMessageReceiver, + fuelTokenMessageNonce, + fuel_testAssetId, + NUM_TOKENS + ); + + // override the default test timeout from 2000ms + this.timeout(FUEL_MESSAGE_TIMEOUT_MS); + }); + + it('Check metadata was registered', async () => { + await fuel_bridge.functions + .asset_to_l1_address({ bits: fuel_testAssetId }) + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .call(); + + const { value: l2_decimals } = await fuel_bridge.functions + .decimals({ bits: fuel_testAssetId }) + .addContracts([fuel_bridge, fuel_bridgeImpl]) + .get(); + + expect(l2_decimals).to.be.equal(9); + }); + + it('Check ERC20 arrived on Fuel', async () => { + // check that the recipient balance has increased by the expected amount + const newReceiverBalance = await fuelTokenReceiver.getBalance( + fuel_testAssetId + ); + + expect( + newReceiverBalance.eq( + fuelTokenReceiverBalance.add(toBeHex(NUM_TOKENS / DECIMAL_DIFF)) + ) + ).to.be.true; + }); + + it('Bridge metadata', async () => { + // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel + const receipt = await env.eth.fuelERC20Gateway + .connect(ethereumTokenSender) + .sendMetadata(eth_testTokenAddress) + .then((tx) => tx.wait()); + + // parse events from logs + const [event, ...restOfEvents] = + await env.eth.fuelMessagePortal.queryFilter( + env.eth.fuelMessagePortal.filters.MessageSent, + receipt!.blockNumber, + receipt!.blockNumber + ); + expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event + + const nonce = new BN(event.args.nonce.toString()); + const fuelReceiver = Address.fromB256(event.args.recipient); + + // relay the message ourselves + const message = await waitForMessage( + env.fuel.provider, + fuelReceiver, + nonce, + FUEL_MESSAGE_TIMEOUT_MS + ); + expect(message).to.not.be.null; + + const tx = await relayCommonMessage(env.fuel.deployer, message, { + ...FUEL_TX_PARAMS, + maturity: undefined, + contractIds: [fuel_bridgeImpl.id.toHexString()], + }); + + const txResult = await tx.waitForResult(); + expect(txResult.status).to.equal('success'); + + const fuel_name = ( + await fuel_bridge.functions.name({ bits: fuel_testAssetId }).dryRun() + ).value; + const fuel_symbol = ( + await fuel_bridge.functions.symbol({ bits: fuel_testAssetId }).dryRun() + ).value; + + const eth_name = await eth_testToken.name(); + const eth_symbol = await eth_testToken.symbol(); + + expect(fuel_name).to.equal(eth_name); + expect(fuel_symbol).to.equal(eth_symbol); + }); + }); + + describe('Bridge ERC20 from Fuel', async () => { + const NUM_TOKENS = 10000000000000000000n; + const largeRateLimit = `30`; + let fuelTokenSender: FuelWallet; + let ethereumTokenReceiver: Signer; + let ethereumTokenReceiverAddress: string; + let ethereumTokenReceiverBalance: bigint; + let withdrawMessageProof: MessageProof | null; + let tokenBalanceBeforeWithdrawingOnFuel: BN; + + before(async () => { + fuelTokenSender = env.fuel.signers[0]; + ethereumTokenReceiver = env.eth.signers[0]; + ethereumTokenReceiverAddress = await ethereumTokenReceiver.getAddress(); + ethereumTokenReceiverBalance = await eth_testToken.balanceOf( + ethereumTokenReceiverAddress + ); + + tokenBalanceBeforeWithdrawingOnFuel = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + }); + + it('Bridge ERC20 via Fuel token contract', async () => { + // withdraw tokens back to the base chain + withdrawMessageProof = await generateWithdrawalMessageProof( + fuel_bridge, + fuelTokenSender, + ethereumTokenReceiverAddress, + NUM_TOKENS, + DECIMAL_DIFF + ); + }); + + it('Relay Message from Fuel on Ethereum', async () => { + const withdrawnAmountBeforeRelay = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + const rateLimitEndDuratioBeforeRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + // relay message + await relayMessageFromFuel(env, withdrawMessageProof!); + + // check rate limit params + const withdrawnAmountAfterRelay = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + const rateLimitEndDuratioAfterRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + expect(rateLimitEndDuratioAfterRelay === rateLimitEndDuratioBeforeRelay) + .to.be.true; + + expect( + withdrawnAmountAfterRelay === NUM_TOKENS + withdrawnAmountBeforeRelay + ).to.be.true; + }); + + it('Check the remaining token balance on Fuel after the first withdrawal', async () => { + // fetch the remaining token balance + const currentTokenBalance = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + + // currentTokenBalance has BN type by default hence the use of BN for conversion here + const expectedRemainingTokenBalanceOnFuel = + tokenBalanceBeforeWithdrawingOnFuel.sub( + new BN((NUM_TOKENS / DECIMAL_DIFF).toString()) + ); + + expect(currentTokenBalance.eq(expectedRemainingTokenBalanceOnFuel)).to.be + .true; + }); + + it('Rate limit parameters are updated when current withdrawn amount is more than the new limit & set a new higher limit', async () => { + const deployer = await env.eth.deployer; + const newRateLimit = '5'; + + let withdrawnAmountBeforeReset = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(newRateLimit), + RATE_LIMIT_DURATION + ); + + let currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + // current withdrawn amount doesn't change when rate limit is updated + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + + withdrawnAmountBeforeReset = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(largeRateLimit), + RATE_LIMIT_DURATION + ); + + currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + }); + + it('Rate limit parameters are updated when the initial duration is over', async () => { + const deployer = await env.eth.deployer; + + const rateLimitDuration = + await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); + + // fast forward time + await hardhatSkipTime( + env.eth.provider as JsonRpcProvider, + rateLimitDuration * 2n + ); + const currentPeriodEndBeforeRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(largeRateLimit), + RATE_LIMIT_DURATION + ); + + const currentWitdrawnAmountAfterReset = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect(currentWitdrawnAmountAfterReset == 0n).to.be.true; + + // withdraw tokens back to the base chain + withdrawMessageProof = await generateWithdrawalMessageProof( + fuel_bridge, + fuelTokenSender, + ethereumTokenReceiverAddress, + NUM_TOKENS, + DECIMAL_DIFF + ); + + // relay message + await relayMessageFromFuel(env, withdrawMessageProof!); + + const currentPeriodEndAfterRelay = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + .true; + + const currentPeriodAmount = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect(currentPeriodAmount === NUM_TOKENS).to.be.true; + }); + + it('Check the remaining token balance on Fuel after the second withdrawal', async () => { + // fetch the remaining token balance + const currentTokenBalance = await fuelTokenSender.getBalance( + fuel_testAssetId + ); + + // currentTokenBalance has BN type by default hence the use of BN for conversion here + const expectedRemainingTokenBalanceOnFuel = + tokenBalanceBeforeWithdrawingOnFuel.sub( + new BN(((NUM_TOKENS * 2n) / DECIMAL_DIFF).toString()) + ); + + expect(currentTokenBalance.eq(expectedRemainingTokenBalanceOnFuel)).to.be + .true; + }); + + it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { + const rateLimitDuration = + await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); + + const deployer = await env.eth.deployer; + const newRateLimit = `40`; + + // fast forward time + await hardhatSkipTime( + env.eth.provider as JsonRpcProvider, + rateLimitDuration * 2n + ); + + const currentWithdrawnAmountBeforeSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + const currentPeriodEndBeforeSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + + await env.eth.fuelERC20Gateway + .connect(deployer) + .resetRateLimitAmount( + eth_testTokenAddress, + parseEther(newRateLimit), + RATE_LIMIT_DURATION + ); + + const currentPeriodEndAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); + const currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelERC20Gateway.currentPeriodAmount( + eth_testTokenAddress + ); + + expect( + currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit + ).to.be.true; + + expect( + currentWithdrawnAmountBeforeSettingLimit > + currentWithdrawnAmountAfterSettingLimit + ).to.be.true; + + expect(currentWithdrawnAmountAfterSettingLimit === 0n).to.be.true; + }); + + it('Check ERC20 arrived on Ethereum', async () => { + // check that the recipient balance has increased by the expected amount + const newReceiverBalance = await eth_testToken.balanceOf( + ethereumTokenReceiverAddress + ); + expect( + newReceiverBalance === ethereumTokenReceiverBalance + NUM_TOKENS * 2n + ).to.be.true; + }); + }); +}); \ No newline at end of file diff --git a/packages/integration-tests/fork-tests/transfer_eth.ts b/packages/integration-tests/fork-tests/transfer_eth.ts new file mode 100644 index 00000000..4608ea01 --- /dev/null +++ b/packages/integration-tests/fork-tests/transfer_eth.ts @@ -0,0 +1,518 @@ +import type { TestEnvironment } from '@fuel-bridge/test-utils'; +import { + setupEnvironment, + fuels_parseEther, + createRelayMessageParams, + getMessageOutReceipt, + waitForMessage, + waitForBlockFinalization, + getBlock, + FUEL_CALL_TX_PARAMS, +} from '@fuel-bridge/test-utils'; +import chai from 'chai'; +import { parseEther, ZeroHash } from 'ethers'; +import type { Signer } from 'ethers'; +import { Address, BN, padFirst12BytesOfEvmAddress } from 'fuels'; +import type { + AbstractAddress, + WalletUnlocked as FuelWallet, + MessageProof, + Provider, +} from 'fuels'; + +const { expect } = chai; + +describe('Transferring ETH', async function () { + // Timeout 6 minutes + const DEFAULT_TIMEOUT_MS: number = 400_000; + const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000; + let BASE_ASSET_ID: string; + + let env: TestEnvironment; + + // override the default test timeout of 2000ms + this.timeout(DEFAULT_TIMEOUT_MS); + + async function forwardFuelChain(provider: Provider, blocksToForward: string) { + await provider.produceBlocks(Number(blocksToForward)).catch(console.error); + } + + async function getBlockWithHeight(env: any, height: string): Promise { + const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { + block(height: $height) { + id + } + }`; + const BLOCK_BY_HEIGHT_ARGS = { + height: height, + }; + + return fetch(env.fuel.provider.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: BLOCK_BY_HEIGHT_QUERY, + variables: BLOCK_BY_HEIGHT_ARGS, + }), + }) + .then((res: any) => res.json()) + .then(async (res) => { + if (!res.data.block) { + throw new Error(`Could not fetch block with height ${height}`); + } + + return res.data.block; + }); + } + + async function generateWithdrawalMessageProof( + fuelETHSender: FuelWallet, + ethereumETHReceiverAddress: string, + NUM_ETH: string + ): Promise { + // withdraw ETH back to the base chain + const fWithdrawTx = await fuelETHSender.withdrawToBaseLayer( + Address.fromString( + padFirst12BytesOfEvmAddress(ethereumETHReceiverAddress) + ), + fuels_parseEther(NUM_ETH), + FUEL_CALL_TX_PARAMS + ); + const fWithdrawTxResult = await fWithdrawTx.waitForResult(); + expect(fWithdrawTxResult.status).to.equal('success'); + + // Wait for the commited block + const withdrawBlock = await getBlock( + env.fuel.provider.url, + fWithdrawTxResult.blockId! + ); + + const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); + + const blocksPerCommitInterval = ( + await env.eth.fuelChainState.BLOCKS_PER_COMMIT_INTERVAL() + ).toString(); + + // Add + 1 to the block height to wait the next block + // that enable to proof the message + const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); + const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); + + let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + // fast forward post the commit cooldown period + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // override the commit hash in a existing block + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit( + ZeroHash, + commitHeight.toString() + ); + + // fast forward to the block finalization time + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + + // fast forward post the commit cooldown period + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // produce more blocks to fetch the block height + await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); + + const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + + // reset the commit hash in the local L2 network + await env.eth.fuelChainState + .connect(env.eth.signers[1]) + .commit(block.id, commitHeight.toString()); + + // fast forward to the block finalization time + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // get message proof + const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); + + return await fuelETHSender.provider.getMessageProof( + fWithdrawTx.id, + messageOutReceipt.nonce, + block.id + ); + } + + async function relayMessage( + env: TestEnvironment, + withdrawMessageProof: MessageProof + ) { + // wait for block finalization + await waitForBlockFinalization(env, withdrawMessageProof); + + // construct relay message proof data + const relayMessageParams = createRelayMessageParams(withdrawMessageProof); + + const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); + + // fast forward to the block finalization time + await env.eth.provider.send('evm_increaseTime', [ + Number(TIME_TO_FINALIZE) * 100, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + // relay message + await env.eth.fuelMessagePortal.relayMessage( + relayMessageParams.message, + relayMessageParams.rootBlockHeader, + relayMessageParams.blockHeader, + relayMessageParams.blockInHistoryProof, + relayMessageParams.messageInBlockProof + ); + } + + before(async () => { + env = await setupEnvironment({}); + BASE_ASSET_ID = env.fuel.provider.getBaseAssetId(); + }); + + describe('Send ETH to Fuel', async () => { + const NUM_ETH = '30'; + let ethereumETHSender: Signer; + let ethereumETHSenderAddress: string; + let fuelETHReceiver: AbstractAddress; + let fuelETHReceiverAddress: string; + let fuelETHReceiverBalance: BN; + let fuelETHMessageNonce: BN; + + before(async () => { + ethereumETHSender = env.eth.signers[0]; + ethereumETHSenderAddress = await ethereumETHSender.getAddress(); + fuelETHReceiver = env.fuel.signers[0].address; + fuelETHReceiverAddress = fuelETHReceiver.toHexString(); + + fuelETHReceiverBalance = await env.fuel.provider.getBalance( + fuelETHReceiver, + BASE_ASSET_ID + ); + }); + + it('Send ETH via MessagePortal', async () => { + // use the FuelMessagePortal to directly send ETH which should be immediately spendable + const tx = await env.eth.fuelMessagePortal + .connect(ethereumETHSender) + .depositETH(fuelETHReceiverAddress, { + value: parseEther(NUM_ETH), + }); + const receipt = await tx.wait(); + expect(receipt!.status).to.equal(1); + + // parse events from logs + const filter = env.eth.fuelMessagePortal.filters.MessageSent( + undefined, // Args set to null since there should be just 1 event for MessageSent + undefined, + undefined, + undefined, + undefined + ); + + const [event, ...restOfEvents] = + await env.eth.fuelMessagePortal.queryFilter( + filter, + receipt!.blockNumber, + receipt!.blockNumber + ); + expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event + + fuelETHMessageNonce = new BN(event.args.nonce.toString()); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await env.eth.provider.getBalance( + ethereumETHSenderAddress, + receipt!.blockNumber + ); + + const txCost = receipt!.fee; + + const expectedSenderBalance = + (await env.eth.provider.getBalance( + ethereumETHSender, + receipt!.blockNumber - 1 + )) - + txCost - + parseEther(NUM_ETH); + + expect(newSenderBalance).to.be.eq(expectedSenderBalance); + }); + + it('Wait for ETH to arrive on Fuel', async function () { + // wait for message to appear in fuel client + 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 + const newReceiverBalance = await env.fuel.provider.getBalance( + fuelETHReceiver, + BASE_ASSET_ID + ); + expect( + newReceiverBalance.eq( + fuelETHReceiverBalance.add(fuels_parseEther(NUM_ETH)) + ) + ).to.be.true; + }); + }); + + describe('Send ETH from Fuel', async () => { + const NUM_ETH = '0.001'; + let fuelETHSender: FuelWallet; + let fuelETHSenderBalance: BN; + let ethereumETHReceiver: Signer; + let ethereumETHReceiverAddress: string; + let ethereumETHReceiverBalance: bigint; + let withdrawMessageProof: MessageProof | null; + + before(async () => { + fuelETHSender = env.fuel.signers[1]; + fuelETHSenderBalance = await fuelETHSender.getBalance(BASE_ASSET_ID); + ethereumETHReceiver = env.eth.signers[1]; + ethereumETHReceiverAddress = await ethereumETHReceiver.getAddress(); + ethereumETHReceiverBalance = await env.eth.provider.getBalance( + ethereumETHReceiver + ); + }); + + it('Send ETH via OutputMessage', async () => { + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + // check that the sender balance has decreased by the expected amount + const newSenderBalance = await fuelETHSender.getBalance(BASE_ASSET_ID); + + // 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(); + expect(diffOnSenderBalance.startsWith(NUM_ETH)).to.be.true; + }); + + it('Relay Message from Fuel on Ethereum', async () => { + await relayMessage(env, withdrawMessageProof!); + }); + + it('Check ETH arrived on Ethereum', async () => { + // check that the recipient balance has increased by the expected amount + const newReceiverBalance = await env.eth.provider.getBalance( + ethereumETHReceiver + ); + + expect( + newReceiverBalance <= ethereumETHReceiverBalance + parseEther(NUM_ETH) + ).to.be.true; + }); + }); + + describe('ETH Withdrawls based on rate limit updates', async () => { + const NUM_ETH = '9'; + const largeRateLimit = `30`; + let fuelETHSender: FuelWallet; + let ethereumETHReceiver: Signer; + let ethereumETHReceiverAddress: string; + let withdrawMessageProof: MessageProof | null; + let rateLimitDuration: bigint; + + before(async () => { + fuelETHSender = env.fuel.signers[1]; + ethereumETHReceiver = env.eth.signers[1]; + ethereumETHReceiverAddress = await ethereumETHReceiver.getAddress(); + + await env.eth.fuelMessagePortal + .connect(env.eth.deployer) + .updateRateLimitStatus(true); + rateLimitDuration = await env.eth.fuelMessagePortal.RATE_LIMIT_DURATION(); + }); + + it('Checks rate limit params after relaying', async () => { + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + const withdrawnAmountBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + await relayMessage(env, withdrawMessageProof!); + + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect( + currentPeriodAmount === parseEther(NUM_ETH) + withdrawnAmountBeforeRelay + ).to.be.true; + }); + + it('Relays ETH after the rate limit is updated', async () => { + const deployer = env.eth.deployer; + const newRateLimit = `30`; + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(newRateLimit)); + + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + const withdrawnAmountBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + let currentWIthdrawnAmountReset = false; + + if (withdrawnAmountBeforeRelay > parseEther(newRateLimit)) { + currentWIthdrawnAmountReset = true; + + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + } + + await relayMessage(env, withdrawMessageProof!); + + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + if (currentWIthdrawnAmountReset) + expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + else { + expect( + currentPeriodAmount === + parseEther(NUM_ETH) + withdrawnAmountBeforeRelay + ).to.be.true; + } + }); + + it('Rate limit parameters are updated when current withdrawn amount is more than the new limit & set a new higher limit', async () => { + const deployer = env.eth.deployer; + const newRateLimit = `10`; + + let withdrawnAmountBeforeReset = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(newRateLimit)); + + let currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + // current withdrawn amount doesn't change when rate limit is updated + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + + withdrawnAmountBeforeReset = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(largeRateLimit)); + + currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect( + currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset + ).to.be.true; + }); + + it('Rate limit parameters are updated when the initial duration is over', async () => { + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + const currentPeriodEndBeforeRelay = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + + withdrawMessageProof = await generateWithdrawalMessageProof( + fuelETHSender, + ethereumETHReceiverAddress, + NUM_ETH + ); + + await relayMessage(env, withdrawMessageProof!); + + const currentPeriodEndAfterRelay = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + + expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be + .true; + + const currentPeriodAmount = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; + }); + + it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { + const deployer = await env.eth.deployer; + const newRateLimit = `40`; + + const currentWithdrawnAmountBeforeSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + const currentPeriodEndBeforeSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + + // fast forward time + await env.eth.provider.send('evm_increaseTime', [ + Number(rateLimitDuration) * 2, + ]); + await env.eth.provider.send('evm_mine', []); // Mine a new block + + await env.eth.fuelMessagePortal + .connect(deployer) + .resetRateLimitAmount(parseEther(newRateLimit)); + + const currentPeriodEndAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodEnd(); + const currentWithdrawnAmountAfterSettingLimit = + await env.eth.fuelMessagePortal.currentPeriodAmount(); + + expect( + currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit + ).to.be.true; + + expect( + currentWithdrawnAmountBeforeSettingLimit > + currentWithdrawnAmountAfterSettingLimit + ).to.be.true; + + expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; + }); + }); +}); \ No newline at end of file diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index ed3fb160..fb635dd1 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -6,6 +6,7 @@ "license": "APACHE-2.0", "scripts": { "test": "pnpm mocha -b -r ts-node/register 'tests/**/*.ts'", + "test-fork": "pnpm mocha -b -r ts-node/register 'fork-tests/**/*.ts'", "test:erc20": "pnpm mocha -b -r ts-node/register 'tests/bridge_erc20.ts'", "test:erc721": "pnpm mocha -b -r ts-node/register 'tests/bridge_erc721.ts'", "test:transfer": "pnpm mocha -b -r ts-node/register 'tests/transfer_eth.ts'", diff --git a/packages/integration-tests/tsconfig.json b/packages/integration-tests/tsconfig.json index 1834f1a8..3f64fc4d 100644 --- a/packages/integration-tests/tsconfig.json +++ b/packages/integration-tests/tsconfig.json @@ -7,5 +7,5 @@ "resolveJsonModule": true, "lib": ["ES2021", "dom"] }, - "include": ["./tests", "./scripts", "./**/*.ts"] + "include": ["./tests", "./fork-tests", "./scripts", "./**/*.ts"] } From 55204c0cb2a78ff11eccaff2b0b43532fb54881a Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:33:10 +0530 Subject: [PATCH 65/81] chore: remove placeholder contract --- packages/solidity-contracts/contracts/test/PlaceHolder.sol | 5 ----- packages/solidity-contracts/tsconfig.json | 3 +-- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 packages/solidity-contracts/contracts/test/PlaceHolder.sol diff --git a/packages/solidity-contracts/contracts/test/PlaceHolder.sol b/packages/solidity-contracts/contracts/test/PlaceHolder.sol deleted file mode 100644 index e18ceb0f..00000000 --- a/packages/solidity-contracts/contracts/test/PlaceHolder.sol +++ /dev/null @@ -1,5 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; -/// @dev The only purpose of this contract is to be copied during the Dockerfile build -/// so that hardhat downloads the compiler and it gets cached -contract PlaceHolder {} \ No newline at end of file diff --git a/packages/solidity-contracts/tsconfig.json b/packages/solidity-contracts/tsconfig.json index a44acc62..6c5f2582 100644 --- a/packages/solidity-contracts/tsconfig.json +++ b/packages/solidity-contracts/tsconfig.json @@ -13,8 +13,7 @@ "./protocol", "./test", "./typechain", - "./deploy", - "./integration-tests" + "./deploy" ], "files": ["./hardhat.config.ts"] } From a32508b28996ecd22aa6ec9a697451efa15186f6 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:34:03 +0530 Subject: [PATCH 66/81] chore: remove integration tests in solidity-contracts package --- .../integration-tests/bridge_erc20.ts | 733 ------------------ .../ethers/createRelayParams.ts | 96 --- .../ethers/getOrDeployECR20Contract.ts | 81 -- .../ethers/hardhatSkipTime.ts | 23 - .../integration-tests/ethers/index.ts | 2 - .../ethers/waitForBlockFinalization.ts | 66 -- .../integration-tests/fuels/getBlock.ts | 62 -- .../fuels/getMessageOutReceipt.ts | 14 - .../fuels/getOrDeployL2Bridge.ts | 91 --- .../integration-tests/fuels/getTokenId.ts | 24 - .../integration-tests/fuels/index.ts | 3 - .../fuels/relayCommonMessage.ts | 248 ------ .../integration-tests/fuels/transaction.ts | 17 - .../integration-tests/fuels/waitForMessage.ts | 49 -- .../integration-tests/setup/setup.ts | 230 ------ .../integration-tests/transfer_eth.ts | 515 ------------ .../integration-tests/utils/constants.ts | 13 - .../integration-tests/utils/debug.ts | 5 - .../integration-tests/utils/parsers.ts | 7 - 19 files changed, 2279 deletions(-) delete mode 100644 packages/solidity-contracts/integration-tests/bridge_erc20.ts delete mode 100644 packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts delete mode 100644 packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts delete mode 100644 packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts delete mode 100644 packages/solidity-contracts/integration-tests/ethers/index.ts delete mode 100644 packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/getBlock.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/getTokenId.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/index.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/transaction.ts delete mode 100644 packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts delete mode 100644 packages/solidity-contracts/integration-tests/setup/setup.ts delete mode 100644 packages/solidity-contracts/integration-tests/transfer_eth.ts delete mode 100644 packages/solidity-contracts/integration-tests/utils/constants.ts delete mode 100644 packages/solidity-contracts/integration-tests/utils/debug.ts delete mode 100644 packages/solidity-contracts/integration-tests/utils/parsers.ts diff --git a/packages/solidity-contracts/integration-tests/bridge_erc20.ts b/packages/solidity-contracts/integration-tests/bridge_erc20.ts deleted file mode 100644 index 73fa844d..00000000 --- a/packages/solidity-contracts/integration-tests/bridge_erc20.ts +++ /dev/null @@ -1,733 +0,0 @@ -import chai from 'chai'; -import { toBeHex, parseEther } from 'ethers'; -import type { JsonRpcProvider, Signer } from 'ethers'; -import { Address, BN } from 'fuels'; -import type { - AbstractAddress, - WalletUnlocked as FuelWallet, - MessageProof, - Provider, -} from 'fuels'; - -import { RATE_LIMIT_AMOUNT, RATE_LIMIT_DURATION } from '../protocol/constants'; -import type { Token } from '../typechain'; - -// due to cyclic workspace dependencies with test-utils package, the CI which runs `pnpm forc fmt --check` was getting timed out -// so hence had to re-use some test helpers in the test-utils package -import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; -import { getOrDeployECR20Contract } from './ethers/getOrDeployECR20Contract'; -import { hardhatSkipTime } from './ethers/hardhatSkipTime'; -import { waitForMessage, getMessageOutReceipt, getBlock } from './fuels'; -import { getOrDeployL2Bridge } from './fuels/getOrDeployL2Bridge'; -import { getTokenId } from './fuels/getTokenId'; -import { relayCommonMessage } from './fuels/relayCommonMessage'; -import type { TestEnvironment } from './setup/setup'; -import { setupEnvironment } from './setup/setup'; -import { FUEL_CALL_TX_PARAMS, FUEL_TX_PARAMS } from './utils/constants'; - -const { expect } = chai; - -describe('Bridging ERC20 tokens', async function () { - // Timeout 6 minutes - const DEFAULT_TIMEOUT_MS: number = 400_000; - const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000; - const DECIMAL_DIFF = 1_000_000_000n; - - let env: TestEnvironment; - let eth_testToken: Token; - let eth_testTokenAddress: string; - let eth_erc20GatewayAddress: string; - let fuel_bridge: any; - let fuel_bridgeImpl: any; - let fuel_bridgeContractId: string; - let fuel_testAssetId: string; - - // override the default test timeout from 2000ms - this.timeout(DEFAULT_TIMEOUT_MS); - - function fuel_to_eth_address(address: string): string { - return `0x${address.substring(26)}`.toLowerCase(); - } - - async function forwardFuelChain(provider: Provider, blocksToForward: string) { - await provider.produceBlocks(Number(blocksToForward)).catch(console.error); - } - - async function getBlockWithHeight(env: any, height: string): Promise { - const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { - block(height: $height) { - id - } - }`; - const BLOCK_BY_HEIGHT_ARGS = { - height: height, - }; - - return fetch(env.fuel.provider.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: BLOCK_BY_HEIGHT_QUERY, - variables: BLOCK_BY_HEIGHT_ARGS, - }), - }) - .then((res: any) => res.json()) - .then(async (res) => { - if (!res.data.block) { - throw new Error(`Could not fetch block with height ${height}`); - } - - return res.data.block; - }); - } - - async function generateWithdrawalMessageProof( - fuel_bridge: any, - fuelTokenSender: FuelWallet, - ethereumTokenReceiverAddress: string, - NUM_TOKENS: bigint, - DECIMAL_DIFF: bigint - ): Promise { - // withdraw tokens back to the base chain - fuel_bridge.account = fuelTokenSender; - const paddedAddress = - '0x' + ethereumTokenReceiverAddress.slice(2).padStart(64, '0'); - const fuelTokenSenderBalance = await fuelTokenSender.getBalance( - fuel_testAssetId - ); - const transactionRequest = await fuel_bridge.functions - .withdraw(paddedAddress) - .addContracts([fuel_bridge, fuel_bridgeImpl]) - .txParams({ - tip: 0, - maxFee: 1, - }) - .callParams({ - forward: { - amount: new BN(NUM_TOKENS.toString()).div( - new BN(DECIMAL_DIFF.toString()) - ), - assetId: fuel_testAssetId, - }, - }) - .fundWithRequiredCoins(); - - const tx = await fuelTokenSender.sendTransaction(transactionRequest); - const fWithdrawTxResult = await tx.waitForResult(); - expect(fWithdrawTxResult.status).to.equal('success'); - - // check that the sender balance has decreased by the expected amount - const newSenderBalance = await fuelTokenSender.getBalance(fuel_testAssetId); - - expect( - newSenderBalance.eq( - fuelTokenSenderBalance.sub(toBeHex(NUM_TOKENS / DECIMAL_DIFF)) - ) - ).to.be.true; - - // Wait for the commited block - const withdrawBlock = await getBlock( - env.fuel.provider.url, - fWithdrawTxResult.blockId! - ); - - const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); - - const blocksPerCommitInterval = ( - await env.eth.fuelChainState.BLOCKS_PER_COMMIT_INTERVAL() - ).toString(); - - // Add + 1 to the block height to wait the next block - // that enable to proof the message - const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); - const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); - - let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - - // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // override the commit hash in a existing block - await env.eth.fuelChainState - .connect(env.eth.signers[1]) - .commit( - '0x0000000000000000000000000000000000000000000000000000000000000000', - commitHeight.toString() - ); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - - // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // produce more blocks to fetch the block height - await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); - - const block = await getBlockWithHeight(env, nextBlockHeight.toString()); - - // reset the commit hash in the local L2 network - await env.eth.fuelChainState - .connect(env.eth.signers[1]) - .commit(block.id, commitHeight.toString()); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); - return await fuelTokenSender.provider.getMessageProof( - tx.id, - messageOutReceipt.nonce, - block.id - ); - } - - async function relayMessageFromFuel( - env: TestEnvironment, - withdrawMessageProof: MessageProof - ) { - // wait for block finalization - await waitForBlockFinalization(env, withdrawMessageProof); - - // construct relay message proof data - const relayMessageParams = createRelayMessageParams(withdrawMessageProof); - - // relay message - await env.eth.fuelMessagePortal.relayMessage( - relayMessageParams.message, - relayMessageParams.rootBlockHeader, - relayMessageParams.blockHeader, - relayMessageParams.blockInHistoryProof, - relayMessageParams.messageInBlockProof - ); - } - - async function relayMessageFromEthereum( - env: TestEnvironment, - fuelTokenMessageReceiver: AbstractAddress, - fuelTokenMessageNonce: BN, - fuel_AssetId: string, - amount: bigint - ) { - // relay the message ourselves - const message: any = await waitForMessage( - env.fuel.provider, - fuelTokenMessageReceiver, - fuelTokenMessageNonce, - FUEL_MESSAGE_TIMEOUT_MS - ); - expect(message).to.not.be.null; - - const tx = await relayCommonMessage(env.fuel.deployer, message, { - maturity: undefined, - contractIds: [fuel_bridgeImpl.id.toHexString()], - }); - - const txResult = await tx.waitForResult(); - - expect(txResult.status).to.equal('success'); - expect(txResult.mintedAssets.length).to.equal(1); - - const [mintedAsset] = txResult.mintedAssets; - - expect(mintedAsset.assetId).to.equal(fuel_AssetId); - expect(mintedAsset.amount.toString()).to.equal( - (amount / DECIMAL_DIFF).toString() - ); - } - - before(async () => { - env = await setupEnvironment(); - eth_erc20GatewayAddress = ( - await env.eth.fuelERC20Gateway.getAddress() - ).toLowerCase(); - - eth_testToken = await getOrDeployECR20Contract(env); - eth_testTokenAddress = (await eth_testToken.getAddress()).toLowerCase(); - - const { contract, implementation } = await getOrDeployL2Bridge( - env, - env.eth.fuelERC20Gateway - ); - - fuel_bridge = contract; - fuel_bridgeImpl = implementation; - - fuel_bridgeContractId = fuel_bridge.id.toHexString(); - - await env.eth.fuelERC20Gateway - .connect(env.eth.deployer) - .setAssetIssuerId(fuel_bridgeContractId); - fuel_testAssetId = getTokenId(fuel_bridge, eth_testTokenAddress); - - // initializing rate limit params for the token - await env.eth.fuelERC20Gateway - .connect(env.eth.deployer) - .resetRateLimitAmount( - eth_testTokenAddress, - RATE_LIMIT_AMOUNT.toString(), - RATE_LIMIT_DURATION - ); - - await env.eth.fuelERC20Gateway - .connect(env.eth.deployer) - .updateRateLimitStatus(eth_testTokenAddress, true); - - const { value: expectedGatewayContractId } = await fuel_bridge.functions - .bridged_token_gateway() - .addContracts([fuel_bridge, fuel_bridgeImpl]) - .txParams(FUEL_CALL_TX_PARAMS) - .dryRun(); - - // 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(expectedGatewayContractId)).to.equal( - eth_erc20GatewayAddress - ); - expect(await eth_testToken.decimals()).to.equal(18n); - - // mint tokens as starting balances - await eth_testToken - .mint(await env.eth.deployer.getAddress(), 10_000) - .then((tx) => tx.wait()); - - await eth_testToken - .mint(await env.eth.signers[0].getAddress(), 10_000) - .then((tx) => tx.wait()); - - await eth_testToken - .mint(await env.eth.signers[1].getAddress(), 10_000) - .then((tx) => tx.wait()); - }); - - describe('Bridge ERC20 to Fuel', async () => { - const NUM_TOKENS = 100000000000000000000n; - let ethereumTokenSender: Signer; - let ethereumTokenSenderAddress: string; - let ethereumTokenSenderBalance: bigint; - let fuelTokenReceiver: FuelWallet; - let fuelTokenReceiverAddress: string; - let fuelTokenReceiverBalance: BN; - let fuelTokenMessageNonce: BN; - let fuelTokenMessageReceiver: AbstractAddress; - - before(async () => { - ethereumTokenSender = env.eth.signers[0]; - ethereumTokenSenderAddress = await ethereumTokenSender.getAddress(); - - await eth_testToken - .mint(ethereumTokenSenderAddress, NUM_TOKENS) - .then((tx) => tx.wait()); - - ethereumTokenSenderBalance = await eth_testToken.balanceOf( - ethereumTokenSenderAddress - ); - - fuelTokenReceiver = env.fuel.signers[0]; - fuelTokenReceiverAddress = fuelTokenReceiver.address.toHexString(); - fuelTokenReceiverBalance = await fuelTokenReceiver.getBalance( - fuel_testAssetId - ); - }); - - it('Bridge ERC20 via FuelERC20Gateway', async () => { - // approve FuelERC20Gateway to spend the tokens - await eth_testToken - .connect(ethereumTokenSender) - .approve(eth_erc20GatewayAddress, NUM_TOKENS) - .then((tx) => tx.wait()); - - // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel - const receipt = await env.eth.fuelERC20Gateway - .connect(ethereumTokenSender) - .deposit(fuelTokenReceiverAddress, eth_testTokenAddress, NUM_TOKENS) - .then((tx) => tx.wait()); - - expect(receipt!.status).to.equal(1); - // parse events from logs - const [event, ...restOfEvents] = - await env.eth.fuelMessagePortal.queryFilter( - env.eth.fuelMessagePortal.filters.MessageSent, - receipt!.blockNumber, - receipt!.blockNumber - ); - expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event - - fuelTokenMessageNonce = new BN(event.args.nonce.toString()); - fuelTokenMessageReceiver = Address.fromB256(event.args.recipient); - - // check that the sender balance has decreased by the expected amount - const newSenderBalance = await eth_testToken.balanceOf( - ethereumTokenSenderAddress - ); - expect(newSenderBalance === ethereumTokenSenderBalance - NUM_TOKENS).to.be - .true; - }); - - it('Relay messages from Ethereum on Fuel', async () => { - // override the default test timeout from 2000ms - this.timeout(FUEL_MESSAGE_TIMEOUT_MS); - // relay the standard erc20 deposit - await relayMessageFromEthereum( - env, - fuelTokenMessageReceiver, - fuelTokenMessageNonce, - fuel_testAssetId, - NUM_TOKENS - ); - - // override the default test timeout from 2000ms - this.timeout(FUEL_MESSAGE_TIMEOUT_MS); - }); - - it('Check metadata was registered', async () => { - await fuel_bridge.functions - .asset_to_l1_address({ bits: fuel_testAssetId }) - .addContracts([fuel_bridge, fuel_bridgeImpl]) - .call(); - - const { value: l2_decimals } = await fuel_bridge.functions - .decimals({ bits: fuel_testAssetId }) - .addContracts([fuel_bridge, fuel_bridgeImpl]) - .get(); - - expect(l2_decimals).to.be.equal(9); - }); - - it('Check ERC20 arrived on Fuel', async () => { - // check that the recipient balance has increased by the expected amount - const newReceiverBalance = await fuelTokenReceiver.getBalance( - fuel_testAssetId - ); - - expect( - newReceiverBalance.eq( - fuelTokenReceiverBalance.add(toBeHex(NUM_TOKENS / DECIMAL_DIFF)) - ) - ).to.be.true; - }); - - it('Bridge metadata', async () => { - // use the FuelERC20Gateway to deposit test tokens and receive equivalent tokens on Fuel - const receipt = await env.eth.fuelERC20Gateway - .connect(ethereumTokenSender) - .sendMetadata(eth_testTokenAddress) - .then((tx) => tx.wait()); - - // parse events from logs - const [event, ...restOfEvents] = - await env.eth.fuelMessagePortal.queryFilter( - env.eth.fuelMessagePortal.filters.MessageSent, - receipt!.blockNumber, - receipt!.blockNumber - ); - expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event - - const nonce = new BN(event.args.nonce.toString()); - const fuelReceiver = Address.fromB256(event.args.recipient); - - // relay the message ourselves - const message: any = await waitForMessage( - env.fuel.provider, - fuelReceiver, - nonce, - FUEL_MESSAGE_TIMEOUT_MS - ); - expect(message).to.not.be.null; - - const tx = await relayCommonMessage(env.fuel.deployer, message, { - ...FUEL_TX_PARAMS, - maturity: undefined, - contractIds: [fuel_bridgeImpl.id.toHexString()], - }); - - const txResult = await tx.waitForResult(); - expect(txResult.status).to.equal('success'); - - const fuel_name = ( - await fuel_bridge.functions.name({ bits: fuel_testAssetId }).dryRun() - ).value; - const fuel_symbol = ( - await fuel_bridge.functions.symbol({ bits: fuel_testAssetId }).dryRun() - ).value; - - const eth_name = await eth_testToken.name(); - const eth_symbol = await eth_testToken.symbol(); - - expect(fuel_name).to.equal(eth_name); - expect(fuel_symbol).to.equal(eth_symbol); - }); - }); - - describe('Bridge ERC20 from Fuel', async () => { - const NUM_TOKENS = 10000000000000000000n; - const largeRateLimit = `30`; - let fuelTokenSender: FuelWallet; - let ethereumTokenReceiver: Signer; - let ethereumTokenReceiverAddress: string; - let ethereumTokenReceiverBalance: bigint; - let withdrawMessageProof: MessageProof | null; - let tokenBalanceBeforeWithdrawingOnFuel: BN; - - before(async () => { - fuelTokenSender = env.fuel.signers[0]; - ethereumTokenReceiver = env.eth.signers[0]; - ethereumTokenReceiverAddress = await ethereumTokenReceiver.getAddress(); - ethereumTokenReceiverBalance = await eth_testToken.balanceOf( - ethereumTokenReceiverAddress - ); - - tokenBalanceBeforeWithdrawingOnFuel = await fuelTokenSender.getBalance( - fuel_testAssetId - ); - }); - - it('Bridge ERC20 via Fuel token contract', async () => { - // withdraw tokens back to the base chain - withdrawMessageProof = await generateWithdrawalMessageProof( - fuel_bridge, - fuelTokenSender, - ethereumTokenReceiverAddress, - NUM_TOKENS, - DECIMAL_DIFF - ); - }); - - it('Relay Message from Fuel on Ethereum', async () => { - const withdrawnAmountBeforeRelay = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - const rateLimitEndDuratioBeforeRelay = - await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - - // relay message - await relayMessageFromFuel(env, withdrawMessageProof!); - - // check rate limit params - const withdrawnAmountAfterRelay = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - const rateLimitEndDuratioAfterRelay = - await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - - expect(rateLimitEndDuratioAfterRelay === rateLimitEndDuratioBeforeRelay) - .to.be.true; - - expect( - withdrawnAmountAfterRelay === NUM_TOKENS + withdrawnAmountBeforeRelay - ).to.be.true; - }); - - it('Check the remaining token balance on Fuel after the first withdrawal', async () => { - // fetch the remaining token balance - const currentTokenBalance = await fuelTokenSender.getBalance( - fuel_testAssetId - ); - - // currentTokenBalance has BN type by default hence the use of BN for conversion here - const expectedRemainingTokenBalanceOnFuel = - tokenBalanceBeforeWithdrawingOnFuel.sub( - new BN((NUM_TOKENS / DECIMAL_DIFF).toString()) - ); - - expect(currentTokenBalance.eq(expectedRemainingTokenBalanceOnFuel)).to.be - .true; - }); - - it('Rate limit parameters are updated when current withdrawn amount is more than the new limit & set a new higher limit', async () => { - const deployer = await env.eth.deployer; - const newRateLimit = '5'; - - let withdrawnAmountBeforeReset = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - await env.eth.fuelERC20Gateway - .connect(deployer) - .resetRateLimitAmount( - eth_testTokenAddress, - parseEther(newRateLimit), - RATE_LIMIT_DURATION - ); - - let currentWithdrawnAmountAfterSettingLimit = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - // current withdrawn amount doesn't change when rate limit is updated - - expect( - currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset - ).to.be.true; - - withdrawnAmountBeforeReset = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - await env.eth.fuelERC20Gateway - .connect(deployer) - .resetRateLimitAmount( - eth_testTokenAddress, - parseEther(largeRateLimit), - RATE_LIMIT_DURATION - ); - - currentWithdrawnAmountAfterSettingLimit = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - expect( - currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset - ).to.be.true; - }); - - it('Rate limit parameters are updated when the initial duration is over', async () => { - const deployer = await env.eth.deployer; - - const rateLimitDuration = - await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); - - // fast forward time - await hardhatSkipTime( - env.eth.provider as JsonRpcProvider, - rateLimitDuration * 2n - ); - const currentPeriodEndBeforeRelay = - await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - - await env.eth.fuelERC20Gateway - .connect(deployer) - .resetRateLimitAmount( - eth_testTokenAddress, - parseEther(largeRateLimit), - RATE_LIMIT_DURATION - ); - - const currentWitdrawnAmountAfterReset = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - expect(currentWitdrawnAmountAfterReset == 0n).to.be.true; - - // withdraw tokens back to the base chain - withdrawMessageProof = await generateWithdrawalMessageProof( - fuel_bridge, - fuelTokenSender, - ethereumTokenReceiverAddress, - NUM_TOKENS, - DECIMAL_DIFF - ); - - // relay message - await relayMessageFromFuel(env, withdrawMessageProof!); - - const currentPeriodEndAfterRelay = - await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - - expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be - .true; - - const currentPeriodAmount = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - expect(currentPeriodAmount === NUM_TOKENS).to.be.true; - }); - - it('Check the remaining token balance on Fuel after the second withdrawal', async () => { - // fetch the remaining token balance - const currentTokenBalance = await fuelTokenSender.getBalance( - fuel_testAssetId - ); - - // currentTokenBalance has BN type by default hence the use of BN for conversion here - const expectedRemainingTokenBalanceOnFuel = - tokenBalanceBeforeWithdrawingOnFuel.sub( - new BN(((NUM_TOKENS * 2n) / DECIMAL_DIFF).toString()) - ); - - expect(currentTokenBalance.eq(expectedRemainingTokenBalanceOnFuel)).to.be - .true; - }); - - it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { - const rateLimitDuration = - await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); - - const deployer = await env.eth.deployer; - const newRateLimit = `40`; - - // fast forward time - await hardhatSkipTime( - env.eth.provider as JsonRpcProvider, - rateLimitDuration * 2n - ); - - const currentWithdrawnAmountBeforeSettingLimit = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - const currentPeriodEndBeforeSettingLimit = - await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - - await env.eth.fuelERC20Gateway - .connect(deployer) - .resetRateLimitAmount( - eth_testTokenAddress, - parseEther(newRateLimit), - RATE_LIMIT_DURATION - ); - - const currentPeriodEndAfterSettingLimit = - await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - const currentWithdrawnAmountAfterSettingLimit = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - expect( - currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit - ).to.be.true; - - expect( - currentWithdrawnAmountBeforeSettingLimit > - currentWithdrawnAmountAfterSettingLimit - ).to.be.true; - - expect(currentWithdrawnAmountAfterSettingLimit === 0n).to.be.true; - }); - - it('Check ERC20 arrived on Ethereum', async () => { - // check that the recipient balance has increased by the expected amount - const newReceiverBalance = await eth_testToken.balanceOf( - ethereumTokenReceiverAddress - ); - expect( - newReceiverBalance === ethereumTokenReceiverBalance + NUM_TOKENS * 2n - ).to.be.true; - }); - }); -}); diff --git a/packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts b/packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts deleted file mode 100644 index 849e621c..00000000 --- a/packages/solidity-contracts/integration-tests/ethers/createRelayParams.ts +++ /dev/null @@ -1,96 +0,0 @@ -import type { MessageProof } from 'fuels'; -import { arrayify } from 'fuels'; - -// The BlockHeader structure. -type MessageBlockHeader = { - prevRoot: string; - height: string; - timestamp: string; - daHeight: string; - txCount: string; - outputMessagesCount: string; - txRoot: string; - outputMessagesRoot: string; - consensusParametersVersion: bigint; - stateTransitionBytecodeVersion: bigint; - eventInboxRoot: string; -}; - -// The BlockHeader structure. -type CommitBlockHeader = { - prevRoot: string; - height: string; - timestamp: string; - applicationHash: string; -}; - -// The MessageOut structure. -type Message = { - sender: string; - recipient: string; - amount: string; - nonce: string; - data: string; -}; - -type Proof = { - key: string; - proof: Array; -}; - -export function createRelayMessageParams(withdrawMessageProof: MessageProof) { - // construct data objects for relaying message on L1 - const message: Message = { - sender: withdrawMessageProof.sender.toHexString(), - recipient: withdrawMessageProof.recipient.toHexString(), - amount: withdrawMessageProof.amount.toHex(), - nonce: withdrawMessageProof.nonce, - data: withdrawMessageProof.data, - }; - const header = withdrawMessageProof.messageBlockHeader; - - const blockHeader: MessageBlockHeader = { - prevRoot: header.prevRoot, - height: header.height.toString(), - timestamp: header.time, - daHeight: header.daHeight.toString(), - txCount: header.transactionsCount.toString(), - txRoot: header.transactionsRoot, - outputMessagesRoot: header.messageOutboxRoot, - outputMessagesCount: header.messageReceiptCount.toString(), - consensusParametersVersion: BigInt(header.consensusParametersVersion), - stateTransitionBytecodeVersion: BigInt( - header.stateTransitionBytecodeVersion - ), - eventInboxRoot: header.eventInboxRoot, - }; - const messageProof = withdrawMessageProof.messageProof; - // Create the message proof object - const messageInBlockProof: Proof = { - key: messageProof.proofIndex.toString(), - proof: messageProof.proofSet.map((p) => arrayify(p)), - }; - - // construct data objects for relaying message on L1 (cont) - const rootHeader = withdrawMessageProof.commitBlockHeader; - const rootBlockHeader: CommitBlockHeader = { - prevRoot: rootHeader.prevRoot, - height: rootHeader.height.toString(), - timestamp: rootHeader.time, - applicationHash: rootHeader.applicationHash, - }; - const blockProof = withdrawMessageProof.blockProof; - // Create the block proof object - const blockInHistoryProof: Proof = { - key: blockProof.proofIndex.toString(), - proof: blockProof.proofSet.map((p) => arrayify(p)), - }; - - return { - message, - rootBlockHeader, - blockHeader, - blockInHistoryProof, - messageInBlockProof, - }; -} diff --git a/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts b/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts deleted file mode 100644 index f9cc1f94..00000000 --- a/packages/solidity-contracts/integration-tests/ethers/getOrDeployECR20Contract.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { parseEther } from 'ethers'; - -import type { Token } from '../../typechain'; -import { Token__factory } from '../../typechain'; -import type { TestEnvironment } from '../setup/setup'; -import { debug } from '../utils/debug'; - -const { ETH_ERC20_TOKEN_ADDRESS } = process.env; -const ETHEREUM_ETH_DECIMALS = 18n; - -function ethers_parseToken( - value: string, - decimals: bigint | number = 18n -): bigint { - const val = parseEther(value); - if (typeof decimals === 'number') decimals = BigInt(decimals); - return val / 10n ** (ETHEREUM_ETH_DECIMALS - decimals); -} - -export async function getOrDeployECR20Contract(env: TestEnvironment) { - debug('Setting up environment...'); - const ethDeployer = env.eth.signers[0]; - const ethDeployerAddr = await ethDeployer.getAddress(); - const ethAcct = env.eth.signers[0]; - - // load ERC20 contract - let ethTestToken; - if (ETH_ERC20_TOKEN_ADDRESS) { - try { - ethTestToken = Token__factory.connect( - ETH_ERC20_TOKEN_ADDRESS, - ethDeployer - ); - const tokenOwner = await ethTestToken._owner(); - if (tokenOwner.toLowerCase() != ethDeployerAddr.toLowerCase()) { - debug( - `The Ethereum ERC-20 token at ${ETH_ERC20_TOKEN_ADDRESS} is not owned by the Ethereum deployer ${ethDeployerAddr}.` - ); - } - } catch (e) { - debug( - `The Ethereum ERC-20 token could not be found at the provided address ${ETH_ERC20_TOKEN_ADDRESS}.` - ); - } - } - if (!ethTestToken) { - debug(`Creating ERC-20 token contract to test with...`); - const eth_tokenFactory = new Token__factory(ethDeployer); - ethTestToken = await eth_tokenFactory - .deploy() - .then((tx) => tx.waitForDeployment()); - debug( - `Ethereum ERC-20 token contract created at address ${await ethTestToken.getAddress()}.` - ); - } - ethTestToken = ethTestToken?.connect(ethAcct); - const ethTestTokenAddress = await ethTestToken.getAddress(); - debug( - `Testing with Ethereum ERC-20 token contract at ${ethTestTokenAddress}.` - ); - - return ethTestToken; -} - -export async function mintECR20( - env: TestEnvironment, - ethTestToken: Token, - ethAcctAddr: string, - amount: string -) { - if ( - (await ethTestToken.balanceOf(ethAcctAddr)) <= - ethers_parseToken(amount, 18n) * 2n - ) { - debug(`Minting ERC-20 tokens to test with...`); - const tokenMintTx1 = await ethTestToken - .connect(env.eth.deployer) - .mint(ethAcctAddr, ethers_parseToken('100', 18n)); - await tokenMintTx1.wait(); - } -} diff --git a/packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts b/packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts deleted file mode 100644 index 9a10a882..00000000 --- a/packages/solidity-contracts/integration-tests/ethers/hardhatSkipTime.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { toBeHex, type BigNumberish, type JsonRpcProvider } from 'ethers'; - -/** - * @description jumps time in the blockchain by the specified amount of time - * @param provider A provider that exposes hardhat_ methods - */ -export async function hardhatSkipTime( - provider: JsonRpcProvider, - time: BigNumberish -) { - const startingBlockNumber = await provider.getBlockNumber(); - const hexTime = toBeHex(time).replace(/^0x0+/, '0x'); - - await provider.send('evm_increaseTime', [hexTime]); - const success = await provider.send('hardhat_mine', ['0x1']); - - while (success) { - if ((await provider.getBlockNumber()) > startingBlockNumber) break; - await new Promise((resolve) => setTimeout(() => resolve(null), 100)); - } - - return success; -} diff --git a/packages/solidity-contracts/integration-tests/ethers/index.ts b/packages/solidity-contracts/integration-tests/ethers/index.ts deleted file mode 100644 index e5a5bf55..00000000 --- a/packages/solidity-contracts/integration-tests/ethers/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './createRelayParams'; -export * from './waitForBlockFinalization'; diff --git a/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts b/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts deleted file mode 100644 index 6972b898..00000000 --- a/packages/solidity-contracts/integration-tests/ethers/waitForBlockFinalization.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { JsonRpcProvider, Provider } from 'ethers'; -import type { MessageProof } from 'fuels'; -import { arrayify } from 'fuels'; - -import type { TestEnvironment } from '../setup/setup'; -import {debug} from '../utils/debug'; - -import { hardhatSkipTime } from './hardhatSkipTime'; - - -async function isHardhatProvider(provider: Provider) { - if (!('send' in provider)) return false; - - try { - const result = await (provider as JsonRpcProvider).send( - 'hardhat_metadata', - [] - ); - - return !!result?.clientVersion; - } catch (e) { - return null; - } -} - -export async function waitForBlockFinalization( - env: TestEnvironment, - messageProof: MessageProof -) { - // connect to FuelChainState contract as the permissioned block comitter - const fuelChainState = env.eth.fuelChainState.connect(env.eth.provider); - - // If we are connecting to a hardhat instance, we can speed up the wait - if (await isHardhatProvider(env.eth.provider)) { - const time = await fuelChainState.TIME_TO_FINALIZE(); - - await hardhatSkipTime(env.eth.provider, time); - - const isFinalized = await fuelChainState.finalized( - arrayify(messageProof.commitBlockHeader.id), - messageProof.commitBlockHeader.height.toString() - ); - - if (isFinalized) return; - } - - return new Promise((resolve) => { - debug('Waiting for block to be finalized on L1...'); - function onBlock() { - fuelChainState - .finalized( - arrayify(messageProof.commitBlockHeader.id), - messageProof.commitBlockHeader.height.toString() - ) - .then((isFinalized) => { - if (isFinalized) { - env.eth.provider.removeListener('block', onBlock); - debug('Block is finalized on L1'); - resolve(true); - } - }) - .catch(() => {}); - } - env.eth.provider.addListener('block', onBlock); - }); -} diff --git a/packages/solidity-contracts/integration-tests/fuels/getBlock.ts b/packages/solidity-contracts/integration-tests/fuels/getBlock.ts deleted file mode 100644 index f9108c03..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/getBlock.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { debug } from '../utils/debug'; - -const query = ` - query Block($id: BlockId!) { - block(id: $id) { - id - header { - prevRoot - transactionsCount - applicationHash - transactionsRoot - height - daHeight - transactionsCount - messageOutboxRoot - messageReceiptCount - time - id - } - } - } -`; - -export interface Block { - id: string; - header: Header; -} - -export interface Header { - prevRoot: string; - transactionsCount: string; - applicationHash: string; - transactionsRoot: string; - height: string; - daHeight: string; - messageOutboxRoot: string; - messageReceiptCount: string; - time: string; - id: string; -} - -export function getBlock(providerUrl: string, blockId: string): Promise { - debug(`Fetching block with id ${blockId}`); - return fetch(providerUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query, - variables: { - id: blockId, - }, - }), - }) - .then((res) => res.json()) - .then((res: any) => { - if (!res.data.block) - throw new Error(`Could not fetch block with id ${blockId}`); - return res.data.block; - }); -} diff --git a/packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts b/packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts deleted file mode 100644 index 4368ab46..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/getMessageOutReceipt.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { Receipt, TransactionResultMessageOutReceipt } from 'fuels'; -import { ReceiptType } from 'fuels'; - -export function getMessageOutReceipt(receipts: Array) { - const messageOutReceipt = receipts.find( - (r) => r.type === ReceiptType.MessageOut - ) as TransactionResultMessageOutReceipt; - - if (!messageOutReceipt) { - throw new Error('Failed to get message out receipt'); - } - - return messageOutReceipt; -} diff --git a/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts b/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts deleted file mode 100644 index 4374c565..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/getOrDeployL2Bridge.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - BridgeFungibleToken, - BridgeFungibleTokenFactory, - Proxy, - ProxyFactory, -} from '@fuel-bridge/fungible-token'; -import { resolveAddress, type AddressLike } from 'ethers'; - -// import { BridgeFungibleToken, BridgeFungibleTokenFactory, Proxy, ProxyFactory } from '../../../fungible-token/exports/types/index' -import type { TestEnvironment } from '../setup/setup'; -import { debug } from '../utils/debug'; -import { eth_address_to_b256 } from '../utils/parsers'; - -const { FUEL_FUNGIBLE_TOKEN_ADDRESS } = process.env; - -export async function getOrDeployL2Bridge( - env: TestEnvironment, - ethTokenGateway: AddressLike -) { - ethTokenGateway = await resolveAddress(ethTokenGateway); - - const tokenGateway = ethTokenGateway.replace('0x', ''); - const fuelAcct = env.fuel.signers[1]; - - let l2Bridge: any; - let proxy: Proxy; - let implementation: BridgeFungibleToken; - - if (FUEL_FUNGIBLE_TOKEN_ADDRESS) { - try { - proxy = new Proxy(FUEL_FUNGIBLE_TOKEN_ADDRESS, fuelAcct); - - const { value: implementationContractId }: any = await proxy.functions - .proxy_target() - .dryRun(); - - implementation = new BridgeFungibleToken( - implementationContractId.bits, - fuelAcct - ); - - l2Bridge = new BridgeFungibleToken(FUEL_FUNGIBLE_TOKEN_ADDRESS, fuelAcct); - - return { contract: l2Bridge, proxy, implementation }; - } catch (e) { - l2Bridge = null; - debug( - `The Fuel bridge contract could not be found at the provided address ${FUEL_FUNGIBLE_TOKEN_ADDRESS}.` - ); - } - } - - debug(`Creating Fuel bridge contract to test with...`); - const implConfigurables: any = { - BRIDGED_TOKEN_GATEWAY: eth_address_to_b256(tokenGateway), - }; - - implementation = await BridgeFungibleTokenFactory.deploy(fuelAcct, { - configurableConstants: implConfigurables, - }) - .then((tx: any) => tx.waitForResult()) - .then(({ contract }: any) => contract); - - debug('Creating proxy contract'); - const proxyConfigurables: any = { - INITIAL_TARGET: { bits: implementation.id.toB256() }, - INITIAL_OWNER: { - Initialized: { - Address: { bits: env.fuel.deployer.address.toHexString() }, - }, - }, - }; - - proxy = await ProxyFactory.deploy(fuelAcct, { - configurableConstants: proxyConfigurables, - }) - .then((tx: any) => tx.waitForResult()) - .then(({ contract }: any) => contract); - - // create contract instance - l2Bridge = new BridgeFungibleToken(proxy.id.toB256(), fuelAcct); - - const [fuelSigner] = env.fuel.signers; - l2Bridge.account = fuelSigner; - - debug('Finished setting up bridge'); - - l2Bridge.account = fuelAcct; - - return { contract: l2Bridge, proxy, implementation }; -} diff --git a/packages/solidity-contracts/integration-tests/fuels/getTokenId.ts b/packages/solidity-contracts/integration-tests/fuels/getTokenId.ts deleted file mode 100644 index c2b64d24..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/getTokenId.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { BytesLike } from 'ethers'; -import { concat, dataLength, sha256, zeroPadValue, toUtf8Bytes } from 'ethers'; -import type { Contract } from 'fuels'; -import { ZeroBytes32 } from 'fuels'; - -export function getTokenId( - contract: Contract | string, - tokenAddress: BytesLike, - tokenId: BytesLike = ZeroBytes32, - chainId: string = '1' -) { - if (dataLength(tokenAddress) < 32) - tokenAddress = zeroPadValue(tokenAddress, 32); - - if (dataLength(tokenId) < 32) tokenId = zeroPadValue(tokenId, 32); - - const id = - typeof contract === 'object' ? contract.id.toHexString() : contract; - - const subId = sha256(concat([toUtf8Bytes(chainId), tokenAddress, tokenId])); - const assetId = sha256(concat([id, subId])); - - return assetId; -} diff --git a/packages/solidity-contracts/integration-tests/fuels/index.ts b/packages/solidity-contracts/integration-tests/fuels/index.ts deleted file mode 100644 index 31425fa8..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './getBlock'; -export * from './getMessageOutReceipt'; -export * from './waitForMessage'; diff --git a/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts b/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts deleted file mode 100644 index 033de52f..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/relayCommonMessage.ts +++ /dev/null @@ -1,248 +0,0 @@ -/// @dev The Fuel testing utils. -/// A set of useful helper methods for the integration test environment. -import { - contractMessagePredicate, - contractMessageScript, -} from '@fuel-bridge/message-predicates'; -import type { - Message, - WalletUnlocked as FuelWallet, - TransactionResponse, - Provider, - ScriptTransactionRequestLike, - BytesLike, -} from 'fuels'; -import { - ZeroBytes32, - ScriptTransactionRequest, - arrayify, - InputType, - hexlify, - OutputType, - Predicate, - bn, - BN, -} from 'fuels'; - -import { debug } from '../utils/debug'; - -import { resourcesToInputs } from './transaction'; - -type RelayMessageOptions = Pick< - ScriptTransactionRequestLike, - 'gasLimit' | 'maturity' | 'maxFee' -> & { - contractIds?: BytesLike[]; -}; - -type CommonMessageDetails = { - name: string; - predicateRoot: string; - predicate: string; - script: string; - buildTx: ( - relayer: FuelWallet, - message: Message, - details: CommonMessageDetails, - opts?: RelayMessageOptions - ) => Promise; -}; - -// const contractMessagePredicate = _contractMessagePredicate; -// const contractMessageScript = _contractMessageScript; - -// Update for mainnet gas costs -const PREDICATE_GAS_LIMIT = 10000000; - -// Details for relaying common messages with certain predicate roots -function getCommonRelayableMessages(provider: Provider) { - // Create a predicate for common messages - const predicate = new Predicate({ - bytecode: contractMessagePredicate, - provider, - }); - - const assetId = provider.getBaseAssetId(); - - // Details for relaying common messages with certain predicate roots - const relayableMessages: CommonMessageDetails[] = [ - { - name: 'Message To Contract v1.3', - predicateRoot: predicate.address.toHexString(), - predicate: contractMessagePredicate, - script: contractMessageScript, - buildTx: async ( - relayer: FuelWallet, - message: Message, - details: CommonMessageDetails, - opts?: RelayMessageOptions - ): Promise => { - const script = arrayify(details.script); - const predicateBytecode = arrayify(details.predicate); - - // get resources to fund the transaction - // these are initial values that enable calling - // `provider.estimatePredicates()` later - const resources = await relayer.getResourcesToSpend([ - { - amount: new BN(1), - assetId, - }, - ]); - - if (resources.length === 0) { - throw new Error('Could not find resources to fund the transaction'); - } - - // convert resources to inputs - const spendableInputs = resourcesToInputs(resources); - - // get contract id - const data = arrayify(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, - }); - transaction.inputs.push({ - type: InputType.Message, - amount: message.amount, - sender: message.sender.toHexString(), - recipient: message.recipient.toHexString(), - witnessIndex: 0, - data: message.data, - nonce: message.nonce, - predicate: predicateBytecode, - }); - - transaction.inputs.push({ - type: InputType.Contract, - txPointer: ZeroBytes32, - contractId, - }); - - for (const additionalContractId of opts?.contractIds || []) { - transaction.inputs.push({ - type: InputType.Contract, - txPointer: ZeroBytes32, - contractId: additionalContractId, - }); - } - - transaction.inputs.push(...spendableInputs); - - transaction.outputs.push({ - type: OutputType.Contract, - inputIndex: 1, - }); - - for (const [index] of (opts?.contractIds || []).entries()) { - transaction.outputs.push({ - type: OutputType.Contract, - inputIndex: 2 + index, - }); - } - - transaction.outputs.push({ - type: OutputType.Change, - to: relayer.address.toB256(), - assetId, - }); - transaction.outputs.push({ - type: OutputType.Variable, - }); - transaction.witnesses.push(ZeroBytes32); - - transaction.gasLimit = bn(PREDICATE_GAS_LIMIT); - - debug( - '-------------------------------------------------------------------' - ); - debug(transaction.inputs); - debug( - '-------------------------------------------------------------------' - ); - debug(transaction.outputs); - debug( - '-------------------------------------------------------------------' - ); - - return transaction; - }, - }, - ]; - - return relayableMessages; -} - -// Relay commonly used messages with predicates spendable by anyone -export async function relayCommonMessage( - relayer: FuelWallet, - message: Message, - opts?: RelayMessageOptions -): Promise { - // find the relay details for the specified message - let messageRelayDetails: CommonMessageDetails | null = null; - const predicateRoot = message.recipient.toHexString(); - - for (const details of getCommonRelayableMessages(relayer.provider)) { - if (details.predicateRoot == predicateRoot) { - messageRelayDetails = details; - break; - } - } - if (messageRelayDetails == null) - throw new Error('message is not a common relayable message'); - - // build and send transaction - const transaction = await messageRelayDetails.buildTx( - relayer, - message, - messageRelayDetails, - opts || {} - ); - - const estimated_tx = await relayer.provider.estimatePredicates(transaction); - - const fees = await relayer.provider.estimateTxGasAndFee({ - transactionRequest: estimated_tx, - }); - const [feeInput] = await relayer - .getResourcesToSpend([ - { - amount: fees.maxFee, - assetId: relayer.provider.getBaseAssetId(), - }, - ]) - .then(resourcesToInputs); - - if (!feeInput) { - throw new Error('Could not find resources to fund the transaction'); - } - - // Find the coins that are being used to pay for the tx - const feeInputIndex = estimated_tx.inputs.findIndex( - (input) => input.type === InputType.Coin - ); - - if (feeInputIndex === -1) { - estimated_tx.inputs.push(feeInput); - } else { - estimated_tx.inputs[feeInputIndex] = feeInput; - } - - estimated_tx.maxFee = fees.maxFee; - - const simulation = await relayer.simulateTransaction(estimated_tx); - debug(simulation); - if (simulation.dryRunStatus?.type === 'DryRunFailureStatus') { - throw new Error( - `Transaction simulation failure: ${JSON.stringify(simulation)}` - ); - } - - return relayer.sendTransaction(estimated_tx); -} diff --git a/packages/solidity-contracts/integration-tests/fuels/transaction.ts b/packages/solidity-contracts/integration-tests/fuels/transaction.ts deleted file mode 100644 index 350c4ee9..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/transaction.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Resource, TransactionRequestInput } from 'fuels'; -import { InputType, ZeroBytes32, isCoin } from 'fuels'; - -export function resourcesToInputs(resources: Array) { - const inputs: Array = resources - .filter((r) => isCoin(r)) - .map((r: any) => ({ - type: InputType.Coin, - id: r.id, - owner: r.owner.toB256(), - amount: r.amount.toHex(), - assetId: r.assetId, - txPointer: ZeroBytes32, - witnessIndex: 0, - })); - return inputs; -} diff --git a/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts b/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts deleted file mode 100644 index c1f68bf9..00000000 --- a/packages/solidity-contracts/integration-tests/fuels/waitForMessage.ts +++ /dev/null @@ -1,49 +0,0 @@ -/// @dev The Fuel testing utils. -/// A set of useful helper methods for the integration test environment. -import type { BN, AbstractAddress } from 'fuels'; -import { type Provider as FuelProvider, type Message, hexlify } from 'fuels'; - -import {debug} from '../utils/debug'; - -// Simple async delay function -function delay(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -// Wait until a message is present in the fuel client -export async function waitForMessage( - provider: FuelProvider, - recipient: AbstractAddress, - nonce: BN, - timeout: number -): Promise { - const FUEL_MESSAGE_POLL_MS: number = 300; - const startTime = new Date().getTime(); - while (new Date().getTime() - startTime < timeout) { - const gqlMessage = await provider.getMessageByNonce( - hexlify(nonce.toBytes()) - ); - - if (gqlMessage) { - if (gqlMessage.recipient.toB256() !== recipient.toB256()) { - return null; - } - - const message: Message = { - messageId: gqlMessage.messageId, - sender: gqlMessage.sender, - recipient: gqlMessage.recipient, - nonce: hexlify(nonce.toBytes(32)), - amount: gqlMessage.amount, - data: gqlMessage.data, - daHeight: gqlMessage.daHeight, - }; - - return message; - } - - debug(`Waiting for message with nonce ${nonce}`); - await delay(FUEL_MESSAGE_POLL_MS); - } - return null; -} diff --git a/packages/solidity-contracts/integration-tests/setup/setup.ts b/packages/solidity-contracts/integration-tests/setup/setup.ts deleted file mode 100644 index 67867c00..00000000 --- a/packages/solidity-contracts/integration-tests/setup/setup.ts +++ /dev/null @@ -1,230 +0,0 @@ -import type { Signer as EthSigner } from 'ethers'; -import { - JsonRpcProvider, - parseEther, - formatEther, - ethers, - NonceManager, -} from 'ethers'; -import type { WalletUnlocked as FuelWallet } from 'fuels'; -import { Wallet, Provider as FuelProvider, BN } from 'fuels'; - -import { - FuelChainState__factory, - FuelMessagePortalV3__factory as FuelMessagePortal__factory, - FuelERC20GatewayV4__factory as FuelERC20Gateway__factory, -} from '../../typechain'; -import type { - FuelChainState, - FuelMessagePortalV3 as FuelMessagePortal, - FuelERC20GatewayV4 as FuelERC20Gateway, -} from '../../typechain'; - -export interface TestEnvironment { - eth: { - provider: JsonRpcProvider; - fuelChainState: FuelChainState; - fuelMessagePortal: FuelMessagePortal; - fuelERC20Gateway: FuelERC20Gateway; - deployer: EthSigner; - signers: EthSigner[]; - }; - fuel: { - provider: FuelProvider; - deployer: FuelWallet; - signers: FuelWallet[]; - }; -} - -const ETHEREUM_ETH_DECIMALS = 18n; -const FUEL_ETH_DECIMALS = 9n; - -const def_pk_fuel_deployer: string = - '0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c'; -const def_pk_fuel_signer1: string = - '0xa349d39f614a3085b7f7f8cef63fd5189136924fc1238e6d25ccdaa43a901cd0'; -const def_pk_fuel_signer2: string = - '0x139f2cd8db62a9d64c3ed4cdc804f1fb53be98d750cd1432a308b34a42d8dcc7'; - -// Parse ETH value as a string -export function fuels_parseEther(ether: string): BN { - let val = ethers.parseEther(ether); - val = val / 10n ** (ETHEREUM_ETH_DECIMALS - FUEL_ETH_DECIMALS); - return new BN(ethers.toBeHex(val)); -} - -// Format ETH value to a string -export function fuels_formatEther(ether: BN): string { - let val = BigInt(ether.toHex()); - val = val * 10n ** (ETHEREUM_ETH_DECIMALS - FUEL_ETH_DECIMALS); - return ethers.formatEther(val); -} - -export async function setupEnvironment(): Promise { - // Default config values - 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/v1/graphql'; - - // Default private keys of the developer mnemonic - const eth_private_keys: string[] = [ - '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', - '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d', - '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a', - '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6', - '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a', - '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba', - '0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e', - '0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356', - '0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97', - '0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6', - ]; - - const def_pk_eth_deployer: string = eth_private_keys[0]; - const def_pk_eth_signer1: string = eth_private_keys[3]; - const def_pk_eth_signer2: string = eth_private_keys[4]; - const def_pk_eth_signer3: string = eth_private_keys[5]; - - let deployerAddresses: any = null; - try { - deployerAddresses = await fetch( - def_http_deployer + '/deployments.local.json' - ).then((resp) => resp.json()); - } catch (e) { - console.error(e); - throw new Error( - 'Failed to connect to the deployer at (' + - def_http_deployer + - "). Are you sure it's running?" - ); - } - - // Create provider and signers from http_ethereum_client - const eth_provider = new JsonRpcProvider(def_http_eth); - - const eth_deployer = new NonceManager( - new ethers.Wallet(def_pk_eth_deployer, eth_provider) - ); - const eth_deployerBalance = await eth_provider.getBalance(eth_deployer); - if (eth_deployerBalance < parseEther('5')) { - throw new Error( - 'Ethereum deployer balance is very low (' + - formatEther(eth_deployerBalance) + - 'ETH)' - ); - } - const eth_signer1 = new NonceManager( - new ethers.Wallet(def_pk_eth_signer1, eth_provider) - ); - const eth_signer1Balance = await eth_provider.getBalance(eth_signer1); - if (eth_signer1Balance < parseEther('1')) { - const tx = await eth_deployer.sendTransaction({ - to: eth_signer1, - value: parseEther('1'), - }); - await tx.wait(); - } - const eth_signer2 = new NonceManager( - new ethers.Wallet(def_pk_eth_signer2, eth_provider) - ); - const eth_signer2Balance = await eth_provider.getBalance(eth_signer2); - if (eth_signer2Balance < parseEther('1')) { - const tx = await eth_deployer.sendTransaction({ - to: eth_signer2, - value: parseEther('1'), - }); - await tx.wait(); - } - - const eth_signer3 = new NonceManager( - new ethers.Wallet(def_pk_eth_signer3, eth_provider) - ); - const eth_signer3Balance = await eth_provider.getBalance(eth_signer3); - if (eth_signer3Balance < parseEther('1')) { - const tx = await eth_deployer.sendTransaction({ - to: eth_signer3, - value: parseEther('1'), - }); - await tx.wait(); - } - - const eth_fuelChainState: FuelChainState = FuelChainState__factory.connect( - deployerAddresses.FuelChainState, - eth_deployer - ); - const eth_fuelMessagePortal: FuelMessagePortal = - FuelMessagePortal__factory.connect( - deployerAddresses.FuelMessagePortal, - eth_deployer - ); - const eth_fuelERC20Gateway: FuelERC20Gateway = - FuelERC20Gateway__factory.connect( - deployerAddresses.FuelERC20GatewayV4, - eth_deployer - ); - - // Create provider from http_fuel_client - const fuel_provider = await FuelProvider.create(def_http_fuel); - try { - await fuel_provider.getBlockNumber(); - } catch (e) { - throw new Error( - 'Failed to connect to the Fuel client at (' + - def_http_fuel + - "). Are you sure it's running?" - ); - } - - const fuel_deployer = Wallet.fromPrivateKey( - def_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)' - ); - } - const fuel_signer1 = Wallet.fromPrivateKey( - def_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() - ); - await tx.wait(); - } - const fuel_signer2 = Wallet.fromPrivateKey( - def_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() - ); - await tx.wait(); - } - - return { - eth: { - provider: eth_provider, - fuelChainState: eth_fuelChainState, - fuelMessagePortal: eth_fuelMessagePortal, - fuelERC20Gateway: eth_fuelERC20Gateway, - deployer: eth_deployer, - signers: [eth_signer1, eth_signer2, eth_signer3], - }, - fuel: { - provider: fuel_provider, - deployer: fuel_deployer, - signers: [fuel_signer1, fuel_signer2], - }, - }; -} diff --git a/packages/solidity-contracts/integration-tests/transfer_eth.ts b/packages/solidity-contracts/integration-tests/transfer_eth.ts deleted file mode 100644 index d30a59c4..00000000 --- a/packages/solidity-contracts/integration-tests/transfer_eth.ts +++ /dev/null @@ -1,515 +0,0 @@ -import chai from 'chai'; -import { parseEther } from 'ethers'; -import type { Signer } from 'ethers'; -import { Address, BN, padFirst12BytesOfEvmAddress } from 'fuels'; -import type { - AbstractAddress, - WalletUnlocked as FuelWallet, - MessageProof, - Provider, -} from 'fuels'; - -// due to cyclic workspace dependencies with test-utils package, the CI which runs `pnpm forc fmt --check` was getting timed out -// so hence had to re-use some test helpers in the test-utils package -import { createRelayMessageParams, waitForBlockFinalization } from './ethers'; -import { waitForMessage, getMessageOutReceipt, getBlock } from './fuels'; -import { setupEnvironment, fuels_parseEther } from './setup/setup'; -import type { TestEnvironment } from './setup/setup'; -import { FUEL_CALL_TX_PARAMS } from './utils/constants'; - -const { expect } = chai; - -describe('Transferring ETH', async function () { - // Timeout 6 minutes - const DEFAULT_TIMEOUT_MS: number = 400_000; - const FUEL_MESSAGE_TIMEOUT_MS: number = 30_000; - let BASE_ASSET_ID: string; - - let env: TestEnvironment; - - // override the default test timeout of 2000ms - this.timeout(DEFAULT_TIMEOUT_MS); - - async function forwardFuelChain(provider: Provider, blocksToForward: string) { - await provider.produceBlocks(Number(blocksToForward)).catch(console.error); - } - - async function getBlockWithHeight(env: any, height: string): Promise { - const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { - block(height: $height) { - id - } - }`; - const BLOCK_BY_HEIGHT_ARGS = { - height: height, - }; - - return fetch(env.fuel.provider.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: BLOCK_BY_HEIGHT_QUERY, - variables: BLOCK_BY_HEIGHT_ARGS, - }), - }) - .then((res: any) => res.json()) - .then(async (res) => { - if (!res.data.block) { - throw new Error(`Could not fetch block with height ${height}`); - } - - return res.data.block; - }); - } - - async function generateWithdrawalMessageProof( - fuelETHSender: FuelWallet, - ethereumETHReceiverAddress: string, - NUM_ETH: string - ): Promise { - // withdraw ETH back to the base chain - const fWithdrawTx = await fuelETHSender.withdrawToBaseLayer( - Address.fromString( - padFirst12BytesOfEvmAddress(ethereumETHReceiverAddress) - ), - fuels_parseEther(NUM_ETH), - FUEL_CALL_TX_PARAMS - ); - const fWithdrawTxResult = await fWithdrawTx.waitForResult(); - expect(fWithdrawTxResult.status).to.equal('success'); - - // Wait for the commited block - const withdrawBlock = await getBlock( - env.fuel.provider.url, - fWithdrawTxResult.blockId! - ); - - const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); - - const blocksPerCommitInterval = ( - await env.eth.fuelChainState.BLOCKS_PER_COMMIT_INTERVAL() - ).toString(); - - // Add + 1 to the block height to wait the next block - // that enable to proof the message - const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); - const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); - - let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - - // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // override the commit hash in a existing block - await env.eth.fuelChainState - .connect(env.eth.signers[1]) - .commit( - '0x0000000000000000000000000000000000000000000000000000000000000000', - commitHeight.toString() - ); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - - // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // produce more blocks to fetch the block height - await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); - - const block = await getBlockWithHeight(env, nextBlockHeight.toString()); - - // reset the commit hash in the local L2 network - await env.eth.fuelChainState - .connect(env.eth.signers[1]) - .commit(block.id, commitHeight.toString()); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // get message proof - const messageOutReceipt = getMessageOutReceipt(fWithdrawTxResult.receipts); - - return await fuelETHSender.provider.getMessageProof( - fWithdrawTx.id, - messageOutReceipt.nonce, - block.id - ); - } - - async function relayMessage( - env: TestEnvironment, - withdrawMessageProof: MessageProof - ) { - // wait for block finalization - await waitForBlockFinalization(env, withdrawMessageProof); - - // construct relay message proof data - const relayMessageParams = createRelayMessageParams(withdrawMessageProof); - - const TIME_TO_FINALIZE = await env.eth.fuelChainState.TIME_TO_FINALIZE(); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 100, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // relay message - await env.eth.fuelMessagePortal.relayMessage( - relayMessageParams.message, - relayMessageParams.rootBlockHeader, - relayMessageParams.blockHeader, - relayMessageParams.blockInHistoryProof, - relayMessageParams.messageInBlockProof - ); - } - - before(async () => { - env = await setupEnvironment(); - BASE_ASSET_ID = env.fuel.provider.getBaseAssetId(); - }); - - describe('Send ETH to Fuel', async () => { - const NUM_ETH = '30'; - let ethereumETHSender: Signer; - let ethereumETHSenderAddress: string; - let fuelETHReceiver: AbstractAddress; - let fuelETHReceiverAddress: string; - let fuelETHReceiverBalance: BN; - let fuelETHMessageNonce: BN; - - before(async () => { - ethereumETHSender = env.eth.signers[0]; - ethereumETHSenderAddress = await ethereumETHSender.getAddress(); - fuelETHReceiver = env.fuel.signers[0].address; - fuelETHReceiverAddress = fuelETHReceiver.toHexString(); - - fuelETHReceiverBalance = await env.fuel.provider.getBalance( - fuelETHReceiver, - BASE_ASSET_ID - ); - }); - - it('Send ETH via MessagePortal', async () => { - // use the FuelMessagePortal to directly send ETH which should be immediately spendable - const tx = await env.eth.fuelMessagePortal - .connect(ethereumETHSender) - .depositETH(fuelETHReceiverAddress, { - value: parseEther(NUM_ETH), - }); - const receipt = await tx.wait(); - expect(receipt!.status).to.equal(1); - - // parse events from logs - const filter = env.eth.fuelMessagePortal.filters.MessageSent( - undefined, // Args set to null since there should be just 1 event for MessageSent - undefined, - undefined, - undefined, - undefined - ); - - const [event, ...restOfEvents] = - await env.eth.fuelMessagePortal.queryFilter( - filter, - receipt!.blockNumber, - receipt!.blockNumber - ); - expect(restOfEvents.length).to.be.eq(0); // Should be only 1 event - - fuelETHMessageNonce = new BN(event.args.nonce.toString()); - - // check that the sender balance has decreased by the expected amount - const newSenderBalance = await env.eth.provider.getBalance( - ethereumETHSenderAddress, - receipt!.blockNumber - ); - - const txCost = receipt!.fee; - - const expectedSenderBalance = - (await env.eth.provider.getBalance( - ethereumETHSender, - receipt!.blockNumber - 1 - )) - - txCost - - parseEther(NUM_ETH); - - expect(newSenderBalance).to.be.eq(expectedSenderBalance); - }); - - it('Wait for ETH to arrive on Fuel', async function () { - // wait for message to appear in fuel client - 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 - const newReceiverBalance = await env.fuel.provider.getBalance( - fuelETHReceiver, - BASE_ASSET_ID - ); - expect( - newReceiverBalance.eq( - fuelETHReceiverBalance.add(fuels_parseEther(NUM_ETH)) - ) - ).to.be.true; - }); - }); - - describe('Send ETH from Fuel', async () => { - const NUM_ETH = '0.001'; - let fuelETHSender: FuelWallet; - let fuelETHSenderBalance: BN; - let ethereumETHReceiver: Signer; - let ethereumETHReceiverAddress: string; - let ethereumETHReceiverBalance: bigint; - let withdrawMessageProof: MessageProof | null; - - before(async () => { - fuelETHSender = env.fuel.signers[1]; - fuelETHSenderBalance = await fuelETHSender.getBalance(BASE_ASSET_ID); - ethereumETHReceiver = env.eth.signers[1]; - ethereumETHReceiverAddress = await ethereumETHReceiver.getAddress(); - ethereumETHReceiverBalance = await env.eth.provider.getBalance( - ethereumETHReceiver - ); - }); - - it('Send ETH via OutputMessage', async () => { - withdrawMessageProof = await generateWithdrawalMessageProof( - fuelETHSender, - ethereumETHReceiverAddress, - NUM_ETH - ); - - // check that the sender balance has decreased by the expected amount - const newSenderBalance = await fuelETHSender.getBalance(BASE_ASSET_ID); - - // 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(); - expect(diffOnSenderBalance.startsWith(NUM_ETH)).to.be.true; - }); - - it('Relay Message from Fuel on Ethereum', async () => { - await relayMessage(env, withdrawMessageProof!); - }); - - it('Check ETH arrived on Ethereum', async () => { - // check that the recipient balance has increased by the expected amount - const newReceiverBalance = await env.eth.provider.getBalance( - ethereumETHReceiver - ); - - expect( - newReceiverBalance <= ethereumETHReceiverBalance + parseEther(NUM_ETH) - ).to.be.true; - }); - }); - - describe('ETH Withdrawls based on rate limit updates', async () => { - const NUM_ETH = '9'; - const largeRateLimit = `30`; - let fuelETHSender: FuelWallet; - let ethereumETHReceiver: Signer; - let ethereumETHReceiverAddress: string; - let withdrawMessageProof: MessageProof | null; - let rateLimitDuration: bigint; - - before(async () => { - fuelETHSender = env.fuel.signers[1]; - ethereumETHReceiver = env.eth.signers[1]; - ethereumETHReceiverAddress = await ethereumETHReceiver.getAddress(); - - await env.eth.fuelMessagePortal - .connect(env.eth.deployer) - .updateRateLimitStatus(true); - rateLimitDuration = await env.eth.fuelMessagePortal.RATE_LIMIT_DURATION(); - }); - - it('Checks rate limit params after relaying', async () => { - withdrawMessageProof = await generateWithdrawalMessageProof( - fuelETHSender, - ethereumETHReceiverAddress, - NUM_ETH - ); - - const withdrawnAmountBeforeRelay = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - await relayMessage(env, withdrawMessageProof!); - - const currentPeriodAmount = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - expect( - currentPeriodAmount === parseEther(NUM_ETH) + withdrawnAmountBeforeRelay - ).to.be.true; - }); - - it('Relays ETH after the rate limit is updated', async () => { - const deployer = env.eth.deployer; - const newRateLimit = `30`; - - await env.eth.fuelMessagePortal - .connect(deployer) - .resetRateLimitAmount(parseEther(newRateLimit)); - - withdrawMessageProof = await generateWithdrawalMessageProof( - fuelETHSender, - ethereumETHReceiverAddress, - NUM_ETH - ); - - const withdrawnAmountBeforeRelay = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - let currentWIthdrawnAmountReset = false; - - if (withdrawnAmountBeforeRelay > parseEther(newRateLimit)) { - currentWIthdrawnAmountReset = true; - - // fast forward time - await env.eth.provider.send('evm_increaseTime', [ - Number(rateLimitDuration) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - } - - await relayMessage(env, withdrawMessageProof!); - - const currentPeriodAmount = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - if (currentWIthdrawnAmountReset) - expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; - else { - expect( - currentPeriodAmount === - parseEther(NUM_ETH) + withdrawnAmountBeforeRelay - ).to.be.true; - } - }); - - it('Rate limit parameters are updated when current withdrawn amount is more than the new limit & set a new higher limit', async () => { - const deployer = env.eth.deployer; - const newRateLimit = `10`; - - let withdrawnAmountBeforeReset = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - await env.eth.fuelMessagePortal - .connect(deployer) - .resetRateLimitAmount(parseEther(newRateLimit)); - - let currentWithdrawnAmountAfterSettingLimit = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - // current withdrawn amount doesn't change when rate limit is updated - expect( - currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset - ).to.be.true; - - withdrawnAmountBeforeReset = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - await env.eth.fuelMessagePortal - .connect(deployer) - .resetRateLimitAmount(parseEther(largeRateLimit)); - - currentWithdrawnAmountAfterSettingLimit = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - expect( - currentWithdrawnAmountAfterSettingLimit === withdrawnAmountBeforeReset - ).to.be.true; - }); - - it('Rate limit parameters are updated when the initial duration is over', async () => { - // fast forward time - await env.eth.provider.send('evm_increaseTime', [ - Number(rateLimitDuration) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - const currentPeriodEndBeforeRelay = - await env.eth.fuelMessagePortal.currentPeriodEnd(); - - withdrawMessageProof = await generateWithdrawalMessageProof( - fuelETHSender, - ethereumETHReceiverAddress, - NUM_ETH - ); - - await relayMessage(env, withdrawMessageProof!); - - const currentPeriodEndAfterRelay = - await env.eth.fuelMessagePortal.currentPeriodEnd(); - - expect(currentPeriodEndAfterRelay > currentPeriodEndBeforeRelay).to.be - .true; - - const currentPeriodAmount = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - expect(currentPeriodAmount === parseEther(NUM_ETH)).to.be.true; - }); - - it('Rate limit parameters are updated when new limit is set after the initial duration', async () => { - const deployer = await env.eth.deployer; - const newRateLimit = `40`; - - const currentWithdrawnAmountBeforeSettingLimit = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - const currentPeriodEndBeforeSettingLimit = - await env.eth.fuelMessagePortal.currentPeriodEnd(); - - // fast forward time - await env.eth.provider.send('evm_increaseTime', [ - Number(rateLimitDuration) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - await env.eth.fuelMessagePortal - .connect(deployer) - .resetRateLimitAmount(parseEther(newRateLimit)); - - const currentPeriodEndAfterSettingLimit = - await env.eth.fuelMessagePortal.currentPeriodEnd(); - const currentWithdrawnAmountAfterSettingLimit = - await env.eth.fuelMessagePortal.currentPeriodAmount(); - - expect( - currentPeriodEndAfterSettingLimit > currentPeriodEndBeforeSettingLimit - ).to.be.true; - - expect( - currentWithdrawnAmountBeforeSettingLimit > - currentWithdrawnAmountAfterSettingLimit - ).to.be.true; - - expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; - }); - }); -}); diff --git a/packages/solidity-contracts/integration-tests/utils/constants.ts b/packages/solidity-contracts/integration-tests/utils/constants.ts deleted file mode 100644 index ce453eed..00000000 --- a/packages/solidity-contracts/integration-tests/utils/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { TxParams } from 'fuels'; -import { bn } from 'fuels'; - -const FUEL_GAS_LIMIT = 100000000; -const FUEL_MAX_FEE = 1; -export const FUEL_TX_PARAMS: TxParams = { - gasLimit: process.env.FUEL_GAS_LIMIT || FUEL_GAS_LIMIT, - maxFee: process.env.FUEL_MAX_FEE || FUEL_MAX_FEE, -}; -export const FUEL_CALL_TX_PARAMS = { - gasLimit: bn(10_000_000), - maxFee: FUEL_TX_PARAMS.maxFee, -}; diff --git a/packages/solidity-contracts/integration-tests/utils/debug.ts b/packages/solidity-contracts/integration-tests/utils/debug.ts deleted file mode 100644 index 99abd2cd..00000000 --- a/packages/solidity-contracts/integration-tests/utils/debug.ts +++ /dev/null @@ -1,5 +0,0 @@ -export function debug(...args: any) { - if (process.env.DEBUG) { - console.log(...args); - } -} diff --git a/packages/solidity-contracts/integration-tests/utils/parsers.ts b/packages/solidity-contracts/integration-tests/utils/parsers.ts deleted file mode 100644 index 49518a8a..00000000 --- a/packages/solidity-contracts/integration-tests/utils/parsers.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function fuel_to_eth_address(address: string): string { - return `0x${address.substring(26)}`.toLowerCase(); -} - -export function eth_address_to_b256(address: string): string { - return `0x000000000000000000000000${address.toLowerCase()}`.toLowerCase(); -} From 0625167af48e2c63980cff550e13d1b848196e7b Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:34:40 +0530 Subject: [PATCH 67/81] chore: add fork test run script in root --- package.json | 5 ++++- scripts/test-fork.sh | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 scripts/test-fork.sh diff --git a/package.json b/package.json index 84ceb1a5..76d6e4d8 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,9 @@ "node:clean": "make -C ./docker clean", "node:logs": "make -C ./docker logs", "test": "sh ./scripts/test.sh", + "test:fork": "sh ./scripts/test-fork.sh", "test:integration": "DEBUG=true pnpm --filter @fuel-bridge/integration-tests test", + "test:integration:fork": "DEBUG=true pnpm --filter @fuel-bridge/integration-tests test-fork", "lint:check": "eslint . --ext .ts,.js", "lint:fix": "pnpm lint:check --fix", "prettier:check": "prettier --check .", @@ -55,5 +57,6 @@ "micromatch@<4.0.8": ">=4.0.8", "cross-spawn@>=7.0.0 <7.0.5": ">=7.0.5" } - } + }, + "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" } diff --git a/scripts/test-fork.sh b/scripts/test-fork.sh new file mode 100644 index 00000000..e3290966 --- /dev/null +++ b/scripts/test-fork.sh @@ -0,0 +1,35 @@ +# Start the docker compose file with L1 and Fuel Node +echo "\n\nStarting docker..." +pnpm run node:up + +# Wait for the nodes to be ready and run the tests +HEALTH_CHECK_COUNTER=0 +HELTH_CHECK_OUTPUT="" +MAX_CHECK_ATTEMPTS=50 + +waitForNodesToBeReady() { + NODE_URL="http://localhost:4000/v1/playground"; + + printf "\rWaiting for node.${HELTH_CHECK_OUTPUT}" + + 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 pnpm run test:integration:fork + else + # 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 + waitForNodesToBeReady + fi +} + +waitForNodesToBeReady \ No newline at end of file From 537ca5a1eee72dade386fc1ec9d706bee838581c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:35:20 +0530 Subject: [PATCH 68/81] chore: handle erc721 gateway setup for fork tests --- packages/test-utils/src/utils/setup.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/test-utils/src/utils/setup.ts b/packages/test-utils/src/utils/setup.ts index e05d08f8..f996d715 100644 --- a/packages/test-utils/src/utils/setup.ts +++ b/packages/test-utils/src/utils/setup.ts @@ -276,12 +276,10 @@ export async function setupEnvironment( } if (!eth_fuelERC721GatewayAddress) { - if (!deployerAddresses.FuelERC721Gateway) { - throw new Error( - 'Failed to get FuelERC721Gateway address from deployer' - ); + if (deployerAddresses.FuelERC721Gateway) { + // we don't use the erc71 gateway for the fork test suite + eth_fuelERC721GatewayAddress = deployerAddresses.FuelERC721Gateway; } - eth_fuelERC721GatewayAddress = deployerAddresses.FuelERC721Gateway; } } @@ -301,7 +299,8 @@ export async function setupEnvironment( eth_fuelERC20GatewayAddress, eth_deployer ); - + + const eth_fuelERC721Gateway: FuelERC721Gateway = FuelERC721Gateway__factory.connect( eth_fuelERC721GatewayAddress, From 517b818c2c87805b4a2f8ff43daf03a86aaef255 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:36:26 +0530 Subject: [PATCH 69/81] chore: remove package manager in package.json --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 76d6e4d8..d448cb1e 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,5 @@ "micromatch@<4.0.8": ">=4.0.8", "cross-spawn@>=7.0.0 <7.0.5": ">=7.0.5" } - }, - "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" + } } From 95194b8a0b00ea9264661a1e70574b6e8a04f2ba Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 13:43:17 +0530 Subject: [PATCH 70/81] chore: update test command in upgrade ci workflow --- .github/workflows/upgrade-test-suite.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/upgrade-test-suite.yml b/.github/workflows/upgrade-test-suite.yml index 9771b8ea..0d4e2638 100644 --- a/.github/workflows/upgrade-test-suite.yml +++ b/.github/workflows/upgrade-test-suite.yml @@ -41,9 +41,6 @@ jobs: FORKING=true EOF working-directory: docker/envs - - name: Starts docker containers - run: pnpm run node:up - name: Run integration tests on a L1 fork after upgrading contracts run: | - pnpm upgrade:test:integration - working-directory: ./packages/solidity-contracts + pnpm run test:fork From b384ca55f067742ea40dd0a4c038e5887d348bbc Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 16:04:27 +0530 Subject: [PATCH 71/81] chore: relocated upgrade scripts to a different deploy folder --- .../deploy/fork/001.token.ts | 20 +++++ .../fork/002.set_canonical_token_bytecode.ts | 86 +++++++++++++++++++ .../003.chain_state_upgrade.ts} | 2 +- .../004.portal_upgrade.ts} | 0 .../005.gateway_upgrade.ts} | 0 .../deploy/fork/999.serve_deployment_file.ts | 80 +++++++++++++++++ 6 files changed, 187 insertions(+), 1 deletion(-) create mode 100644 packages/solidity-contracts/deploy/fork/001.token.ts create mode 100644 packages/solidity-contracts/deploy/fork/002.set_canonical_token_bytecode.ts rename packages/solidity-contracts/deploy/{hardhat/009.chain_state_upgrade.ts => fork/003.chain_state_upgrade.ts} (99%) rename packages/solidity-contracts/deploy/{hardhat/010.portal_upgrade.ts => fork/004.portal_upgrade.ts} (100%) rename packages/solidity-contracts/deploy/{hardhat/011.gateway_upgrade.ts => fork/005.gateway_upgrade.ts} (100%) create mode 100644 packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts diff --git a/packages/solidity-contracts/deploy/fork/001.token.ts b/packages/solidity-contracts/deploy/fork/001.token.ts new file mode 100644 index 00000000..9688ec73 --- /dev/null +++ b/packages/solidity-contracts/deploy/fork/001.token.ts @@ -0,0 +1,20 @@ +import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { DeployFunction } from 'hardhat-deploy/dist/types'; + +/** + * @description Deployed for testing purposes + */ +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { + ethers, + deployments: { deploy }, + } = hre; + + const [deployer] = await ethers.getSigners(); + + await deploy('Token', { from: deployer.address, log: true }); +}; + +func.tags = ['token']; +func.id = 'token'; +export default func; diff --git a/packages/solidity-contracts/deploy/fork/002.set_canonical_token_bytecode.ts b/packages/solidity-contracts/deploy/fork/002.set_canonical_token_bytecode.ts new file mode 100644 index 00000000..f7ec8ccb --- /dev/null +++ b/packages/solidity-contracts/deploy/fork/002.set_canonical_token_bytecode.ts @@ -0,0 +1,86 @@ +import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { DeployFunction } from 'hardhat-deploy/dist/types'; + +import { + USDT_ADDRESS, + USDC_ADDRESS, + WBTC_ADDRESS, + WETH_ADDRESS, +} from '../../protocol/constants'; +import { CustomToken__factory } from '../../typechain'; + +// script used to set custom token contract bytecodes for mainnet token addresses for testing purposes[USDC, USDT, WBTC, WETH] +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { ethers } = hre; + const [deployer] = await ethers.getSigners(); + + const customTokenFactory = await ethers.getContractFactory( + 'CustomToken', + deployer + ); + const customTokenWETHFactory = await ethers.getContractFactory( + 'CustomTokenWETH', + deployer + ); + + let customToken = await customTokenFactory.deploy(); + await customToken.waitForDeployment(); + + let runtimeBytecode = await ethers.provider.getCode( + await customToken.getAddress() + ); + + await ethers.provider.send('hardhat_setCode', [ + USDT_ADDRESS, + runtimeBytecode, + ]); + + let tokenInstance = CustomToken__factory.connect(USDT_ADDRESS, deployer); + await tokenInstance.setDecimals(6n); + + customToken = await customTokenFactory.deploy(); + await customToken.waitForDeployment(); + + runtimeBytecode = await ethers.provider.getCode( + await customToken.getAddress() + ); + + await ethers.provider.send('hardhat_setCode', [ + USDC_ADDRESS, + runtimeBytecode, + ]); + + tokenInstance = CustomToken__factory.connect(USDC_ADDRESS, deployer); + await tokenInstance.setDecimals(6n); + + customToken = await customTokenFactory.deploy(); + await customToken.waitForDeployment(); + + runtimeBytecode = await ethers.provider.getCode( + await customToken.getAddress() + ); + + await ethers.provider.send('hardhat_setCode', [ + WBTC_ADDRESS, + runtimeBytecode, + ]); + + tokenInstance = CustomToken__factory.connect(WBTC_ADDRESS, deployer); + await tokenInstance.setDecimals(8n); + + const customTokenWETH = await customTokenWETHFactory.deploy(); + await customTokenWETH.waitForDeployment(); + + runtimeBytecode = await ethers.provider.getCode( + await customTokenWETH.getAddress() + ); + + await ethers.provider.send('hardhat_setCode', [ + WETH_ADDRESS, + runtimeBytecode, + ]); +}; + +func.tags = ['set_canonical_token_bytecode']; +func.id = 'set_canonical_token_bytecode'; +export default func; diff --git a/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts b/packages/solidity-contracts/deploy/fork/003.chain_state_upgrade.ts similarity index 99% rename from packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts rename to packages/solidity-contracts/deploy/fork/003.chain_state_upgrade.ts index eb413e19..3f0dfc05 100644 --- a/packages/solidity-contracts/deploy/hardhat/009.chain_state_upgrade.ts +++ b/packages/solidity-contracts/deploy/fork/003.chain_state_upgrade.ts @@ -75,7 +75,7 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { to: address, data: txData, }); - + // hardhat with forking sometimes throws a `nonce too low error` using only one committer, so added another to be used in tests COMMITTER_ADDRESS = '0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65'; diff --git a/packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts b/packages/solidity-contracts/deploy/fork/004.portal_upgrade.ts similarity index 100% rename from packages/solidity-contracts/deploy/hardhat/010.portal_upgrade.ts rename to packages/solidity-contracts/deploy/fork/004.portal_upgrade.ts diff --git a/packages/solidity-contracts/deploy/hardhat/011.gateway_upgrade.ts b/packages/solidity-contracts/deploy/fork/005.gateway_upgrade.ts similarity index 100% rename from packages/solidity-contracts/deploy/hardhat/011.gateway_upgrade.ts rename to packages/solidity-contracts/deploy/fork/005.gateway_upgrade.ts diff --git a/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts b/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts new file mode 100644 index 00000000..4dc1b75e --- /dev/null +++ b/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts @@ -0,0 +1,80 @@ +import fs, { writeFile } from 'fs'; +import type { HardhatRuntimeEnvironment } from 'hardhat/types'; +import type { DeployFunction } from 'hardhat-deploy/dist/types'; +import path from 'path'; +import { promisify } from 'util'; + +import { FuelChainState__factory } from '../../typechain'; + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const { deployments, ethers } = hre; + + let address: any; + const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + const deploymentsFile: { [name: string]: string } = {}; + + if (isForking) { + let deploymentDir = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet' + ); + + fs.readdirSync(deploymentDir) + .filter((file) => path.extname(file) === '.json') + .forEach((file) => { + const filePath = path.join(deploymentDir, file); + try { + const deployment = JSON.parse(fs.readFileSync(filePath, 'utf8')); + // Use filename (without .json) as the key + const contractName = path.basename(file, '.json'); + deploymentsFile[contractName] = deployment.address; + } catch (error) { + console.error(`Error reading deployment file ${file}:`, error); + } + }); + + deploymentDir = path.join( + __dirname, + '..', + '..', + '/', + 'deployments', + 'mainnet', + 'FuelChainState.json' + ); + + const chainStateDeployment = JSON.parse( + fs.readFileSync(deploymentDir, 'utf8') + ); + address = chainStateDeployment.address; + } + + const state = FuelChainState__factory.connect(address, ethers.provider); + + deploymentsFile['BLOCKS_PER_COMMIT_INTERVAL'] = ( + await state.BLOCKS_PER_COMMIT_INTERVAL() + ).toString(); + deploymentsFile['NUM_COMMIT_SLOTS'] = ( + await state.NUM_COMMIT_SLOTS() + ).toString(); + deploymentsFile['TIME_TO_FINALIZE'] = ( + await state.TIME_TO_FINALIZE() + ).toString(); + + const writeFileAsync = promisify(writeFile); + + await writeFileAsync( + 'deployments/deployments.local.json', + JSON.stringify(deploymentsFile, null, 2), + 'utf8' + ); +}; + +func.tags = ['all']; +func.id = 'all'; +func.runAtTheEnd = true; +export default func; From 7990f2e64227d4f34d7de39024261e341f1d86d6 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 16:05:21 +0530 Subject: [PATCH 72/81] chore: revert updates in the original hardhat scripts --- .../deploy/hardhat/001.chain_state.ts | 3 - .../hardhat/002.fuel_message_portal_v3.ts | 3 - .../deploy/hardhat/003.erc20_gateway_v4.ts | 3 - .../hardhat/005.register_block_committer.ts | 3 - .../deploy/hardhat/006._erc721_gateway_v3.ts | 3 - .../deploy/hardhat/007.set_asset_issuer_id.ts | 3 - .../hardhat/999.serve_deployment_file.ts | 67 +++---------------- 7 files changed, 9 insertions(+), 76 deletions(-) diff --git a/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts b/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts index 30b4b275..e12339de 100644 --- a/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts +++ b/packages/solidity-contracts/deploy/hardhat/001.chain_state.ts @@ -14,9 +14,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { deployments: { save }, } = hre; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; - if (isForking) return; - const [deployer] = await ethers.getSigners(); const contract = await deployProxy(new FuelChainState(deployer), [], { diff --git a/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts b/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts index 5468571c..a662bb43 100644 --- a/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts +++ b/packages/solidity-contracts/deploy/hardhat/002.fuel_message_portal_v3.ts @@ -15,9 +15,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { deployments: { get, save }, } = hre; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; - if (isForking) return; - const [deployer] = await ethers.getSigners(); const { address: fuelChainState } = await get('FuelChainState'); diff --git a/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts b/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts index 2f117733..b548e213 100644 --- a/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts +++ b/packages/solidity-contracts/deploy/hardhat/003.erc20_gateway_v4.ts @@ -10,9 +10,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { deployments: { get, save }, } = hre; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; - if (isForking) return; - const [deployer] = await ethers.getSigners(); const fuelMessagePortal = await get('FuelMessagePortal'); diff --git a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts index 521d68ab..4b037856 100644 --- a/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts +++ b/packages/solidity-contracts/deploy/hardhat/005.register_block_committer.ts @@ -8,9 +8,6 @@ const COMMITTER_ADDRESS = '0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { ethers, deployments } = hre; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; - if (isForking) return; - const [deployer] = await ethers.getSigners(); const { address } = await deployments.get('FuelChainState'); diff --git a/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts b/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts index 3bafb3cc..0e020e70 100644 --- a/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts +++ b/packages/solidity-contracts/deploy/hardhat/006._erc721_gateway_v3.ts @@ -10,9 +10,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { deployments: { get, save }, } = hre; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; - if (isForking) return; - const [deployer] = await ethers.getSigners(); const fuelMessagePortal = await get('FuelMessagePortal'); diff --git a/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts b/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts index f689131a..5847683e 100644 --- a/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts +++ b/packages/solidity-contracts/deploy/hardhat/007.set_asset_issuer_id.ts @@ -17,9 +17,6 @@ const ASSET_ISSUER_ID = const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { ethers, deployments } = hre; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; - if (isForking) return; - const [deployer] = await ethers.getSigners(); await deployments.execute( diff --git a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts index 54c4f6b1..a5ed9834 100644 --- a/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts +++ b/packages/solidity-contracts/deploy/hardhat/999.serve_deployment_file.ts @@ -1,65 +1,18 @@ -import fs, { writeFile } from 'fs'; +import { promises as fs } from 'fs'; import type { HardhatRuntimeEnvironment } from 'hardhat/types'; import type { DeployFunction } from 'hardhat-deploy/dist/types'; -import path from 'path'; -import { promisify } from 'util'; import { FuelChainState__factory } from '../../typechain'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const { deployments, ethers } = hre; - - let address: any; - const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; + const allDeployments = await deployments.all(); const deploymentsFile: { [name: string]: string } = {}; - - if (isForking) { - let deploymentDir = path.join( - __dirname, - '..', - '..', - '/', - 'deployments', - 'mainnet' - ); - - fs.readdirSync(deploymentDir) - .filter((file) => path.extname(file) === '.json') - .forEach((file) => { - const filePath = path.join(deploymentDir, file); - try { - const deployment = JSON.parse(fs.readFileSync(filePath, 'utf8')); - // Use filename (without .json) as the key - const contractName = path.basename(file, '.json'); - deploymentsFile[contractName] = deployment.address; - } catch (error) { - console.error(`Error reading deployment file ${file}:`, error); - } - }); - - deploymentDir = path.join( - __dirname, - '..', - '..', - '/', - 'deployments', - 'mainnet', - 'FuelChainState.json' - ); - - const chainStateDeployment = JSON.parse( - fs.readFileSync(deploymentDir, 'utf8') - ); - address = chainStateDeployment.address; - } else { - const allDeployments = await deployments.all(); - for (const key of Object.keys(allDeployments)) { - deploymentsFile[key] = allDeployments[key].address; - } - - ({ address } = await deployments.get('FuelChainState')); + for (const key of Object.keys(allDeployments)) { + deploymentsFile[key] = allDeployments[key].address; } + const { address } = await deployments.get('FuelChainState'); const state = FuelChainState__factory.connect(address, ethers.provider); deploymentsFile['BLOCKS_PER_COMMIT_INTERVAL'] = ( @@ -72,16 +25,14 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { await state.TIME_TO_FINALIZE() ).toString(); - const writeFileAsync = promisify(writeFile); - - await writeFileAsync( + await fs.writeFile( 'deployments/deployments.local.json', - JSON.stringify(deploymentsFile, null, 2), - 'utf8' + JSON.stringify(deploymentsFile, null, ' '), + 'utf-8' ); }; func.tags = ['all']; func.id = 'all'; func.runAtTheEnd = true; -export default func; +export default func; \ No newline at end of file From 58e22cf861a7f2ba98632fe072320705abbcd2d1 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 16:06:09 +0530 Subject: [PATCH 73/81] chore: add forking related logic in hardhat config --- packages/solidity-contracts/hardhat.config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/solidity-contracts/hardhat.config.ts b/packages/solidity-contracts/hardhat.config.ts index 887110fb..4c41912f 100644 --- a/packages/solidity-contracts/hardhat.config.ts +++ b/packages/solidity-contracts/hardhat.config.ts @@ -16,6 +16,7 @@ const CONTRACTS_DEPLOYER_KEY = process.env.CONTRACTS_DEPLOYER_KEY || ''; const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY || ''; const INFURA_API_KEY = process.env.INFURA_API_KEY || ''; const RPC_URL = process.env.RPC_URL || 'http://127.0.0.1:8545'; +const isForkingEnabled = !!process.env.TENDERLY_RPC_URL; const config: HardhatUserConfig = { defaultNetwork: 'hardhat', @@ -42,10 +43,10 @@ const config: HardhatUserConfig = { count: 128, }, forking: { - enabled: !!process.env.TENDERLY_RPC_URL, + enabled: isForkingEnabled, url: process.env.TENDERLY_RPC_URL ? process.env.TENDERLY_RPC_URL : '', }, - deploy: ['deploy/hardhat'], + deploy: isForkingEnabled ? ['deploy/fork'] : ['deploy/hardhat'], }, localhost: { url: 'http://127.0.0.1:8545/', From f4cea4db350e0b6c2e990b12171f8677663e2d71 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 16:06:46 +0530 Subject: [PATCH 74/81] chore: formatting --- .../integration-tests/fork-tests/bridge_erc20.ts | 12 +++--------- .../integration-tests/fork-tests/transfer_eth.ts | 7 ++----- packages/solidity-contracts/tsconfig.json | 8 +------- packages/test-utils/src/utils/setup.ts | 3 +-- 4 files changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/integration-tests/fork-tests/bridge_erc20.ts b/packages/integration-tests/fork-tests/bridge_erc20.ts index 33c9ebea..c2982889 100644 --- a/packages/integration-tests/fork-tests/bridge_erc20.ts +++ b/packages/integration-tests/fork-tests/bridge_erc20.ts @@ -3,9 +3,7 @@ import { RATE_LIMIT_AMOUNT, RATE_LIMIT_DURATION, } from '@fuel-bridge/solidity-contracts/protocol/constants'; -import type { - Token, -} from '@fuel-bridge/solidity-contracts/typechain'; +import type { Token } from '@fuel-bridge/solidity-contracts/typechain'; import type { TestEnvironment } from '@fuel-bridge/test-utils'; import { setupEnvironment, @@ -34,7 +32,6 @@ import type { Provider, } from 'fuels'; - const { expect } = chai; describe('Bridging ERC20 tokens', async function () { @@ -159,10 +156,7 @@ describe('Bridging ERC20 tokens', async function () { // override the commit hash in a existing block await env.eth.fuelChainState .connect(env.eth.signers[1]) - .commit( - ZeroHash, - commitHeight.toString() - ); + .commit(ZeroHash, commitHeight.toString()); // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ @@ -736,4 +730,4 @@ describe('Bridging ERC20 tokens', async function () { ).to.be.true; }); }); -}); \ No newline at end of file +}); diff --git a/packages/integration-tests/fork-tests/transfer_eth.ts b/packages/integration-tests/fork-tests/transfer_eth.ts index 4608ea01..444f94bb 100644 --- a/packages/integration-tests/fork-tests/transfer_eth.ts +++ b/packages/integration-tests/fork-tests/transfer_eth.ts @@ -109,10 +109,7 @@ describe('Transferring ETH', async function () { // override the commit hash in a existing block await env.eth.fuelChainState .connect(env.eth.signers[1]) - .commit( - ZeroHash, - commitHeight.toString() - ); + .commit(ZeroHash, commitHeight.toString()); // fast forward to the block finalization time await env.eth.provider.send('evm_increaseTime', [ @@ -515,4 +512,4 @@ describe('Transferring ETH', async function () { expect(currentWithdrawnAmountAfterSettingLimit == 0n).to.be.true; }); }); -}); \ No newline at end of file +}); diff --git a/packages/solidity-contracts/tsconfig.json b/packages/solidity-contracts/tsconfig.json index 6c5f2582..3dacae81 100644 --- a/packages/solidity-contracts/tsconfig.json +++ b/packages/solidity-contracts/tsconfig.json @@ -8,12 +8,6 @@ "lib": ["ES2021"], "strict": true }, - "include": [ - "./scripts", - "./protocol", - "./test", - "./typechain", - "./deploy" - ], + "include": ["./scripts", "./protocol", "./test", "./typechain", "./deploy"], "files": ["./hardhat.config.ts"] } diff --git a/packages/test-utils/src/utils/setup.ts b/packages/test-utils/src/utils/setup.ts index f996d715..d04b452b 100644 --- a/packages/test-utils/src/utils/setup.ts +++ b/packages/test-utils/src/utils/setup.ts @@ -299,8 +299,7 @@ export async function setupEnvironment( eth_fuelERC20GatewayAddress, eth_deployer ); - - + const eth_fuelERC721Gateway: FuelERC721Gateway = FuelERC721Gateway__factory.connect( eth_fuelERC721GatewayAddress, From 92d10238029a042cfe309d40a94ceab589bc10c9 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 16:16:38 +0530 Subject: [PATCH 75/81] chore: ts linting --- .../solidity-contracts/deploy/fork/999.serve_deployment_file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts b/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts index 4dc1b75e..e6fafd1b 100644 --- a/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts +++ b/packages/solidity-contracts/deploy/fork/999.serve_deployment_file.ts @@ -7,7 +7,7 @@ import { promisify } from 'util'; import { FuelChainState__factory } from '../../typechain'; const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const { deployments, ethers } = hre; + const { ethers } = hre; let address: any; const isForking = hre.config.networks[hre.network.name]?.forking?.enabled; From bfd2896aa1c8c32cbf5fec63b6c7c19b72e49fa7 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Wed, 8 Jan 2025 17:38:58 +0530 Subject: [PATCH 76/81] chore: remove mocha in solidity-contracts --- packages/solidity-contracts/package.json | 1 - pnpm-lock.yaml | 3 --- 2 files changed, 4 deletions(-) diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index ad754d80..cbacd48b 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -54,7 +54,6 @@ "@typescript-eslint/parser": "^5.43.0", "chai": "^4.3.7", "cors": "2.8.5", - "mocha": "^10.0.0", "dotenv": "^16.0.3", "ethers": "6.13.1", "express": "^4.18.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11c155b8..bfa198cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -224,9 +224,6 @@ importers: markdownlint-cli: specifier: ^0.32.2 version: 0.32.2 - mocha: - specifier: ^10.0.0 - version: 10.4.0 node-fetch: specifier: ^2.6.6 version: 2.7.0 From 3f0e2bd203d2eaf45d77ef6f0d5775ba89608e7c Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 9 Jan 2025 17:05:23 +0530 Subject: [PATCH 77/81] chore: remove unused signer --- packages/test-utils/src/utils/setup.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/test-utils/src/utils/setup.ts b/packages/test-utils/src/utils/setup.ts index d04b452b..ca9d2aa4 100644 --- a/packages/test-utils/src/utils/setup.ts +++ b/packages/test-utils/src/utils/setup.ts @@ -50,7 +50,6 @@ const eth_private_keys: string[] = [ const def_pk_eth_deployer: string = eth_private_keys[0]; const def_pk_eth_signer1: string = eth_private_keys[3]; const def_pk_eth_signer2: string = eth_private_keys[4]; -const def_pk_eth_signer3: string = eth_private_keys[5]; const def_pk_fuel_deployer: string = '0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c'; @@ -111,8 +110,6 @@ export async function setupEnvironment( 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_eth_signer3: string = - opts.pk_eth_signer3 || process.env.PK_ETH_SIGNER3 || def_pk_eth_signer3; const pk_fuel_deployer: string = opts.pk_fuel_deployer || process.env.PK_FUEL_DEPLOYER || @@ -217,18 +214,6 @@ export async function setupEnvironment( await tx.wait(); } - const eth_signer3 = new NonceManager( - new ethers.Wallet(pk_eth_signer3, eth_provider) - ); - const eth_signer3Balance = await eth_provider.getBalance(eth_signer3); - if (eth_signer3Balance < parseEther('1') && skip_deployer_balance) { - const tx = await eth_deployer.sendTransaction({ - to: eth_signer3, - value: parseEther('1'), - }); - await tx.wait(); - } - // Get contract addresses let eth_fuelChainStateAddress: string = fuel_chain_consensus_addr; let eth_fuelMessagePortalAddress: string = fuel_message_portal_addr; @@ -316,7 +301,7 @@ export async function setupEnvironment( fuelERC20Gateway: eth_fuelERC20Gateway, fuelERC721Gateway: eth_fuelERC721Gateway, deployer: eth_deployer, - signers: [eth_signer1, eth_signer2, eth_signer3], + signers: [eth_signer1, eth_signer2], }, fuel: { provider: fuel_provider, From 37df68cc53b2df03bf0feaacbfb83c8cc3c58f8f Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 9 Jan 2025 17:06:35 +0530 Subject: [PATCH 78/81] test: remove zero hash commit logic --- .../fork-tests/bridge_erc20.ts | 21 ++----------------- .../fork-tests/transfer_eth.ts | 21 ++----------------- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/packages/integration-tests/fork-tests/bridge_erc20.ts b/packages/integration-tests/fork-tests/bridge_erc20.ts index c2982889..70f29384 100644 --- a/packages/integration-tests/fork-tests/bridge_erc20.ts +++ b/packages/integration-tests/fork-tests/bridge_erc20.ts @@ -147,27 +147,10 @@ describe('Bridging ERC20 tokens', async function () { const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); - let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + const cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // override the commit hash in a existing block - await env.eth.fuelChainState - .connect(env.eth.signers[1]) - .commit(ZeroHash, commitHeight.toString()); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - - // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); await env.eth.provider.send('evm_mine', []); // Mine a new block // produce more blocks to fetch the block height diff --git a/packages/integration-tests/fork-tests/transfer_eth.ts b/packages/integration-tests/fork-tests/transfer_eth.ts index 444f94bb..086cd58d 100644 --- a/packages/integration-tests/fork-tests/transfer_eth.ts +++ b/packages/integration-tests/fork-tests/transfer_eth.ts @@ -100,27 +100,10 @@ describe('Transferring ETH', async function () { const nextBlockHeight = new BN(withdrawBlock.header.height).add(new BN(1)); const commitHeight = new BN(nextBlockHeight).div(blocksPerCommitInterval); - let cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); + const cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour - await env.eth.provider.send('evm_mine', []); // Mine a new block - - // override the commit hash in a existing block - await env.eth.fuelChainState - .connect(env.eth.signers[1]) - .commit(ZeroHash, commitHeight.toString()); - - // fast forward to the block finalization time - await env.eth.provider.send('evm_increaseTime', [ - Number(TIME_TO_FINALIZE) * 2, - ]); - await env.eth.provider.send('evm_mine', []); // Mine a new block - - cooldown = await env.eth.fuelChainState.COMMIT_COOLDOWN(); - - // fast forward post the commit cooldown period - await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); // Advance 1 hour + await env.eth.provider.send('evm_increaseTime', [Number(cooldown) * 10]); await env.eth.provider.send('evm_mine', []); // Mine a new block // produce more blocks to fetch the block height From 0ceff15a010d44746db0e544bf2c3219473936b3 Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 9 Jan 2025 17:12:35 +0530 Subject: [PATCH 79/81] fix: ts linting --- packages/integration-tests/fork-tests/bridge_erc20.ts | 2 +- packages/integration-tests/fork-tests/transfer_eth.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/integration-tests/fork-tests/bridge_erc20.ts b/packages/integration-tests/fork-tests/bridge_erc20.ts index 70f29384..37e2d8af 100644 --- a/packages/integration-tests/fork-tests/bridge_erc20.ts +++ b/packages/integration-tests/fork-tests/bridge_erc20.ts @@ -22,7 +22,7 @@ import { hardhatSkipTime, } from '@fuel-bridge/test-utils'; import chai from 'chai'; -import { toBeHex, parseEther, ZeroHash } from 'ethers'; +import { toBeHex, parseEther } from 'ethers'; import type { JsonRpcProvider, Signer } from 'ethers'; import { Address, BN } from 'fuels'; import type { diff --git a/packages/integration-tests/fork-tests/transfer_eth.ts b/packages/integration-tests/fork-tests/transfer_eth.ts index 086cd58d..a1d76f13 100644 --- a/packages/integration-tests/fork-tests/transfer_eth.ts +++ b/packages/integration-tests/fork-tests/transfer_eth.ts @@ -10,7 +10,7 @@ import { FUEL_CALL_TX_PARAMS, } from '@fuel-bridge/test-utils'; import chai from 'chai'; -import { parseEther, ZeroHash } from 'ethers'; +import { parseEther } from 'ethers'; import type { Signer } from 'ethers'; import { Address, BN, padFirst12BytesOfEvmAddress } from 'fuels'; import type { From aff2ddcd2aea38c9d172d5138535e2c6674b480f Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 9 Jan 2025 17:42:52 +0530 Subject: [PATCH 80/81] test: remove duplicate code --- .../fork-tests/bridge_erc20.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/packages/integration-tests/fork-tests/bridge_erc20.ts b/packages/integration-tests/fork-tests/bridge_erc20.ts index 37e2d8af..0d400659 100644 --- a/packages/integration-tests/fork-tests/bridge_erc20.ts +++ b/packages/integration-tests/fork-tests/bridge_erc20.ts @@ -586,8 +586,6 @@ describe('Bridging ERC20 tokens', async function () { }); it('Rate limit parameters are updated when the initial duration is over', async () => { - const deployer = await env.eth.deployer; - const rateLimitDuration = await env.eth.fuelERC20Gateway.rateLimitDuration(eth_testTokenAddress); @@ -599,21 +597,6 @@ describe('Bridging ERC20 tokens', async function () { const currentPeriodEndBeforeRelay = await env.eth.fuelERC20Gateway.currentPeriodEnd(eth_testTokenAddress); - await env.eth.fuelERC20Gateway - .connect(deployer) - .resetRateLimitAmount( - eth_testTokenAddress, - parseEther(largeRateLimit), - RATE_LIMIT_DURATION - ); - - const currentWitdrawnAmountAfterReset = - await env.eth.fuelERC20Gateway.currentPeriodAmount( - eth_testTokenAddress - ); - - expect(currentWitdrawnAmountAfterReset == 0n).to.be.true; - // withdraw tokens back to the base chain withdrawMessageProof = await generateWithdrawalMessageProof( fuel_bridge, From f9b9acb5ec3a0226cb6981306bf5b8fb92d9ca0d Mon Sep 17 00:00:00 2001 From: viraj124 Date: Thu, 9 Jan 2025 19:25:02 +0530 Subject: [PATCH 81/81] chore: use fuel provider to fetch block using height --- .../fork-tests/bridge_erc20.ts | 32 +------------------ .../fork-tests/transfer_eth.ts | 32 +------------------ 2 files changed, 2 insertions(+), 62 deletions(-) diff --git a/packages/integration-tests/fork-tests/bridge_erc20.ts b/packages/integration-tests/fork-tests/bridge_erc20.ts index 0d400659..43865cb6 100644 --- a/packages/integration-tests/fork-tests/bridge_erc20.ts +++ b/packages/integration-tests/fork-tests/bridge_erc20.ts @@ -56,36 +56,6 @@ describe('Bridging ERC20 tokens', async function () { await provider.produceBlocks(Number(blocksToForward)).catch(console.error); } - async function getBlockWithHeight(env: any, height: string): Promise { - const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { - block(height: $height) { - id - } - }`; - const BLOCK_BY_HEIGHT_ARGS = { - height: height, - }; - - return fetch(env.fuel.provider.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: BLOCK_BY_HEIGHT_QUERY, - variables: BLOCK_BY_HEIGHT_ARGS, - }), - }) - .then((res: any) => res.json()) - .then(async (res) => { - if (!res.data.block) { - throw new Error(`Could not fetch block with height ${height}`); - } - - return res.data.block; - }); - } - async function generateWithdrawalMessageProof( fuel_bridge: BridgeFungibleToken, fuelTokenSender: FuelWallet, @@ -156,7 +126,7 @@ describe('Bridging ERC20 tokens', async function () { // produce more blocks to fetch the block height await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); - const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + const block = await env.fuel.provider.getBlock(nextBlockHeight.toString()); // reset the commit hash in the local L2 network await env.eth.fuelChainState diff --git a/packages/integration-tests/fork-tests/transfer_eth.ts b/packages/integration-tests/fork-tests/transfer_eth.ts index a1d76f13..f6781733 100644 --- a/packages/integration-tests/fork-tests/transfer_eth.ts +++ b/packages/integration-tests/fork-tests/transfer_eth.ts @@ -37,36 +37,6 @@ describe('Transferring ETH', async function () { await provider.produceBlocks(Number(blocksToForward)).catch(console.error); } - async function getBlockWithHeight(env: any, height: string): Promise { - const BLOCK_BY_HEIGHT_QUERY = `query Block($height: U64) { - block(height: $height) { - id - } - }`; - const BLOCK_BY_HEIGHT_ARGS = { - height: height, - }; - - return fetch(env.fuel.provider.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: BLOCK_BY_HEIGHT_QUERY, - variables: BLOCK_BY_HEIGHT_ARGS, - }), - }) - .then((res: any) => res.json()) - .then(async (res) => { - if (!res.data.block) { - throw new Error(`Could not fetch block with height ${height}`); - } - - return res.data.block; - }); - } - async function generateWithdrawalMessageProof( fuelETHSender: FuelWallet, ethereumETHReceiverAddress: string, @@ -109,7 +79,7 @@ describe('Transferring ETH', async function () { // produce more blocks to fetch the block height await forwardFuelChain(env.fuel.provider, blocksPerCommitInterval); - const block = await getBlockWithHeight(env, nextBlockHeight.toString()); + const block = await env.fuel.provider.getBlock(nextBlockHeight.toString()); // reset the commit hash in the local L2 network await env.eth.fuelChainState