diff --git a/.changeset/silent-apples-smile.md b/.changeset/silent-apples-smile.md new file mode 100644 index 00000000..27374802 --- /dev/null +++ b/.changeset/silent-apples-smile.md @@ -0,0 +1,5 @@ +--- +'@fuel-bridge/solidity-contracts': minor +--- + +add manual CI to simulate upgrades with tenderly diff --git a/.github/workflows/manual-simulate-upgrade.yml b/.github/workflows/manual-simulate-upgrade.yml new file mode 100644 index 00000000..7bf4314b --- /dev/null +++ b/.github/workflows/manual-simulate-upgrade.yml @@ -0,0 +1,35 @@ +name: Manual Simulate Upgrade + +on: + workflow_dispatch: + inputs: + vnetId: + description: 'Enter the tenderly virtual testnet id' + required: true + type: string + accessKey: + description: 'Enter the tenderly account access key' + required: true + type: string + rpc: + description: 'Enter tenderly virtual network rpc' + required: true + type: string + +jobs: + simulate-upgrades: + runs-on: ubuntu-latest + env: + RPC_URL: ${{ github.event.inputs.rpc }} + VNET_ID: ${{ github.event.inputs.vnetId }} + ACCESS_KEY: ${{ github.event.inputs.accessKey }} + steps: + - uses: actions/checkout@v3 + - uses: FuelLabs/github-actions/setups/node@master + with: + node-version: 20.16.0 + pnpm-version: 9.0.6 + - name: Simulate Upgrades + run: | + npx hardhat compile && npx hardhat simulate-upgrades --network mainnet + working-directory: ./packages/solidity-contracts diff --git a/packages/solidity-contracts/contracts/test/PlaceHolder.sol b/packages/solidity-contracts/contracts/test/PlaceHolder.sol new file mode 100644 index 00000000..ae3c2380 --- /dev/null +++ b/packages/solidity-contracts/contracts/test/PlaceHolder.sol @@ -0,0 +1,6 @@ +// 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/docker/block-committer/block-committer.sh b/packages/solidity-contracts/docker/block-committer/block-committer.sh new file mode 100644 index 00000000..9c4e7790 --- /dev/null +++ b/packages/solidity-contracts/docker/block-committer/block-committer.sh @@ -0,0 +1,28 @@ +#!/bin/sh +set -euo + +RETRIES=${RETRIES:-60} +DELAY=${DELAY:-10} + +# 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 \ + $FUEL_GRAPHQL_ENDPOINT/health > /dev/null +echo "Connected to Fuel Core chain." + + +# pull data from deployer dump +export STATE_CONTRACT_ADDRESS=$(jq -r '.address' /l1chain/fuel-v2-contracts/deployments/localhost/FuelChainState.json) +echo "STATE_CONTRACT_ADDRESS: $STATE_CONTRACT_ADDRESS" +echo "FUEL_GRAPHQL_ENDPOINT: $FUEL_GRAPHQL_ENDPOINT" +echo "RPC: $ETHEREUM_RPC" + +# start the Block Commiter +echo "Launching block committer with STATE_CONTRACT_ADDRESS=$STATE_CONTRACT_ADDRESS" +exec /root/fuel-block-committer \ No newline at end of file diff --git a/packages/solidity-contracts/docker/docker-compose.yml b/packages/solidity-contracts/docker/docker-compose.yml new file mode 100644 index 00000000..4d0a5ec6 --- /dev/null +++ b/packages/solidity-contracts/docker/docker-compose.yml @@ -0,0 +1,60 @@ +version: '3.4' + +services: + l1_chain: + platform: linux/amd64 + image: fueldev/l1chain:${DOCKER_TAG_L1_CHAIN:-latest} + container_name: l1_chain + build: + context: ../ + dockerfile: ./docker/docker.l1_chain.Dockerfile + volumes: + - ../deployments/localhost:/l1chain/fuel-v2-contracts/deployments + ports: + # expose the service to the host for integration testing + - ${L1_CHAIN_HTTP_PORT:-8545}:8545 + stop_grace_period: 1s + + fuel_core: + container_name: fuel_core + image: fueldev/fuelcore:${DOCKER_TAG_FUEL_CORE:-latest} + depends_on: + l1_chain: + condition: service_started + volumes: + - ../deployments/localhost:/l1chain/fuel-v2-contracts/deployments + platform: linux/amd64 + build: + context: ../ + dockerfile: ./docker/docker.fuel_core.Dockerfile + environment: + L1_CHAIN_HTTP: http://l1_chain:8545 + RUST_LOG: debug + DEBUG: true + CONSENSUS_KEY_SECRET: $CONSENSUS_KEY_SECRET + 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: + container_name: fuel_block_commiter + image: fueldev/block-committer:${DOCKER_TAG_FUEL_CORE:-latest} + depends_on: + fuel_core: + condition: service_started + volumes: + - ../deployments/localhost:/l1chain/fuel-v2-contracts/deployments + platform: linux/amd64 + build: + context: ../ + dockerfile: ./docker/docker.block_committer.Dockerfile + environment: + ETHEREUM_RPC: ws://l1_chain:8545/ + FUEL_GRAPHQL_ENDPOINT: http://fuel_core:4001/v1 + ports: + # expose the service to the host for integration testing + - ${COMMITTER_HTTP_PORT:-8888}:8888 + stop_grace_period: 1s + restart: always diff --git a/packages/solidity-contracts/docker/docker.block_committer.Dockerfile b/packages/solidity-contracts/docker/docker.block_committer.Dockerfile new file mode 100644 index 00000000..f1979b06 --- /dev/null +++ b/packages/solidity-contracts/docker/docker.block_committer.Dockerfile @@ -0,0 +1,26 @@ +FROM ghcr.io/fuellabs/fuel-block-committer:v0.4.0 + +ARG ETHEREUM_WALLET_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" +ARG COMMIT_INTERVAL=30 +ARG COMMITER_IP=0.0.0.0 +ARG COMMITER_PORT=8888 +ARG ETHEREUM_CHAIN="hardhat" + +# 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 ETHEREUM_WALLET_KEY="${ETHEREUM_WALLET_KEY}" +ENV COMMIT_INTERVAL="${COMMIT_INTERVAL}" +ENV HOST="${COMMITER_IP}" +ENV PORT="${COMMITER_PORT}" +ENV ETHEREUM_CHAIN="${ETHEREUM_CHAIN}" +EXPOSE ${PORT} + +# copy over script and run +COPY ./docker/block-committer/block-committer.sh . +CMD ["sh", "./block-committer.sh"] \ No newline at end of file diff --git a/packages/solidity-contracts/docker/docker.fuel_core.Dockerfile b/packages/solidity-contracts/docker/docker.fuel_core.Dockerfile new file mode 100644 index 00000000..06899e75 --- /dev/null +++ b/packages/solidity-contracts/docker/docker.fuel_core.Dockerfile @@ -0,0 +1,54 @@ +# # 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.36.0 + +ARG FUEL_IP=0.0.0.0 +ARG FUEL_PORT=4001 +ARG CONSENSUS_KEY_SECRET=$CONSENSUS_KEY_SECRET + +# dependencies +RUN apt update && apt install -y git curl jq && rm -rf /var/lib/apt/lists/* + +# copy chain config +WORKDIR /fuel + +COPY ./docker/fuel_core/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 fetch && git checkout c1c4d3bca57f64118a8d157310e0a839ae5c180a + +# 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/9.json \ + ./latest_consensus_parameters.json +RUN cp /chain-configuration/upgradelog/ignition-devnet/state_transition_function/9.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 ./docker/fuel_core/fuel_core.sh . +CMD ["sh", "./fuel_core.sh"] \ No newline at end of file diff --git a/packages/solidity-contracts/docker/docker.l1_chain.Dockerfile b/packages/solidity-contracts/docker/docker.l1_chain.Dockerfile new file mode 100644 index 00000000..9463feb3 --- /dev/null +++ b/packages/solidity-contracts/docker/docker.l1_chain.Dockerfile @@ -0,0 +1,42 @@ +FROM node:20-alpine AS BUILD_IMAGE + +# dependencies +RUN apk --no-cache add git curl +RUN npm i -g pnpm + +WORKDIR /l1chain/fuel-v2-contracts + +ARG L1_IP=0.0.0.0 +ARG L1_PORT=8545 + +# clone the contracts repo +COPY package.json /l1chain/fuel-v2-contracts/ + +# copy over the fuel chain and replace consts values + +# build the ethereum contracts and environment +RUN pnpm install +COPY contracts/test/PlaceHolder.sol /l1chain/fuel-v2-contracts/contracts/test/PlaceHolder.sol +COPY .env /l1chain/fuel-v2-contracts/ +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 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}" +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/fuel_core/fuel_core.sh b/packages/solidity-contracts/docker/fuel_core/fuel_core.sh new file mode 100644 index 00000000..84acee38 --- /dev/null +++ b/packages/solidity-contracts/docker/fuel_core/fuel_core.sh @@ -0,0 +1,23 @@ +#!/bin/sh +set -euo + +PORTAL_ADDRESS=$(jq -r '.address' /l1chain/fuel-v2-contracts/deployments/localhost/FuelMessagePortal.json) + +echo "Launching fuel node with PORTAL_ADDRESS=$PORTAL_ADDRESS" + +exec /root/fuel-core run \ + --ip $FUEL_IP \ + --port $FUEL_PORT \ + --db-type in-memory \ + --utxo-validation \ + --vm-backtrace \ + --enable-relayer \ + --relayer $L1_CHAIN_HTTP \ + --relayer-v2-listening-contracts $PORTAL_ADDRESS \ + --relayer-da-deploy-height 0 \ + --poa-interval-period 1sec \ + --debug \ + --min-gas-price 0 \ + --snapshot ./ + +echo "Launched fuel node" \ No newline at end of file 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/l1_chain.sh b/packages/solidity-contracts/docker/l1-chain/l1_chain.sh new file mode 100644 index 00000000..fbb41f7e --- /dev/null +++ b/packages/solidity-contracts/docker/l1-chain/l1_chain.sh @@ -0,0 +1,25 @@ +#!/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" + +pnpm run node +# 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." + + diff --git a/packages/solidity-contracts/hardhat.config.ts b/packages/solidity-contracts/hardhat.config.ts index 2815dc62..60f3e6ce 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: { diff --git a/packages/solidity-contracts/package.json b/packages/solidity-contracts/package.json index 70087cf7..2bdef0f6 100644 --- a/packages/solidity-contracts/package.json +++ b/packages/solidity-contracts/package.json @@ -20,10 +20,12 @@ "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", + "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", + "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'", @@ -52,6 +54,7 @@ "@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", diff --git a/packages/solidity-contracts/scripts/hardhat/index.ts b/packages/solidity-contracts/scripts/hardhat/index.ts index 422b3bfd..b3fff332 100644 --- a/packages/solidity-contracts/scripts/hardhat/index.ts +++ b/packages/solidity-contracts/scripts/hardhat/index.ts @@ -12,4 +12,5 @@ export * from './withdrawalResume'; export * from './withdrawalBlacklist'; export * from './withdrawalWhitelist'; export * from './verifyMainnetDeployment'; +export * from './simulateUpgrades'; export * from './eventFilter'; diff --git a/packages/solidity-contracts/scripts/hardhat/simulateUpgrades.ts b/packages/solidity-contracts/scripts/hardhat/simulateUpgrades.ts new file mode 100644 index 00000000..9b62f531 --- /dev/null +++ b/packages/solidity-contracts/scripts/hardhat/simulateUpgrades.ts @@ -0,0 +1,140 @@ +import { task } from 'hardhat/config'; +import { HardhatRuntimeEnvironment } from 'hardhat/types'; +import { ContractFactory, ethers } from 'ethers'; + +const SECURITY_COUNCIL_MULTISIG = '0x32da601374b38154f05904B16F44A1911Aa6f314'; +const ETH_BALANCE_TO_SET = '0xDE0B6B3A7640000'; // 1 ether +const GAS_AMOUNT = '0x7a1200'; +const GAS_PRICE = '0xF4241'; + +task( + 'simulate-upgrades', + 'Mocks proxy upgrades with tenderly simulation' +).setAction( + async (taskArgs: any, hre: HardhatRuntimeEnvironment): Promise => { + const network = hre.network.name; + + if (network !== 'mainnet') { + return; + } + + const { + upgrades: { prepareUpgrade }, + ethers, + } = hre; + + console.log( + `Mocking proxy upgrades on ${network}:${hre.network.config.chainId}...` + ); + + const signer = await ethers.provider.getSigner(0); + + // fund the signer for deploying the new implementation + const setBalancePayload = { + jsonrpc: '2.0', + method: 'tenderly_setBalance', + params: [[(await signer.getAddress()).toString()], ETH_BALANCE_TO_SET], + }; + + const rpc: string = process.env.RPC_URL!; + + const deployments = await hre.deployments.all(); + + const apiUrl = `https://api.tenderly.co/api/v1/account/fuel-network/project/preprod/vnets/${process.env.VNET_ID}/transactions/simulate`; + const accessKey = process.env.ACCESS_KEY; + + try { + await fetch(rpc, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(setBalancePayload), + }); + + for (const [contractName, deployment] of Object.entries(deployments)) { + if (deployment.abi.length == 0) continue; + + // mocking the deployment for the new implementation too. + // we currently assume that this script will be ran, after the `upgradeVerification` script is executed so we have access to the updated `constructorArgs`, otherwise we can allow the contructor arguments to be entered manually. + const factory = (await ethers.getContractFactory( + deployment.linkedData.factory + )) as ContractFactory; // Typing bug in `getContractFactory` + + const deploymentResponse = (await prepareUpgrade( + deployment.address, + factory, + { + kind: 'uups', + constructorArgs: deployment.linkedData.constructorArgs, + getTxResponse: true, + redeployImplementation: 'always', + } + )) as ethers.TransactionResponse; + + const receipt = await hre.ethers.provider.getTransactionReceipt( + deploymentResponse.hash + ); + + const newImplementationAddress = receipt?.contractAddress!; + + const proxyContractInstance = new ethers.Contract( + deployment.address, + deployment.abi + ); + + // simulating upgrade enables to impersonate the security council multisig, without the need of signatures + const encodedUpgradeData = + proxyContractInstance.interface.encodeFunctionData('upgradeTo', [ + newImplementationAddress, + ]); + + const upgradeImplementationPayload = { + callArgs: { + from: SECURITY_COUNCIL_MULTISIG, + to: deployment.address, + gas: GAS_AMOUNT, + gasPrice: GAS_PRICE, + value: '0x0', + data: encodedUpgradeData, + }, + blockNumber: 'latest', + }; + + const response = await fetch(apiUrl, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Access-Key': accessKey, + }, + body: JSON.stringify(upgradeImplementationPayload), + } as any); // typing bug with the request headers + + // simulations don't result in visible state changes so not checking the new implementation here, so instead we check + // the event logs that the `Upgraded` event was emitted + if (response.ok) { + const responsePayload: any = await response.json(); + + if (responsePayload.logs[0].name === 'Upgraded') { + console.log( + `✅ Upgrade simulation successful for ${contractName} (${deployment.address})` + ); + } else { + console.log( + `❌ Upgrade simulation failed for ${contractName} (${deployment.address})` + ); + throw new Error('Upgrade simulation failed'); + } + } else { + console.log( + `❌ Upgrade simulation failed for ${contractName} (${deployment.address})` + ); + throw new Error('Upgrade simulation failed'); + } + } + } catch (error) { + console.log(`❌ Upgrade simulation failed: ${error}`); + } + } +); 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/pnpm-lock.yaml b/pnpm-lock.yaml index 2840c14c..9c5882e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -193,6 +193,9 @@ importers: '@typescript-eslint/parser': specifier: ^5.43.0 version: 5.62.0(eslint@8.57.0)(typescript@4.9.5) + axios: + specifier: ^1.7.7 + version: 1.7.7(debug@4.3.4) chai: specifier: ^4.3.7 version: 4.4.1 @@ -2557,8 +2560,8 @@ packages: resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} engines: {node: '>=4'} - axios@1.7.5: - resolution: {integrity: sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==} + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} axobject-query@3.2.1: resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} @@ -3298,11 +3301,13 @@ packages: eslint@5.16.0: resolution: {integrity: sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==} engines: {node: ^6.14.0 || ^8.10.0 || >=9.10.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@5.0.1: @@ -7625,7 +7630,7 @@ snapshots: '@openzeppelin/defender-admin-client@1.54.1(bufferutil@4.0.5)(debug@4.3.4)(utf-8-validate@5.0.7)': dependencies: '@openzeppelin/defender-base-client': 1.54.1(debug@4.3.4) - axios: 1.7.5(debug@4.3.4) + axios: 1.7.7(debug@4.3.4) ethers: 5.7.2(bufferutil@4.0.5)(utf-8-validate@5.0.7) lodash: 4.17.21 node-fetch: 2.7.0 @@ -7639,7 +7644,7 @@ snapshots: dependencies: amazon-cognito-identity-js: 6.3.12 async-retry: 1.3.3 - axios: 1.7.5(debug@4.3.4) + axios: 1.7.7(debug@4.3.4) lodash: 4.17.21 node-fetch: 2.7.0 transitivePeerDependencies: @@ -7656,7 +7661,7 @@ snapshots: '@openzeppelin/defender-sdk-deploy-client@1.12.0(debug@4.3.4)': dependencies: '@openzeppelin/defender-sdk-base-client': 1.12.0 - axios: 1.7.5(debug@4.3.4) + axios: 1.7.7(debug@4.3.4) lodash: 4.17.21 transitivePeerDependencies: - debug @@ -7665,7 +7670,7 @@ snapshots: '@openzeppelin/defender-sdk-network-client@1.12.0(debug@4.3.4)': dependencies: '@openzeppelin/defender-sdk-base-client': 1.12.0 - axios: 1.7.5(debug@4.3.4) + axios: 1.7.7(debug@4.3.4) lodash: 4.17.21 transitivePeerDependencies: - debug @@ -8797,7 +8802,7 @@ snapshots: axe-core@4.7.0: {} - axios@1.7.5(debug@4.3.4): + axios@1.7.7(debug@4.3.4): dependencies: follow-redirects: 1.15.6(debug@4.3.4) form-data: 4.0.0 @@ -10464,7 +10469,7 @@ snapshots: '@ethersproject/transactions': 5.7.0 '@ethersproject/wallet': 5.7.0 '@types/qs': 6.9.15 - axios: 1.7.5(debug@4.3.4) + axios: 1.7.7(debug@4.3.4) chalk: 4.1.2 chokidar: 3.6.0 debug: 4.3.4(supports-color@8.1.1)