diff --git a/.changeset/chatty-glasses-buy.md b/.changeset/chatty-glasses-buy.md deleted file mode 100644 index 1f9cd06cc..000000000 --- a/.changeset/chatty-glasses-buy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Add CLI e2e typescript tests diff --git a/.changeset/cold-dingos-give.md b/.changeset/cold-dingos-give.md new file mode 100644 index 000000000..d9eaf08d3 --- /dev/null +++ b/.changeset/cold-dingos-give.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': patch +--- + +Optimize HyperlaneRelayer routing config derivation diff --git a/.changeset/config.json b/.changeset/config.json index 03a168c16..d72044ed2 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -1,8 +1,8 @@ { "$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json", "changelog": "@changesets/cli/changelog", - "commit": false, - "fixed": [["@hyperlane-xyz/*"]], + "commit": true, + "fixed": [["@hyperlane-xyz/!(core)|*"]], "linked": [], "access": "public", "baseBranch": "main", diff --git a/.changeset/cuddly-baboons-drive.md b/.changeset/cuddly-baboons-drive.md new file mode 100644 index 000000000..b5a02fb3d --- /dev/null +++ b/.changeset/cuddly-baboons-drive.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/sdk': minor +'@hyperlane-xyz/core': minor +--- + +Checking for sufficient fees in `AbstractMessageIdAuthHook` and refund surplus diff --git a/.changeset/dirty-swans-drum.md b/.changeset/dirty-swans-drum.md new file mode 100644 index 000000000..963f91b39 --- /dev/null +++ b/.changeset/dirty-swans-drum.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/utils': patch +'@hyperlane-xyz/sdk': patch +--- + +Dedupe internals of hook and ISM module deploy code diff --git a/.changeset/dull-days-yell.md b/.changeset/dull-days-yell.md deleted file mode 100644 index 4ad4fb61d..000000000 --- a/.changeset/dull-days-yell.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@hyperlane-xyz/infra': minor -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor -'@hyperlane-xyz/core': minor ---- - -Added sdk support for Stake weighted ISM diff --git a/.changeset/fresh-pigs-work.md b/.changeset/fresh-pigs-work.md new file mode 100644 index 000000000..a43a7e339 --- /dev/null +++ b/.changeset/fresh-pigs-work.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Deploy to apechain, arbitrumnova, b3, fantom, gravity, harmony, kaia, morph, orderly, snaxchain, zeronetwork, zksync. Update default metadata in `HyperlaneCore` to `0x00001` to ensure empty metadata does not break on zksync. diff --git a/.changeset/giant-items-suffer.md b/.changeset/giant-items-suffer.md deleted file mode 100644 index 53b286689..000000000 --- a/.changeset/giant-items-suffer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Enroll new validators for cyber degenchain kroma lisk lukso merlin metis mint proofofplay real sanko tangle xai taiko diff --git a/.changeset/gorgeous-dragons-swim.md b/.changeset/gorgeous-dragons-swim.md deleted file mode 100644 index 4888ca7e1..000000000 --- a/.changeset/gorgeous-dragons-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': patch ---- - -Estimate and add 10% gas bump for ICA initialization and enrollment diff --git a/.changeset/healthy-boats-lie.md b/.changeset/healthy-boats-lie.md new file mode 100644 index 000000000..2eb1bb0a4 --- /dev/null +++ b/.changeset/healthy-boats-lie.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/utils': patch +--- + +fix median utils func + add test diff --git a/.changeset/tricky-horses-repair.md b/.changeset/long-queens-deny.md similarity index 51% rename from .changeset/tricky-horses-repair.md rename to .changeset/long-queens-deny.md index b54639407..9ffa67201 100644 --- a/.changeset/tricky-horses-repair.md +++ b/.changeset/long-queens-deny.md @@ -3,4 +3,4 @@ '@hyperlane-xyz/sdk': minor --- -Add ChainSubmissionStrategySchema +Add feat to allow updates to destination gas using warp apply diff --git a/.changeset/neat-sloths-agree.md b/.changeset/neat-sloths-agree.md new file mode 100644 index 000000000..979753ebe --- /dev/null +++ b/.changeset/neat-sloths-agree.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/core': minor +--- + +Added msg.value to preverifyMessage to commit it as part of external hook payload diff --git a/.changeset/nice-deers-tan.md b/.changeset/nice-deers-tan.md deleted file mode 100644 index 1acc20ce1..000000000 --- a/.changeset/nice-deers-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': patch ---- - -Support DefaultFallbackRoutingIsm in metadata builder diff --git a/.changeset/plenty-pens-peel.md b/.changeset/plenty-pens-peel.md new file mode 100644 index 000000000..9b69b8003 --- /dev/null +++ b/.changeset/plenty-pens-peel.md @@ -0,0 +1,11 @@ +--- +'@hyperlane-xyz/widgets': minor +--- + +Update widgets with components from explorer and warp ui + +- Add icons: Discord, Docs, Github, History, LinkedIn, Medium, Twitter, Wallet and Web +- Add animation component: Fade component +- Add components: DatetimeField and SelectField +- New stories: IconList and Fade +- Add "Icon" suffix for icons that did not have it diff --git a/.changeset/red-nails-trade.md b/.changeset/red-nails-trade.md deleted file mode 100644 index 42981e08f..000000000 --- a/.changeset/red-nails-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor ---- - -Sorted cwNative funds by denom in transfer tx diff --git a/.changeset/silent-berries-attend.md b/.changeset/silent-berries-attend.md new file mode 100644 index 000000000..2b6b43e5f --- /dev/null +++ b/.changeset/silent-berries-attend.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/core': minor +--- + +disabled the ICARouter's ability to change hook given that the user doesn't expect the hook to change after they deploy their ICA account. Hook is not part of the derivation like ism on the destination chain and hence, cannot be configured custom by the user. diff --git a/.changeset/sixty-eggs-smoke.md b/.changeset/sixty-eggs-smoke.md new file mode 100644 index 000000000..24906d7c6 --- /dev/null +++ b/.changeset/sixty-eggs-smoke.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Enable configuration of IGP hooks in the CLI diff --git a/.changeset/stale-planets-dance.md b/.changeset/stale-planets-dance.md deleted file mode 100644 index 5da4b82e7..000000000 --- a/.changeset/stale-planets-dance.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor -'@hyperlane-xyz/core': minor ---- - -ArbL2ToL1Ism handles value via the executeTransaction branch diff --git a/.changeset/sweet-houses-type.md b/.changeset/sweet-houses-type.md new file mode 100644 index 000000000..27fa3feeb --- /dev/null +++ b/.changeset/sweet-houses-type.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': patch +--- + +Fix ICA ISM self relay diff --git a/.changeset/thin-tips-explain.md b/.changeset/thin-tips-explain.md new file mode 100644 index 000000000..330e57a8a --- /dev/null +++ b/.changeset/thin-tips-explain.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/sdk': minor +--- + +Introduce utils that can be reused by the CLI and Infra for fetching token prices from Coingecko and gas prices from EVM/Cosmos chains. diff --git a/.changeset/tidy-meals-add.md b/.changeset/tidy-meals-add.md new file mode 100644 index 000000000..2d1ba70ca --- /dev/null +++ b/.changeset/tidy-meals-add.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/utils': patch +--- + +Filter undefined/null values in invertKeysAndValues function diff --git a/.changeset/warm-grapes-talk.md b/.changeset/warm-grapes-talk.md deleted file mode 100644 index 75c996c8b..000000000 --- a/.changeset/warm-grapes-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': patch ---- - -Improved check for mailbox initialization diff --git a/.changeset/wicked-knives-care.md b/.changeset/wicked-knives-care.md deleted file mode 100644 index 04e60a98e..000000000 --- a/.changeset/wicked-knives-care.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -Add Safe submit functionality to warp apply diff --git a/.codespell/.codespellrc b/.codespell/.codespellrc index c21a79e68..1b6b585fd 100644 --- a/.codespell/.codespellrc +++ b/.codespell/.codespellrc @@ -1,5 +1,5 @@ [codespell] -skip = .git,node_modules,yarn.lock,Cargo.lock,./typescript/helloworld,./rust/config,./rust/sealevel/environments/mainnet3/chain-config.json +skip = .git,node_modules,yarn.lock,Cargo.lock,./typescript/helloworld,./rust/main/config,./rust/sealevel/environments/mainnet3/chain-config.json count = quiet-level = 3 ignore-words = ./.codespell/ignore.txt diff --git a/.gitattributes b/.gitattributes index d061a18bd..eb647bb3b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ typescript/sdk/src/cw-types/*.types.ts linguist-generated=true -rust/chains/hyperlane-ethereum/abis/*.abi.json linguist-generated=true +rust/main/chains/hyperlane-ethereum/abis/*.abi.json linguist-generated=true solidity/contracts/interfaces/avs/*.sol linguist-vendored=true solidity/contracts/avs/ECDSA*.sol linguist-vendored=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ab8ed10d1..314d1e01d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,29 +1,22 @@ # File extension owners -*.sol @yorhodes @tkporter @aroralanuk @nbayindirli -*.ts @yorhodes @jmrossy @nbayindirli -*.rs @tkporter @daniel-savu -*.md @Skunkchain @avious00 +*.sol @yorhodes @aroralanuk @ltyu +*.ts @yorhodes @jmrossy +*.rs @tkporter @daniel-savu @ameten # Package owners ## Contracts -solidity/ @yorhodes @tkporter @aroralanuk @nbayindirli +solidity/ @yorhodes @tkporter @aroralanuk @ltyu ## Agents rust/ @tkporter @daniel-savu ## SDK -typescript/sdk @yorhodes @jmrossy - -## Token -typescript/token @yorhodes @jmrossy @tkporter @aroralanuk @nbayindirli - -## Hello World -typescript/helloworld @yorhodes +typescript/sdk @yorhodes @jmrossy @ltyu @paulbalaji ## CLI -typescript/cli @jmrossy @yorhodes @aroralanuk @nbayindirli +typescript/cli @jmrossy @yorhodes @ltyu ## Infra -typescript/infra @tkporter +typescript/infra @tkporter @paulbalaji @Mo-Hussain diff --git a/.github/actions/yarn-build-with-cache/action.yml b/.github/actions/yarn-build-with-cache/action.yml new file mode 100644 index 000000000..2aec33acc --- /dev/null +++ b/.github/actions/yarn-build-with-cache/action.yml @@ -0,0 +1,37 @@ +name: 'Yarn Build with Cache' +description: 'Run yarn build using yarn cache' + +inputs: + ref: + description: 'The Git ref to checkout' + required: true + +runs: + using: "composite" + steps: + - name: Cache + uses: buildjet/cache@v4 + id: cache + with: + path: | + **/node_modules + .yarn + key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} + + # Typically, the cache will be hit, but if there's a network error when + # restoring the cache, let's run the install step ourselves. + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + shell: bash + run: | + yarn install + CHANGES=$(git status -s --ignore-submodules) + if [[ ! -z $CHANGES ]]; then + echo "Changes found: $CHANGES" + git diff + exit 1 + fi + + - name: Build + shell: bash + run: yarn build diff --git a/.github/workflows/agent-release-artifacts.yml b/.github/workflows/agent-release-artifacts.yml index f54822817..ee27483c8 100644 --- a/.github/workflows/agent-release-artifacts.yml +++ b/.github/workflows/agent-release-artifacts.yml @@ -16,7 +16,7 @@ env: jobs: prepare: - runs-on: larger-runner + runs-on: ubuntu-latest outputs: tag_date: ${{ steps.taggen.outputs.TAG_DATE }} tag_sha: ${{ steps.taggen.outputs.TAG_SHA }} @@ -58,30 +58,28 @@ jobs: linker = "aarch64-linux-gnu-gcc" EOF - name: setup rust - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - profile: minimal target: ${{ matrix.TARGET }} - name: setup target run: rustup target add ${{ matrix.TARGET }} - working-directory: ./rust + working-directory: ./rust/main - name: build run: cargo build --release --target ${{ matrix.TARGET }} --bin relayer --bin scraper --bin validator - working-directory: ./rust + working-directory: ./rust/main - name: make executable if: ${{ matrix.OS == 'larger-runner' || matrix.OS == 'macos-latest' }} run: chmod ug+x,-w relayer scraper validator - working-directory: rust/target/${{ matrix.TARGET }}/release + working-directory: rust/main/target/${{ matrix.TARGET }}/release - name: upload binaries uses: actions/upload-artifact@v4 with: name: ${{ matrix.TARGET }}-${{ needs.prepare.outputs.tag_sha }}-${{ needs.prepare.outputs.tag_date }} path: | - rust/target/${{ matrix.TARGET }}/release/relayer - rust/target/${{ matrix.TARGET }}/release/relayer.exe - rust/target/${{ matrix.TARGET }}/release/scraper - rust/target/${{ matrix.TARGET }}/release/scraper.exe - rust/target/${{ matrix.TARGET }}/release/validator - rust/target/${{ matrix.TARGET }}/release/validator.exe + rust/main/target/${{ matrix.TARGET }}/release/relayer + rust/main/target/${{ matrix.TARGET }}/release/relayer.exe + rust/main/target/${{ matrix.TARGET }}/release/scraper + rust/main/target/${{ matrix.TARGET }}/release/scraper.exe + rust/main/target/${{ matrix.TARGET }}/release/validator + rust/main/target/${{ matrix.TARGET }}/release/validator.exe if-no-files-found: error diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 8848a92bf..aacb0bac5 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -21,15 +21,13 @@ jobs: - name: Checkout the repository uses: actions/checkout@v4 - - name: pip cache - uses: actions/cache@v4 + - name: Setup python + uses: actions/setup-python@v5 with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: ${{ runner.os }}-pip- + python-version: '3.x' - - name: Install prerequisites - run: sudo pip install -r ./.codespell/requirements.txt + - name: Install codespell requirements + run: pip install -r ./.codespell/requirements.txt - name: Spell check - run: codespell --config=./.codespell/.codespellrc \ No newline at end of file + run: codespell --config=./.codespell/.codespellrc diff --git a/.github/workflows/monorepo-docker.yml b/.github/workflows/monorepo-docker.yml index 27690bede..e697aae22 100644 --- a/.github/workflows/monorepo-docker.yml +++ b/.github/workflows/monorepo-docker.yml @@ -29,7 +29,7 @@ jobs: GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} if: "${{ env.GCLOUD_SERVICE_KEY != '' }}" # runs if GCLOUD_SERVICE_KEY is defined, so we set the output to true - run: echo "::set-output name=defined::true" + run: echo "defined=true" >> $GITHUB_OUTPUT build-and-push-to-gcr: runs-on: ubuntu-latest @@ -77,7 +77,7 @@ jobs: echo "REGISTRY_VERSION=$REGISTRY_VERSION" >> $GITHUB_ENV - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ./ file: ./Dockerfile diff --git a/.github/workflows/rust-docker.yml b/.github/workflows/rust-docker.yml index bc70a1f86..a1fed4f55 100644 --- a/.github/workflows/rust-docker.yml +++ b/.github/workflows/rust-docker.yml @@ -24,10 +24,10 @@ jobs: GCLOUD_SERVICE_KEY: ${{ secrets.GCLOUD_SERVICE_KEY }} if: "${{ env.GCLOUD_SERVICE_KEY != '' }}" # runs if GCLOUD_SERVICE_KEY is defined, so we set the output to true - run: echo "::set-output name=defined::true" + run: echo "defined=true" >> $GITHUB_OUTPUT build-and-push-to-gcr: - runs-on: ubuntu-latest + runs-on: buildjet-8vcpu-ubuntu-2204 # uses check-env to determine if secrets.GCLOUD_SERVICE_KEY is defined needs: [check-env] @@ -63,12 +63,10 @@ jobs: username: _json_key password: ${{ secrets.GCLOUD_SERVICE_KEY }} - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . file: ./rust/Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/rust-skipped.yml b/.github/workflows/rust-skipped.yml index e4d00219a..d6900af9a 100644 --- a/.github/workflows/rust-skipped.yml +++ b/.github/workflows/rust-skipped.yml @@ -4,16 +4,18 @@ name: rust on: push: branches: [main] - paths-ignore: - - 'rust/**' - - .github/workflows/rust.yml + paths: + - '**/*' + - '!rust/main/**' + - '!rust/sealevel/**' + - '!.github/workflows/rust.yml' pull_request: branches: [main] - paths-ignore: - - 'rust/**' - - .github/workflows/rust.yml - # Support for merge queues - merge_group: + paths: + - '**/*' + - '!rust/main/**' + - '!rust/sealevel/**' + - '!.github/workflows/rust.yml' env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 62d0c718a..c0431b4e9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,19 +1,23 @@ name: rust on: + # Triggers the workflow on pushes to main branch + # only if rust/** or .github/workflows/rust.yml is changed push: branches: [main] paths: - - 'rust/**' + - 'rust/main/**' + - 'rust/sealevel/**' - .github/workflows/rust.yml + # Triggers the workflow on pull requests + # only if rust/** or .github/workflows/rust.yml is changed pull_request: branches: [main] paths: - - 'rust/**' + - 'rust/main/**' + - 'rust/sealevel/**' - .github/workflows/rust.yml - # Support for merge queues merge_group: - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: concurrency: @@ -24,71 +28,84 @@ env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full -defaults: - run: - working-directory: ./rust - jobs: test-rs: - runs-on: larger-runner - + runs-on: buildjet-8vcpu-ubuntu-2204 steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal + - uses: dtolnay/rust-toolchain@stable - name: rust cache uses: Swatinem/rust-cache@v2 with: - prefix-key: "v3-rust" + prefix-key: 'v3' + shared-key: 'rust' + cache-provider: 'buildjet' + save-if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} workspaces: | - ./rust + ./rust/main + ./rust/sealevel - name: Free disk space run: | - # Based on https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 sudo rm -rf /usr/share/dotnet sudo rm -rf /opt/ghc sudo rm -rf "/usr/local/share/boost" sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - name: Run tests + - name: Run tests for main workspace + run: cargo test + working-directory: ./rust/main + - name: Run tests for sealevel workspace run: cargo test + working-directory: ./rust/sealevel lint-rs: - runs-on: larger-runner - + runs-on: buildjet-8vcpu-ubuntu-2204 steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - - uses: actions-rs/toolchain@v1 + - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - profile: minimal components: rustfmt, clippy target: wasm32-unknown-unknown - name: rust cache uses: Swatinem/rust-cache@v2 with: - prefix-key: "v3-rust" + prefix-key: 'v3' + shared-key: 'rust' + cache-provider: 'buildjet' + save-if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} workspaces: | - ./rust + ./rust/main + ./rust/sealevel - name: Free disk space run: | - # Based on https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 sudo rm -rf /usr/share/dotnet sudo rm -rf /opt/ghc sudo rm -rf "/usr/local/share/boost" sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - name: Check + - name: Check for main workspace + run: cargo check --release --all-features --all-targets + working-directory: ./rust/main + - name: Check for sealevel workspace run: cargo check --release --all-features --all-targets - - name: Rustfmt + working-directory: ./rust/sealevel + - name: Rustfmt for main workspace run: cargo fmt --all -- --check - - name: Clippy + working-directory: ./rust/main + - name: Rustfmt for sealevel workspace + run: cargo fmt --all --check + working-directory: ./rust/sealevel + - name: Clippy for main workspace + run: cargo clippy -- -D warnings + working-directory: ./rust/main + - name: Clippy for sealevel workspace run: cargo clippy -- -D warnings - - name: Setup WASM + working-directory: ./rust/sealevel + - name: Setup WASM for main workspace run: rustup target add wasm32-unknown-unknown - - name: Check WASM + working-directory: ./rust/main + - name: Check WASM for hyperlane-core run: cargo check --release -p hyperlane-core --features=strum,test-utils --target wasm32-unknown-unknown + working-directory: ./rust/main diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index a3eba2c88..4fde41968 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -24,12 +24,14 @@ jobs: submodules: recursive - name: yarn-cache - uses: actions/cache@v4 + uses: buildjet/cache@v4 with: path: | **/node_modules .yarn key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-cache- - name: yarn-install run: yarn install @@ -54,3 +56,18 @@ jobs: uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.slither.outputs.sarif }} + category: "slither" + + - name: Olympix Integrated Security + uses: olympix/integrated-security@main + env: + OLYMPIX_API_TOKEN: ${{ secrets.OLYMPIX_API_TOKEN }} + OLYMPIX_CLI_LOG_LEVEL: 0 + with: + args: -p ./solidity/contracts --output-format sarif --output-path ./ + + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: olympix.sarif + category: "olympix" diff --git a/.github/workflows/storage-analysis.yml b/.github/workflows/storage-analysis.yml index 70e77f0dd..979c23fbe 100644 --- a/.github/workflows/storage-analysis.yml +++ b/.github/workflows/storage-analysis.yml @@ -24,12 +24,14 @@ jobs: node-version: 18 - name: yarn-cache - uses: actions/cache@v4 + uses: buildjet/cache@v4 with: path: | **/node_modules .yarn key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-cache- - name: yarn-install run: yarn install diff --git a/.github/workflows/test-skipped.yml b/.github/workflows/test-skipped.yml deleted file mode 100644 index 6b3f3b081..000000000 --- a/.github/workflows/test-skipped.yml +++ /dev/null @@ -1,106 +0,0 @@ -name: test - -on: - push: - branches: [main] - paths: - - '*.md' - - '!**/*' - pull_request: - branches: - - '*' - paths: - - '*.md' - - '!**/*' - merge_group: - -concurrency: - group: e2e-${{ github.ref }} - cancel-in-progress: ${{ github.ref_name != 'main' }} - -jobs: - yarn-install: - runs-on: ubuntu-latest - steps: - - name: Instant pass - run: echo "yarn-install job passed" - - yarn-build: - runs-on: ubuntu-latest - steps: - - name: Instant pass - run: echo "yarn-build job passed" - - lint-prettier: - runs-on: ubuntu-latest - steps: - - name: Instant pass - run: echo "lint-prettier job passed" - - yarn-test: - runs-on: ubuntu-latest - steps: - - name: Instant pass - run: echo "yarn-test job passed" - - agent-configs: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - environment: [mainnet3, testnet4] - steps: - - name: Instant pass - run: echo "agent-configs job passed" - - e2e-matrix: - runs-on: ubuntu-latest - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' - strategy: - matrix: - e2e-type: [cosmwasm, non-cosmwasm] - steps: - - name: Instant pass - run: echo "e2e-matrix job passed" - - e2e: - runs-on: ubuntu-latest - if: always() - steps: - - name: Instant pass - run: echo "e2e job passed" - - cli-advanced-e2e: - runs-on: ubuntu-latest - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' - strategy: - matrix: - include: - - test-type: preset_hook_enabled - - test-type: configure_hook_enabled - - test-type: pi_with_core_chain - steps: - - name: Instant pass - run: echo "cli-advanced-e2e job passed" - - env-test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - environment: [mainnet3] - chain: [ethereum, arbitrum, optimism, inevm, viction] - module: [core, igp] - include: - - environment: testnet4 - chain: sepolia - module: core - steps: - - name: Instant pass - run: echo "env-test job passed" - - coverage: - runs-on: ubuntu-latest - steps: - - name: Instant pass - run: echo "coverage job passed" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6305e22df..92d203446 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,16 +1,14 @@ name: test on: - # Triggers the workflow on pushes to main branch + # Triggers the workflow on pushes to main branch, ignoring md files push: branches: [main] - # Triggers on pull requests ignoring md files + # Triggers on pull requests, ignoring md files pull_request: branches: - - '*' # run against all branches - # Support for merge queues + - '*' merge_group: - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: concurrency: @@ -37,12 +35,14 @@ jobs: submodules: recursive - name: yarn-cache - uses: actions/cache@v4 + uses: buildjet/cache@v4 with: path: | **/node_modules .yarn key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn-cache- - name: yarn-install run: | @@ -54,35 +54,6 @@ jobs: exit 1 fi - yarn-build: - runs-on: ubuntu-latest - needs: [yarn-install] - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - submodules: recursive - fetch-depth: 0 - - - name: yarn-cache - uses: actions/cache@v4 - with: - path: | - **/node_modules - .yarn - key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - - - name: build-cache - uses: actions/cache@v4 - with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: build - run: yarn build - lint-prettier: runs-on: ubuntu-latest needs: [yarn-install] @@ -94,12 +65,13 @@ jobs: fetch-depth: 0 - name: yarn-cache - uses: actions/cache@v4 + uses: buildjet/cache@v4 with: path: | **/node_modules .yarn key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} + fail-on-cache-miss: true - name: lint run: yarn lint @@ -115,7 +87,7 @@ jobs: yarn-test: runs-on: ubuntu-latest - needs: [yarn-build] + needs: [yarn-install] steps: - uses: actions/checkout@v4 with: @@ -126,13 +98,10 @@ jobs: - name: foundry-install uses: foundry-rs/foundry-toolchain@v1 - - name: build-cache - uses: actions/cache@v4 + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -140,9 +109,33 @@ jobs: - name: Unit Tests run: yarn test:ci + cli-e2e: + runs-on: ubuntu-latest + needs: [yarn-install] + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + submodules: recursive + fetch-depth: 0 + + - name: foundry-install + uses: foundry-rs/foundry-toolchain@v1 + + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Checkout registry + uses: ./.github/actions/checkout-registry + + - name: CLI e2e tests + run: yarn --cwd typescript/cli test:e2e + agent-configs: runs-on: ubuntu-latest - needs: [yarn-build] + needs: [yarn-install] strategy: fail-fast: false matrix: @@ -153,21 +146,10 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 - - name: yarn-cache - uses: actions/cache@v4 + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache with: - path: | - **/node_modules - .yarn - key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - - - name: build-cache - uses: actions/cache@v4 - with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -183,9 +165,9 @@ jobs: fi e2e-matrix: - runs-on: larger-runner + runs-on: buildjet-8vcpu-ubuntu-2204 if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main' || github.base_ref == 'cli-2.0') || github.event_name == 'merge_group' - needs: [yarn-build] + needs: [yarn-install] strategy: fail-fast: false matrix: @@ -199,23 +181,24 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive + fetch-depth: 0 - name: foundry-install uses: foundry-rs/foundry-toolchain@v1 - name: setup rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal + uses: dtolnay/rust-toolchain@stable - name: rust cache uses: Swatinem/rust-cache@v2 with: - prefix-key: "v1-${{ runner.os }}-rust-cache" + prefix-key: 'v2-rust-e2e' shared-key: ${{ matrix.e2e-type }} + cache-provider: 'buildjet' + save-if: ${{ !startsWith(github.ref, 'refs/heads/gh-readonly-queue') }} workspaces: | - ./rust + ./rust/main + ${{ matrix.e2e-type == 'non-cosmwasm' && './rust/sealevel' || '' }} - name: Free disk space run: | @@ -231,21 +214,10 @@ jobs: mold-version: 2.0.0 make-default: true - - name: yarn-cache - uses: actions/cache@v4 + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache with: - path: | - **/node_modules - .yarn - key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - - - name: build-cache - uses: actions/cache@v4 - with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Install system dependencies run: | @@ -258,151 +230,30 @@ jobs: - name: agent tests (CosmWasm) run: cargo test --release --package run-locally --bin run-locally --features cosmos test-utils -- cosmos::test --nocapture if: matrix.e2e-type == 'cosmwasm' - working-directory: ./rust + working-directory: ./rust/main env: RUST_BACKTRACE: 'full' + - name: Check for Rust file changes + id: check-rust-changes + run: | + if [[ -n "$(git diff ${{ github.event.pull_request.head.sha || github.sha }} ${{ github.event.pull_request.base.sha }} -- ./rust)" ]]; then + echo "rust_changes=true" >> $GITHUB_OUTPUT + echo "$(git diff ${{ github.event.pull_request.head.sha || github.sha }} ${{ github.event.pull_request.base.sha }} -- ./rust)" + else + echo "rust_changes=false" >> $GITHUB_OUTPUT + fi + - name: agent tests (EVM and Sealevel) run: cargo run --release --bin run-locally --features test-utils if: matrix.e2e-type == 'non-cosmwasm' - working-directory: ./rust + working-directory: ./rust/main env: E2E_CI_MODE: 'true' E2E_CI_TIMEOUT_SEC: '600' E2E_KATHY_MESSAGES: '20' RUST_BACKTRACE: 'full' - - e2e: - runs-on: ubuntu-latest - needs: [e2e-matrix] - if: always() # This ensures that the job runs even if the e2e jobs fail - steps: - - name: Report Matrix Result - run: | - echo "All e2e-matrix jobs have completed." - # You can add additional commands here to report the result as needed - - prebuild-cli-e2e: - runs-on: larger-runner - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main' || github.base_ref == 'cli-2.0') || github.event_name == 'merge_group' - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - submodules: recursive - - - name: setup rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - - - name: rust cache - uses: Swatinem/rust-cache@v2 - with: - prefix-key: "v1-${{ runner.os }}-rust-cache" - shared-key: "cli-e2e" - workspaces: | - ./rust - - - name: Free disk space - run: | - # Based on https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - sudo rm -rf "/usr/local/share/boost" - sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - - name: Install mold linker - uses: rui314/setup-mold@v1 - with: - mold-version: 2.0.0 - make-default: true - - - name: Build validator - run: cargo build --bin validator --features test-utils - working-directory: ./rust - env: - RUST_BACKTRACE: 'full' - - - name: Build relayer - run: cargo build --bin relayer --features test-utils - working-directory: ./rust - env: - RUST_BACKTRACE: 'full' - - cli-advanced-e2e: - runs-on: larger-runner - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main' || github.base_ref == 'cli-2.0') || github.event_name == 'merge_group' - needs: [yarn-build, prebuild-cli-e2e] - strategy: - matrix: - include: - - test-type: preset_hook_enabled - - test-type: configure_hook_enabled - - test-type: pi_with_core_chain - steps: - - uses: actions/setup-node@v4 - with: - node-version: 18 - - - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - submodules: recursive - - - name: foundry-install - uses: foundry-rs/foundry-toolchain@v1 - - - name: setup rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - - - name: rust cache - uses: Swatinem/rust-cache@v2 - with: - prefix-key: "v1-${{ runner.os }}-rust-cache" - shared-key: "cli-e2e" - workspaces: | - ./rust - - - name: Free disk space - run: | - # Based on https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 - sudo rm -rf /usr/share/dotnet - sudo rm -rf /opt/ghc - sudo rm -rf "/usr/local/share/boost" - sudo rm -rf "$AGENT_TOOLSDIRECTORY" - - - name: Install mold linker - uses: rui314/setup-mold@v1 - with: - mold-version: 2.0.0 - make-default: true - - - name: yarn-cache - uses: actions/cache@v4 - with: - path: | - **/node_modules - .yarn - key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - - - name: build-cache - uses: actions/cache@v4 - with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Checkout registry - uses: ./.github/actions/checkout-registry - - - name: cli e2e tests - run: ./typescript/cli/ci-advanced-test.sh ${{ matrix.test-type }} + SEALEVEL_ENABLED: ${{ steps.check-rust-changes.outputs.rust_changes }} env-test: runs-on: ubuntu-latest @@ -411,7 +262,7 @@ jobs: MAINNET3_OPTIMISM_RPC_URLS: ${{ secrets.MAINNET3_OPTIMISM_RPC_URLS }} timeout-minutes: 10 - needs: [yarn-build] + needs: [yarn-install] strategy: fail-fast: false matrix: @@ -431,13 +282,10 @@ jobs: - name: foundry-install uses: foundry-rs/foundry-toolchain@v1 - - name: build-cache - uses: actions/cache@v4 + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Checkout registry uses: ./.github/actions/checkout-registry @@ -447,29 +295,17 @@ jobs: coverage: runs-on: ubuntu-latest - needs: [yarn-test] - + needs: [yarn-install] steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 - - name: yarn-cache - uses: actions/cache@v4 - with: - path: | - **/node_modules - .yarn - key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - - - name: build-cache - uses: actions/cache@v4 + - name: yarn-build + uses: ./.github/actions/yarn-build-with-cache with: - path: | - ./* - !./rust - key: ${{ github.event.pull_request.head.sha || github.sha }} + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: foundry-install uses: foundry-rs/foundry-toolchain@v1 diff --git a/.gitignore b/.gitignore index f9892417a..82e68e79d 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,5 @@ yarn-error.log .idea **/*.ignore -.vscode tsconfig.editor.json diff --git a/.husky/pre-commit b/.husky/pre-commit index 8eb8e9c84..9edc7156e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -7,6 +7,9 @@ echo "📝 If you haven't yet, please add a changeset for your changes via 'yarn # if any *.rs files have changed if git diff --staged --exit-code --name-only | grep -q -E ".*\.rs$"; then - echo "Running cargo fmt pre-commit hook" - cargo fmt --all --check --manifest-path rust/Cargo.toml + echo "Running cargo fmt pre-commit hook for rust/main" + cargo fmt --all --check --manifest-path rust/main/Cargo.toml + + echo "Running cargo fmt pre-commit hook for rust/sealevel" + cargo fmt --all --check --manifest-path rust/sealevel/Cargo.toml fi diff --git a/.registryrc b/.registryrc index a7b8f56df..e1877ed1f 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -1d43e33fc84f486d0edf20a9e573f914e53fe94c +302be4817c063629cec70c0b02322b250df71122 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..6cb83cc9f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "rust-analyzer.linkedProjects": [ + "./rust/main/Cargo.toml", + "./rust/sealevel/Cargo.toml", + ], +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 5dcd5686a..6a6af6c32 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,7 @@ COPY typescript/cli/package.json ./typescript/cli/ COPY typescript/infra/package.json ./typescript/infra/ COPY typescript/ccip-server/package.json ./typescript/ccip-server/ COPY typescript/widgets/package.json ./typescript/widgets/ +COPY typescript/github-proxy/package.json ./typescript/github-proxy/ COPY solidity/package.json ./solidity/ RUN yarn install && yarn cache clean diff --git a/mono.code-workspace b/mono.code-workspace index 447d67634..f23838cd6 100644 --- a/mono.code-workspace +++ b/mono.code-workspace @@ -20,6 +20,10 @@ "cSpell.words": [ "hyperlane" ], + "rust-analyzer.linkedProjects": [ + "./rust/main/Cargo.toml", + "./rust/sealevel/Cargo.toml", + ], }, "folders": [ { @@ -33,7 +37,10 @@ "path": "./solidity" }, { - "path": "./rust" + "path": "./rust/main" + }, + { + "path": "./rust/sealevel" } ], "extensions": { @@ -66,6 +73,8 @@ "yoavbls.pretty-ts-errors", // Yaml language support "redhat.vscode-yaml", + // Rust language support + "rust-lang.rust-analyzer" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. "unwantedRecommendations": [] diff --git a/package.json b/package.json index 36ea71a2a..072ac6c78 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "packageManager": "yarn@4.0.2", "private": true, "scripts": { + "agent-configs": "yarn --cwd typescript/infra/ update-agent-config:mainnet3 && yarn --cwd typescript/infra/ update-agent-config:testnet4 && yarn prettier", "build": "yarn workspaces foreach --all --parallel --topological run build", "clean": "yarn workspaces foreach --all --parallel run clean", "prettier": "yarn workspaces foreach --since --parallel run prettier", diff --git a/rust/Dockerfile b/rust/Dockerfile index 75be92001..98f638bb4 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:experimental -FROM rust:1.72.1 as builder +FROM rust:1.80.1 as builder WORKDIR /usr/src # 1a: Prepare for static linking @@ -9,35 +9,34 @@ RUN apt-get update && \ apt-get install -y musl-tools clang && \ rustup target add x86_64-unknown-linux-musl -RUN mkdir rust +RUN mkdir -p rust/main +RUN mkdir -p rust/sealevel # Add workspace to workdir -COPY rust/agents rust/agents -COPY rust/chains rust/chains -COPY rust/hyperlane-base rust/hyperlane-base -COPY rust/hyperlane-core rust/hyperlane-core -COPY rust/hyperlane-test rust/hyperlane-test -COPY rust/ethers-prometheus rust/ethers-prometheus -COPY rust/utils rust/utils +COPY rust/main/agents rust/main/agents +COPY rust/main/chains rust/main/chains +COPY rust/main/hyperlane-base rust/main/hyperlane-base +COPY rust/main/hyperlane-core rust/main/hyperlane-core +COPY rust/main/hyperlane-test rust/main/hyperlane-test +COPY rust/main/ethers-prometheus rust/main/ethers-prometheus +COPY rust/main/utils rust/main/utils COPY rust/sealevel rust/sealevel -COPY rust/Cargo.toml rust/. -COPY rust/Cargo.lock rust/. +COPY rust/main/Cargo.toml rust/main/. +COPY rust/main/Cargo.lock rust/main/. +# Required for VERGEN_GIT_SHA to be populated COPY .git .git -WORKDIR /usr/src/rust +WORKDIR /usr/src/rust/main # Build binaries RUN \ - --mount=id=cargo,type=cache,sharing=locked,target=/usr/src/target \ - --mount=id=cargo-home-registry,type=cache,sharing=locked,target=/usr/local/cargo/registry \ - --mount=id=cargo-home-git,type=cache,sharing=locked,target=/usr/local/cargo/git \ RUSTFLAGS="--cfg tokio_unstable" cargo build --release --bin validator --bin relayer --bin scraper && \ mkdir -p /release && \ - cp /usr/src/rust/target/release/validator /release && \ - cp /usr/src/rust/target/release/relayer /release && \ - cp /usr/src/rust/target/release/scraper /release + cp /usr/src/rust/main/target/release/validator /release && \ + cp /usr/src/rust/main/target/release/relayer /release && \ + cp /usr/src/rust/main/target/release/scraper /release ## 2: Copy the binaries to release image FROM ubuntu:22.04 @@ -49,7 +48,8 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/* WORKDIR /app -COPY rust/config ./config +RUN mkdir -p /app/config +COPY rust/main/config /app/config COPY --from=builder /release/* . RUN chmod 777 /app && \ diff --git a/rust/README.md b/rust/README.md index 0dcba6e26..289d1dc1d 100644 --- a/rust/README.md +++ b/rust/README.md @@ -90,7 +90,7 @@ env $(cat ./config/validator.fuji.env | grep -v "#" | xargs) ./target/debug/vali Clone `hyperlane-registry` repo next to `hyperlane-monorepo` repo. -To perform an automated e2e test of the agents locally, from within the `hyperlane-monorepo/rust` directory, run: +To perform an automated e2e test of the agents locally, from within the `hyperlane-monorepo/rust/main` directory, run: ```bash cargo run --release --bin run-locally @@ -117,7 +117,7 @@ cd rust ### Deploy Procedure -The contract addresses of each deploy can be found in `rust/config`. The agents will +The contract addresses of each deploy can be found in `rust/main/config`. The agents will automatically pull in all configs in this directory. When agents are deployed to point at a new environment, they cease to point at diff --git a/rust/agents/relayer/src/server/message_retry.rs b/rust/agents/relayer/src/server/message_retry.rs deleted file mode 100644 index 3fff125ee..000000000 --- a/rust/agents/relayer/src/server/message_retry.rs +++ /dev/null @@ -1,170 +0,0 @@ -use axum::{ - extract::{Query, State}, - routing, Router, -}; -use derive_new::new; -use hyperlane_core::{ChainCommunicationError, QueueOperation, H256}; -use serde::Deserialize; -use std::str::FromStr; -use tokio::sync::broadcast::Sender; - -const MESSAGE_RETRY_API_BASE: &str = "/message_retry"; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum MessageRetryRequest { - MessageId(H256), - DestinationDomain(u32), -} - -impl PartialEq for &MessageRetryRequest { - fn eq(&self, other: &QueueOperation) -> bool { - match self { - MessageRetryRequest::MessageId(message_id) => message_id == &other.id(), - MessageRetryRequest::DestinationDomain(destination_domain) => { - destination_domain == &other.destination_domain().id() - } - } - } -} - -#[derive(new, Clone)] -pub struct MessageRetryApi { - tx: Sender, -} - -#[derive(Deserialize)] -struct RawMessageRetryRequest { - message_id: Option, - destination_domain: Option, -} - -impl TryFrom for Vec { - type Error = ChainCommunicationError; - - fn try_from(request: RawMessageRetryRequest) -> Result { - let mut retry_requests = Vec::new(); - if let Some(message_id) = request.message_id { - retry_requests.push(MessageRetryRequest::MessageId(H256::from_str(&message_id)?)); - } - if let Some(destination_domain) = request.destination_domain { - retry_requests.push(MessageRetryRequest::DestinationDomain(destination_domain)); - } - Ok(retry_requests) - } -} - -async fn retry_message( - State(tx): State>, - Query(request): Query, -) -> String { - let retry_requests: Vec = match request.try_into() { - Ok(retry_requests) => retry_requests, - // Technically it's bad practice to print the error message to the user, but - // this endpoint is for debugging purposes only. - Err(err) => { - return format!("Failed to parse retry request: {}", err); - } - }; - - if retry_requests.is_empty() { - return "No retry requests found. Please provide either a message_id or destination_domain.".to_string(); - } - - if let Err(err) = retry_requests - .into_iter() - .map(|req| tx.send(req)) - .collect::, _>>() - { - return format!("Failed to send retry request to the queue: {}", err); - } - - "Moved message(s) to the front of the queue".to_string() -} - -impl MessageRetryApi { - pub fn router(&self) -> Router { - Router::new() - .route("/", routing::get(retry_message)) - .with_state(self.tx.clone()) - } - - pub fn get_route(&self) -> (&'static str, Router) { - (MESSAGE_RETRY_API_BASE, self.router()) - } -} - -#[cfg(test)] -mod tests { - use crate::server::ENDPOINT_MESSAGES_QUEUE_SIZE; - - use super::*; - use axum::http::StatusCode; - use ethers::utils::hex::ToHex; - use std::net::SocketAddr; - use tokio::sync::broadcast::{Receiver, Sender}; - - fn setup_test_server() -> (SocketAddr, Receiver) { - let broadcast_tx = Sender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); - let message_retry_api = MessageRetryApi::new(broadcast_tx.clone()); - let (path, retry_router) = message_retry_api.get_route(); - let app = Router::new().nest(path, retry_router); - - // Running the app in the background using a test server - let server = - axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); - let addr = server.local_addr(); - tokio::spawn(server); - - (addr, broadcast_tx.subscribe()) - } - - #[tokio::test] - async fn test_message_id_retry() { - let (addr, mut rx) = setup_test_server(); - - // Create a random message ID - let message_id = H256::random(); - - // Send a GET request to the server - let response = reqwest::get(format!( - "http://{}{}?message_id={}", - addr, - MESSAGE_RETRY_API_BASE, - message_id.encode_hex::() - )) - .await - .unwrap(); - - // Check that the response status code is OK - assert_eq!(response.status(), StatusCode::OK); - - assert_eq!( - rx.try_recv().unwrap(), - MessageRetryRequest::MessageId(message_id) - ); - } - - #[tokio::test] - async fn test_destination_domain_retry() { - let (addr, mut rx) = setup_test_server(); - - // Create a random destination domain - let destination_domain = 42; - - // Send a GET request to the server - let response = reqwest::get(format!( - "http://{}{}?destination_domain={}", - addr, MESSAGE_RETRY_API_BASE, destination_domain - )) - .await - .unwrap(); - - // Check that the response status code is OK - assert_eq!(response.status(), StatusCode::OK); - - assert_eq!( - rx.try_recv().unwrap(), - MessageRetryRequest::DestinationDomain(destination_domain) - ); - } -} diff --git a/rust/agents/scraper/src/conversions.rs b/rust/agents/scraper/src/conversions.rs deleted file mode 100644 index 84a64a857..000000000 --- a/rust/agents/scraper/src/conversions.rs +++ /dev/null @@ -1,39 +0,0 @@ -use num_bigint::{BigInt, Sign}; -use sea_orm::prelude::BigDecimal; - -use hyperlane_core::{H256, U256}; - -// Creates a big-endian hex representation of the address -pub fn address_to_bytes(data: &H256) -> Vec { - if hex::is_h160(data.as_fixed_bytes()) { - // take the last 20 bytes - data.as_fixed_bytes()[12..32].into() - } else { - h256_to_bytes(data) - } -} - -// Creates a big-endian hex representation of the address -pub fn bytes_to_address(data: Vec) -> eyre::Result { - if (data.len() != 20) && (data.len() != 32) { - return Err(eyre::eyre!("Invalid address length")); - } - if data.len() == 20 { - let mut prefix = vec![0; 12]; - prefix.extend(data); - Ok(H256::from_slice(&prefix[..])) - } else { - Ok(H256::from_slice(&data[..])) - } -} - -// Creates a big-endian hex representation of the address hash -pub fn h256_to_bytes(data: &H256) -> Vec { - data.as_fixed_bytes().as_slice().into() -} - -pub fn u256_to_decimal(v: U256) -> BigDecimal { - let mut buf = [0u8; 32]; - v.to_little_endian(&mut buf); - BigDecimal::from(BigInt::from_bytes_le(Sign::Plus, &buf as &[u8])) -} diff --git a/rust/agents/validator/src/submit.rs b/rust/agents/validator/src/submit.rs deleted file mode 100644 index 8f91f284f..000000000 --- a/rust/agents/validator/src/submit.rs +++ /dev/null @@ -1,314 +0,0 @@ -use std::num::NonZeroU64; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use std::vec; - -use hyperlane_core::rpc_clients::call_and_retry_indefinitely; -use hyperlane_core::{ChainResult, MerkleTreeHook}; -use prometheus::IntGauge; -use tokio::time::sleep; -use tracing::{debug, error, info}; - -use hyperlane_base::{db::HyperlaneRocksDB, CheckpointSyncer, CoreMetrics}; -use hyperlane_core::{ - accumulator::incremental::IncrementalMerkle, Checkpoint, CheckpointWithMessageId, - HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSignerExt, -}; -use hyperlane_ethereum::SingletonSignerHandle; - -#[derive(Clone)] -pub(crate) struct ValidatorSubmitter { - interval: Duration, - reorg_period: Option, - signer: SingletonSignerHandle, - merkle_tree_hook: Arc, - checkpoint_syncer: Arc, - message_db: HyperlaneRocksDB, - metrics: ValidatorSubmitterMetrics, -} - -impl ValidatorSubmitter { - pub(crate) fn new( - interval: Duration, - reorg_period: u64, - merkle_tree_hook: Arc, - signer: SingletonSignerHandle, - checkpoint_syncer: Arc, - message_db: HyperlaneRocksDB, - metrics: ValidatorSubmitterMetrics, - ) -> Self { - Self { - reorg_period: NonZeroU64::new(reorg_period), - interval, - merkle_tree_hook, - signer, - checkpoint_syncer, - message_db, - metrics, - } - } - - pub(crate) fn checkpoint(&self, tree: &IncrementalMerkle) -> Checkpoint { - Checkpoint { - root: tree.root(), - index: tree.index(), - merkle_tree_hook_address: self.merkle_tree_hook.address(), - mailbox_domain: self.merkle_tree_hook.domain().id(), - } - } - - /// Submits signed checkpoints from index 0 until the target checkpoint (inclusive). - /// Runs idly forever once the target checkpoint is reached to avoid exiting the task. - pub(crate) async fn backfill_checkpoint_submitter(self, target_checkpoint: Checkpoint) { - let mut tree = IncrementalMerkle::default(); - self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &target_checkpoint) - .await; - - info!( - ?target_checkpoint, - "Backfill checkpoint submitter successfully reached target checkpoint" - ); - } - - /// Submits signed checkpoints indefinitely, starting from the `tree`. - pub(crate) async fn checkpoint_submitter(self, mut tree: IncrementalMerkle) { - // How often to log checkpoint info - once every minute - let checkpoint_info_log_period = Duration::from_secs(60); - // The instant in which we last logged checkpoint info, if at all - let mut latest_checkpoint_info_log: Option = None; - // Returns whether checkpoint info should be logged based off the - // checkpoint_info_log_period having elapsed since the last log. - // Sets latest_checkpoint_info_log to the current instant if true. - let mut should_log_checkpoint_info = || { - if let Some(instant) = latest_checkpoint_info_log { - if instant.elapsed() < checkpoint_info_log_period { - return false; - } - } - latest_checkpoint_info_log = Some(Instant::now()); - true - }; - - loop { - // Lag by reorg period because this is our correctness checkpoint. - let latest_checkpoint = call_and_retry_indefinitely(|| { - let merkle_tree_hook = self.merkle_tree_hook.clone(); - Box::pin(async move { merkle_tree_hook.latest_checkpoint(self.reorg_period).await }) - }) - .await; - - self.metrics - .latest_checkpoint_observed - .set(latest_checkpoint.index as i64); - - if should_log_checkpoint_info() { - info!( - ?latest_checkpoint, - tree_count = tree.count(), - "Latest checkpoint" - ); - } - - // This may occur e.g. if RPC providers are unreliable and make calls against - // inconsistent block tips. - // - // In this case, we just sleep a bit until we fetch a new latest checkpoint - // that at least meets the tree. - if tree_exceeds_checkpoint(&latest_checkpoint, &tree) { - debug!( - ?latest_checkpoint, - tree_count = tree.count(), - "Latest checkpoint is behind tree, sleeping briefly" - ); - sleep(self.interval).await; - continue; - } - self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &latest_checkpoint) - .await; - - self.metrics - .latest_checkpoint_processed - .set(latest_checkpoint.index as i64); - - sleep(self.interval).await; - } - } - - /// Submits signed checkpoints relating to the given tree until the correctness checkpoint (inclusive). - /// Only submits the signed checkpoints once the correctness checkpoint is reached. - async fn submit_checkpoints_until_correctness_checkpoint( - &self, - tree: &mut IncrementalMerkle, - correctness_checkpoint: &Checkpoint, - ) { - // This should never be called with a tree that is ahead of the correctness checkpoint. - assert!( - !tree_exceeds_checkpoint(correctness_checkpoint, tree), - "tree (count: {}) is ahead of correctness checkpoint {:?}", - tree.count(), - correctness_checkpoint, - ); - - // All intermediate checkpoints will be stored here and signed once the correctness - // checkpoint is reached. - let mut checkpoint_queue = vec![]; - - // If the correctness checkpoint is ahead of the tree, we need to ingest more messages. - // - // tree.index() will panic if the tree is empty, so we use tree.count() instead - // and convert the correctness_checkpoint.index to a count by adding 1. - while tree.count() as u32 <= correctness_checkpoint.index { - if let Some(insertion) = self - .message_db - .retrieve_merkle_tree_insertion_by_leaf_index(&(tree.count() as u32)) - .unwrap_or_else(|err| { - panic!( - "Error fetching merkle tree insertion for leaf index {}: {}", - tree.count(), - err - ) - }) - { - debug!( - index = insertion.index(), - queue_length = checkpoint_queue.len(), - "Ingesting leaf to tree" - ); - let message_id = insertion.message_id(); - tree.ingest(message_id); - - let checkpoint = self.checkpoint(tree); - - checkpoint_queue.push(CheckpointWithMessageId { - checkpoint, - message_id, - }); - } else { - // If we haven't yet indexed the next merkle tree insertion but know that - // it will soon exist (because we know the correctness checkpoint), wait a bit and - // try again. - sleep(Duration::from_millis(100)).await - } - } - - // At this point we know that correctness_checkpoint.index == tree.index(). - assert_eq!( - correctness_checkpoint.index, - tree.index(), - "correctness checkpoint index {} != tree index {}", - correctness_checkpoint.index, - tree.index(), - ); - - let checkpoint = self.checkpoint(tree); - - // If the tree's checkpoint doesn't match the correctness checkpoint, something went wrong - // and we bail loudly. - if checkpoint != *correctness_checkpoint { - error!( - ?checkpoint, - ?correctness_checkpoint, - "Incorrect tree root, something went wrong" - ); - panic!("Incorrect tree root, something went wrong"); - } - - if !checkpoint_queue.is_empty() { - info!( - index = checkpoint.index, - queue_len = checkpoint_queue.len(), - "Reached tree consistency" - ); - self.sign_and_submit_checkpoints(checkpoint_queue).await; - - info!( - index = checkpoint.index, - "Signed all queued checkpoints until index" - ); - } - } - - async fn sign_and_submit_checkpoint( - &self, - checkpoint: CheckpointWithMessageId, - ) -> ChainResult<()> { - let existing = self - .checkpoint_syncer - .fetch_checkpoint(checkpoint.index) - .await?; - if existing.is_some() { - debug!(index = checkpoint.index, "Checkpoint already submitted"); - return Ok(()); - } - let signed_checkpoint = self.signer.sign(checkpoint).await?; - self.checkpoint_syncer - .write_checkpoint(&signed_checkpoint) - .await?; - debug!(index = checkpoint.index, "Signed and submitted checkpoint"); - - // TODO: move these into S3 implementations - // small sleep before signing next checkpoint to avoid rate limiting - sleep(Duration::from_millis(100)).await; - Ok(()) - } - - /// Signs and submits any previously unsubmitted checkpoints. - async fn sign_and_submit_checkpoints(&self, checkpoints: Vec) { - let last_checkpoint = checkpoints.as_slice()[checkpoints.len() - 1]; - // Submits checkpoints to the store in reverse order. This speeds up processing historic checkpoints (those before the validator is spun up), - // since those are the most likely to make messages become processable. - // A side effect is that new checkpoints will also be submitted in reverse order. - for queued_checkpoint in checkpoints.into_iter().rev() { - // certain checkpoint stores rate limit very aggressively, so we retry indefinitely - call_and_retry_indefinitely(|| { - let self_clone = self.clone(); - Box::pin(async move { - self_clone - .sign_and_submit_checkpoint(queued_checkpoint) - .await?; - Ok(()) - }) - }) - .await; - } - - call_and_retry_indefinitely(|| { - let self_clone = self.clone(); - Box::pin(async move { - self_clone - .checkpoint_syncer - .update_latest_index(last_checkpoint.index) - .await?; - Ok(()) - }) - }) - .await; - } -} - -/// Returns whether the tree exceeds the checkpoint. -fn tree_exceeds_checkpoint(checkpoint: &Checkpoint, tree: &IncrementalMerkle) -> bool { - // tree.index() will panic if the tree is empty, so we use tree.count() instead - // and convert the correctness_checkpoint.index to a count by adding 1. - checkpoint.index + 1 < tree.count() as u32 -} - -#[derive(Clone)] -pub(crate) struct ValidatorSubmitterMetrics { - latest_checkpoint_observed: IntGauge, - latest_checkpoint_processed: IntGauge, -} - -impl ValidatorSubmitterMetrics { - pub fn new(metrics: &CoreMetrics, mailbox_chain: &HyperlaneDomain) -> Self { - let chain_name = mailbox_chain.name(); - Self { - latest_checkpoint_observed: metrics - .latest_checkpoint() - .with_label_values(&["validator_observed", chain_name]), - latest_checkpoint_processed: metrics - .latest_checkpoint() - .with_label_values(&["validator_processed", chain_name]), - } - } -} diff --git a/rust/chains/hyperlane-cosmos/src/libs/account.rs b/rust/chains/hyperlane-cosmos/src/libs/account.rs deleted file mode 100644 index bac0fe4bc..000000000 --- a/rust/chains/hyperlane-cosmos/src/libs/account.rs +++ /dev/null @@ -1,57 +0,0 @@ -use cosmrs::{crypto::PublicKey, AccountId}; -use tendermint::account::Id as TendermintAccountId; -use tendermint::public_key::PublicKey as TendermintPublicKey; - -use hyperlane_core::Error::Overflow; -use hyperlane_core::{ChainCommunicationError, ChainResult, H256}; - -use crate::HyperlaneCosmosError; - -pub(crate) struct CosmosAccountId<'a> { - account_id: &'a AccountId, -} - -impl<'a> CosmosAccountId<'a> { - pub fn new(account_id: &'a AccountId) -> Self { - Self { account_id } - } - - pub fn account_id_from_pubkey(pub_key: PublicKey, prefix: &str) -> ChainResult { - // Get the inner type - let tendermint_pub_key = TendermintPublicKey::from(pub_key); - // Get the RIPEMD160(SHA256(pub_key)) - let tendermint_id = TendermintAccountId::from(tendermint_pub_key); - // Bech32 encoding - let account_id = AccountId::new(prefix, tendermint_id.as_bytes()) - .map_err(Into::::into)?; - - Ok(account_id) - } -} - -impl TryFrom<&CosmosAccountId<'_>> for H256 { - type Error = ChainCommunicationError; - - /// Builds a H256 digest from a cosmos AccountId (Bech32 encoding) - fn try_from(account_id: &CosmosAccountId) -> Result { - let bytes = account_id.account_id.to_bytes(); - let h256_len = H256::len_bytes(); - let Some(start_point) = h256_len.checked_sub(bytes.len()) else { - // input is too large to fit in a H256 - return Err(Overflow.into()); - }; - let mut empty_hash = H256::default(); - let result = empty_hash.as_bytes_mut(); - result[start_point..].copy_from_slice(bytes.as_slice()); - Ok(H256::from_slice(result)) - } -} - -impl TryFrom> for H256 { - type Error = ChainCommunicationError; - - /// Builds a H256 digest from a cosmos AccountId (Bech32 encoding) - fn try_from(account_id: CosmosAccountId) -> Result { - (&account_id).try_into() - } -} diff --git a/rust/chains/hyperlane-cosmos/src/providers/mod.rs b/rust/chains/hyperlane-cosmos/src/providers/mod.rs deleted file mode 100644 index f033b313c..000000000 --- a/rust/chains/hyperlane-cosmos/src/providers/mod.rs +++ /dev/null @@ -1,287 +0,0 @@ -use async_trait::async_trait; -use cosmrs::cosmwasm::MsgExecuteContract; -use cosmrs::crypto::PublicKey; -use cosmrs::tx::{MessageExt, SequenceNumber, SignerInfo}; -use cosmrs::{AccountId, Tx}; -use itertools::Itertools; -use tendermint::hash::Algorithm; -use tendermint::Hash; -use tendermint_rpc::{client::CompatMode, Client, HttpClient}; -use time::OffsetDateTime; - -use hyperlane_core::{ - BlockInfo, ChainCommunicationError, ChainInfo, ChainResult, ContractLocator, HyperlaneChain, - HyperlaneDomain, HyperlaneProvider, TxnInfo, TxnReceiptInfo, H256, U256, -}; - -use crate::address::CosmosAddress; -use crate::grpc::WasmProvider; -use crate::libs::account::CosmosAccountId; -use crate::{ConnectionConf, CosmosAmount, HyperlaneCosmosError, Signer}; - -use self::grpc::WasmGrpcProvider; - -/// cosmos grpc provider -pub mod grpc; -/// cosmos rpc provider -pub mod rpc; - -/// Abstraction over a connection to a Cosmos chain -#[derive(Debug, Clone)] -pub struct CosmosProvider { - domain: HyperlaneDomain, - connection_conf: ConnectionConf, - grpc_client: WasmGrpcProvider, - rpc_client: HttpClient, -} - -impl CosmosProvider { - /// Create a reference to a Cosmos chain - pub fn new( - domain: HyperlaneDomain, - conf: ConnectionConf, - locator: Option, - signer: Option, - ) -> ChainResult { - let gas_price = CosmosAmount::try_from(conf.get_minimum_gas_price().clone())?; - let grpc_client = WasmGrpcProvider::new( - domain.clone(), - conf.clone(), - gas_price.clone(), - locator, - signer, - )?; - let rpc_client = HttpClient::builder( - conf.get_rpc_url() - .parse() - .map_err(Into::::into)?, - ) - // Consider supporting different compatibility modes. - .compat_mode(CompatMode::latest()) - .build() - .map_err(Into::::into)?; - - Ok(Self { - domain, - connection_conf: conf, - rpc_client, - grpc_client, - }) - } - - /// Get a grpc client - pub fn grpc(&self) -> &WasmGrpcProvider { - &self.grpc_client - } - - /// Get an rpc client - pub fn rpc(&self) -> &HttpClient { - &self.rpc_client - } - - fn search_payer_in_signer_infos( - &self, - signer_infos: &[SignerInfo], - payer: &AccountId, - ) -> ChainResult<(AccountId, SequenceNumber)> { - signer_infos - .iter() - .map(|si| self.convert_signer_info_into_account_id_and_nonce(si)) - // After the following we have a single Ok entry and, possibly, many Err entries - .filter_ok(|(a, s)| payer == a) - // If we have Ok entry, use it since it is the payer, if not, use the first entry with error - .find_or_first(|r| match r { - Ok((a, s)) => payer == a, - Err(e) => false, - }) - // If there were not any signer info with non-empty public key or no signers for the transaction, - // we get None here - .unwrap_or_else(|| Err(ChainCommunicationError::from_other_str("no signer info"))) - } - - fn convert_signer_info_into_account_id_and_nonce( - &self, - signer_info: &SignerInfo, - ) -> ChainResult<(AccountId, SequenceNumber)> { - let signer_public_key = signer_info.public_key.clone().ok_or_else(|| { - HyperlaneCosmosError::PublicKeyError("no public key for default signer".to_owned()) - })?; - - let public_key = PublicKey::try_from(signer_public_key)?; - - let account_id = CosmosAccountId::account_id_from_pubkey( - public_key, - &self.connection_conf.get_bech32_prefix(), - )?; - - Ok((account_id, signer_info.sequence)) - } - - /// Calculates the sender and the nonce for the transaction. - /// We use `payer` of the fees as the sender of the transaction, and we search for `payer` - /// signature information to find the nonce. - /// If `payer` is not specified, we use the account which signed the transaction first, as - /// the sender. - fn sender_and_nonce(&self, tx: &Tx) -> ChainResult<(H256, SequenceNumber)> { - let (sender, nonce) = tx - .auth_info - .fee - .payer - .as_ref() - .map(|payer| self.search_payer_in_signer_infos(&tx.auth_info.signer_infos, payer)) - .map_or_else( - || { - let signer_info = tx.auth_info.signer_infos.get(0).ok_or_else(|| { - HyperlaneCosmosError::SignerInfoError( - "no signer info in default signer".to_owned(), - ) - })?; - self.convert_signer_info_into_account_id_and_nonce(signer_info) - }, - |p| p, - ) - .map(|(a, n)| CosmosAddress::from_account_id(a).map(|a| (a.digest(), n)))??; - Ok((sender, nonce)) - } - - /// Extract contract address from transaction. - /// Assumes that there is only one `MsgExecuteContract` message in the transaction - fn contract(tx: &Tx) -> ChainResult { - use cosmrs::proto::cosmwasm::wasm::v1::MsgExecuteContract as ProtoMsgExecuteContract; - - let any = tx - .body - .messages - .iter() - .find(|a| a.type_url == "/cosmwasm.wasm.v1.MsgExecuteContract") - .ok_or_else(|| { - ChainCommunicationError::from_other_str("could not find contract execution message") - })?; - let proto = - ProtoMsgExecuteContract::from_any(any).map_err(Into::::into)?; - let msg = MsgExecuteContract::try_from(proto)?; - let contract = H256::try_from(CosmosAccountId::new(&msg.contract))?; - Ok(contract) - } -} - -impl HyperlaneChain for CosmosProvider { - fn domain(&self) -> &HyperlaneDomain { - &self.domain - } - - fn provider(&self) -> Box { - Box::new(self.clone()) - } -} - -#[async_trait] -impl HyperlaneProvider for CosmosProvider { - async fn get_block_by_hash(&self, hash: &H256) -> ChainResult { - let tendermint_hash = Hash::from_bytes(Algorithm::Sha256, hash.as_bytes()) - .expect("block hash should be of correct size"); - - let response = self - .rpc_client - .block_by_hash(tendermint_hash) - .await - .map_err(ChainCommunicationError::from_other)?; - - let received_hash = H256::from_slice(response.block_id.hash.as_bytes()); - - if &received_hash != hash { - return Err(ChainCommunicationError::from_other_str( - &format!("received incorrect block, expected hash: {hash:?}, received hash: {received_hash:?}") - )); - } - - let block = response.block.ok_or_else(|| { - ChainCommunicationError::from_other_str(&format!( - "empty block info for block: {:?}", - hash - )) - })?; - - let time: OffsetDateTime = block.header.time.into(); - - let block_info = BlockInfo { - hash: hash.to_owned(), - timestamp: time.unix_timestamp() as u64, - number: block.header.height.value(), - }; - - Ok(block_info) - } - - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult { - let tendermint_hash = Hash::from_bytes(Algorithm::Sha256, hash.as_bytes()) - .expect("transaction hash should be of correct size"); - - let response = self - .rpc_client - .tx(tendermint_hash, false) - .await - .map_err(Into::::into)?; - - let received_hash = H256::from_slice(response.hash.as_bytes()); - - if &received_hash != hash { - return Err(ChainCommunicationError::from_other_str(&format!( - "received incorrect transaction, expected hash: {:?}, received hash: {:?}", - hash, received_hash, - ))); - } - - let tx = Tx::from_bytes(&response.tx)?; - - let contract = Self::contract(&tx)?; - let (sender, nonce) = self.sender_and_nonce(&tx)?; - - // TODO support multiple denomination for amount - let gas_limit = U256::from(tx.auth_info.fee.gas_limit); - let fee = tx - .auth_info - .fee - .amount - .iter() - .fold(U256::zero(), |acc, a| acc + a.amount); - - let gas_price = fee / gas_limit; - - let tx_info = TxnInfo { - hash: hash.to_owned(), - gas_limit: U256::from(response.tx_result.gas_wanted), - max_priority_fee_per_gas: None, - max_fee_per_gas: None, - gas_price: Some(gas_price), - nonce, - sender, - recipient: Some(contract), - receipt: Some(TxnReceiptInfo { - gas_used: U256::from(response.tx_result.gas_used), - cumulative_gas_used: U256::from(response.tx_result.gas_used), - effective_gas_price: Some(gas_price), - }), - }; - - Ok(tx_info) - } - - async fn is_contract(&self, address: &H256) -> ChainResult { - match self.grpc_client.wasm_contract_info().await { - Ok(c) => Ok(true), - Err(e) => Ok(false), - } - } - - async fn get_balance(&self, address: String) -> ChainResult { - Ok(self - .grpc_client - .get_balance(address, self.connection_conf.get_canonical_asset()) - .await?) - } - - async fn get_chain_metrics(&self) -> ChainResult> { - Ok(None) - } -} diff --git a/rust/chains/hyperlane-ethereum/src/config.rs b/rust/chains/hyperlane-ethereum/src/config.rs deleted file mode 100644 index 8735b5fd2..000000000 --- a/rust/chains/hyperlane-ethereum/src/config.rs +++ /dev/null @@ -1,54 +0,0 @@ -use hyperlane_core::{config::OperationBatchConfig, U256}; -use url::Url; - -/// Ethereum RPC connection configuration -#[derive(Debug, Clone)] -pub enum RpcConnectionConf { - /// An HTTP-only quorum. - HttpQuorum { - /// List of urls to connect to - urls: Vec, - }, - /// An HTTP-only fallback set. - HttpFallback { - /// List of urls to connect to in order of priority - urls: Vec, - }, - /// HTTP connection details - Http { - /// Url to connect to - url: Url, - }, - /// Websocket connection details - Ws { - /// Url to connect to - url: Url, - }, -} - -/// Ethereum connection configuration -#[derive(Debug, Clone)] -pub struct ConnectionConf { - /// RPC connection configuration - pub rpc_connection: RpcConnectionConf, - /// Transaction overrides to use when sending transactions. - pub transaction_overrides: TransactionOverrides, - /// Operation batching configuration - pub operation_batch: OperationBatchConfig, -} - -/// Ethereum transaction overrides. -#[derive(Debug, Clone, Default)] -pub struct TransactionOverrides { - /// Gas price to use for transactions, in wei. - /// If specified, non-1559 transactions will be used with this gas price. - pub gas_price: Option, - /// Gas limit to use for transactions. - /// If unspecified, the gas limit will be estimated. - /// If specified, transactions will use `max(estimated_gas, gas_limit)` - pub gas_limit: Option, - /// Max fee per gas to use for EIP-1559 transactions. - pub max_fee_per_gas: Option, - /// Max priority fee per gas to use for EIP-1559 transactions. - pub max_priority_fee_per_gas: Option, -} diff --git a/rust/chains/hyperlane-fuel/abis/Mailbox.abi.json b/rust/chains/hyperlane-fuel/abis/Mailbox.abi.json deleted file mode 100644 index c979c0aa4..000000000 --- a/rust/chains/hyperlane-fuel/abis/Mailbox.abi.json +++ /dev/null @@ -1,530 +0,0 @@ -{ - "types": [ - { - "typeId": 0, - "type": "()", - "components": [], - "typeParameters": null - }, - { - "typeId": 1, - "type": "(_, _)", - "components": [ - { - "name": "__tuple_element", - "type": 2, - "typeArguments": null - }, - { - "name": "__tuple_element", - "type": 20, - "typeArguments": null - } - ], - "typeParameters": null - }, - { - "typeId": 2, - "type": "b256", - "components": null, - "typeParameters": null - }, - { - "typeId": 3, - "type": "bool", - "components": null, - "typeParameters": null - }, - { - "typeId": 4, - "type": "enum Identity", - "components": [ - { - "name": "Address", - "type": 14, - "typeArguments": null - }, - { - "name": "ContractId", - "type": 15, - "typeArguments": null - } - ], - "typeParameters": null - }, - { - "typeId": 5, - "type": "enum Option", - "components": [ - { - "name": "None", - "type": 0, - "typeArguments": null - }, - { - "name": "Some", - "type": 6, - "typeArguments": null - } - ], - "typeParameters": [ - 6 - ] - }, - { - "typeId": 6, - "type": "generic T", - "components": null, - "typeParameters": null - }, - { - "typeId": 7, - "type": "raw untyped ptr", - "components": null, - "typeParameters": null - }, - { - "typeId": 8, - "type": "str[12]", - "components": null, - "typeParameters": null - }, - { - "typeId": 9, - "type": "str[16]", - "components": null, - "typeParameters": null - }, - { - "typeId": 10, - "type": "str[6]", - "components": null, - "typeParameters": null - }, - { - "typeId": 11, - "type": "str[7]", - "components": null, - "typeParameters": null - }, - { - "typeId": 12, - "type": "str[8]", - "components": null, - "typeParameters": null - }, - { - "typeId": 13, - "type": "str[9]", - "components": null, - "typeParameters": null - }, - { - "typeId": 14, - "type": "struct Address", - "components": [ - { - "name": "value", - "type": 2, - "typeArguments": null - } - ], - "typeParameters": null - }, - { - "typeId": 15, - "type": "struct ContractId", - "components": [ - { - "name": "value", - "type": 2, - "typeArguments": null - } - ], - "typeParameters": null - }, - { - "typeId": 16, - "type": "struct Message", - "components": [ - { - "name": "version", - "type": 22, - "typeArguments": null - }, - { - "name": "nonce", - "type": 20, - "typeArguments": null - }, - { - "name": "origin", - "type": 20, - "typeArguments": null - }, - { - "name": "sender", - "type": 2, - "typeArguments": null - }, - { - "name": "destination", - "type": 20, - "typeArguments": null - }, - { - "name": "recipient", - "type": 2, - "typeArguments": null - }, - { - "name": "body", - "type": 19, - "typeArguments": [ - { - "name": "", - "type": 22, - "typeArguments": null - } - ] - } - ], - "typeParameters": null - }, - { - "typeId": 17, - "type": "struct OwnershipTransferredEvent", - "components": [ - { - "name": "previous_owner", - "type": 5, - "typeArguments": [ - { - "name": "", - "type": 4, - "typeArguments": null - } - ] - }, - { - "name": "new_owner", - "type": 5, - "typeArguments": [ - { - "name": "", - "type": 4, - "typeArguments": null - } - ] - } - ], - "typeParameters": null - }, - { - "typeId": 18, - "type": "struct RawVec", - "components": [ - { - "name": "ptr", - "type": 7, - "typeArguments": null - }, - { - "name": "cap", - "type": 21, - "typeArguments": null - } - ], - "typeParameters": [ - 6 - ] - }, - { - "typeId": 19, - "type": "struct Vec", - "components": [ - { - "name": "buf", - "type": 18, - "typeArguments": [ - { - "name": "", - "type": 6, - "typeArguments": null - } - ] - }, - { - "name": "len", - "type": 21, - "typeArguments": null - } - ], - "typeParameters": [ - 6 - ] - }, - { - "typeId": 20, - "type": "u32", - "components": null, - "typeParameters": null - }, - { - "typeId": 21, - "type": "u64", - "components": null, - "typeParameters": null - }, - { - "typeId": 22, - "type": "u8", - "components": null, - "typeParameters": null - } - ], - "functions": [ - { - "inputs": [], - "name": "count", - "output": { - "name": "", - "type": 20, - "typeArguments": null - } - }, - { - "inputs": [ - { - "name": "message_id", - "type": 2, - "typeArguments": null - } - ], - "name": "delivered", - "output": { - "name": "", - "type": 3, - "typeArguments": null - } - }, - { - "inputs": [ - { - "name": "destination_domain", - "type": 20, - "typeArguments": null - }, - { - "name": "recipient", - "type": 2, - "typeArguments": null - }, - { - "name": "message_body", - "type": 19, - "typeArguments": [ - { - "name": "", - "type": 22, - "typeArguments": null - } - ] - } - ], - "name": "dispatch", - "output": { - "name": "", - "type": 2, - "typeArguments": null - } - }, - { - "inputs": [], - "name": "get_default_ism", - "output": { - "name": "", - "type": 15, - "typeArguments": null - } - }, - { - "inputs": [], - "name": "latest_checkpoint", - "output": { - "name": "", - "type": 1, - "typeArguments": null - } - }, - { - "inputs": [ - { - "name": "metadata", - "type": 19, - "typeArguments": [ - { - "name": "", - "type": 22, - "typeArguments": null - } - ] - }, - { - "name": "_message", - "type": 16, - "typeArguments": null - } - ], - "name": "process", - "output": { - "name": "", - "type": 0, - "typeArguments": null - } - }, - { - "inputs": [], - "name": "root", - "output": { - "name": "", - "type": 2, - "typeArguments": null - } - }, - { - "inputs": [ - { - "name": "module", - "type": 15, - "typeArguments": null - } - ], - "name": "set_default_ism", - "output": { - "name": "", - "type": 0, - "typeArguments": null - } - }, - { - "inputs": [], - "name": "owner", - "output": { - "name": "", - "type": 5, - "typeArguments": [ - { - "name": "", - "type": 4, - "typeArguments": null - } - ] - } - }, - { - "inputs": [ - { - "name": "new_owner", - "type": 5, - "typeArguments": [ - { - "name": "", - "type": 4, - "typeArguments": null - } - ] - } - ], - "name": "transfer_ownership", - "output": { - "name": "", - "type": 0, - "typeArguments": null - } - } - ], - "loggedTypes": [ - { - "logId": 0, - "loggedType": { - "name": "", - "type": 8, - "typeArguments": null - } - }, - { - "logId": 1, - "loggedType": { - "name": "", - "type": 9, - "typeArguments": null - } - }, - { - "logId": 2, - "loggedType": { - "name": "", - "type": 12, - "typeArguments": null - } - }, - { - "logId": 3, - "loggedType": { - "name": "", - "type": 8, - "typeArguments": null - } - }, - { - "logId": 4, - "loggedType": { - "name": "", - "type": 13, - "typeArguments": null - } - }, - { - "logId": 5, - "loggedType": { - "name": "", - "type": 11, - "typeArguments": null - } - }, - { - "logId": 6, - "loggedType": { - "name": "", - "type": 2, - "typeArguments": null - } - }, - { - "logId": 7, - "loggedType": { - "name": "", - "type": 10, - "typeArguments": null - } - }, - { - "logId": 8, - "loggedType": { - "name": "", - "type": 10, - "typeArguments": null - } - }, - { - "logId": 9, - "loggedType": { - "name": "", - "type": 17, - "typeArguments": [] - } - } - ], - "messagesTypes": [] -} \ No newline at end of file diff --git a/rust/chains/hyperlane-fuel/src/mailbox.rs b/rust/chains/hyperlane-fuel/src/mailbox.rs deleted file mode 100644 index 5e8f0cf05..000000000 --- a/rust/chains/hyperlane-fuel/src/mailbox.rs +++ /dev/null @@ -1,164 +0,0 @@ -use std::collections::HashMap; -use std::fmt::{Debug, Formatter}; -use std::num::NonZeroU64; -use std::ops::RangeInclusive; - -use async_trait::async_trait; -use fuels::prelude::{Bech32ContractId, WalletUnlocked}; -use hyperlane_core::Indexed; -use tracing::instrument; - -use hyperlane_core::{ - utils::bytes_to_hex, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, - HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, - Indexer, LogMeta, Mailbox, TxCostEstimate, TxOutcome, H256, U256, -}; - -use crate::{ - contracts::mailbox::Mailbox as FuelMailboxInner, conversions::*, make_provider, ConnectionConf, -}; - -/// A reference to a Mailbox contract on some Fuel chain -pub struct FuelMailbox { - contract: FuelMailboxInner, - domain: HyperlaneDomain, -} - -impl FuelMailbox { - /// Create a new fuel mailbox - pub fn new( - conf: &ConnectionConf, - locator: ContractLocator, - mut wallet: WalletUnlocked, - ) -> ChainResult { - let provider = make_provider(conf)?; - wallet.set_provider(provider); - let address = Bech32ContractId::from_h256(&locator.address); - - Ok(FuelMailbox { - contract: FuelMailboxInner::new(address, wallet), - domain: locator.domain.clone(), - }) - } -} - -impl HyperlaneContract for FuelMailbox { - fn address(&self) -> H256 { - self.contract.contract_id().into_h256() - } -} - -impl HyperlaneChain for FuelMailbox { - fn domain(&self) -> &HyperlaneDomain { - &self.domain - } - - fn provider(&self) -> Box { - todo!() - } -} - -impl Debug for FuelMailbox { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self as &dyn HyperlaneContract) - } -} - -#[async_trait] -impl Mailbox for FuelMailbox { - #[instrument(level = "debug", err, ret, skip(self))] - async fn count(&self, lag: Option) -> ChainResult { - assert!( - lag.is_none(), - "Fuel does not support querying point-in-time" - ); - self.contract - .methods() - .count() - .simulate() - .await - .map(|r| r.value) - .map_err(ChainCommunicationError::from_other) - } - - #[instrument(level = "debug", err, ret, skip(self))] - async fn delivered(&self, id: H256) -> ChainResult { - todo!() - } - - #[instrument(err, ret, skip(self))] - async fn default_ism(&self) -> ChainResult { - todo!() - } - - #[instrument(err, ret, skip(self))] - async fn recipient_ism(&self, recipient: H256) -> ChainResult { - todo!() - } - - #[instrument(err, ret, skip(self))] - async fn process( - &self, - message: &HyperlaneMessage, - metadata: &[u8], - tx_gas_limit: Option, - ) -> ChainResult { - todo!() - } - - #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] - async fn process_estimate_costs( - &self, - message: &HyperlaneMessage, - metadata: &[u8], - ) -> ChainResult { - todo!() - } - - fn process_calldata(&self, message: &HyperlaneMessage, metadata: &[u8]) -> Vec { - todo!() - } -} - -/// Struct that retrieves event data for a Fuel Mailbox contract -#[derive(Debug)] -pub struct FuelMailboxIndexer {} - -#[async_trait] -impl Indexer for FuelMailboxIndexer { - async fn fetch_logs_in_range( - &self, - range: RangeInclusive, - ) -> ChainResult, LogMeta)>> { - todo!() - } - - async fn get_finalized_block_number(&self) -> ChainResult { - todo!() - } -} - -#[async_trait] -impl Indexer for FuelMailboxIndexer { - async fn fetch_logs_in_range( - &self, - range: RangeInclusive, - ) -> ChainResult, LogMeta)>> { - todo!() - } - - async fn get_finalized_block_number(&self) -> ChainResult { - todo!() - } -} - -struct FuelMailboxAbi; - -impl HyperlaneAbi for FuelMailboxAbi { - const SELECTOR_SIZE_BYTES: usize = 8; - - fn fn_map() -> HashMap, &'static str> { - // Can't support this without Fuels exporting it in the generated code - todo!() - } -} diff --git a/rust/chains/hyperlane-fuel/src/provider.rs b/rust/chains/hyperlane-fuel/src/provider.rs deleted file mode 100644 index 992608fac..000000000 --- a/rust/chains/hyperlane-fuel/src/provider.rs +++ /dev/null @@ -1,43 +0,0 @@ -use async_trait::async_trait; - -use hyperlane_core::{ - BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, - H256, U256, -}; - -/// A wrapper around a fuel provider to get generic blockchain information. -#[derive(Debug)] -pub struct FuelProvider {} - -impl HyperlaneChain for FuelProvider { - fn domain(&self) -> &HyperlaneDomain { - todo!() - } - - fn provider(&self) -> Box { - todo!() - } -} - -#[async_trait] -impl HyperlaneProvider for FuelProvider { - async fn get_block_by_hash(&self, hash: &H256) -> ChainResult { - todo!() - } - - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult { - todo!() - } - - async fn is_contract(&self, address: &H256) -> ChainResult { - todo!() - } - - async fn get_balance(&self, address: String) -> ChainResult { - todo!() - } - - async fn get_chain_metrics(&self) -> ChainResult> { - Ok(None) - } -} diff --git a/rust/chains/hyperlane-sealevel/Cargo.toml b/rust/chains/hyperlane-sealevel/Cargo.toml deleted file mode 100644 index 2a3eed634..000000000 --- a/rust/chains/hyperlane-sealevel/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -cargo-features = ["workspace-inheritance"] - -[package] -name = "hyperlane-sealevel" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow.workspace = true -async-trait.workspace = true -base64.workspace = true -borsh.workspace = true -derive-new.workspace = true -jsonrpc-core.workspace = true -num-traits.workspace = true -serde.workspace = true -solana-account-decoder.workspace = true -solana-client.workspace = true -solana-sdk.workspace = true -solana-transaction-status.workspace = true -thiserror.workspace = true -tracing-futures.workspace = true -tracing.workspace = true -url.workspace = true - -account-utils = { path = "../../sealevel/libraries/account-utils" } -hyperlane-core = { path = "../../hyperlane-core", features = ["solana", "async"] } -hyperlane-sealevel-interchain-security-module-interface = { path = "../../sealevel/libraries/interchain-security-module-interface" } -hyperlane-sealevel-mailbox = { path = "../../sealevel/programs/mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../../sealevel/programs/hyperlane-sealevel-igp", features = ["no-entrypoint"] } -hyperlane-sealevel-message-recipient-interface = { path = "../../sealevel/libraries/message-recipient-interface" } -hyperlane-sealevel-multisig-ism-message-id = { path = "../../sealevel/programs/ism/multisig-ism-message-id", features = ["no-entrypoint"] } -hyperlane-sealevel-validator-announce = { path = "../../sealevel/programs/validator-announce", features = ["no-entrypoint"] } -multisig-ism = { path = "../../sealevel/libraries/multisig-ism" } -serializable-account-meta = { path = "../../sealevel/libraries/serializable-account-meta" } diff --git a/rust/chains/hyperlane-sealevel/src/client.rs b/rust/chains/hyperlane-sealevel/src/client.rs deleted file mode 100644 index cc41cd0b2..000000000 --- a/rust/chains/hyperlane-sealevel/src/client.rs +++ /dev/null @@ -1,29 +0,0 @@ -use solana_client::nonblocking::rpc_client::RpcClient; -use solana_sdk::commitment_config::CommitmentConfig; - -/// Kludge to implement Debug for RpcClient. -pub struct RpcClientWithDebug(RpcClient); - -impl RpcClientWithDebug { - pub fn new(rpc_endpoint: String) -> Self { - Self(RpcClient::new(rpc_endpoint)) - } - - pub fn new_with_commitment(rpc_endpoint: String, commitment: CommitmentConfig) -> Self { - Self(RpcClient::new_with_commitment(rpc_endpoint, commitment)) - } -} - -impl std::fmt::Debug for RpcClientWithDebug { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("RpcClient { ... }") - } -} - -impl std::ops::Deref for RpcClientWithDebug { - type Target = RpcClient; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} diff --git a/rust/chains/hyperlane-sealevel/src/error.rs b/rust/chains/hyperlane-sealevel/src/error.rs deleted file mode 100644 index 55b81b416..000000000 --- a/rust/chains/hyperlane-sealevel/src/error.rs +++ /dev/null @@ -1,23 +0,0 @@ -use hyperlane_core::ChainCommunicationError; -use solana_client::client_error::ClientError; -use solana_sdk::pubkey::ParsePubkeyError; - -/// Errors from the crates specific to the hyperlane-sealevel -/// implementation. -/// This error can then be converted into the broader error type -/// in hyperlane-core using the `From` trait impl -#[derive(Debug, thiserror::Error)] -pub enum HyperlaneSealevelError { - /// ParsePubkeyError error - #[error("{0}")] - ParsePubkeyError(#[from] ParsePubkeyError), - /// ClientError error - #[error("{0}")] - ClientError(#[from] ClientError), -} - -impl From for ChainCommunicationError { - fn from(value: HyperlaneSealevelError) -> Self { - ChainCommunicationError::from_other(value) - } -} diff --git a/rust/chains/hyperlane-sealevel/src/provider.rs b/rust/chains/hyperlane-sealevel/src/provider.rs deleted file mode 100644 index 42b266550..000000000 --- a/rust/chains/hyperlane-sealevel/src/provider.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::{str::FromStr, sync::Arc}; - -use async_trait::async_trait; - -use hyperlane_core::{ - BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, - H256, U256, -}; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; - -use crate::{client::RpcClientWithDebug, error::HyperlaneSealevelError, ConnectionConf}; - -/// A wrapper around a Sealevel provider to get generic blockchain information. -#[derive(Debug)] -pub struct SealevelProvider { - domain: HyperlaneDomain, - rpc_client: Arc, -} - -impl SealevelProvider { - /// Create a new Sealevel provider. - pub fn new(domain: HyperlaneDomain, conf: &ConnectionConf) -> Self { - // Set the `processed` commitment at rpc level - let rpc_client = Arc::new(RpcClientWithDebug::new_with_commitment( - conf.url.to_string(), - CommitmentConfig::processed(), - )); - - SealevelProvider { domain, rpc_client } - } - - /// Get an rpc client - pub fn rpc(&self) -> &RpcClientWithDebug { - &self.rpc_client - } - - /// Get the balance of an address - pub async fn get_balance(&self, address: String) -> ChainResult { - let pubkey = Pubkey::from_str(&address).map_err(Into::::into)?; - let balance = self - .rpc_client - .get_balance(&pubkey) - .await - .map_err(Into::::into)?; - Ok(balance.into()) - } -} - -impl HyperlaneChain for SealevelProvider { - fn domain(&self) -> &HyperlaneDomain { - &self.domain - } - - fn provider(&self) -> Box { - Box::new(SealevelProvider { - domain: self.domain.clone(), - rpc_client: self.rpc_client.clone(), - }) - } -} - -#[async_trait] -impl HyperlaneProvider for SealevelProvider { - async fn get_block_by_hash(&self, _hash: &H256) -> ChainResult { - todo!() // FIXME - } - - async fn get_txn_by_hash(&self, _hash: &H256) -> ChainResult { - todo!() // FIXME - } - - async fn is_contract(&self, _address: &H256) -> ChainResult { - // FIXME - Ok(true) - } - - async fn get_balance(&self, address: String) -> ChainResult { - self.get_balance(address).await - } - - async fn get_chain_metrics(&self) -> ChainResult> { - Ok(None) - } -} diff --git a/rust/chains/hyperlane-sealevel/src/utils.rs b/rust/chains/hyperlane-sealevel/src/utils.rs deleted file mode 100644 index 56bec9e51..000000000 --- a/rust/chains/hyperlane-sealevel/src/utils.rs +++ /dev/null @@ -1,93 +0,0 @@ -use base64::Engine; -use borsh::{BorshDeserialize, BorshSerialize}; -use hyperlane_core::{ChainCommunicationError, ChainResult}; - -use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; -use solana_client::nonblocking::rpc_client::RpcClient; -use solana_sdk::{ - commitment_config::CommitmentConfig, - instruction::{AccountMeta, Instruction}, - message::Message, - signature::{Keypair, Signer}, - transaction::Transaction, -}; -use solana_transaction_status::UiReturnDataEncoding; - -use crate::client::RpcClientWithDebug; - -/// Simulates an instruction, and attempts to deserialize it into a T. -/// If no return data at all was returned, returns Ok(None). -/// If some return data was returned but deserialization was unsuccessful, -/// an Err is returned. -pub async fn simulate_instruction( - rpc_client: &RpcClient, - payer: &Keypair, - instruction: Instruction, -) -> ChainResult> { - let commitment = CommitmentConfig::finalized(); - let (recent_blockhash, _) = rpc_client - .get_latest_blockhash_with_commitment(commitment) - .await - .map_err(ChainCommunicationError::from_other)?; - let return_data = rpc_client - .simulate_transaction(&Transaction::new_unsigned(Message::new_with_blockhash( - &[instruction], - Some(&payer.pubkey()), - &recent_blockhash, - ))) - .await - .map_err(ChainCommunicationError::from_other)? - .value - .return_data; - - if let Some(return_data) = return_data { - let bytes = match return_data.data.1 { - UiReturnDataEncoding::Base64 => base64::engine::general_purpose::STANDARD - .decode(return_data.data.0) - .map_err(ChainCommunicationError::from_other)?, - }; - - let decoded_data = - T::try_from_slice(bytes.as_slice()).map_err(ChainCommunicationError::from_other)?; - - return Ok(Some(decoded_data)); - } - - Ok(None) -} - -/// Simulates an Instruction that will return a list of AccountMetas. -pub async fn get_account_metas( - rpc_client: &RpcClient, - payer: &Keypair, - instruction: Instruction, -) -> ChainResult> { - // If there's no data at all, default to an empty vec. - let account_metas = simulate_instruction::>>( - rpc_client, - payer, - instruction, - ) - .await? - .map(|serializable_account_metas| { - serializable_account_metas - .return_data - .into_iter() - .map(|serializable_account_meta| serializable_account_meta.into()) - .collect() - }) - .unwrap_or_else(Vec::new); - - Ok(account_metas) -} - -pub async fn get_finalized_block_number(rpc_client: &RpcClientWithDebug) -> ChainResult { - let height = rpc_client - .get_block_height() - .await - .map_err(ChainCommunicationError::from_other)? - .try_into() - // FIXME solana block height is u64... - .expect("sealevel block height exceeds u32::MAX"); - Ok(height) -} diff --git a/rust/config/testnet_config.json b/rust/config/testnet_config.json deleted file mode 100644 index 3b9d19ac9..000000000 --- a/rust/config/testnet_config.json +++ /dev/null @@ -1,1002 +0,0 @@ -{ - "chains": { - "alfajores": { - "aggregationHook": "0xdBabD76358897E68E4964647C1fb8Bf524f5EFdB", - "blockExplorers": [ - { - "apiUrl": "https://api-alfajores.celoscan.io/api", - "family": "etherscan", - "name": "CeloScan", - "url": "https://alfajores.celoscan.io" - }, - { - "apiUrl": "https://explorer.celo.org/alfajores/api", - "family": "blockscout", - "name": "Blockscout", - "url": "https://explorer.celo.org/alfajores" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 5, - "reorgPeriod": 0 - }, - "chainId": 44787, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Alfajores", - "domainId": 44787, - "domainRoutingIsm": "0xD1DCBe1546bb911f2570E939a231A28F14C29638", - "domainRoutingIsmFactory": "0x30d9A03762431F8A917a0C469E7A62Bf55092Ca6", - "fallbackRoutingHook": "0x3528B1aeF3a3d29E0eae90ad777A2b4A6a48aC3F", - "index": { - "from": 20231908 - }, - "interchainAccountIsm": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "interchainAccountRouter": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", - "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", - "interchainSecurityModule": "0xEE179dd1b1beD39449e81c003D9629e92A5c0085", - "isTestnet": true, - "mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", - "merkleTreeHook": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", - "name": "alfajores", - "nativeToken": { - "decimals": 18, - "name": "CELO", - "symbol": "CELO" - }, - "pausableHook": "0x4a2902395A40Ecf0B57CaB362b59bAffba9BB4aE", - "pausableIsm": "0xA4caB1565083D33899A6eE69B174cC7729b3EaDF", - "protocol": "ethereum", - "protocolFee": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", - "proxyAdmin": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", - "rpcUrls": [ - { - "http": "https://alfajores-forno.celo-testnet.org" - } - ], - "staticAggregationHookFactory": "0x71bB34Ee833467443628CEdFAA188B2387827Cee", - "staticAggregationIsm": "0x8B29157852340cC5d3d0E289be3B0344E8812173", - "staticAggregationIsmFactory": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", - "staticMerkleRootMultisigIsmFactory": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", - "staticMessageIdMultisigIsmFactory": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", - "storageGasOracle": "0x8356113754C7aCa297Db3089b89F87CC125499fb", - "testRecipient": "0x6489d13AcAd3B8dce4c5B31f375DE4f9451E7b38", - "testTokenRecipient": "0x92dC0a76452a9D9358D2d2dEd8CddA209DF67c45", - "timelockController": "0x0000000000000000000000000000000000000000", - "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", - "staticMerkleRootWeightedMultisigIsmFactory": "0x374961678da5911083599314974B94094513F95c", - "staticMessageIdWeightedMultisigIsmFactory": "0x1Fa22d908f5a5E7F5429D9146E5a3740D8AC10d7" - }, - "arbitrumsepolia": { - "aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19", - "blockExplorers": [ - { - "apiUrl": "https://api-sepolia.arbiscan.io/api", - "family": "etherscan", - "name": "Arbiscan", - "url": "https://sepolia.arbiscan.io" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 3, - "reorgPeriod": 0 - }, - "chainId": 421614, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Arbitrum Sepolia", - "domainId": 421614, - "domainRoutingIsm": "0xaCA2f65aFDa2cbC8BF28DdE096eCF83aCd121c0b", - "domainRoutingIsmFactory": "0xd785272D240B07719e417622cbd2cfA0E584d1bd", - "fallbackRoutingHook": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", - "index": { - "from": 49690504 - }, - "interchainGasPaymaster": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "interchainSecurityModule": "0x29cEAFEE1F76B9CE271750f86B2bD12C23F9dDb6", - "isTestnet": true, - "mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "merkleTreeHook": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "name": "arbitrumsepolia", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "pausableIsm": "0x54ba950390670f27892AFFC670Ba6ed598E5B8Df", - "protocol": "ethereum", - "protocolFee": "0x75f3E2a4f424401195A5E176246Ecc9f7e7680ff", - "proxyAdmin": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", - "rpcUrls": [ - { - "http": "https://arbitrum-sepolia.blockpi.network/v1/rpc/public" - }, - { - "http": "https://sepolia-rollup.arbitrum.io/rpc" - } - ], - "staticAggregationHookFactory": "0x0526E47C49742C15F8817ef8cf0d8FFc72139D4F", - "staticAggregationIsm": "0x29cEAFEE1F76B9CE271750f86B2bD12C23F9dDb6", - "staticAggregationIsmFactory": "0xfc8d0D2E15A36f1A3F3aE3Cb127B706c1f23Aadc", - "staticMerkleRootMultisigIsmFactory": "0x1D5EbC3e15e9ECDe0e3530C85899556797eeaea5", - "staticMessageIdMultisigIsmFactory": "0xF7F0DaB0BECE4498dAc7eb616e288809D4499371", - "storageGasOracle": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "technicalStack": "arbitrumnitro", - "testRecipient": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", - "validatorAnnounce": "0x1b33611fCc073aB0737011d5512EF673Bff74962", - "staticMerkleRootWeightedMultisigIsmFactory": "0x1aFD5191738d365C8079e955E4cEdDfe7e01C62d", - "staticMessageIdWeightedMultisigIsmFactory": "0xC81e6D1070aFA48DA4e4f35E744CC1aE43532a10" - }, - "basesepolia": { - "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", - "blockExplorers": [ - { - "apiUrl": "https://api-sepolia.basescan.org/api", - "family": "etherscan", - "name": "BaseScan", - "url": "https://sepolia.basescan.org" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 2, - "reorgPeriod": 1 - }, - "chainId": 84532, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Base Sepolia", - "domainId": 84532, - "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", - "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "index": { - "from": 13851043 - }, - "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", - "isTestnet": true, - "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "name": "basesepolia", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", - "pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "protocol": "ethereum", - "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", - "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "rpcUrls": [ - { - "http": "https://sepolia.base.org" - }, - { - "http": "https://base-sepolia-rpc.publicnode.com" - } - ], - "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", - "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", - "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", - "staticMerkleRootWeightedMultisigIsmFactory": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", - "staticMessageIdWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" - }, - "bsctestnet": { - "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", - "blockExplorers": [ - { - "apiUrl": "https://api-testnet.bscscan.com/api", - "family": "etherscan", - "name": "BscScan", - "url": "https://testnet.bscscan.com" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 3, - "reorgPeriod": 9 - }, - "chainId": 97, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "BSC Testnet", - "domainId": 97, - "domainRoutingIsm": "0x82A4aac9bED3DFDfb9569031E19d18431204681C", - "domainRoutingIsmFactory": "0xD2a0c68ed92D1Eb3C699D2808b06dd7b70367F92", - "fallbackRoutingHook": "0x2670ED2EC08cAd135307556685a96bD4c16b007b", - "index": { - "chunk": 1000, - "from": 34323977 - }, - "interchainAccountIsm": "0xa9D8Ec959F34272B1a56D09AF00eeee58970d3AE", - "interchainAccountRouter": "0x6d2B3e304E58c2a19f1492E7cf15CaF63Ce6e0d2", - "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", - "interchainSecurityModule": "0xE758870D4E50c2aF2b03341808d54d79F5ec3c1E", - "isTestnet": true, - "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", - "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", - "name": "bsctestnet", - "nativeToken": { - "decimals": 18, - "name": "BNB", - "symbol": "BNB" - }, - "pausableHook": "0xA71E50eFd93600933650A324AE43d395a8aE4AC7", - "pausableIsm": "0xb98B1596897d3c122111ea6b6b9014A95920F459", - "protocol": "ethereum", - "protocolFee": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", - "proxyAdmin": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", - "rpcUrls": [ - { - "http": "https://bsc-testnet.publicnode.com" - }, - { - "http": "https://bsc-testnet.blockpi.network/v1/rpc/public" - } - ], - "staticAggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", - "staticAggregationIsm": "0x8fb481f65d04c590b8507F75D05Ed29594590376", - "staticAggregationIsmFactory": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", - "staticMerkleRootMultisigIsmFactory": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", - "staticMessageIdMultisigIsmFactory": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", - "storageGasOracle": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", - "testRecipient": "0xfbcD1c00a3d809f36cC1A15918694B17B32c0b6c", - "testTokenRecipient": "0x260f6024119549a40595d0937471e607411E8ea5", - "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": 8000000000 - }, - "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465", - "staticMerkleRootWeightedMultisigIsmFactory": "0xCa152b249791Adf7A09C6c1bdbAb05e4A594966e", - "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1" - }, - "connextsepolia": { - "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", - "blockExplorers": [ - { - "apiUrl": "https://connext-sepolia.blockscout.com/api", - "family": "blockscout", - "name": "Connext Explorer", - "url": "https://connext-sepolia.blockscout.com" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 10, - "reorgPeriod": 0 - }, - "chainId": 6398, - "deployer": { - "name": "Everclear", - "url": "https://everclear.org" - }, - "displayName": "Connext Sepolia", - "domainId": 6398, - "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", - "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "fallbackRoutingHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", - "index": { - "from": 4950 - }, - "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", - "interchainSecurityModule": "0x415a45C98288059Cce9c32AE4a09AB19C91d5056", - "isTestnet": true, - "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", - "name": "connextsepolia", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", - "pausableIsm": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "protocol": "ethereum", - "protocolFee": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "rpcUrls": [ - { - "http": "https://rpc.connext-sepolia.gelato.digital" - } - ], - "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticAggregationIsm": "0x5e6Fe18eC7D4b159bDC5Fa0C32bB1996277B3ddF", - "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "storageGasOracle": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", - "technicalStack": "arbitrumnitro", - "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", - "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "staticMerkleRootWeightedMultisigIsmFactory": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", - "staticMessageIdWeightedMultisigIsmFactory": "0xcCB305B1f21e5FbC85D1DD7Be5cd8d5bf5B7f863" - }, - "eclipsetestnet": { - "blocks": { - "confirmations": 1, - "estimateBlockTime": 0.4, - "reorgPeriod": 0 - }, - "chainId": 239092742, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Eclipse Testnet", - "domainId": 239092742, - "index": { - "from": 1, - "mode": "sequence" - }, - "interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", - "isTestnet": true, - "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", - "merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", - "name": "eclipsetestnet", - "nativeToken": { - "decimals": 9, - "name": "Ether", - "symbol": "ETH" - }, - "protocol": "sealevel", - "rpcUrls": [ - { - "http": "https://testnet.dev2.eclipsenetwork.xyz" - } - ], - "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" - }, - "ecotestnet": { - "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", - "blockExplorers": [ - { - "apiUrl": "https://eco-testnet.explorer.caldera.xyz/api", - "family": "blockscout", - "name": "ECO Testnet explorer", - "url": "https://eco-testnet.explorer.caldera.xyz/" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 1, - "reorgPeriod": 0 - }, - "chainId": 471923, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Eco Testnet", - "domainId": 471923, - "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", - "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "index": { - "from": 1606754 - }, - "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", - "isTestnet": true, - "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "name": "ecotestnet", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", - "pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "protocol": "ethereum", - "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", - "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "rpcUrls": [ - { - "http": "https://eco-testnet.rpc.caldera.xyz/http" - } - ], - "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", - "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", - "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", - "staticMerkleRootWeightedMultisigIsmFactory": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "staticMessageIdWeightedMultisigIsmFactory": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E" - }, - "fuji": { - "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", - "blockExplorers": [ - { - "apiUrl": "https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api", - "family": "etherscan", - "name": "SnowTrace", - "url": "https://testnet.snowtrace.io" - } - ], - "blocks": { - "confirmations": 3, - "estimateBlockTime": 2, - "reorgPeriod": 3 - }, - "chainId": 43113, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Fuji", - "domainId": 43113, - "domainRoutingIsm": "0xe82EbDCC1B546CEDa0cf5B5495728700f6dE41B4", - "domainRoutingIsmFactory": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", - "fallbackRoutingHook": "0xc684f7F50DB4b2563218512e021fBdd0BeD6b57E", - "index": { - "from": 26503317 - }, - "interchainAccountIsm": "0xfaB4815BDC5c60c6bD625459C8577aFdD79D9311", - "interchainAccountRouter": "0xeEF6933122894fF217a7dd07510b3D64b747e29b", - "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "interchainSecurityModule": "0x3dA17519c799f86000FADb7bfCCdc0CaB0D36fDd", - "isTestnet": true, - "mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", - "name": "fuji", - "nativeToken": { - "decimals": 18, - "name": "Avalanche", - "symbol": "AVAX" - }, - "pausableHook": "0x495e9E119b2aa848b418EF6A4d30b42803de43A9", - "pausableIsm": "0x8e3D9139547be48942Aa4a489bc250FEa51679fa", - "protocol": "ethereum", - "protocolFee": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", - "proxyAdmin": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "rpcUrls": [ - { - "http": "https://api.avax-test.network/ext/bc/C/rpc", - "pagination": { - "maxBlockRange": 2048 - } - } - ], - "staticAggregationHookFactory": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", - "staticAggregationIsm": "0xdDB75f480841A8333A31c4198ecf1780936B222D", - "staticAggregationIsmFactory": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", - "staticMerkleRootMultisigIsmFactory": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", - "staticMessageIdMultisigIsmFactory": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", - "storageGasOracle": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", - "testRecipient": "0x44a7e1d76fD8AfA244AdE7278336E3D5C658D398", - "testTokenRecipient": "0x9CC10c844B3Bbae2444E39991aB027C4A05D1F2e", - "timelockController": "0x0000000000000000000000000000000000000000", - "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", - "staticMerkleRootWeightedMultisigIsmFactory": "0xff93F32997Ac5450995121385aCE96b184efe89E", - "staticMessageIdWeightedMultisigIsmFactory": "0x8eAB8cBb9037e818C321f675c0bc2EA4649003CF" - }, - "holesky": { - "aggregationHook": "0xb1FfD51f03c69A0a3e5AFEBDE639752DB1d56bc9", - "blockExplorers": [ - { - "apiUrl": "https://api-holesky.etherscan.io/api", - "family": "etherscan", - "name": "Etherscan", - "url": "https://holesky.etherscan.io" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 13, - "reorgPeriod": 2 - }, - "chainId": 17000, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Holesky", - "domainId": 17000, - "domainRoutingIsm": "0x51890869940b4B7eD7426A612676eC63223eF3Db", - "domainRoutingIsmFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", - "fallbackRoutingHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", - "index": { - "from": 1543015 - }, - "interchainGasPaymaster": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", - "interchainSecurityModule": "0x7C32096A30Ef0bC550e3b8AcB0D87F72A3910D04", - "isTestnet": true, - "mailbox": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", - "merkleTreeHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", - "name": "holesky", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", - "pausableIsm": "0x80fE4Cb8c70fc60B745d4ffD4403c27a8cBC9e02", - "protocol": "ethereum", - "protocolFee": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", - "proxyAdmin": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", - "rpcUrls": [ - { - "http": "https://ethereum-holesky-rpc.publicnode.com" - } - ], - "staticAggregationHookFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", - "staticAggregationIsm": "0x296Be783e0b1CdDed847E34e6587C24dDb710cf9", - "staticAggregationIsmFactory": "0x54148470292C24345fb828B003461a9444414517", - "staticMerkleRootMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", - "staticMessageIdMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "storageGasOracle": "0x2b2a158B4059C840c7aC67399B153bb567D06303", - "testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", - "staticMerkleRootWeightedMultisigIsmFactory": "0xFb55597F07417b08195Ba674f4dd58aeC9B89FBB", - "staticMessageIdWeightedMultisigIsmFactory": "0x0E18b28D98C2efDb59252c021320F203305b1B66" - }, - "optimismsepolia": { - "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", - "blockExplorers": [ - { - "apiUrl": "https://api-sepolia-optimistic.etherscan.io/api", - "family": "etherscan", - "name": "OP Sepolia Explorer", - "url": "https://sepolia-optimistic.etherscan.io" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 2, - "reorgPeriod": 0 - }, - "chainId": 11155420, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Optimism Sepolia", - "domainId": 11155420, - "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", - "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "gasCurrencyCoinGeckoId": "ethereum", - "index": { - "from": 15833917 - }, - "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", - "isTestnet": true, - "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "name": "optimismsepolia", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", - "pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "protocol": "ethereum", - "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", - "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "rpcUrls": [ - { - "http": "https://sepolia.optimism.io" - } - ], - "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", - "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", - "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", - "staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", - "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F" - }, - "plumetestnet": { - "aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", - "blockExplorers": [ - { - "apiUrl": "https://plume-testnet.explorer.caldera.xyz/api", - "family": "blockscout", - "name": "Plume Testnet Explorer", - "url": "https://plume-testnet.explorer.caldera.xyz" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 5, - "reorgPeriod": 0 - }, - "chainId": 161221135, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Plume Testnet", - "domainId": 161221135, - "domainRoutingIsm": "0xF554Be1611572dF824556e8060bf90Fe5bE7Ff08", - "domainRoutingIsmFactory": "0x54148470292C24345fb828B003461a9444414517", - "fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", - "index": { - "from": 5284139 - }, - "interchainAccountIsm": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", - "interchainAccountRouter": "0xB6F8aA9B1b314A6E6DFB465DD3e0E95936347517", - "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "interchainSecurityModule": "0x5708e5CAd632898E16D6A7bA286EC8b8eFD3056b", - "isTestnet": true, - "mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", - "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "name": "plumetestnet", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "pausableIsm": "0xDaAc5C311d6e9134C725171Ef06a8a2BaAF4a10f", - "protocol": "ethereum", - "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", - "proxyAdmin": "0x589C201a07c26b4725A4A829d772f24423da480B", - "rpcConsensusType": "single", - "rpcUrls": [ - { - "http": "https://plume-testnet.rpc.caldera.xyz/http" - } - ], - "staticAggregationHookFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "staticAggregationIsm": "0xDaE6E59fB970F8df1cCCC7d230a7cdeD8BDfCb95", - "staticAggregationIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", - "staticMerkleRootMultisigIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "staticMessageIdMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "technicalStack": "arbitrumnitro", - "testRecipient": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", - "timelockController": "0x0000000000000000000000000000000000000000", - "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", - "staticMerkleRootWeightedMultisigIsmFactory": "0x7924A1569fE0b860F1eA3c7b4Ed97b5528946f83", - "staticMessageIdWeightedMultisigIsmFactory": "0xb04961F492f447A8bA10f6694Bd888C7619CD2D5" - }, - "polygonamoy": { - "aggregationHook": "0x06a54A2db82D37410C1383c51F96Bd7b3ABD243E", - "blockExplorers": [ - { - "apiUrl": "https://api-amoy.polygonscan.com/api", - "family": "etherscan", - "name": "Polygon Amoy Explorer", - "url": "https://amoy.polygonscan.com" - } - ], - "blocks": { - "confirmations": 5, - "estimateBlockTime": 2, - "reorgPeriod": 10 - }, - "chainId": 80002, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Polygon Amoy", - "domainId": 80002, - "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", - "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", - "index": { - "from": 10634605 - }, - "interchainGasPaymaster": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", - "interchainSecurityModule": "0x831Ee59F524C94A320821f4d30B6581Ace69379d", - "isTestnet": true, - "mailbox": "0x54148470292C24345fb828B003461a9444414517", - "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "name": "polygonamoy", - "nativeToken": { - "decimals": 18, - "name": "Polygon Ecosystem Token", - "symbol": "POL" - }, - "pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "pausableIsm": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", - "protocol": "ethereum", - "protocolFee": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", - "proxyAdmin": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", - "rpcUrls": [ - { - "http": "https://rpc-amoy.polygon.technology" - }, - { - "http": "https://polygon-amoy-bor-rpc.publicnode.com" - }, - { - "http": "https://polygon-amoy.blockpi.network/v1/rpc/public" - }, - { - "http": "https://rpc.ankr.com/polygon_amoy" - } - ], - "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "staticAggregationIsm": "0x1D770b978d915bD96F2ad41b25824C4193EBAfA2", - "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "storageGasOracle": "0xD0680F80F4f947968206806C2598Cbc5b6FE5b03", - "testRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48", - "validatorAnnounce": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", - "staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", - "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F" - }, - "scrollsepolia": { - "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", - "blockExplorers": [ - { - "apiUrl": "https://api-sepolia.scrollscan.com/api", - "family": "etherscan", - "name": "Scroll Explorer", - "url": "https://sepolia.scrollscan.dev/" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 3, - "reorgPeriod": 1 - }, - "chainId": 534351, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Scroll Sepolia", - "domainId": 534351, - "domainRoutingIsm": "0x606511c593fd78AFbeD8A2e02EDe2C6179722276", - "domainRoutingIsmFactory": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", - "fallbackRoutingHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", - "index": { - "from": 1344054 - }, - "interchainAccountIsm": "0xE023239c8dfc172FF008D8087E7442d3eBEd9350", - "interchainAccountRouter": "0xe17c37212d785760E8331D4A4395B17b34Ba8cDF", - "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "interchainSecurityModule": "0x476a8f40c81E69f5d676ccaA9709c0dE61F907E1", - "isTestnet": true, - "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", - "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "name": "scrollsepolia", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0x390A48fC8Cb8F29B3ffE1B95aD3773414B8DD704", - "pausableIsm": "0xcD19Ff7306E04EA6b8f4B5Ab1c5A198c186aaB42", - "protocol": "ethereum", - "protocolFee": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "proxyAdmin": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "rpcUrls": [ - { - "http": "https://sepolia-rpc.scroll.io" - }, - { - "http": "https://rpc.ankr.com/scroll_sepolia_testnet" - }, - { - "http": "https://scroll-sepolia.blockpi.network/v1/rpc/public" - }, - { - "http": "https://scroll-sepolia.chainstacklabs.com" - }, - { - "http": "https://scroll-public.scroll-testnet.quiknode.pro" - } - ], - "staticAggregationHookFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "staticAggregationIsm": "0xcc5BAaa44A8749c7C566b1cb578a315427632053", - "staticAggregationIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "staticMerkleRootMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMessageIdMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "storageGasOracle": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", - "testRecipient": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", - "testTokenRecipient": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", - "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": 500000000 - }, - "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381", - "staticMerkleRootWeightedMultisigIsmFactory": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb", - "staticMessageIdWeightedMultisigIsmFactory": "0x63dFf524F1c7361f4F1bf07D658Bf7f2d5Dd5B20" - }, - "sepolia": { - "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", - "blockExplorers": [ - { - "apiUrl": "https://api-sepolia.etherscan.io/api", - "family": "etherscan", - "name": "Etherscan", - "url": "https://sepolia.etherscan.io" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 13, - "reorgPeriod": 2 - }, - "chainId": 11155111, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Sepolia", - "domainId": 11155111, - "domainRoutingIsm": "0xfa9a26cCc5417d1C1D03C949b5013Bb5898dA905", - "domainRoutingIsmFactory": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", - "fallbackRoutingHook": "0x17Dc724B7a2F09141C13b8AC33B396073785c2BC", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-sepolia.safe.global", - "index": { - "from": 4517401 - }, - "interchainAccountIsm": "0x83a3068B719F764d413625dA77468ED74789ae02", - "interchainAccountRouter": "0x8e131c8aE5BF1Ed38D05a00892b6001a7d37739d", - "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", - "interchainSecurityModule": "0x76B21a2241f6A6FbaE624A7Fe5d7D3919C9ce3E3", - "isTestnet": true, - "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", - "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", - "name": "sepolia", - "nativeToken": { - "decimals": 18, - "name": "Ether", - "symbol": "ETH" - }, - "pausableHook": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", - "pausableIsm": "0x21bdaB116d9DA77E312910fB53aD35dD82C8a76c", - "protocol": "ethereum", - "protocolFee": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", - "proxyAdmin": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", - "rpcUrls": [ - { - "http": "https://ethereum-sepolia.publicnode.com" - }, - { - "http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" - }, - { - "http": "https://rpc.sepolia.org" - } - ], - "staticAggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", - "staticAggregationIsm": "0x7856078C4881e236660Be82fdb12473B4a33cCFf", - "staticAggregationIsmFactory": "0xC83e12EF2627ACE445C298e6eC418684918a6002", - "staticMerkleRootMultisigIsmFactory": "0x0a71AcC99967829eE305a285750017C4916Ca269", - "staticMessageIdMultisigIsmFactory": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", - "storageGasOracle": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", - "testRecipient": "0xeDc1A3EDf87187085A3ABb7A9a65E1e7aE370C07", - "testTokenRecipient": "0x031AD9c560D37baC7d6Bd2d27A2443bAfd10101A", - "timelockController": "0x0000000000000000000000000000000000000000", - "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", - "staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f", - "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94" - }, - "solanatestnet": { - "blockExplorers": [ - { - "apiUrl": "https://explorer.solana.com?cluster=testnet", - "family": "other", - "name": "Solana Explorer", - "url": "https://explorer.solana.com?cluster=testnet" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 0.4, - "reorgPeriod": 0 - }, - "chainId": 1399811150, - "deployer": { - "name": "Abacus Works", - "url": "https://www.hyperlane.xyz" - }, - "displayName": "Solana Testnet", - "displayNameShort": "Sol Testnet", - "domainId": 1399811150, - "index": { - "from": 1, - "mode": "sequence" - }, - "interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", - "isTestnet": true, - "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", - "merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", - "name": "solanatestnet", - "nativeToken": { - "decimals": 9, - "name": "Solana", - "symbol": "SOL" - }, - "protocol": "sealevel", - "rpcUrls": [ - { - "http": "https://api.testnet.solana.com" - } - ], - "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" - }, - "superpositiontestnet": { - "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", - "blockExplorers": [ - { - "apiUrl": "https://testnet-explorer.superposition.so/api", - "family": "blockscout", - "name": "CatScan", - "url": "https://testnet-explorer.superposition.so" - } - ], - "blocks": { - "confirmations": 1, - "estimateBlockTime": 1, - "reorgPeriod": 1 - }, - "chainId": 98985, - "displayName": "Superposition Testnet", - "domainId": 98985, - "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", - "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "fallbackRoutingHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", - "index": { - "from": 3111622 - }, - "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", - "interchainSecurityModule": "0x415a45C98288059Cce9c32AE4a09AB19C91d5056", - "isTestnet": true, - "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", - "name": "superpositiontestnet", - "nativeToken": { - "decimals": 18, - "name": "Superposition", - "symbol": "SPN" - }, - "pausableHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", - "pausableIsm": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "protocol": "ethereum", - "protocolFee": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "rpcUrls": [ - { - "http": "https://testnet-rpc.superposition.so" - } - ], - "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "staticAggregationIsm": "0x5e6Fe18eC7D4b159bDC5Fa0C32bB1996277B3ddF", - "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "storageGasOracle": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", - "technicalStack": "arbitrumnitro", - "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", - "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "staticMerkleRootWeightedMultisigIsmFactory": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c", - "staticMessageIdWeightedMultisigIsmFactory": "0x867f2089D09903f208AeCac84E599B90E5a4A821" - } - }, - "defaultRpcConsensusType": "fallback" -} diff --git a/rust/hyperlane-base/src/db/mod.rs b/rust/hyperlane-base/src/db/mod.rs deleted file mode 100644 index 55c11ddc9..000000000 --- a/rust/hyperlane-base/src/db/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub use rocks::*; -mod rocks; diff --git a/rust/.cargo/config.toml b/rust/main/.cargo/config.toml similarity index 100% rename from rust/.cargo/config.toml rename to rust/main/.cargo/config.toml diff --git a/rust/.vscode/extensions.json b/rust/main/.vscode/extensions.json similarity index 100% rename from rust/.vscode/extensions.json rename to rust/main/.vscode/extensions.json diff --git a/rust/.vscode/settings.json b/rust/main/.vscode/settings.json similarity index 100% rename from rust/.vscode/settings.json rename to rust/main/.vscode/settings.json diff --git a/rust/Cargo.lock b/rust/main/Cargo.lock similarity index 80% rename from rust/Cargo.lock rename to rust/main/Cargo.lock index b5d7c442f..27b11dcab 100644 --- a/rust/Cargo.lock +++ b/rust/main/Cargo.lock @@ -20,7 +20,7 @@ dependencies = [ "ethers", "fuels", "fuels-code-gen", - "which", + "which 4.4.2", ] [[package]] @@ -54,6 +54,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "aead" version = "0.5.2" @@ -66,24 +72,12 @@ dependencies = [ [[package]] name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aes" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] @@ -94,9 +88,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" dependencies = [ "aead", - "aes 0.8.3", - "cipher 0.4.4", - "ctr 0.9.2", + "aes", + "cipher", + "ctr", "polyval", "subtle", "zeroize", @@ -104,20 +98,20 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.15", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -127,9 +121,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -157,9 +151,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -186,70 +180,143 @@ dependencies = [ ] [[package]] -name = "anstream" -version = "0.6.7" +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "ark-bls12-381" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "c775f0d12169cba7aae4caeb547bb6a50781c7449a8aa53793827c9ec4abf488" dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", ] [[package]] -name = "anstyle" -version = "1.0.4" +name = "ark-ec" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defd9a439d56ac24968cca0571f598a61bc8c55f71d50a89cda591cb750670ba" +dependencies = [ + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", + "itertools 0.10.5", + "num-traits", + "rayon", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rayon", + "rustc_version", + "zeroize", +] [[package]] -name = "anstyle-parse" -version = "0.2.3" +name = "ark-ff-asm" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "utf8parse", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] -name = "anstyle-query" -version = "1.0.2" +name = "ark-ff-macros" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "windows-sys 0.52.0", + "num-bigint 0.4.6", + "num-traits", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", ] [[package]] -name = "anstyle-wincon" -version = "3.0.2" +name = "ark-poly" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "d320bfc44ee185d899ccbadfa8bc31aab923ce1558716e1997a1e74057fe86bf" dependencies = [ - "anstyle", - "windows-sys 0.52.0", + "ark-ff", + "ark-serialize", + "ark-std", + "derivative", + "hashbrown 0.13.2", ] [[package]] -name = "anyhow" -version = "1.0.79" +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "ark-std" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", + "rayon", +] [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ascii" @@ -279,10 +346,10 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", - "synstructure", + "synstructure 0.12.6", ] [[package]] @@ -291,8 +358,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -304,9 +371,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.5" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" dependencies = [ "brotli", "flate2", @@ -352,20 +419,20 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -415,28 +482,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "auto_impl" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ - "proc-macro-error", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 1.0.109", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "axum" @@ -449,7 +515,7 @@ dependencies = [ "bitflags 1.3.2", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "hyper", "itoa", @@ -479,7 +545,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", + "http 0.2.12", "http-body", "mime", "rustversion", @@ -489,17 +555,18 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", "cfg-if", "libc", - "miniz_oxide", + "miniz_oxide 0.7.4", "object", "rustc-demangle", + "serde", ] [[package]] @@ -518,8 +585,8 @@ checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -569,6 +636,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -587,26 +660,32 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +[[package]] +name = "bech32" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" + [[package]] name = "bigdecimal" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" dependencies = [ - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", ] [[package]] name = "bigdecimal" -version = "0.4.2" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06619be423ea5bb86c95f087d5707942791a08a85530df0db2209a3ecfb8bc9" +checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" dependencies = [ "autocfg", "libm", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", ] @@ -633,12 +712,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "rustc-hash", "shlex", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] @@ -649,9 +728,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitmaps" @@ -755,15 +837,15 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "bnum" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab9008b6bb9fc80b5277f2fe481c09e828743d9151203e804583eb4c9e15b31d" +checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f" [[package]] -name = "borrown" -version = "0.1.0" +name = "bnum" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b57b368e638ed60664350ea4f2f3647a0192173478df2736cc255a025a796" +checksum = "3e31ea183f6ee62ac8b8a8cf7feddd766317adfb13ff469de57ce033efd6a790" [[package]] name = "borsh" @@ -777,11 +859,11 @@ dependencies = [ [[package]] name = "borsh" -version = "1.3.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" dependencies = [ - "borsh-derive 1.3.1", + "borsh-derive 1.5.1", "cfg_aliases", ] @@ -794,21 +876,21 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.76", + "proc-macro2 1.0.86", "syn 1.0.109", ] [[package]] name = "borsh-derive" -version = "1.3.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" dependencies = [ "once_cell", - "proc-macro-crate 3.0.0", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", "syn_derive", ] @@ -818,8 +900,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -829,16 +911,16 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "brotli" -version = "3.4.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -847,9 +929,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.5.1" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -863,18 +945,19 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bs58" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5353f36341f7451062466f0b755b96ac3a9547e4d7f6b70d603fc721a7d7896" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ + "sha2 0.10.8", "tinyvec", ] [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bv" @@ -900,9 +983,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytecheck" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ "bytecheck_derive", "ptr_meta", @@ -911,33 +994,33 @@ dependencies = [ [[package]] name = "bytecheck_derive" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -948,23 +1031,13 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" dependencies = [ "serde", ] -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - [[package]] name = "bzip2-sys" version = "0.1.11+1.0.8" @@ -978,9 +1051,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.6" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" dependencies = [ "serde", ] @@ -997,9 +1070,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +checksum = "24b1f0365a6c6bb4020cd05806fd0d33c44d38046b8bd7f0e40814b9763cabfc" dependencies = [ "serde", ] @@ -1020,9 +1093,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" dependencies = [ "jobserver", "libc", @@ -1045,15 +1118,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1061,25 +1134,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", -] - -[[package]] -name = "chrono-humanize" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" -dependencies = [ - "chrono", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array 0.14.7", + "windows-targets 0.52.6", ] [[package]] @@ -1100,7 +1155,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.4", + "libloading 0.8.5", ] [[package]] @@ -1126,35 +1181,13 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive 3.2.25", - "clap_lex 0.2.4", + "clap_derive", + "clap_lex", "indexmap 1.9.3", "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.16.0", -] - -[[package]] -name = "clap" -version = "4.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253" -dependencies = [ - "clap_builder", - "clap_derive 4.4.7", -] - -[[package]] -name = "clap_builder" -version = "4.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa" -dependencies = [ - "anstream", - "anstyle", - "clap_lex 0.6.0", - "strsim 0.10.0", + "textwrap 0.16.1", ] [[package]] @@ -1165,23 +1198,11 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck 0.4.1", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", -] - [[package]] name = "clap_lex" version = "0.2.4" @@ -1191,12 +1212,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - [[package]] name = "cloudabi" version = "0.0.3" @@ -1220,9 +1235,9 @@ checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" dependencies = [ "bincode", "bs58 0.4.0", - "coins-core", + "coins-core 0.7.0", "digest 0.10.7", - "getrandom 0.2.12", + "getrandom 0.2.15", "hmac 0.12.1", "k256 0.11.6", "lazy_static", @@ -1231,6 +1246,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "coins-bip32" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b6be4a5df2098cd811f3194f64ddb96c267606bffd9689ac7b0160097b01ad3" +dependencies = [ + "bs58 0.5.1", + "coins-core 0.8.7", + "digest 0.10.7", + "hmac 0.12.1", + "k256 0.13.4", + "serde", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "coins-bip39" version = "0.7.0" @@ -1238,8 +1269,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" dependencies = [ "bitvec 0.17.4", - "coins-bip32", - "getrandom 0.2.12", + "coins-bip32 0.7.0", + "getrandom 0.2.15", "hex 0.4.3", "hmac 0.12.1", "pbkdf2 0.11.0", @@ -1248,6 +1279,22 @@ dependencies = [ "thiserror", ] +[[package]] +name = "coins-bip39" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" +dependencies = [ + "bitvec 1.0.1", + "coins-bip32 0.8.7", + "hmac 0.12.1", + "once_cell", + "pbkdf2 0.12.2", + "rand 0.8.5", + "sha2 0.10.8", + "thiserror", +] + [[package]] name = "coins-core" version = "0.7.0" @@ -1269,11 +1316,31 @@ dependencies = [ "thiserror", ] +[[package]] +name = "coins-core" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5286a0843c21f8367f7be734f89df9b822e0321d8bcce8d6e735aadff7d74979" +dependencies = [ + "base64 0.21.7", + "bech32 0.9.1", + "bs58 0.5.1", + "digest 0.10.7", + "generic-array 0.14.7", + "hex 0.4.3", + "ripemd", + "serde", + "serde_derive", + "sha2 0.10.8", + "sha3 0.10.8", + "thiserror", +] + [[package]] name = "color-eyre" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" dependencies = [ "backtrace", "color-spantrace", @@ -1296,12 +1363,6 @@ dependencies = [ "tracing-error", ] -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - [[package]] name = "combine" version = "3.8.1" @@ -1354,8 +1415,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" dependencies = [ "futures-core", - "prost 0.12.4", - "prost-types 0.12.4", + "prost 0.12.6", + "prost-types 0.12.6", "tonic 0.10.2", "tracing-core", ] @@ -1372,7 +1433,7 @@ dependencies = [ "futures-task", "hdrhistogram", "humantime", - "prost-types 0.12.4", + "prost-types 0.12.6", "serde", "serde_json", "thread_local", @@ -1439,9 +1500,9 @@ dependencies = [ [[package]] name = "cookie" -version = "0.16.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24" dependencies = [ "percent-encoding", "time", @@ -1450,12 +1511,12 @@ dependencies = [ [[package]] name = "cookie_store" -version = "0.16.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" +checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" dependencies = [ "cookie", - "idna 0.2.3", + "idna 0.3.0", "log", "publicsuffix", "serde", @@ -1477,9 +1538,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cosmos-sdk-proto" @@ -1502,8 +1563,8 @@ dependencies = [ "cosmos-sdk-proto", "ecdsa 0.16.9", "eyre", - "getrandom 0.2.12", - "k256 0.13.3", + "getrandom 0.2.15", + "k256 0.13.4", "rand_core 0.6.4", "serde", "serde_json", @@ -1514,34 +1575,73 @@ dependencies = [ "tokio", ] +[[package]] +name = "cosmwasm-core" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d905990ef3afb5753bb709dc7de88e9e370aa32bcc2f31731d4b533b63e82490" + [[package]] name = "cosmwasm-crypto" -version = "1.5.2" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f862b355f7e47711e0acfe6af92cb3fd8fd5936b66a9eaa338b51edabd1e77d" +dependencies = [ + "digest 0.10.7", + "ed25519-zebra 3.1.0", + "k256 0.13.4", + "rand_core 0.6.4", + "thiserror", +] + +[[package]] +name = "cosmwasm-crypto" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" +checksum = "5b2a7bd9c1dd9a377a4dc0f4ad97d24b03c33798cd5a6d7ceb8869b41c5d2f2d" dependencies = [ + "ark-bls12-381", + "ark-ec", + "ark-ff", + "ark-serialize", + "cosmwasm-core", "digest 0.10.7", "ecdsa 0.16.9", - "ed25519-zebra", - "k256 0.13.3", + "ed25519-zebra 4.0.3", + "k256 0.13.4", + "num-traits", + "p256", "rand_core 0.6.4", + "rayon", + "sha2 0.10.8", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40abec852f3d4abec6d44ead9a58b78325021a1ead1e7229c3471414e57b2e49" +checksum = "cd85de6467cd1073688c86b39833679ae6db18cf4771471edd9809f15f1679f1" dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cosmwasm-derive" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029910b409398fdf81955d7301b906caf81f2c42b013ea074fbd89720229c424" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "cosmwasm-schema" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b166215fbfe93dc5575bae062aa57ae7bb41121cffe53bac33b033257949d2a9" +checksum = "5b4cd28147a66eba73720b47636a58097a979ad8c8bfdb4ed437ebcbfe362576" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -1552,44 +1652,67 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf12f8e20bb29d1db66b7ca590bc2f670b548d21e9be92499bc0f9022a994a8" +checksum = "9acd45c63d41bc9b16bc6dc7f6bd604a8c2ad29ce96c8f3c96d7fc8ef384392e" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.5.2" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad011ae7447188e26e4a7dbca2fcd0fc186aa21ae5c86df0503ea44c78f9e469" +checksum = "2685c2182624b2e9e17f7596192de49a3f86b7a0c9a5f6b25c1df5e24592e836" dependencies = [ "base64 0.21.7", "bech32 0.9.1", - "bnum", - "cosmwasm-crypto", - "cosmwasm-derive", + "bnum 0.10.0", + "cosmwasm-crypto 1.5.7", + "cosmwasm-derive 1.5.7", "derivative", "forward_ref", "hex 0.4.3", "schemars", "serde", - "serde-json-wasm", + "serde-json-wasm 0.5.2", "sha2 0.10.8", "static_assertions 1.1.0", "thiserror", ] [[package]] -name = "cosmwasm-storage" -version = "1.5.2" +name = "cosmwasm-std" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" +checksum = "51dec99a2e478715c0a4277f0dbeadbb8466500eb7dec873d0924edd086e77f1" dependencies = [ - "cosmwasm-std", + "base64 0.22.1", + "bech32 0.11.0", + "bnum 0.11.0", + "cosmwasm-core", + "cosmwasm-crypto 2.1.3", + "cosmwasm-derive 2.1.3", + "derive_more 1.0.0", + "hex 0.4.3", + "rand_core 0.6.4", + "schemars", + "serde", + "serde-json-wasm 1.0.1", + "sha2 0.10.8", + "static_assertions 1.1.0", + "thiserror", +] + +[[package]] +name = "cosmwasm-storage" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" +dependencies = [ + "cosmwasm-std 1.5.7", "serde", ] @@ -1604,33 +1727,33 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "critical-section" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" +checksum = "f64009896348fc5af4222e9cf7d7d82a95a256c634ebcf61c53e4ea461422242" [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -1665,9 +1788,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1676,15 +1799,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] -name = "crypto-bigint" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +name = "crypto" +version = "0.1.0" dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", + "elliptic-curve 0.13.8", + "hex 0.4.3", + "k256 0.13.4", + "thiserror", ] [[package]] @@ -1734,9 +1855,9 @@ dependencies = [ [[package]] name = "crypto-mac" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ "generic-array 0.14.7", "subtle", @@ -1751,32 +1872,23 @@ dependencies = [ "sct 0.6.1", ] -[[package]] -name = "ctr" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" -dependencies = [ - "cipher 0.3.0", -] - [[package]] name = "ctr" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] name = "ctrlc" -version = "3.4.2" +version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" +checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" dependencies = [ - "nix 0.27.1", - "windows-sys 0.52.0", + "nix 0.29.0", + "windows-sys 0.59.0", ] [[package]] @@ -1792,6 +1904,33 @@ dependencies = [ "zeroize", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "curve25519-dalek-ng" version = "4.1.1" @@ -1811,7 +1950,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5ff29294ee99373e2cd5fd21786a3c0ced99a52fec2ca347d565489c61b723c" dependencies = [ - "cosmwasm-std", + "cosmwasm-std 1.5.7", "schemars", "serde", ] @@ -1823,7 +1962,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c4a657e5caacc3a0d00ee96ca8618745d050b8f757c709babafb81208d4239c" dependencies = [ "cosmwasm-schema", - "cosmwasm-std", + "cosmwasm-std 1.5.7", "cw2", "schemars", "semver", @@ -1838,7 +1977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6c120b24fbbf5c3bedebb97f2cc85fbfa1c3287e09223428e7e597b5293c1fa" dependencies = [ "cosmwasm-schema", - "cosmwasm-std", + "cosmwasm-std 1.5.7", "cw-storage-plus", "schemars", "semver", @@ -1853,7 +1992,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "526e39bb20534e25a1cd0386727f0038f4da294e5e535729ba3ef54055246abd" dependencies = [ "cosmwasm-schema", - "cosmwasm-std", + "cosmwasm-std 1.5.7", "cw-utils", "schemars", "serde", @@ -1866,7 +2005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ad79e86ea3707229bf78df94e08732e8f713207b4a77b2699755596725e7d9" dependencies = [ "cosmwasm-schema", - "cosmwasm-std", + "cosmwasm-std 1.5.7", "cw-storage-plus", "cw2", "cw20", @@ -1900,8 +2039,8 @@ dependencies = [ "darling 0.13.4", "graphql-parser", "once_cell", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.10.0", "syn 1.0.109", ] @@ -1936,6 +2075,16 @@ dependencies = [ "darling_macro 0.14.4", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core 0.20.10", + "darling_macro 0.20.10", +] + [[package]] name = "darling_core" version = "0.13.4" @@ -1944,8 +2093,8 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.10.0", "syn 1.0.109", ] @@ -1958,12 +2107,26 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "strsim 0.10.0", "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.86", + "quote 1.0.37", + "strsim 0.11.1", + "syn 2.0.77", +] + [[package]] name = "darling_macro" version = "0.13.4" @@ -1971,7 +2134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.35", + "quote 1.0.37", "syn 1.0.109", ] @@ -1982,26 +2145,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.35", + "quote 1.0.37", "syn 1.0.109", ] [[package]] -name = "dashmap" -version = "4.0.2" +name = "darling_macro" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "cfg-if", - "num_cpus", - "rayon", + "darling_core 0.20.10", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "der" @@ -2024,9 +2187,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid 0.9.6", "zeroize", @@ -2041,7 +2204,7 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "rusticata-macros", ] @@ -2068,8 +2231,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -2079,8 +2242,8 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -2100,8 +2263,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -2117,15 +2280,36 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustc_version", - "syn 1.0.109", + "syn 2.0.77", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", + "unicode-xid 0.2.5", ] [[package]] @@ -2137,7 +2321,7 @@ dependencies = [ "backtrace", "lazy_static", "mintex", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rustc-hash", "serde", "serde_json", @@ -2192,15 +2376,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dir-diff" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ad16bf5f84253b50d6557681c58c3ab67c47c77d39fed9aeb56e947290bd10" -dependencies = [ - "walkdir", -] - [[package]] name = "dirs" version = "4.0.0" @@ -2244,13 +2419,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -2294,17 +2469,23 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "eager" @@ -2330,7 +2511,7 @@ version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ - "der 0.7.8", + "der 0.7.9", "digest 0.10.7", "elliptic-curve 0.13.8", "rfc6979 0.4.0", @@ -2342,7 +2523,7 @@ dependencies = [ name = "ecdsa-signature" version = "0.1.0" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.15", "hyperlane-core", "solana-program", "thiserror", @@ -2385,7 +2566,7 @@ name = "ed25519-dalek" version = "1.0.1" source = "git+https://github.com/Eclipse-Laboratories-Inc/ed25519-dalek?branch=main#7529d65506147b6cb24ca6d8f4fc062cac33b395" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.2", "ed25519 1.5.3", "rand 0.7.3", "serde", @@ -2393,6 +2574,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", + "sha2 0.10.8", + "subtle", +] + [[package]] name = "ed25519-dalek-bip32" version = "0.2.0" @@ -2400,7 +2593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" dependencies = [ "derivation-path", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "hmac 0.12.1", "sha2 0.10.8", ] @@ -2411,7 +2604,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ - "curve25519-dalek", + "curve25519-dalek 3.2.2", "hashbrown 0.12.3", "hex 0.4.3", "rand_core 0.6.4", @@ -2421,37 +2614,25 @@ dependencies = [ ] [[package]] -name = "educe" -version = "0.4.23" +name = "ed25519-zebra" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9" dependencies = [ - "enum-ordinalize", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 1.0.109", + "curve25519-dalek 4.1.3", + "ed25519 2.2.3", + "hashbrown 0.14.5", + "hex 0.4.3", + "rand_core 0.6.4", + "sha2 0.10.8", + "zeroize", ] [[package]] name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.11.12" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b477563c2bfed38a3b7a60964c49e058b2510ad3f12ba3483fd8f62c2306d6" -dependencies = [ - "base16ct 0.1.1", - "crypto-bigint 0.3.2", - "der 0.5.1", - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "elliptic-curve" @@ -2498,6 +2679,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -2506,9 +2693,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -2519,7 +2706,16 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2953d1df47ac0eb70086ccabf0275aa8da8591a28bd358ee2b52bd9f9e3ff9e9" dependencies = [ - "enum-iterator-derive", + "enum-iterator-derive 0.8.1", +] + +[[package]] +name = "enum-iterator" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd242f399be1da0a5354aa462d57b4ab2b4ee0683cc552f7c007d2d12d36e94" +dependencies = [ + "enum-iterator-derive 1.4.0", ] [[package]] @@ -2528,34 +2724,32 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] -name = "enum-ordinalize" -version = "3.1.15" +name = "enum-iterator-derive" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ - "num-bigint 0.4.4", - "num-traits", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "enum_dispatch" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" dependencies = [ "once_cell", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -2571,19 +2765,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "env_logger" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" -dependencies = [ - "humantime", - "is-terminal", - "log", - "regex", - "termcolor", -] - [[package]] name = "equivalent" version = "1.0.1" @@ -2602,50 +2783,28 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "eth-keystore" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d47d900a7dea08593d398104f8288e37858b0ad714c8d08cd03fdb86563e6402" -dependencies = [ - "aes 0.7.5", - "ctr 0.7.0", - "digest 0.9.0", - "hex 0.4.3", - "hmac 0.11.0", - "pbkdf2 0.8.0", - "rand 0.8.5", - "scrypt 0.7.0", - "serde", - "serde_json", - "sha2 0.9.9", - "sha3 0.9.1", - "thiserror", - "uuid 0.8.2", -] - [[package]] name = "eth-keystore" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" dependencies = [ - "aes 0.8.3", - "ctr 0.9.2", + "aes", + "ctr", "digest 0.10.7", "hex 0.4.3", "hmac 0.12.1", "pbkdf2 0.11.0", "rand 0.8.5", - "scrypt 0.10.0", + "scrypt", "serde", "serde_json", "sha2 0.10.8", @@ -2791,10 +2950,10 @@ dependencies = [ "dunce", "ethers-core", "eyre", - "getrandom 0.2.12", + "getrandom 0.2.15", "hex 0.4.3", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "reqwest", "serde", @@ -2813,8 +2972,8 @@ dependencies = [ "ethers-contract-abigen", "ethers-core", "hex 0.4.3", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde_json", "syn 1.0.109", ] @@ -2836,7 +2995,7 @@ dependencies = [ "k256 0.11.6", "once_cell", "open-fastrlp", - "proc-macro2 1.0.76", + "proc-macro2 1.0.86", "rand 0.8.5", "rlp 0.5.2", "rlp-derive", @@ -2846,7 +3005,7 @@ dependencies = [ "syn 1.0.109", "thiserror", "tiny-keccak 2.0.2", - "unicode-xid 0.2.4", + "unicode-xid 0.2.5", ] [[package]] @@ -2855,7 +3014,7 @@ version = "1.0.2" source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" dependencies = [ "ethers-core", - "getrandom 0.2.12", + "getrandom 0.2.15", "reqwest", "semver", "serde", @@ -2904,7 +3063,7 @@ dependencies = [ "hyperlane-core", "log", "maplit", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "primitive-types", "prometheus", "serde", @@ -2919,17 +3078,17 @@ version = "1.0.2" source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" dependencies = [ "async-trait", - "auto_impl 1.1.0", + "auto_impl 1.2.0", "base64 0.13.1", "ethers-core", "futures-channel", "futures-core", "futures-timer", "futures-util", - "getrandom 0.2.12", + "getrandom 0.2.15", "hashers", "hex 0.4.3", - "http", + "http 0.2.12", "once_cell", "parking_lot 0.11.2", "pin-project", @@ -2955,10 +3114,10 @@ version = "1.0.2" source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" dependencies = [ "async-trait", - "coins-bip32", - "coins-bip39", + "coins-bip32 0.7.0", + "coins-bip39 0.7.0", "elliptic-curve 0.12.3", - "eth-keystore 0.5.0", + "eth-keystore", "ethers-core", "hex 0.4.3", "rand 0.8.5", @@ -2970,6 +3129,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "ethnum" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" + [[package]] name = "event-listener" version = "2.5.3" @@ -2978,16 +3143,17 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "eventsource-client" -version = "0.10.2" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9146112ee3ce031aa5aebe3e049e10b1d353b9c7630cc6be488c2c62cc5d9c42" +checksum = "4c80c6714d1a380314fcb11a22eeff022e1e1c9642f0bb54e15dc9cb29f37b29" dependencies = [ "futures", "hyper", - "hyper-rustls 0.22.1", + "hyper-rustls 0.24.2", "hyper-timeout", "log", "pin-project", + "rand 0.8.5", "tokio", ] @@ -3009,9 +3175,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "feature-probe" @@ -3040,22 +3206,10 @@ dependencies = [ ] [[package]] -name = "filetime" -version = "0.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "windows-sys 0.52.0", -] - -[[package]] -name = "finl_unicode" -version = "1.2.0" +name = "fiat-crypto" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fixed-hash" @@ -3095,12 +3249,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.8.0", ] [[package]] @@ -3172,38 +3326,45 @@ checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "fuel-abi-types" -version = "0.2.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d99a7aeb41cdabffa38418b00fd57b5571dc58ee5af606e845a088befecd36" +checksum = "e0e7e87f94417ff1a5d60e496906033c58bfe5367546621f131fe8cdabaa2671" dependencies = [ + "itertools 0.10.5", "lazy_static", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "serde", + "serde_json", + "syn 2.0.77", "thiserror", ] [[package]] name = "fuel-asm" -version = "0.26.3" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0030cc1247de0507e547d8ea33484b82420fe61221b94b985d193ec7f63587ae" +checksum = "491f1777538b0e1d479609d0d75bca5242c7fd3394f2ddd4ea55b8c96bcc8387" dependencies = [ + "bitflags 2.6.0", "fuel-types", "serde", + "strum 0.24.1", ] [[package]] name = "fuel-core-chain-config" -version = "0.17.13" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff4779c1649be51e1244aca7322c625064d052f9f6f529feb26f538cc839bd6" +checksum = "05c13f888fb9b705b64bbcb56d022345cf85a86535d646bf53e20771eb4b986a" dependencies = [ "anyhow", "bech32 0.9.1", + "derivative", "fuel-core-storage", "fuel-core-types", - "hex 0.4.3", - "itertools 0.10.5", + "itertools 0.12.1", "postcard", "rand 0.8.5", "serde", @@ -3214,20 +3375,21 @@ dependencies = [ [[package]] name = "fuel-core-client" -version = "0.17.13" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b386680fb36dfee949c2977c1c682cf459c6b7862d243fb4615193b769d2b9" +checksum = "2bd1910fce3eebe33b5acba656e092e5ede267acb4b1c3f17c122a0477270091" dependencies = [ "anyhow", "cynic", - "derive_more", + "derive_more 0.99.18", "eventsource-client", "fuel-core-types", "futures", "hex 0.4.3", - "hyper-rustls 0.22.1", - "itertools 0.10.5", + "hyper-rustls 0.24.2", + "itertools 0.12.1", "reqwest", + "schemafy_lib", "serde", "serde_json", "tai64", @@ -3235,27 +3397,85 @@ dependencies = [ "tracing", ] +[[package]] +name = "fuel-core-metrics" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e2f22f6c4ce2696c29c14083c465f276c8d8eca67f051cb7d09a72442ceb5e" +dependencies = [ + "parking_lot 0.12.3", + "pin-project-lite", + "prometheus-client", + "regex", + "tracing", +] + +[[package]] +name = "fuel-core-poa" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c646e9246bc333e365d130f5a854fb9c33f9237e178d87c75a7d136d1f3211f9" +dependencies = [ + "anyhow", + "async-trait", + "fuel-core-chain-config", + "fuel-core-services", + "fuel-core-storage", + "fuel-core-types", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "fuel-core-services" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff8a175199e0e7b1373ac10d45eb26563c1e8299298c9589ab60efb1c7cae6ac" +dependencies = [ + "anyhow", + "async-trait", + "fuel-core-metrics", + "futures", + "parking_lot 0.12.3", + "tokio", + "tracing", +] + [[package]] name = "fuel-core-storage" -version = "0.17.13" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240b31746485e24215a1159b0887f2013545c78252377d55601b07f9f25367ae" +checksum = "6a3ee3b462cc9b7e62b3ae04d5e3b792e6742c479bd75d6bc0987443a92b5299" dependencies = [ "anyhow", + "derive_more 0.99.18", + "enum-iterator 1.5.0", "fuel-core-types", "fuel-vm", - "thiserror", + "impl-tools", + "itertools 0.12.1", + "num_enum 0.7.3", + "paste", + "postcard", + "primitive-types", + "serde", + "strum 0.25.0", + "strum_macros 0.25.3", ] [[package]] name = "fuel-core-types" -version = "0.17.13" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75b20301e07c8dfd793c8a5385d3b2ee0e80c36f7323957260174819d8a25fe" +checksum = "615783f63b40075d1bf64a42b4fd4edce076458c94b0fab2278a570b2b7a8e0e" dependencies = [ "anyhow", - "derive_more", + "bs58 0.5.1", + "derivative", + "derive_more 0.99.18", "fuel-vm", + "rand 0.8.5", "secrecy", "serde", "tai64", @@ -3265,16 +3485,18 @@ dependencies = [ [[package]] name = "fuel-crypto" -version = "0.26.3" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7356deff8ce5a9b6bc8d9e7cacc6c1d1f7abf5cdd4d729869afb401befa495" +checksum = "f74f03ba9b27f375a0482b1afe20d5b8cfd032fedba683a584cdbd6d10147439" dependencies = [ - "borrown", - "coins-bip32", - "coins-bip39", + "coins-bip32 0.8.7", + "coins-bip39 0.8.7", + "ecdsa 0.16.9", + "ed25519-dalek 2.1.1", "fuel-types", - "getrandom 0.2.12", + "k256 0.13.4", "lazy_static", + "p256", "rand 0.8.5", "secp256k1", "serde", @@ -3283,49 +3505,68 @@ dependencies = [ ] [[package]] -name = "fuel-merkle" -version = "0.26.3" +name = "fuel-derive" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b13103bf12f62930dd26f75f90d6a95d952fdcd677a356f57d8ef8df7ae02b84" +checksum = "89ad30ad1a11e5a811ae67b6b0cb6785ce21bcd5ef0afd442fd963d5be95d09d" dependencies = [ - "digest 0.10.7", - "fuel-storage", - "hashbrown 0.13.2", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", + "synstructure 0.13.1", +] + +[[package]] +name = "fuel-merkle" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5433c41ffbf531eed1380148cd68e37f9dd7e25966a9c59518f6b09e346e80e2" +dependencies = [ + "derive_more 0.99.18", + "digest 0.10.7", + "fuel-storage", + "hashbrown 0.13.2", "hex 0.4.3", + "serde", "sha2 0.10.8", - "thiserror", ] [[package]] name = "fuel-storage" -version = "0.26.3" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "998d49926c8ae3e545e348075075c2fe85caae4474e01d2da65a9a8edc3277e9" +checksum = "ce3fc3cd96fe312442cdf35966b96d66becd02582b505f856f74953f57adf020" [[package]] name = "fuel-tx" -version = "0.26.3" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09bb28731979db1f5192c04f2a330a15b8c2f5ef2b47836b3282c7fd9d7703c" +checksum = "e00cc42ae3121b1881a6ae8306696d1bea73adca424216d9f676ee91d3927c74" dependencies = [ + "bitflags 2.6.0", "derivative", + "derive_more 0.99.18", "fuel-asm", "fuel-crypto", "fuel-merkle", "fuel-types", + "hashbrown 0.14.5", "itertools 0.10.5", - "num-integer", + "postcard", "rand 0.8.5", "serde", "serde_json", + "strum 0.24.1", + "strum_macros 0.24.3", ] [[package]] name = "fuel-types" -version = "0.26.3" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89fc99a9878b98135c4b05c71fe63b82f4cb3a00abac278935f8be7282f8e468" +checksum = "ae98e143dec4e6cb114a92435e314f1d4815e17e8fded24332fb285319d60167" dependencies = [ + "fuel-derive", "hex 0.4.3", "rand 0.8.5", "serde", @@ -3333,201 +3574,176 @@ dependencies = [ [[package]] name = "fuel-vm" -version = "0.26.3" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36aac727729b94c620265da76112e1d36a1af0c067745491c376f084f5b7b38" +checksum = "641a2ee5a3398633fa243fba3343cbe2225ae335a09141f6b94041720cfc3520" dependencies = [ - "bitflags 1.3.2", + "anyhow", + "async-trait", + "backtrace", + "bitflags 2.6.0", "derivative", + "derive_more 0.99.18", + "ethnum", "fuel-asm", "fuel-crypto", "fuel-merkle", "fuel-storage", "fuel-tx", "fuel-types", + "hashbrown 0.14.5", "itertools 0.10.5", + "libm", + "paste", + "percent-encoding", + "primitive-types", "rand 0.8.5", "serde", + "serde_with", "sha3 0.10.8", + "static_assertions 1.1.0", + "strum 0.24.1", "tai64", - "thiserror", ] [[package]] name = "fuels" -version = "0.38.1" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eede2cde3dd4e8cba56ed5bf4412836d7be280e013e392b2756af99ffb9e714" +checksum = "601ed66a0485065471cd9c8bab2db7cfa58bc7ed5d2e68bd26fc573ac2575827" dependencies = [ "fuel-core-client", + "fuel-crypto", "fuel-tx", + "fuels-accounts", "fuels-core", "fuels-macros", "fuels-programs", - "fuels-signers", "fuels-test-helpers", - "fuels-types", ] [[package]] -name = "fuels-code-gen" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d03eee7fa041bb4da8cdecc84b81f1ab8e5810d67bcec0c090bdd58f07e955" -dependencies = [ - "Inflector", - "fuel-abi-types", - "itertools 0.10.5", - "lazy_static", - "proc-macro2 1.0.76", - "quote 1.0.35", - "regex", - "serde_json", - "syn 1.0.109", -] - -[[package]] -name = "fuels-core" -version = "0.38.1" +name = "fuels-accounts" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd7273ee45a548acbd95e67405809e4b1778f4973851c16633e75b902f0ea9e" +checksum = "fed97e653906fe0bc60b5d7a7421f3c5fe766f516b762def8f4ccac707ac4bc3" dependencies = [ + "async-trait", + "chrono", + "elliptic-curve 0.13.8", + "eth-keystore", + "fuel-core-client", + "fuel-core-types", + "fuel-crypto", "fuel-tx", "fuel-types", - "fuel-vm", - "fuels-types", - "hex 0.4.3", - "itertools 0.10.5", - "sha2 0.9.9", + "fuels-core", + "itertools 0.12.1", + "rand 0.8.5", + "semver", + "tai64", + "thiserror", + "tokio", + "zeroize", ] [[package]] -name = "fuels-macros" -version = "0.38.1" +name = "fuels-code-gen" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107ffb7dda1051d1db5d1bc209a66d3c2911a8fc844cf3662ca4b705556b57a" +checksum = "1edef30656b740ca9c279a7bcfe9e366557c271a2751e36316f780f18dc99c85" dependencies = [ "Inflector", "fuel-abi-types", - "fuels-code-gen", - "itertools 0.10.5", - "lazy_static", - "proc-macro2 1.0.76", - "quote 1.0.35", - "rand 0.8.5", + "itertools 0.12.1", + "proc-macro2 1.0.86", + "quote 1.0.37", "regex", "serde_json", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] -name = "fuels-programs" -version = "0.38.1" +name = "fuels-core" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c1034d747824c089622ca9ec400dc44a7a7119d9caf001116d8befa441bd47" +checksum = "ff741c9f1ba2c701b50c76a98a5655d8bc0f275f7ae2dd0e724f8fc36eeb8a9f" dependencies = [ - "bytes", + "async-trait", + "bech32 0.9.1", + "chrono", "fuel-abi-types", + "fuel-asm", + "fuel-core-chain-config", + "fuel-core-client", + "fuel-core-types", + "fuel-crypto", "fuel-tx", "fuel-types", "fuel-vm", - "fuels-core", - "fuels-signers", - "fuels-types", - "futures", + "fuels-macros", "hex 0.4.3", - "itertools 0.10.5", - "proc-macro2 1.0.76", - "rand 0.8.5", - "regex", + "itertools 0.12.1", + "postcard", "serde", "serde_json", - "sha2 0.9.9", - "strum 0.21.0", - "strum_macros 0.21.1", "thiserror", - "tokio", + "uint 0.9.5", ] [[package]] -name = "fuels-signers" -version = "0.38.1" +name = "fuels-macros" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862c60e598272158d3877896b1a47ae6109733f4538711a4d898c571a88aacb7" +checksum = "bba1c2fd149a310879249144f2589336708ae860563a45b792907ae34ae6b959" +dependencies = [ + "fuels-code-gen", + "itertools 0.12.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "fuels-programs" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45652fa07c48d5fba2ee50ddd279eead2c55b251b3d426d2189394b475330e9" dependencies = [ "async-trait", - "bytes", - "chrono", - "elliptic-curve 0.11.12", - "eth-keystore 0.3.0", - "fuel-core-client", - "fuel-crypto", + "fuel-abi-types", + "fuel-asm", "fuel-tx", "fuel-types", - "fuel-vm", + "fuels-accounts", "fuels-core", - "fuels-types", - "hex 0.4.3", - "itertools 0.10.5", + "itertools 0.12.1", "rand 0.8.5", - "serde", - "sha2 0.9.9", - "tai64", - "thiserror", + "serde_json", "tokio", ] [[package]] name = "fuels-test-helpers" -version = "0.38.1" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51cb48e1a813b61d811f0811d9758e0f4527097b6a17fccf613ecc3595d3e4d" +checksum = "967a140a51095d071c84970365c37f856f4f098b835cb609b934dff4b8296cce" dependencies = [ "fuel-core-chain-config", "fuel-core-client", - "fuel-core-types", + "fuel-core-poa", + "fuel-core-services", + "fuel-crypto", "fuel-tx", "fuel-types", - "fuel-vm", - "fuels-signers", - "fuels-types", + "fuels-accounts", + "fuels-core", "futures", - "hex 0.4.3", "portpicker", "rand 0.8.5", - "serde", - "serde_json", - "serde_with", "tempfile", "tokio", - "which", -] - -[[package]] -name = "fuels-types" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2ee9c34502c6427661beb51ee954b2c7c50e51a9bb518f2a7d512682cddd31" -dependencies = [ - "bech32 0.9.1", - "chrono", - "fuel-abi-types", - "fuel-asm", - "fuel-core-chain-config", - "fuel-core-client", - "fuel-tx", - "fuel-types", - "fuels-macros", - "hex 0.4.3", - "itertools 0.10.5", - "lazy_static", - "proc-macro2 1.0.76", - "regex", - "serde", - "serde_json", - "strum 0.21.0", - "strum_macros 0.21.1", - "thiserror", + "which 6.0.3", ] [[package]] @@ -3611,9 +3827,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -3630,9 +3846,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -3707,9 +3923,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -3730,17 +3946,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "goblin" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" -dependencies = [ - "log", - "plain", - "scroll", -] - [[package]] name = "graphql-parser" version = "0.4.0" @@ -3775,20 +3980,20 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.1.0", + "http 0.2.12", + "indexmap 2.5.0", "slab", "tokio", - "tokio-util 0.7.10", + "tokio-util", "tracing", ] @@ -3807,7 +4012,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -3816,7 +4021,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", ] [[package]] @@ -3825,17 +4030,18 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", ] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.7", + "ahash 0.8.11", "allocator-api2", + "serde", ] [[package]] @@ -3853,7 +4059,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -3878,7 +4084,7 @@ dependencies = [ "base64 0.21.7", "bytes", "headers-core", - "http", + "http 0.2.12", "httpdate", "mime", "sha1", @@ -3890,7 +4096,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" dependencies = [ - "http", + "http 0.2.12", ] [[package]] @@ -3967,9 +4173,6 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] [[package]] name = "histogram" @@ -4002,7 +4205,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ - "crypto-mac 0.11.0", + "crypto-mac 0.11.1", "digest 0.9.0", ] @@ -4037,9 +4240,20 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -4053,15 +4267,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "httpdate" @@ -4087,22 +4301,22 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -4118,7 +4332,7 @@ dependencies = [ "bytes", "futures", "headers", - "http", + "http 0.2.12", "hyper", "hyper-rustls 0.22.1", "rustls-native-certs 0.5.0", @@ -4153,13 +4367,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" dependencies = [ "futures-util", - "http", + "http 0.2.12", "hyper", "log", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", + "webpki-roots 0.25.4", ] [[package]] @@ -4196,14 +4411,14 @@ dependencies = [ "axum", "backtrace", "backtrace-oneline", - "bs58 0.5.0", + "bs58 0.5.1", "color-eyre", "config", "console-subscriber", "convert_case 0.6.0", "derive-new", "derive_builder", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "ethers", "ethers-prometheus", "eyre", @@ -4216,7 +4431,7 @@ dependencies = [ "hyperlane-fuel", "hyperlane-sealevel", "hyperlane-test", - "itertools 0.12.0", + "itertools 0.12.1", "maplit", "mockall", "paste", @@ -4252,28 +4467,29 @@ version = "0.1.0" dependencies = [ "async-rwlock", "async-trait", - "auto_impl 1.1.0", - "bigdecimal 0.4.2", + "auto_impl 1.2.0", + "bigdecimal 0.4.5", "borsh 0.9.3", - "bs58 0.5.0", + "bs58 0.5.1", "bytes", "config", "convert_case 0.6.0", "derive-new", - "derive_more", + "derive_more 0.99.18", "ethers-contract", "ethers-core", "ethers-providers", "eyre", "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures", - "getrandom 0.2.12", + "getrandom 0.2.15", "hex 0.4.3", - "itertools 0.12.0", - "num 0.4.1", - "num-derive 0.4.1", + "itertools 0.12.1", + "num 0.4.3", + "num-derive 0.4.2", "num-traits", "primitive-types", + "prometheus", "serde", "serde_json", "sha3 0.10.8", @@ -4296,18 +4512,19 @@ dependencies = [ "base64 0.21.7", "bech32 0.9.1", "cosmrs", - "cosmwasm-std", + "cosmwasm-std 2.1.3", + "crypto", "derive-new", "futures", "hex 0.4.3", - "http", + "http 0.2.12", "hyper", "hyper-tls", "hyperlane-core", "hyperlane-cosmwasm-interface", "injective-protobuf", "injective-std", - "itertools 0.12.0", + "itertools 0.12.1", "once_cell", "protobuf", "ripemd", @@ -4334,7 +4551,7 @@ checksum = "e5e622014ab94f1e7f0acbe71df7c1384224261e2c76115807aaf24215970942" dependencies = [ "bech32 0.9.1", "cosmwasm-schema", - "cosmwasm-std", + "cosmwasm-std 1.5.7", "cosmwasm-storage", "cw-storage-plus", "cw2", @@ -4364,8 +4581,8 @@ dependencies = [ "futures-util", "hex 0.4.3", "hyperlane-core", - "itertools 0.12.0", - "num 0.4.1", + "itertools 0.12.1", + "num 0.4.3", "num-traits", "reqwest", "serde", @@ -4385,6 +4602,7 @@ dependencies = [ "anyhow", "async-trait", "fuels", + "futures", "hyperlane-core", "serde", "thiserror", @@ -4401,6 +4619,7 @@ dependencies = [ "anyhow", "async-trait", "base64 0.21.7", + "bincode", "borsh 0.9.3", "derive-new", "hyperlane-core", @@ -4413,83 +4632,21 @@ dependencies = [ "jsonrpc-core", "multisig-ism", "num-traits", + "reqwest", "serde", + "serde_json", "serializable-account-meta", "solana-account-decoder", "solana-client", "solana-sdk", "solana-transaction-status", "thiserror", + "tokio", "tracing", "tracing-futures", "url", ] -[[package]] -name = "hyperlane-sealevel-client" -version = "0.1.0" -dependencies = [ - "account-utils", - "bincode", - "borsh 0.9.3", - "bs58 0.5.0", - "clap 4.4.17", - "ethers", - "hex 0.4.3", - "hyperlane-core", - "hyperlane-sealevel-connection-client", - "hyperlane-sealevel-hello-world", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-multisig-ism-message-id", - "hyperlane-sealevel-token", - "hyperlane-sealevel-token-collateral", - "hyperlane-sealevel-token-lib", - "hyperlane-sealevel-token-native", - "hyperlane-sealevel-validator-announce", - "pretty_env_logger", - "serde", - "serde_json", - "solana-clap-utils", - "solana-cli-config", - "solana-client", - "solana-program", - "solana-sdk", - "solana-transaction-status", -] - -[[package]] -name = "hyperlane-sealevel-connection-client" -version = "0.1.0" -dependencies = [ - "access-control", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "solana-program", -] - -[[package]] -name = "hyperlane-sealevel-hello-world" -version = "0.1.0" -dependencies = [ - "access-control", - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-connection-client", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-test-utils", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-noop", -] - [[package]] name = "hyperlane-sealevel-igp" version = "0.1.0" @@ -4497,32 +4654,15 @@ dependencies = [ "access-control", "account-utils", "borsh 0.9.3", - "getrandom 0.2.12", + "getrandom 0.2.15", "hyperlane-core", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", - "serde", "serializable-account-meta", "solana-program", "thiserror", ] -[[package]] -name = "hyperlane-sealevel-igp-test" -version = "0.1.0" -dependencies = [ - "access-control", - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-igp", - "hyperlane-test-utils", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", -] - [[package]] name = "hyperlane-sealevel-interchain-security-module-interface" version = "0.1.0" @@ -4538,58 +4678,27 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.7", "blake3", "borsh 0.9.3", - "getrandom 0.2.12", + "getrandom 0.2.15", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", "hyperlane-sealevel-message-recipient-interface", - "itertools 0.12.0", - "log", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "proc-macro-crate 1.2.1", - "serde", "serializable-account-meta", "solana-program", "spl-noop", "thiserror", ] -[[package]] -name = "hyperlane-sealevel-mailbox-test" -version = "0.1.0" -dependencies = [ - "access-control", - "account-utils", - "base64 0.21.7", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-interchain-security-module-interface", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-sealevel-test-ism", - "hyperlane-sealevel-test-send-receiver", - "hyperlane-test-utils", - "itertools 0.12.0", - "log", - "num-derive 0.4.1", - "num-traits", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-noop", - "thiserror", -] - [[package]] name = "hyperlane-sealevel-message-recipient-interface" version = "0.1.0" dependencies = [ "borsh 0.9.3", - "getrandom 0.2.12", + "getrandom 0.2.15", "hyperlane-core", "solana-program", "spl-type-length-value", @@ -4603,154 +4712,14 @@ dependencies = [ "account-utils", "borsh 0.9.3", "ecdsa-signature", - "hex 0.4.3", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-multisig-ism-message-id", - "hyperlane-test-utils", "multisig-ism", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", - "rand 0.8.5", "serializable-account-meta", "solana-program", - "solana-program-test", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "hyperlane-sealevel-test-ism" -version = "0.1.0" -dependencies = [ - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-interchain-security-module-interface", - "hyperlane-sealevel-mailbox", - "hyperlane-test-transaction-utils", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", -] - -[[package]] -name = "hyperlane-sealevel-test-send-receiver" -version = "0.1.0" -dependencies = [ - "account-utils", - "borsh 0.9.3", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-test-utils", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-noop", -] - -[[package]] -name = "hyperlane-sealevel-token" -version = "0.1.0" -dependencies = [ - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-connection-client", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-sealevel-test-ism", - "hyperlane-sealevel-token-lib", - "hyperlane-test-utils", - "num-derive 0.4.1", - "num-traits", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-associated-token-account", - "spl-noop", - "spl-token", - "spl-token-2022", - "thiserror", -] - -[[package]] -name = "hyperlane-sealevel-token-collateral" -version = "0.1.0" -dependencies = [ - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-connection-client", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-sealevel-test-ism", - "hyperlane-sealevel-token-lib", - "hyperlane-test-utils", - "num-derive 0.4.1", - "num-traits", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-associated-token-account", - "spl-noop", - "spl-token", - "spl-token-2022", - "thiserror", -] - -[[package]] -name = "hyperlane-sealevel-token-lib" -version = "0.1.0" -dependencies = [ - "access-control", - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-connection-client", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "num-derive 0.4.1", - "num-traits", - "serializable-account-meta", - "solana-program", - "spl-associated-token-account", - "spl-noop", - "spl-token", - "spl-token-2022", - "thiserror", -] - -[[package]] -name = "hyperlane-sealevel-token-native" -version = "0.1.0" -dependencies = [ - "account-utils", - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-connection-client", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-sealevel-test-ism", - "hyperlane-sealevel-token-lib", - "hyperlane-test-utils", - "num-derive 0.4.1", - "num-traits", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-noop", - "tarpc", "thiserror", ] @@ -4761,14 +4730,10 @@ dependencies = [ "account-utils", "borsh 0.9.3", "ecdsa-signature", - "hex 0.4.3", "hyperlane-core", "hyperlane-sealevel-mailbox", - "hyperlane-test-utils", "serializable-account-meta", "solana-program", - "solana-program-test", - "solana-sdk", "thiserror", ] @@ -4781,40 +4746,11 @@ dependencies = [ "mockall", ] -[[package]] -name = "hyperlane-test-transaction-utils" -version = "0.1.0" -dependencies = [ - "solana-program", - "solana-program-test", - "solana-sdk", -] - -[[package]] -name = "hyperlane-test-utils" -version = "0.1.0" -dependencies = [ - "borsh 0.9.3", - "hyperlane-core", - "hyperlane-sealevel-igp", - "hyperlane-sealevel-interchain-security-module-interface", - "hyperlane-sealevel-mailbox", - "hyperlane-sealevel-message-recipient-interface", - "hyperlane-sealevel-test-ism", - "hyperlane-test-transaction-utils", - "serializable-account-meta", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-noop", - "spl-token-2022", -] - [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -4839,17 +4775,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.3.0" @@ -4955,14 +4880,38 @@ dependencies = [ "serde", ] +[[package]] +name = "impl-tools" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82c305b1081f1a99fda262883c788e50ab57d36c00830bdd7e0a82894ad965c" +dependencies = [ + "autocfg", + "impl-tools-lib", + "proc-macro-error", + "syn 2.0.77", +] + +[[package]] +name = "impl-tools-lib" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85d3946d886eaab0702fa0c6585adcced581513223fa9df7ccfabbd9fa331a88" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -4972,12 +4921,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" -[[package]] -name = "index_list" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70891286cb8e844fdfcf1178b47569699f9e20b5ecc4b45a6240a64771444638" - [[package]] name = "indexmap" version = "1.9.3" @@ -4986,16 +4929,18 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] name = "indexmap" -version = "2.1.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -5016,9 +4961,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a52219a08aba8c17846fd23d472d1d69c817fe5b427d135273e4c7311edd6972" dependencies = [ - "cosmwasm-std", + "cosmwasm-std 1.5.7", "ethereum-types 0.5.2", - "num 0.4.1", + "num 0.4.3", "protobuf", "protobuf-codegen-pure", "schemars", @@ -5033,7 +4978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd7a5b52d19dca05823c7e4b481d41b49c04a0e56f66a5c92396a6fdd3314710" dependencies = [ "chrono", - "cosmwasm-std", + "cosmwasm-std 1.5.7", "osmosis-std-derive", "prost 0.11.9", "prost-types 0.11.9", @@ -5053,9 +4998,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", "js-sys", @@ -5075,17 +5020,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi 0.3.9", - "rustix", - "windows-sys 0.52.0", -] - [[package]] name = "itertools" version = "0.10.5" @@ -5097,18 +5031,18 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" @@ -5121,9 +5055,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" dependencies = [ "wasm-bindgen", ] @@ -5169,9 +5103,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa 0.16.9", @@ -5192,9 +5126,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lazycell" @@ -5204,9 +5138,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -5220,12 +5154,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -5236,13 +5170,12 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "libc", - "redox_syscall 0.4.1", ] [[package]] @@ -5328,15 +5261,15 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -5344,34 +5277,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "lru" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" -dependencies = [ - "hashbrown 0.12.3", -] - -[[package]] -name = "lz4" -version = "1.24.0" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" -dependencies = [ - "libc", - "lz4-sys", -] +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lz4-sys" -version = "1.9.4" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" +checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868" dependencies = [ "cc", "libc", @@ -5408,12 +5322,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.7.3" @@ -5428,7 +5336,7 @@ checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15" dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -5443,9 +5351,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -5498,9 +5406,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -5514,13 +5422,22 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mintex" version = "0.1.3" @@ -5529,9 +5446,9 @@ checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07" [[package]] name = "mio" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi 0.3.9", "libc", @@ -5561,29 +5478,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 1.0.109", -] - -[[package]] -name = "modular-bitfield" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" -dependencies = [ - "modular-bitfield-impl", - "static_assertions 1.1.0", -] - -[[package]] -name = "modular-bitfield-impl" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" -dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -5596,7 +5492,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http", + "http 0.2.12", "httparse", "log", "memchr", @@ -5611,7 +5507,6 @@ version = "0.1.0" dependencies = [ "borsh 0.9.3", "ecdsa-signature", - "hex 0.4.3", "hyperlane-core", "solana-program", "spl-type-length-value", @@ -5620,11 +5515,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -5661,12 +5555,13 @@ dependencies = [ [[package]] name = "nix" -version = "0.27.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if", + "cfg_aliases", "libc", ] @@ -5712,15 +5607,15 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint 0.4.4", - "num-complex 0.4.4", + "num-bigint 0.4.6", + "num-complex 0.4.6", "num-integer", "num-iter", - "num-rational 0.4.1", + "num-rational 0.4.2", "num-traits", ] @@ -5737,11 +5632,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", "serde", @@ -5759,51 +5653,56 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", "serde", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "num-derive" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -5824,12 +5723,11 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-integer", "num-traits", "serde", @@ -5837,9 +5735,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -5872,6 +5770,15 @@ dependencies = [ "num_enum_derive 0.6.1", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive 0.7.3", +] + [[package]] name = "num_enum_derive" version = "0.5.11" @@ -5879,8 +5786,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -5891,16 +5798,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "num_threads" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" dependencies = [ "libc", ] @@ -5943,9 +5862,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "opaque-debug" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open-fastrlp" @@ -5954,7 +5873,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" dependencies = [ "arrayvec", - "auto_impl 1.1.0", + "auto_impl 1.2.0", "bytes", "ethereum-types 0.14.1", "open-fastrlp-derive", @@ -5967,18 +5886,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "cfg-if", "foreign-types", "libc", @@ -5993,9 +5912,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -6006,9 +5925,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" dependencies = [ "cc", "libc", @@ -6016,25 +5935,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "js-sys", - "lazy_static", - "percent-encoding", - "pin-project", - "rand 0.8.5", - "thiserror", -] - [[package]] name = "ordered-multimap" version = "0.4.3" @@ -6058,8 +5958,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4d482a16be198ee04e0f94e10dd9b8d02332dcf33bc5ea4b255e7e25eedc5df" dependencies = [ "itertools 0.10.5", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -6081,8 +5981,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -6098,11 +5998,23 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.8", +] + [[package]] name = "parity-scale-codec" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" dependencies = [ "arrayvec", "bitvec 1.0.1", @@ -6114,13 +6026,13 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.6.9" +version = "3.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" dependencies = [ - "proc-macro-crate 2.0.0", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -6137,12 +6049,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.10", ] [[package]] @@ -6161,26 +6073,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.3", "smallvec", - "windows-targets 0.48.5", -] - -[[package]] -name = "password-hash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a5d4e9c205d2c1ae73b84aab6240e98218c0e72e63b50422cfb2d1ca952282" -dependencies = [ - "base64ct", - "rand_core 0.6.4", - "subtle", + "windows-targets 0.52.6", ] [[package]] @@ -6196,9 +6097,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" @@ -6217,27 +6118,24 @@ dependencies = [ [[package]] name = "pbkdf2" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "base64ct", - "crypto-mac 0.11.0", - "hmac 0.11.0", - "password-hash 0.2.1", - "sha2 0.9.9", + "digest 0.10.7", + "hmac 0.12.1", + "password-hash", + "sha2 0.10.8", ] [[package]] name = "pbkdf2" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest 0.10.7", "hmac 0.12.1", - "password-hash 0.4.2", - "sha2 0.10.8", ] [[package]] @@ -6263,8 +6161,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", ] [[package]] @@ -6299,9 +6197,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" dependencies = [ "memchr", "thiserror", @@ -6310,9 +6208,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" +checksum = "2a548d2beca6773b1c244554d36fcf8548a8a58e74156968211567250e48e49a" dependencies = [ "pest", "pest_generator", @@ -6320,22 +6218,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" +checksum = "3c93a82e8d145725dcbaf44e5ea887c8a869efdcc28706df2d08c69e17077183" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "pest_meta" -version = "2.7.6" +version = "2.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" +checksum = "a941429fea7e08bedec25e4f6785b6ffaacc6b755da98df5ef3e7dcf4a124c4f" dependencies = [ "once_cell", "pest", @@ -6354,29 +6252,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -6411,31 +6309,25 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ - "der 0.7.8", + "der 0.7.9", "spki 0.7.3", ] [[package]] name = "pkg-config" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" - -[[package]] -name = "plain" -version = "0.2.3" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "polyval" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", "cpufeatures", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", "universal-hash", ] @@ -6450,12 +6342,13 @@ dependencies = [ [[package]] name = "postcard" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" dependencies = [ "cobs", - "embedded-io", + "embedded-io 0.4.0", + "embedded-io 0.6.1", "heapless", "serde", ] @@ -6468,9 +6361,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "predicates" @@ -6488,38 +6384,37 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" [[package]] name = "predicates-tree" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" dependencies = [ "predicates-core", "termtree", ] [[package]] -name = "pretty_env_logger" -version = "0.5.0" +name = "prettyplease" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +checksum = "479cf940fbbb3426c32c5d5176f62ad57549a0bb84773423ba8be9d089f5faba" dependencies = [ - "env_logger 0.10.1", - "log", + "proc-macro2 1.0.86", + "syn 2.0.77", ] [[package]] -name = "prettyplease" -version = "0.2.17" +name = "primeorder" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3928fb5db768cb86f891ff014f0144589297e3c6a1aba6ed7cecfdace270c7" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" dependencies = [ - "proc-macro2 1.0.76", - "syn 2.0.48", + "elliptic-curve 0.13.8", ] [[package]] @@ -6557,20 +6452,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - -[[package]] -name = "proc-macro-crate" -version = "3.0.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.21.0", + "toml_edit 0.22.20", ] [[package]] @@ -6580,8 +6466,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", "version_check", ] @@ -6592,8 +6478,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "version_check", ] @@ -6608,28 +6494,51 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "prometheus" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ "cfg-if", "fnv", "lazy_static", "memchr", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "protobuf", "thiserror", ] +[[package]] +name = "prometheus-client" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.3", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + [[package]] name = "prost" version = "0.11.9" @@ -6642,12 +6551,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" dependencies = [ "bytes", - "prost-derive 0.12.5", + "prost-derive 0.12.6", ] [[package]] @@ -6658,22 +6567,22 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "prost-derive" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.0", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "itertools 0.12.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -6687,11 +6596,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.4" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" dependencies = [ - "prost 0.12.4", + "prost 0.12.6", ] [[package]] @@ -6743,8 +6652,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -6831,11 +6740,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.76", + "proc-macro2 1.0.86", ] [[package]] @@ -6937,7 +6846,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.15", ] [[package]] @@ -6960,9 +6869,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -6970,9 +6879,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -7001,34 +6910,34 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.15", "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", ] [[package]] @@ -7042,13 +6951,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.4", ] [[package]] @@ -7059,9 +6968,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "relayer" @@ -7074,7 +6983,7 @@ dependencies = [ "convert_case 0.6.0", "ctrlc", "derive-new", - "derive_more", + "derive_more 0.99.18", "dhat", "ethers", "ethers-contract", @@ -7085,9 +6994,9 @@ dependencies = [ "hyperlane-core", "hyperlane-ethereum", "hyperlane-test", - "itertools 0.12.0", + "itertools 0.12.1", "mockall", - "num-derive 0.4.1", + "num-derive 0.4.2", "num-traits", "once_cell", "prometheus", @@ -7108,18 +7017,18 @@ dependencies = [ [[package]] name = "rend" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" dependencies = [ "bytecheck", ] [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ "async-compression", "base64 0.21.7", @@ -7130,7 +7039,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-rustls 0.24.2", @@ -7143,22 +7052,23 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls 0.24.1", - "tokio-util 0.7.10", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.25.3", + "webpki-roots 0.25.4", "winreg", ] @@ -7200,16 +7110,17 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.12", + "cfg-if", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7223,9 +7134,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.43" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ "bitvec 1.0.1", "bytecheck", @@ -7236,17 +7147,17 @@ dependencies = [ "rkyv_derive", "seahash", "tinyvec", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] name = "rkyv_derive" -version = "0.7.43" +version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -7274,8 +7185,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -7329,7 +7240,7 @@ dependencies = [ "hyperlane-cosmos", "hyperlane-cosmwasm-interface", "jobserver", - "k256 0.13.3", + "k256 0.13.4", "macro_rules_attribute", "maplit", "nix 0.26.4", @@ -7345,7 +7256,7 @@ dependencies = [ "toml_edit 0.19.15", "ureq", "vergen", - "which", + "which 4.4.2", ] [[package]] @@ -7359,7 +7270,7 @@ dependencies = [ "bytes", "crc32fast", "futures", - "http", + "http 0.2.12", "hyper", "hyper-tls", "lazy_static", @@ -7431,7 +7342,7 @@ dependencies = [ "futures", "hex 0.4.3", "hmac 0.11.0", - "http", + "http 0.2.12", "hyper", "log", "md-5 0.9.1", @@ -7471,12 +7382,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.33.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", - "borsh 1.3.1", + "borsh 1.5.1", "bytes", "num-traits", "rand 0.8.5", @@ -7487,9 +7398,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -7505,9 +7416,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -7523,11 +7434,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -7561,30 +7472,16 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.21.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", - "ring 0.17.7", - "rustls-webpki 0.101.7", + "ring 0.17.8", + "rustls-webpki", "sct 0.7.1", ] -[[package]] -name = "rustls" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" -dependencies = [ - "log", - "ring 0.17.7", - "rustls-pki-types", - "rustls-webpki 0.102.1", - "subtle", - "zeroize", -] - [[package]] name = "rustls-native-certs" version = "0.5.0" @@ -7627,53 +7524,27 @@ dependencies = [ "base64 0.21.7", ] -[[package]] -name = "rustls-pki-types" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" - [[package]] name = "rustls-webpki" version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.7", - "untrusted 0.9.0", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" -dependencies = [ - "ring 0.17.7", - "rustls-pki-types", + "ring 0.17.8", "untrusted 0.9.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" - -[[package]] -name = "salsa20" -version = "0.8.1" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0" -dependencies = [ - "cipher 0.3.0", -] +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "salsa20" @@ -7681,7 +7552,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" dependencies = [ - "cipher 0.4.4", + "cipher", ] [[package]] @@ -7695,25 +7566,25 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.10.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", - "derive_more", + "derive_more 0.99.18", "parity-scale-codec", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.10.0" +version = "2.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" dependencies = [ - "proc-macro-crate 1.2.1", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -7726,11 +7597,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "schemafy_core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41781ae092f4fd52c9287efb74456aea0d3b90032d2ecad272bd14dbbcb0511b" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "schemafy_lib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e953db32579999ca98c451d80801b6f6a7ecba6127196c5387ec0774c528befa" +dependencies = [ + "Inflector", + "proc-macro2 1.0.86", + "quote 1.0.37", + "schemafy_core", + "serde", + "serde_derive", + "serde_json", + "syn 1.0.109", +] + [[package]] name = "schemars" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45a28f4c49489add4ce10783f7911893516f15afe45d015608d41faca6bc4d29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "dyn-clone", "schemars_derive", @@ -7740,14 +7637,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.16" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde_derive_internals", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -7769,17 +7666,16 @@ dependencies = [ "async-trait", "config", "console-subscriber", - "derive_more", + "derive_more 0.99.18", "ethers", "eyre", "futures", - "hex 0.1.0", "hyperlane-base", "hyperlane-core", "hyperlane-test", - "itertools 0.12.0", + "itertools 0.12.1", "migration", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "num-traits", "prometheus", "sea-orm", @@ -7793,40 +7689,6 @@ dependencies = [ "tracing-futures", ] -[[package]] -name = "scroll" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" -dependencies = [ - "scroll_derive", -] - -[[package]] -name = "scroll_derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" -dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", -] - -[[package]] -name = "scrypt" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879588d8f90906e73302547e20fffefdd240eb3e0e744e142321f5d49dea0518" -dependencies = [ - "base64ct", - "hmac 0.11.0", - "password-hash 0.2.1", - "pbkdf2 0.8.0", - "salsa20 0.8.1", - "sha2 0.9.9", -] - [[package]] name = "scrypt" version = "0.10.0" @@ -7835,7 +7697,7 @@ checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" dependencies = [ "hmac 0.12.1", "pbkdf2 0.11.0", - "salsa20 0.10.2", + "salsa20", "sha2 0.10.8", ] @@ -7855,7 +7717,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -7884,7 +7746,7 @@ dependencies = [ "time", "tracing", "url", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] @@ -7911,8 +7773,8 @@ checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" dependencies = [ "bae", "heck 0.3.3", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -7945,7 +7807,7 @@ dependencies = [ "sea-query-derive", "serde_json", "time", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] @@ -7961,7 +7823,7 @@ dependencies = [ "serde_json", "sqlx", "time", - "uuid 1.6.1", + "uuid 1.10.0", ] [[package]] @@ -7971,8 +7833,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", "thiserror", ] @@ -7995,8 +7857,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", ] @@ -8016,8 +7878,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", "syn 1.0.109", ] @@ -8049,7 +7911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct 0.2.0", - "der 0.7.8", + "der 0.7.9", "generic-array 0.14.7", "pkcs8 0.10.2", "subtle", @@ -8058,9 +7920,9 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.3" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "4124a35fe33ae14259c490fd70fa199a32b9ce9502f2ee6bc4f81ec06fa65894" dependencies = [ "rand 0.8.5", "secp256k1-sys", @@ -8068,9 +7930,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ "cc", ] @@ -8086,11 +7948,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -8099,9 +7961,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" dependencies = [ "core-foundation-sys", "libc", @@ -8109,9 +7971,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] @@ -8124,18 +7986,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" dependencies = [ "serde_derive", ] [[package]] name = "serde-aux" -version = "4.4.0" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a86348501c129f3ad50c2f4635a01971f76974cd8a3f335988a0f1581c082765" +checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95" dependencies = [ "serde", "serde_json", @@ -8152,60 +8014,70 @@ dependencies = [ [[package]] name = "serde-json-wasm" -version = "0.5.1" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9213a07d53faa0b8dd81e767a54a8188a242fdb9be99ab75ec576a774bfdd7" +dependencies = [ + "serde", +] + +[[package]] +name = "serde-json-wasm" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16a62a1fad1e1828b24acac8f2b468971dade7b8c3c2e672bcadefefb1f8c137" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" dependencies = [ "serde", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.209" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "serde_derive_internals" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 1.0.109", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_path_to_error" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -8213,13 +8085,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -8236,25 +8108,32 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.14.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ + "base64 0.22.1", + "chrono", + "hex 0.4.3", + "indexmap 1.9.3", + "indexmap 2.5.0", "serde", + "serde_derive", "serde_json", "serde_with_macros", + "time", ] [[package]] name = "serde_with_macros" -version = "1.5.2" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" dependencies = [ - "darling 0.13.4", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 1.0.109", + "darling 0.20.10", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -8321,7 +8200,7 @@ dependencies = [ "cfg-if", "cpufeatures", "digest 0.9.0", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -8357,7 +8236,7 @@ dependencies = [ "block-buffer 0.9.0", "digest 0.9.0", "keccak", - "opaque-debug 0.3.0", + "opaque-debug 0.3.1", ] [[package]] @@ -8387,15 +8266,15 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -8447,9 +8326,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -8463,12 +8342,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -8515,83 +8394,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "solana-banks-client" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "borsh 0.9.3", - "futures", - "solana-banks-interface", - "solana-program", - "solana-sdk", - "tarpc", - "thiserror", - "tokio", - "tokio-serde", -] - -[[package]] -name = "solana-banks-interface" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "serde", - "solana-sdk", - "tarpc", -] - -[[package]] -name = "solana-banks-server" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "bincode", - "crossbeam-channel", - "futures", - "solana-banks-interface", - "solana-client", - "solana-runtime", - "solana-sdk", - "solana-send-transaction-service", - "tarpc", - "tokio", - "tokio-serde", - "tokio-stream", -] - -[[package]] -name = "solana-bpf-loader-program" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "bincode", - "byteorder", - "libsecp256k1", - "log", - "solana-measure", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "solana-zk-token-sdk", - "solana_rbpf", - "thiserror", -] - -[[package]] -name = "solana-bucket-map" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "log", - "memmap2", - "modular-bitfield", - "rand 0.7.3", - "solana-measure", - "solana-sdk", - "tempfile", -] - [[package]] name = "solana-clap-utils" version = "1.14.13" @@ -8677,15 +8479,6 @@ dependencies = [ "url", ] -[[package]] -name = "solana-compute-budget-program" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "solana-program-runtime", - "solana-sdk", -] - [[package]] name = "solana-config-program" version = "1.14.13" @@ -8727,7 +8520,7 @@ name = "solana-frozen-abi" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", "blake3", "block-buffer 0.9.0", "bs58 0.4.0", @@ -8760,8 +8553,8 @@ name = "solana-frozen-abi-macro" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustc_version", "syn 1.0.109", ] @@ -8771,7 +8564,7 @@ name = "solana-logger" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "env_logger 0.9.3", + "env_logger", "lazy_static", "log", ] @@ -8824,11 +8617,11 @@ name = "solana-perf" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", "bincode", "bv", "caps", - "curve25519-dalek", + "curve25519-dalek 3.2.2", "dlopen", "dlopen_derive", "fnv", @@ -8862,8 +8655,8 @@ dependencies = [ "cc", "console_error_panic_hook", "console_log", - "curve25519-dalek", - "getrandom 0.2.12", + "curve25519-dalek 3.2.2", + "getrandom 0.2.15", "itertools 0.10.5", "js-sys", "lazy_static", @@ -8873,7 +8666,7 @@ dependencies = [ "memoffset", "num-derive 0.3.3", "num-traits", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "rand 0.7.3", "rand_chacha 0.2.2", "rustc_version", @@ -8901,7 +8694,7 @@ dependencies = [ "base64 0.13.1", "bincode", "eager", - "enum-iterator", + "enum-iterator 0.8.1", "itertools 0.10.5", "libc", "libloading 0.7.4", @@ -8919,30 +8712,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "solana-program-test" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "assert_matches", - "async-trait", - "base64 0.13.1", - "bincode", - "chrono-humanize", - "log", - "serde", - "solana-banks-client", - "solana-banks-server", - "solana-bpf-loader-program", - "solana-logger", - "solana-program-runtime", - "solana-runtime", - "solana-sdk", - "solana-vote-program", - "thiserror", - "tokio", -] - [[package]] name = "solana-rayon-threadlimit" version = "1.14.13" @@ -8962,7 +8731,7 @@ dependencies = [ "log", "num-derive 0.3.3", "num-traits", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "qstring", "semver", "solana-sdk", @@ -8970,66 +8739,6 @@ dependencies = [ "uriparse", ] -[[package]] -name = "solana-runtime" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "arrayref", - "bincode", - "blake3", - "bv", - "bytemuck", - "byteorder", - "bzip2", - "crossbeam-channel", - "dashmap", - "dir-diff", - "flate2", - "fnv", - "im", - "index_list", - "itertools 0.10.5", - "lazy_static", - "log", - "lru", - "lz4", - "memmap2", - "num-derive 0.3.3", - "num-traits", - "num_cpus", - "once_cell", - "ouroboros", - "rand 0.7.3", - "rayon", - "regex", - "rustc_version", - "serde", - "serde_derive", - "solana-address-lookup-table-program", - "solana-bucket-map", - "solana-compute-budget-program", - "solana-config-program", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-measure", - "solana-metrics", - "solana-program-runtime", - "solana-rayon-threadlimit", - "solana-sdk", - "solana-stake-program", - "solana-vote-program", - "solana-zk-token-proof-program", - "solana-zk-token-sdk", - "strum 0.24.1", - "strum_macros 0.24.3", - "symlink", - "tar", - "tempfile", - "thiserror", - "zstd", -] - [[package]] name = "solana-sdk" version = "1.14.13" @@ -9046,7 +8755,7 @@ dependencies = [ "chrono", "derivation-path", "digest 0.10.7", - "ed25519-dalek", + "ed25519-dalek 1.0.1", "ed25519-dalek-bip32", "generic-array 0.14.7", "hmac 0.12.1", @@ -9082,50 +8791,14 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "bs58 0.4.0", - "proc-macro2 1.0.76", - "quote 1.0.35", - "rustversion", - "syn 1.0.109", -] - -[[package]] -name = "solana-send-transaction-service" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "crossbeam-channel", - "log", - "solana-client", - "solana-measure", - "solana-metrics", - "solana-runtime", - "solana-sdk", -] - -[[package]] -name = "solana-stake-program" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "bincode", - "log", - "num-derive 0.3.3", - "num-traits", - "rustc_version", - "serde", - "serde_derive", - "solana-config-program", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "solana-vote-program", - "thiserror", +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bs58 0.4.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustversion", + "syn 1.0.109", ] [[package]] @@ -9219,20 +8892,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "solana-zk-token-proof-program" -version = "1.14.13" -source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" -dependencies = [ - "bytemuck", - "getrandom 0.1.16", - "num-derive 0.3.3", - "num-traits", - "solana-program-runtime", - "solana-sdk", - "solana-zk-token-sdk", -] - [[package]] name = "solana-zk-token-sdk" version = "1.14.13" @@ -9244,8 +8903,8 @@ dependencies = [ "bincode", "bytemuck", "byteorder", - "cipher 0.4.4", - "curve25519-dalek", + "cipher", + "curve25519-dalek 3.2.2", "getrandom 0.1.16", "itertools 0.10.5", "lazy_static", @@ -9263,24 +8922,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "solana_rbpf" -version = "0.2.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80a28c5dfe7e8af38daa39d6561c8e8b9ed7a2f900951ebe7362ad6348d36c73" -dependencies = [ - "byteorder", - "combine", - "goblin", - "hash32", - "libc", - "log", - "rand 0.8.5", - "rustc-demangle", - "scroll", - "thiserror", -] - [[package]] name = "spin" version = "0.5.2" @@ -9323,7 +8964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" dependencies = [ "base64ct", - "der 0.7.8", + "der 0.7.9", ] [[package]] @@ -9413,11 +9054,10 @@ dependencies = [ [[package]] name = "sqlformat" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce81b7bd7c4493975347ef60d8c7e8b742d4694f4c49f93e0a12ea263938176c" +checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" dependencies = [ - "itertools 0.12.0", "nom", "unicode_categories", ] @@ -9438,7 +9078,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" dependencies = [ - "ahash 0.7.7", + "ahash 0.7.8", "atoi", "base64 0.13.1", "bigdecimal 0.3.1", @@ -9465,7 +9105,7 @@ dependencies = [ "log", "md-5 0.10.6", "memchr", - "num-bigint 0.4.4", + "num-bigint 0.4.6", "once_cell", "paste", "percent-encoding", @@ -9483,7 +9123,7 @@ dependencies = [ "time", "tokio-stream", "url", - "uuid 1.6.1", + "uuid 1.10.0", "whoami", ] @@ -9497,8 +9137,8 @@ dependencies = [ "either", "heck 0.4.1", "once_cell", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "serde_json", "sqlx-core", "sqlx-rt", @@ -9538,13 +9178,13 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] [[package]] @@ -9560,10 +9200,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "strum" -version = "0.21.0" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" @@ -9574,6 +9214,12 @@ dependencies = [ "strum_macros 0.24.3", ] +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "strum" version = "0.26.3" @@ -9585,27 +9231,28 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.21.1" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ - "heck 0.3.3", - "proc-macro2 1.0.76", - "quote 1.0.35", + "heck 0.4.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustversion", "syn 1.0.109", ] [[package]] name = "strum_macros" -version = "0.24.3" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 1.0.109", + "syn 2.0.77", ] [[package]] @@ -9615,17 +9262,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "rustversion", - "syn 2.0.48", + "syn 2.0.77", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "subtle-encoding" @@ -9642,12 +9289,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "734676eb262c623cec13c3155096e08d1f8f29adce39ba17948b18dad1e54142" -[[package]] -name = "symlink" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" - [[package]] name = "syn" version = "0.15.44" @@ -9665,19 +9306,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.48" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "unicode-ident", ] @@ -9688,9 +9329,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9705,10 +9346,21 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", + "proc-macro2 1.0.86", + "quote 1.0.37", "syn 1.0.109", - "unicode-xid 0.2.4", + "unicode-xid 0.2.5", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9750,7 +9402,7 @@ dependencies = [ "base64 0.13.1", "bytes", "chrono", - "http", + "http 0.2.12", "percent-encoding", "serde", "serde_json", @@ -9765,63 +9417,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "tar" -version = "0.4.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tarpc" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38a012bed6fb9681d3bf71ffaa4f88f3b4b9ed3198cda6e4c8462d24d4bb80" -dependencies = [ - "anyhow", - "fnv", - "futures", - "humantime", - "opentelemetry", - "pin-project", - "rand 0.8.5", - "serde", - "static_assertions 1.1.0", - "tarpc-plugins", - "thiserror", - "tokio", - "tokio-serde", - "tokio-util 0.6.10", - "tracing", - "tracing-opentelemetry", -] - -[[package]] -name = "tarpc-plugins" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" -dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 1.0.109", -] - [[package]] name = "tempfile" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -9835,7 +9441,7 @@ dependencies = [ "ed25519-consensus", "flex-error", "futures", - "k256 0.13.3", + "k256 0.13.4", "num-traits", "once_cell", "prost 0.11.9", @@ -9911,8 +9517,8 @@ dependencies = [ "bytes", "flex-error", "futures", - "getrandom 0.2.12", - "http", + "getrandom 0.2.15", + "http 0.2.12", "hyper", "hyper-proxy", "hyper-rustls 0.22.1", @@ -9962,28 +9568,28 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -9994,9 +9600,9 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -10004,13 +9610,14 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", "libc", + "num-conv", "num_threads", "powerfmt", "serde", @@ -10026,10 +9633,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -10072,9 +9680,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -10087,18 +9695,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.39.1" +version = "1.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" dependencies = [ "backtrace", "bytes", "libc", "mio", - "parking_lot 0.12.1", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.7", "tokio-macros", "tracing", "windows-sys 0.52.0", @@ -10120,9 +9728,9 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -10174,31 +9782,15 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.10", + "rustls 0.21.12", "tokio", ] -[[package]] -name = "tokio-serde" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" -dependencies = [ - "bincode", - "bytes", - "educe", - "futures-core", - "futures-sink", - "pin-project", - "serde", - "serde_json", -] - [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -10207,9 +9799,9 @@ dependencies = [ [[package]] name = "tokio-test" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" dependencies = [ "async-stream", "bytes", @@ -10236,43 +9828,27 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", "tokio", - "tungstenite 0.20.1", -] - -[[package]] -name = "tokio-util" -version = "0.6.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "slab", - "tokio", + "tungstenite 0.21.0", ] [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -10286,9 +9862,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" @@ -10296,31 +9872,20 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap 2.1.0", + "indexmap 2.5.0", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.5.0", "toml_datetime", - "winnow", + "winnow 0.6.18", ] [[package]] @@ -10337,7 +9902,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", @@ -10367,14 +9932,14 @@ dependencies = [ "base64 0.21.7", "bytes", "h2", - "http", + "http 0.2.12", "http-body", "hyper", "hyper-timeout", "percent-encoding", "pin-project", - "prost 0.12.4", - "rustls 0.21.10", + "prost 0.12.6", + "rustls 0.21.12", "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", "tokio", @@ -10400,7 +9965,7 @@ dependencies = [ "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.10", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -10408,15 +9973,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -10436,9 +10001,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -10482,19 +10047,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" -dependencies = [ - "once_cell", - "opentelemetry", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "tracing-serde" version = "0.1.3" @@ -10528,11 +10080,10 @@ dependencies = [ [[package]] name = "tracing-test" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" dependencies = [ - "lazy_static", "tracing-core", "tracing-subscriber", "tracing-test-macro", @@ -10540,13 +10091,12 @@ dependencies = [ [[package]] name = "tracing-test-macro" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" dependencies = [ - "lazy_static", - "quote 1.0.35", - "syn 1.0.109", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -10564,7 +10114,7 @@ dependencies = [ "base64 0.13.1", "byteorder", "bytes", - "http", + "http 0.2.12", "httparse", "log", "rand 0.8.5", @@ -10579,14 +10129,14 @@ dependencies = [ [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.1.0", "httparse", "log", "rand 0.8.5", @@ -10598,9 +10148,9 @@ dependencies = [ [[package]] name = "typeid" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" [[package]] name = "typenum" @@ -10610,9 +10160,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "typetag" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "661d18414ec032a49ece2d56eee03636e43c4e8d577047ab334c0ba892e29aaf" +checksum = "52ba3b6e86ffe0054b2c44f2d86407388b933b16cb0a70eea3929420db1d9bbe" dependencies = [ "erased-serde", "inventory", @@ -10623,13 +10173,13 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac73887f47b9312552aa90ef477927ff014d63d1920ca8037c6c1951eab64bb1" +checksum = "70b20a22c42c8f1cd23ce5e34f165d4d37038f5b663ad20fb6adbdf029172483" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -10673,9 +10223,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -10685,24 +10235,30 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" + [[package]] name = "unicode-segmentation" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" @@ -10712,9 +10268,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" [[package]] name = "unicode_categories" @@ -10755,11 +10311,11 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "2.9.1" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "log", "once_cell", "url", @@ -10777,9 +10333,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna 0.5.0", @@ -10792,27 +10348,21 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - [[package]] name = "uuid" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.12", + "getrandom 0.2.15", "serde", ] [[package]] name = "uuid" -version = "1.6.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "serde", ] @@ -10823,10 +10373,11 @@ version = "0.1.0" dependencies = [ "async-trait", "axum", + "chrono", "config", "console-subscriber", "derive-new", - "derive_more", + "derive_more 0.99.18", "ethers", "eyre", "futures", @@ -10836,7 +10387,8 @@ dependencies = [ "hyperlane-cosmos", "hyperlane-ethereum", "hyperlane-test", - "k256 0.13.3", + "k256 0.13.4", + "mockall", "prometheus", "reqwest", "serde", @@ -10880,9 +10432,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "void" @@ -10892,9 +10444,9 @@ checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -10911,15 +10463,15 @@ dependencies = [ [[package]] name = "warp" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e92e22e03ff1230c03a1a8ee37d2f89cd489e2e541b7550d6afad96faed169" +checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" dependencies = [ "bytes", "futures-channel", "futures-util", "headers", - "http", + "http 0.2.12", "hyper", "log", "mime", @@ -10927,15 +10479,13 @@ dependencies = [ "multer", "percent-encoding", "pin-project", - "rustls-pemfile 1.0.4", "scoped-tls", "serde", "serde_json", "serde_urlencoded", "tokio", - "tokio-stream", - "tokio-tungstenite 0.20.1", - "tokio-util 0.7.10", + "tokio-tungstenite 0.21.0", + "tokio-util", "tower-service", "tracing", ] @@ -10952,36 +10502,43 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" dependencies = [ "cfg-if", "js-sys", @@ -10991,32 +10548,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" dependencies = [ - "quote 1.0.35", + "quote 1.0.37", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" [[package]] name = "wasm-timer" @@ -11035,9 +10592,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" dependencies = [ "js-sys", "wasm-bindgen", @@ -11059,7 +10616,7 @@ version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" dependencies = [ - "ring 0.17.7", + "ring 0.17.8", "untrusted 0.9.0", ] @@ -11083,9 +10640,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" [[package]] name = "which" @@ -11099,13 +10656,26 @@ dependencies = [ "rustix", ] +[[package]] +name = "which" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" +dependencies = [ + "either", + "home", + "rustix", + "winsafe", +] + [[package]] name = "whoami" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "wasm-bindgen", + "redox_syscall 0.5.3", + "wasite", "web-sys", ] @@ -11127,11 +10697,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -11146,7 +10716,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -11164,7 +10734,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -11184,17 +10763,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -11205,9 +10785,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -11217,9 +10797,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -11229,9 +10809,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -11241,9 +10827,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -11253,9 +10839,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -11265,9 +10851,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -11277,15 +10863,24 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] [[package]] name = "winnow" -version = "0.5.34" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] @@ -11300,6 +10895,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -11346,38 +10947,27 @@ dependencies = [ "time", ] -[[package]] -name = "xattr" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" -dependencies = [ - "libc", - "linux-raw-sys", - "rustix", -] - [[package]] name = "xml-rs" -version = "0.8.19" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +checksum = "539a77ee7c0de333dcc6da69b177380a0b81e0dacfa4f7344c465a36871ee601" [[package]] name = "ya-gcp" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186a4237c7bddb8a13c1056f45bdfb5c548020327203a66878d74fa7cf31286e" +checksum = "acaf2e321fc6f853572b372962fa253cba1b62a0025116bb463ce3c00b4394dc" dependencies = [ "cfg-if", "futures", - "http", + "http 0.2.12", "humantime-serde", "hyper", "hyper-rustls 0.24.2", "paste", "rand 0.8.5", - "rustls 0.21.10", + "rustls 0.21.12", "rustls-native-certs 0.6.3", "serde", "tame-gcs", @@ -11409,21 +10999,21 @@ dependencies = [ [[package]] name = "yup-oauth2" -version = "8.3.2" +version = "8.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b61da40aeb0907a65f7fb5c1de83c5a224d6a9ebb83bf918588a2bb744d636b8" +checksum = "24bea7df5a9a74a9a0de92f22e5ab3fb9505dd960c7f1f00de5b7231d9d97206" dependencies = [ "anyhow", "async-trait", - "base64 0.21.7", + "base64 0.13.1", "futures", - "http", + "http 0.2.12", "hyper", "hyper-rustls 0.24.2", - "itertools 0.12.0", + "itertools 0.10.5", "log", "percent-encoding", - "rustls 0.22.2", + "rustls 0.21.12", "rustls-pemfile 1.0.4", "seahash", "serde", @@ -11436,29 +11026,30 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -11469,9 +11060,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.76", - "quote 1.0.35", - "syn 2.0.48", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", ] [[package]] @@ -11495,9 +11086,9 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" dependencies = [ "cc", "pkg-config", diff --git a/rust/main/Cargo.toml b/rust/main/Cargo.toml new file mode 100644 index 000000000..0f1ec5f74 --- /dev/null +++ b/rust/main/Cargo.toml @@ -0,0 +1,329 @@ +[workspace] +members = [ + "agents/relayer", + "agents/scraper", + "agents/validator", + "chains/hyperlane-cosmos", + "chains/hyperlane-ethereum", + "chains/hyperlane-fuel", + "chains/hyperlane-sealevel", + "ethers-prometheus", + "hyperlane-base", + "hyperlane-core", + "hyperlane-test", + "utils/abigen", + "utils/backtrace-oneline", + "utils/crypto", + "utils/hex", + "utils/run-locally", +] + +[workspace.package] +documentation = "https://docs.hyperlane.xyz" +edition = "2021" +homepage = "https://hyperlane.xyz" +license-file = "../LICENSE.md" +publish = false +version = "0.1.0" + +[workspace.dependencies] +Inflector = "0.11.4" +anyhow = "1.0" +async-trait = "0.1" +async-rwlock = "1.3" +auto_impl = "1.0" +axum = "0.6.1" +backtrace = "0.3" +base64 = "0.21.2" +bigdecimal = "0.4.2" +bincode = "1.3" +borsh = "0.9" +bs58 = "0.5.0" +bytes = "1" +clap = "4" +chrono = "*" +color-eyre = "0.6" +config = "0.13.3" +console-subscriber = "0.2.0" +convert_case = "0.6" +cosmrs = { version = "0.14", default-features = false, features = [ + "cosmwasm", + "rpc", + "tokio", + "grpc", +] } +cosmwasm-std = "*" +crunchy = "0.2" +ctrlc = "3.2" +curve25519-dalek = { version = "~3.2", features = ["serde"] } +derive-new = "0.5" +derive_builder = "0.12" +derive_more = "0.99" +dhat = "0.3.3" +ed25519-dalek = "~1.0" +eyre = "=0.6.8" +fixed-hash = "0.8.0" +fuels = "0.65.0" +fuels-code-gen = "0.65.0" +futures = "0.3" +futures-util = "0.3" +generic-array = { version = "0.14", features = ["serde", "more_lengths"] } +# Required for WASM support https://docs.rs/getrandom/latest/getrandom/#webassembly-support +bech32 = "0.9.1" +elliptic-curve = "0.13.8" +getrandom = { version = "0.2", features = ["js"] } +hex = "0.4.3" +http = "0.2.12" +hyper = "0.14" +hyper-tls = "0.5.0" +hyperlane-cosmwasm-interface = "=0.0.6-rc6" +injective-protobuf = "0.2.2" +injective-std = "=0.1.5" +itertools = "*" +jobserver = "=0.1.26" +jsonrpc-core = "18.0" +k256 = { version = "0.13.4", features = ["arithmetic", "std", "ecdsa"] } +log = "0.4" +macro_rules_attribute = "0.2" +maplit = "1.0" +mockall = "0.11" +nix = { version = "0.26", default-features = false } +num = "0.4" +num-bigint = "0.4" +num-derive = "0.4.0" +num-traits = "0.2" +once_cell = "1.18.0" +parking_lot = "0.12" +paste = "1.0" +pretty_env_logger = "0.5.0" +primitive-types = "=0.12.1" +prometheus = "0.13" +protobuf = "*" +rand = "0.8.5" +regex = "1.5" +reqwest = "0.11" +ripemd = "0.1.3" +rlp = "=0.5.2" +rocksdb = "0.21.0" +sea-orm = { version = "0.11.1", features = [ + "sqlx-postgres", + "runtime-tokio-native-tls", + "with-bigdecimal", + "with-time", + "macros", +] } +sea-orm-migration = { version = "0.11.1", features = [ + "sqlx-postgres", + "runtime-tokio-native-tls", +] } +semver = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_bytes = "0.11" +serde_derive = "1.0" +serde_json = "1.0" +sha2 = { version = "0.10.6", default-features = false } +sha256 = "1.1.4" +sha3 = "0.10" +solana-account-decoder = "=1.14.13" +solana-banks-client = "=1.14.13" +solana-banks-interface = "=1.14.13" +solana-banks-server = "=1.14.13" +solana-clap-utils = "=1.14.13" +solana-cli-config = "=1.14.13" +solana-client = "=1.14.13" +solana-program = "=1.14.13" +solana-program-test = "=1.14.13" +solana-sdk = "=1.14.13" +solana-transaction-status = "=1.14.13" +solana-zk-token-sdk = "=1.14.13" +spl-associated-token-account = { version = "=1.1.2", features = [ + "no-entrypoint", +] } +spl-noop = { version = "=0.1.3", features = ["no-entrypoint"] } +spl-token = { version = "=3.5.0", features = ["no-entrypoint"] } +spl-token-2022 = { version = "=0.5.0", features = ["no-entrypoint"] } +spl-type-length-value = "=0.1.0" +static_assertions = "1.1" +strum = "0.26.2" +strum_macros = "0.26.2" +tempfile = "3.3" +tendermint = "0.32.2" +tendermint-rpc = { version = "0.32.0", features = ["http-client", "tokio"] } +thiserror = "1.0" +time = "0.3" +tiny-keccak = "2.0.2" +tokio = { version = "1.4", features = ["parking_lot", "tracing"] } +tokio-metrics = { version = "0.3.1", default-features = false } +tokio-test = "0.4" +toml_edit = "0.19.14" +tonic = "0.9.2" +tracing = { version = "0.1" } +tracing-error = "0.2" +tracing-futures = "0.2" +tracing-subscriber = { version = "0.3", default-features = false } +tracing-test = "0.2.2" +typetag = "0.2" +uint = "0.9.5" +ureq = { version = "2.4", default-features = false } +url = "2.3" +walkdir = "2" +warp = "0.3" +which = "4.3" +ya-gcp = { version = "0.11.3", features = ["storage"] } + +## TODO: remove this +cosmwasm-schema = "1.2.7" + +[profile.release.package.access-control] +overflow-checks = true + +[profile.release.package.account-utils] +overflow-checks = true + +[profile.release.package.ecdsa-signature] +overflow-checks = true + +[profile.release.package.hyperlane-sealevel-interchain-security-module-interface] +overflow-checks = true + +[profile.release.package.hyperlane-sealevel-message-recipient-interface] +overflow-checks = true + +[profile.release.package.multisig-ism] +overflow-checks = true + +[profile.release.package.serializable-account-meta] +overflow-checks = true + +[profile.release.package.hyperlane-sealevel-mailbox] +overflow-checks = true + +[profile.release.package.hyperlane-sealevel-igp] +overflow-checks = true + +[profile.release.package.hyperlane-sealevel-multisig-ism-message-id] +overflow-checks = true + +[profile.release.package.hyperlane-sealevel-validator-announce] +overflow-checks = true + + +[workspace.dependencies.ethers] +features = [] +git = "https://github.com/hyperlane-xyz/ethers-rs" +tag = "2024-04-25" + +[workspace.dependencies.ethers-contract] +features = ["legacy"] +git = "https://github.com/hyperlane-xyz/ethers-rs" +tag = "2024-04-25" + +[workspace.dependencies.ethers-core] +features = [] +git = "https://github.com/hyperlane-xyz/ethers-rs" +tag = "2024-04-25" + +[workspace.dependencies.ethers-providers] +features = [] +git = "https://github.com/hyperlane-xyz/ethers-rs" +tag = "2024-04-25" + +[workspace.dependencies.ethers-signers] +features = ["aws"] +git = "https://github.com/hyperlane-xyz/ethers-rs" +tag = "2024-04-25" + +[patch.crates-io.curve25519-dalek] +branch = "v3.2.2-relax-zeroize" +git = "https://github.com/Eclipse-Laboratories-Inc/curve25519-dalek" +version = "3.2.2" + +[patch.crates-io.ed25519-dalek] +branch = "main" +git = "https://github.com/Eclipse-Laboratories-Inc/ed25519-dalek" +version = "1.0.1" + +[patch.crates-io.primitive-types] +branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/parity-common.git" +version = "=0.12.1" + +[patch.crates-io.rlp] +branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/parity-common.git" +version = "=0.5.2" + +[patch.crates-io.solana-account-decoder] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-clap-utils] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-cli-config] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-client] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-program] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-sdk] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-transaction-status] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.solana-zk-token-sdk] +git = "https://github.com/hyperlane-xyz/solana.git" +tag = "hyperlane-1.14.13-2023-07-04" +version = "=1.14.13" + +[patch.crates-io.spl-associated-token-account] +branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=1.1.2" + +[patch.crates-io.spl-noop] +branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=0.1.3" + +[patch.crates-io.spl-token] +branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=3.5.0" + +[patch.crates-io.spl-token-2022] +branch = "hyperlane" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +version = "=0.5.0" + +[patch.crates-io.spl-type-length-value] +version = "=0.1.0" +git = "https://github.com/hyperlane-xyz/solana-program-library.git" +branch = "hyperlane" + +[patch.crates-io.tendermint] +branch = "trevor/0.32.2-fork" +git = "https://github.com/hyperlane-xyz/tendermint-rs.git" +version = "=0.32.2" + +[patch.crates-io.tendermint-rpc] +branch = "trevor/0.32.2-fork" +git = "https://github.com/hyperlane-xyz/tendermint-rs.git" +version = "=0.32.2" diff --git a/rust/agents/relayer/.cargo/config.toml b/rust/main/agents/relayer/.cargo/config.toml similarity index 100% rename from rust/agents/relayer/.cargo/config.toml rename to rust/main/agents/relayer/.cargo/config.toml diff --git a/rust/agents/relayer/Cargo.toml b/rust/main/agents/relayer/Cargo.toml similarity index 88% rename from rust/agents/relayer/Cargo.toml rename to rust/main/agents/relayer/Cargo.toml index 41fff1b24..5a891d912 100644 --- a/rust/agents/relayer/Cargo.toml +++ b/rust/main/agents/relayer/Cargo.toml @@ -1,4 +1,3 @@ -cargo-features = ["workspace-inheritance"] [package] name = "relayer" @@ -35,13 +34,21 @@ serde.workspace = true serde_json.workspace = true strum.workspace = true thiserror.workspace = true -tokio = { workspace = true, features = ["rt", "macros", "parking_lot", "rt-multi-thread"] } +tokio = { workspace = true, features = [ + "rt", + "macros", + "parking_lot", + "rt-multi-thread", +] } tokio-metrics.workspace = true tracing-futures.workspace = true tracing.workspace = true typetag.workspace = true -hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async"] } +hyperlane-core = { path = "../../hyperlane-core", features = [ + "agent", + "async", +] } hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } @@ -51,6 +58,7 @@ mockall.workspace = true tokio-test.workspace = true hyperlane-test = { path = "../../hyperlane-test" } hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } +hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async", "test-utils"] } [features] default = ["color-eyre", "oneline-errors"] diff --git a/rust/agents/relayer/src/lib.rs b/rust/main/agents/relayer/src/lib.rs similarity index 100% rename from rust/agents/relayer/src/lib.rs rename to rust/main/agents/relayer/src/lib.rs diff --git a/rust/agents/relayer/src/main.rs b/rust/main/agents/relayer/src/main.rs similarity index 100% rename from rust/agents/relayer/src/main.rs rename to rust/main/agents/relayer/src/main.rs diff --git a/rust/agents/relayer/src/memory_profiler.rs b/rust/main/agents/relayer/src/memory_profiler.rs similarity index 100% rename from rust/agents/relayer/src/memory_profiler.rs rename to rust/main/agents/relayer/src/memory_profiler.rs diff --git a/rust/agents/relayer/src/merkle_tree/builder.rs b/rust/main/agents/relayer/src/merkle_tree/builder.rs similarity index 100% rename from rust/agents/relayer/src/merkle_tree/builder.rs rename to rust/main/agents/relayer/src/merkle_tree/builder.rs diff --git a/rust/agents/relayer/src/merkle_tree/mod.rs b/rust/main/agents/relayer/src/merkle_tree/mod.rs similarity index 100% rename from rust/agents/relayer/src/merkle_tree/mod.rs rename to rust/main/agents/relayer/src/merkle_tree/mod.rs diff --git a/rust/agents/relayer/src/merkle_tree/processor.rs b/rust/main/agents/relayer/src/merkle_tree/processor.rs similarity index 97% rename from rust/agents/relayer/src/merkle_tree/processor.rs rename to rust/main/agents/relayer/src/merkle_tree/processor.rs index 251e8e7c6..9ddcc2ee0 100644 --- a/rust/agents/relayer/src/merkle_tree/processor.rs +++ b/rust/main/agents/relayer/src/merkle_tree/processor.rs @@ -7,7 +7,7 @@ use std::{ use async_trait::async_trait; use derive_new::new; use eyre::Result; -use hyperlane_base::db::HyperlaneRocksDB; +use hyperlane_base::db::{HyperlaneDb, HyperlaneRocksDB}; use hyperlane_core::{HyperlaneDomain, MerkleTreeInsertion}; use prometheus::IntGauge; use tokio::sync::RwLock; diff --git a/rust/agents/relayer/src/msg/blacklist.rs b/rust/main/agents/relayer/src/msg/blacklist.rs similarity index 100% rename from rust/agents/relayer/src/msg/blacklist.rs rename to rust/main/agents/relayer/src/msg/blacklist.rs diff --git a/rust/agents/relayer/src/msg/gas_payment/mod.rs b/rust/main/agents/relayer/src/msg/gas_payment/mod.rs similarity index 98% rename from rust/agents/relayer/src/msg/gas_payment/mod.rs rename to rust/main/agents/relayer/src/msg/gas_payment/mod.rs index 0a4684863..fa9f40b45 100644 --- a/rust/agents/relayer/src/msg/gas_payment/mod.rs +++ b/rust/main/agents/relayer/src/msg/gas_payment/mod.rs @@ -105,7 +105,7 @@ impl GasPaymentEnforcer { for (policy, whitelist) in &self.policies { if !whitelist.msg_matches(message, true) { trace!( - msg=%message, + hyp_message=%message, ?policy, ?whitelist, "Message did not match whitelist for policy" @@ -114,16 +114,17 @@ impl GasPaymentEnforcer { } trace!( - msg=%message, + hyp_message=%message, ?policy, ?whitelist, "Message matched whitelist for policy" ); debug!( - msg=%message, + hyp_message=%message, ?policy, ?current_payment, ?current_expenditure, + ?tx_cost_estimate, "Evaluating if message meets gas payment requirement", ); return policy @@ -148,7 +149,7 @@ impl GasPaymentEnforcer { } error!( - msg=%message, + hyp_message=%message, policies=?self.policies, "No gas payment policy matched for message; consider adding a default policy to the end of the policies array which uses a wildcard whitelist." ); @@ -158,7 +159,7 @@ impl GasPaymentEnforcer { pub fn record_tx_outcome(&self, message: &HyperlaneMessage, outcome: TxOutcome) -> Result<()> { // This log is required in E2E, hence the use of a `const` debug!( - msg=%message, + hyp_message=%message, ?outcome, "{}", GAS_EXPENDITURE_LOG_MESSAGE, diff --git a/rust/agents/relayer/src/msg/gas_payment/policies/minimum.rs b/rust/main/agents/relayer/src/msg/gas_payment/policies/minimum.rs similarity index 100% rename from rust/agents/relayer/src/msg/gas_payment/policies/minimum.rs rename to rust/main/agents/relayer/src/msg/gas_payment/policies/minimum.rs diff --git a/rust/agents/relayer/src/msg/gas_payment/policies/mod.rs b/rust/main/agents/relayer/src/msg/gas_payment/policies/mod.rs similarity index 100% rename from rust/agents/relayer/src/msg/gas_payment/policies/mod.rs rename to rust/main/agents/relayer/src/msg/gas_payment/policies/mod.rs diff --git a/rust/agents/relayer/src/msg/gas_payment/policies/none.rs b/rust/main/agents/relayer/src/msg/gas_payment/policies/none.rs similarity index 100% rename from rust/agents/relayer/src/msg/gas_payment/policies/none.rs rename to rust/main/agents/relayer/src/msg/gas_payment/policies/none.rs diff --git a/rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs b/rust/main/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs similarity index 100% rename from rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs rename to rust/main/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs diff --git a/rust/agents/relayer/src/msg/metadata/aggregation.rs b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs similarity index 99% rename from rust/agents/relayer/src/msg/metadata/aggregation.rs rename to rust/main/agents/relayer/src/msg/metadata/aggregation.rs index a5022e727..072ce62b6 100644 --- a/rust/agents/relayer/src/msg/metadata/aggregation.rs +++ b/rust/main/agents/relayer/src/msg/metadata/aggregation.rs @@ -118,6 +118,7 @@ impl AggregationIsmMetadataBuilder { #[async_trait] impl MetadataBuilder for AggregationIsmMetadataBuilder { #[instrument(err, skip(self), ret)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn build( &self, ism_address: H256, diff --git a/rust/agents/relayer/src/msg/metadata/base.rs b/rust/main/agents/relayer/src/msg/metadata/base.rs similarity index 98% rename from rust/agents/relayer/src/msg/metadata/base.rs rename to rust/main/agents/relayer/src/msg/metadata/base.rs index 1416a7f56..be10af611 100644 --- a/rust/agents/relayer/src/msg/metadata/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/base.rs @@ -1,3 +1,6 @@ +#![allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue +#![allow(clippy::unnecessary_get_then_check)] // TODO: `rustc` 1.80.1 clippy issue + use std::{ collections::HashMap, fmt::Debug, @@ -19,7 +22,7 @@ use crate::{ use async_trait::async_trait; use derive_new::new; use eyre::{Context, Result}; -use hyperlane_base::db::HyperlaneRocksDB; +use hyperlane_base::db::{HyperlaneDb, HyperlaneRocksDB}; use hyperlane_base::{ settings::{ChainConf, CheckpointSyncerConf}, CheckpointSyncer, CoreMetrics, MultisigCheckpointSyncer, @@ -397,7 +400,7 @@ impl BaseMetadataBuilder { continue; } - match config.build(None).await { + match config.build_and_validate(None).await { Ok(checkpoint_syncer) => { // found the syncer for this validator checkpoint_syncers.insert(validator.into(), checkpoint_syncer.into()); diff --git a/rust/agents/relayer/src/msg/metadata/ccip_read.rs b/rust/main/agents/relayer/src/msg/metadata/ccip_read.rs similarity index 97% rename from rust/agents/relayer/src/msg/metadata/ccip_read.rs rename to rust/main/agents/relayer/src/msg/metadata/ccip_read.rs index 1ca3fbe1b..507271c12 100644 --- a/rust/agents/relayer/src/msg/metadata/ccip_read.rs +++ b/rust/main/agents/relayer/src/msg/metadata/ccip_read.rs @@ -1,3 +1,5 @@ +#![allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + use async_trait::async_trait; use derive_more::Deref; use derive_new::new; diff --git a/rust/agents/relayer/src/msg/metadata/mod.rs b/rust/main/agents/relayer/src/msg/metadata/mod.rs similarity index 100% rename from rust/agents/relayer/src/msg/metadata/mod.rs rename to rust/main/agents/relayer/src/msg/metadata/mod.rs diff --git a/rust/agents/relayer/src/msg/metadata/multisig/base.rs b/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs similarity index 95% rename from rust/agents/relayer/src/msg/metadata/multisig/base.rs rename to rust/main/agents/relayer/src/msg/metadata/multisig/base.rs index 328b8848b..f7898c721 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/base.rs +++ b/rust/main/agents/relayer/src/msg/metadata/multisig/base.rs @@ -126,11 +126,11 @@ impl MetadataBuilder for T { .await .context(CTX)? { - debug!(?message, ?metadata.checkpoint, "Found checkpoint with quorum"); + debug!(hyp_message=?message, ?metadata.checkpoint, "Found checkpoint with quorum"); Ok(Some(self.format_metadata(metadata)?)) } else { info!( - ?message, ?validators, threshold, ism=%multisig_ism.address(), + hyp_message=?message, ?validators, threshold, ism=%multisig_ism.address(), "Could not fetch metadata: Unable to reach quorum" ); Ok(None) diff --git a/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs b/rust/main/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs similarity index 98% rename from rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs rename to rust/main/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs index b8bcca040..ea73c5537 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs +++ b/rust/main/agents/relayer/src/msg/metadata/multisig/merkle_root_multisig.rs @@ -45,7 +45,7 @@ impl MultisigIsmMetadataBuilder for MerkleRootMultisigMetadataBuilder { .await .context(CTX)?, debug!( - ?message, + hyp_message=?message, "No merkle leaf found for message id, must have not been enqueued in the tree" ) ); diff --git a/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs b/rust/main/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs similarity index 98% rename from rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs rename to rust/main/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs index 9866c98b0..ab920c326 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs +++ b/rust/main/agents/relayer/src/msg/metadata/multisig/message_id_multisig.rs @@ -42,7 +42,7 @@ impl MultisigIsmMetadataBuilder for MessageIdMultisigMetadataBuilder { .await .context(CTX)?, debug!( - ?message, + hyp_message=?message, "No merkle leaf found for message id, must have not been enqueued in the tree" ) ); diff --git a/rust/agents/relayer/src/msg/metadata/multisig/mod.rs b/rust/main/agents/relayer/src/msg/metadata/multisig/mod.rs similarity index 81% rename from rust/agents/relayer/src/msg/metadata/multisig/mod.rs rename to rust/main/agents/relayer/src/msg/metadata/multisig/mod.rs index 5c53cc414..37265d24e 100644 --- a/rust/agents/relayer/src/msg/metadata/multisig/mod.rs +++ b/rust/main/agents/relayer/src/msg/metadata/multisig/mod.rs @@ -2,6 +2,7 @@ mod base; mod merkle_root_multisig; mod message_id_multisig; +#[allow(unused_imports)] // TODO: `rustc` 1.80.1 clippy issue pub use base::{MetadataToken, MultisigIsmMetadataBuilder, MultisigMetadata}; pub use merkle_root_multisig::MerkleRootMultisigMetadataBuilder; diff --git a/rust/agents/relayer/src/msg/metadata/null_metadata.rs b/rust/main/agents/relayer/src/msg/metadata/null_metadata.rs similarity index 85% rename from rust/agents/relayer/src/msg/metadata/null_metadata.rs rename to rust/main/agents/relayer/src/msg/metadata/null_metadata.rs index 1291f8d49..3793fe6b1 100644 --- a/rust/agents/relayer/src/msg/metadata/null_metadata.rs +++ b/rust/main/agents/relayer/src/msg/metadata/null_metadata.rs @@ -10,6 +10,7 @@ pub struct NullMetadataBuilder {} #[async_trait] impl MetadataBuilder for NullMetadataBuilder { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue #[instrument(err, skip(self))] async fn build( &self, diff --git a/rust/agents/relayer/src/msg/metadata/routing.rs b/rust/main/agents/relayer/src/msg/metadata/routing.rs similarity index 91% rename from rust/agents/relayer/src/msg/metadata/routing.rs rename to rust/main/agents/relayer/src/msg/metadata/routing.rs index c16fbc2a2..0f319e825 100644 --- a/rust/agents/relayer/src/msg/metadata/routing.rs +++ b/rust/main/agents/relayer/src/msg/metadata/routing.rs @@ -15,6 +15,7 @@ pub struct RoutingIsmMetadataBuilder { #[async_trait] impl MetadataBuilder for RoutingIsmMetadataBuilder { #[instrument(err, skip(self), ret)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn build( &self, ism_address: H256, diff --git a/rust/agents/relayer/src/msg/mod.rs b/rust/main/agents/relayer/src/msg/mod.rs similarity index 95% rename from rust/agents/relayer/src/msg/mod.rs rename to rust/main/agents/relayer/src/msg/mod.rs index abf00ec05..e47015709 100644 --- a/rust/agents/relayer/src/msg/mod.rs +++ b/rust/main/agents/relayer/src/msg/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::doc_lazy_continuation)] // TODO: `rustc` 1.80.1 clippy issue + //! Processor scans DB for new messages and wraps relevant messages as a //! `PendingOperation` and then sends it over a channel to a submitter for //! delivery. diff --git a/rust/agents/relayer/src/msg/op_queue.rs b/rust/main/agents/relayer/src/msg/op_queue.rs similarity index 81% rename from rust/agents/relayer/src/msg/op_queue.rs rename to rust/main/agents/relayer/src/msg/op_queue.rs index 7ea122c61..95025d391 100644 --- a/rust/agents/relayer/src/msg/op_queue.rs +++ b/rust/main/agents/relayer/src/msg/op_queue.rs @@ -6,7 +6,7 @@ use prometheus::{IntGauge, IntGaugeVec}; use tokio::sync::{broadcast::Receiver, Mutex}; use tracing::{debug, info, instrument}; -use crate::server::MessageRetryRequest; +use crate::settings::matching_list::MatchingList; pub type OperationPriorityQueue = Arc>>>; @@ -16,7 +16,7 @@ pub type OperationPriorityQueue = Arc>> pub struct OpQueue { metrics: IntGaugeVec, queue_metrics_label: String, - retry_rx: Arc>>, + retry_rx: Arc>>, #[new(default)] pub queue: OperationPriorityQueue, } @@ -29,11 +29,10 @@ impl OpQueue { /// it's very likely that its status has just changed, so this forces the caller to consider the new status #[instrument(skip(self), ret, fields(queue_label=%self.queue_metrics_label), level = "trace")] pub async fn push(&self, mut op: QueueOperation, new_status: Option) { - if let Some(new_status) = new_status { - op.set_status(new_status); - } - // increment the metric before pushing onto the queue, because we lose ownership afterwards - self.get_operation_metric(op.as_ref()).inc(); + op.set_status_and_update_metrics( + new_status, + Arc::new(self.get_operation_metric(op.as_ref())), + ); self.queue.lock().await.push(Reverse(op)); } @@ -52,9 +51,6 @@ impl OpQueue { let mut queue = self.queue.lock().await; let mut popped = vec![]; while let Some(Reverse(op)) = queue.pop() { - // even if the metric is decremented here, the operation may fail to process and be re-added to the queue. - // in those cases, the queue length will look like it has spikes whose sizes are at most `limit` - self.get_operation_metric(op.as_ref()).dec(); popped.push(op); if popped.len() >= limit { break; @@ -88,9 +84,7 @@ impl OpQueue { let mut reprioritized_queue: BinaryHeap<_> = queue .drain() .map(|Reverse(mut op)| { - // Can check for equality here because of the PartialEq implementation for MessageRetryRequest, - // but can't use `contains` because the types are different - if message_retry_requests.iter().any(|r| r == op) { + if message_retry_requests.iter().any(|r| r.op_matches(&op)) { info!( operation = %op, queue_label = %self.queue_metrics_label, @@ -120,12 +114,14 @@ impl OpQueue { pub mod test { use super::*; use hyperlane_core::{ - HyperlaneDomain, HyperlaneMessage, KnownHyperlaneDomain, PendingOperationResult, + HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneDomainTechnicalStack, + HyperlaneDomainType, HyperlaneMessage, KnownHyperlaneDomain, PendingOperationResult, TryBatchAs, TxOutcome, H256, U256, }; use serde::Serialize; use std::{ collections::VecDeque, + str::FromStr, time::{Duration, Instant}, }; use tokio::sync; @@ -133,6 +129,10 @@ pub mod test { #[derive(Debug, Clone, Serialize)] pub struct MockPendingOperation { id: H256, + sender_address: H256, + origin_domain_id: u32, + destination_domain_id: u32, + recipient_address: H256, seconds_to_next_attempt: u64, destination_domain: HyperlaneDomain, } @@ -142,7 +142,50 @@ pub mod test { Self { id: H256::random(), seconds_to_next_attempt, + destination_domain_id: destination_domain.id(), destination_domain, + sender_address: H256::random(), + recipient_address: H256::random(), + origin_domain_id: 0, + } + } + + pub fn with_message_data(message: HyperlaneMessage) -> Self { + Self { + id: message.id(), + sender_address: message.sender, + recipient_address: message.recipient, + origin_domain_id: message.origin, + destination_domain_id: message.destination, + seconds_to_next_attempt: 0, + destination_domain: HyperlaneDomain::Unknown { + domain_id: message.destination, + domain_name: "test".to_string(), + domain_type: HyperlaneDomainType::Unknown, + domain_protocol: HyperlaneDomainProtocol::Ethereum, + domain_technical_stack: HyperlaneDomainTechnicalStack::Other, + }, + } + } + + pub fn with_id(self, id: &str) -> Self { + Self { + id: H256::from_str(id).unwrap(), + ..self + } + } + + pub fn with_sender_address(self, sender_address: &str) -> Self { + Self { + sender_address: H256::from_str(sender_address).unwrap(), + ..self + } + } + + pub fn with_recipient_address(self, recipient_address: &str) -> Self { + Self { + recipient_address: H256::from_str(recipient_address).unwrap(), + ..self } } } @@ -166,6 +209,20 @@ pub mod test { self.seconds_to_next_attempt = 0; } + fn sender_address(&self) -> &H256 { + &self.sender_address + } + + fn recipient_address(&self) -> &H256 { + &self.recipient_address + } + + fn get_metric(&self) -> Option> { + None + } + + fn set_metric(&mut self, _metric: Arc) {} + fn priority(&self) -> u32 { todo!() } @@ -179,7 +236,7 @@ pub mod test { } fn origin_domain_id(&self) -> u32 { - todo!() + self.origin_domain_id } fn destination_domain(&self) -> &HyperlaneDomain { @@ -196,7 +253,7 @@ pub mod test { /// Submit this operation to the blockchain and report if it was successful /// or not. - async fn submit(&mut self) { + async fn submit(&mut self) -> PendingOperationResult { todo!() } @@ -306,10 +363,10 @@ pub mod test { // Retry by message ids broadcaster - .send(MessageRetryRequest::MessageId(op_ids[1])) + .send(MatchingList::with_message_id(op_ids[1])) .unwrap(); broadcaster - .send(MessageRetryRequest::MessageId(op_ids[2])) + .send(MatchingList::with_message_id(op_ids[2])) .unwrap(); // Pop elements from queue 1 @@ -367,7 +424,7 @@ pub mod test { // Retry by domain broadcaster - .send(MessageRetryRequest::DestinationDomain( + .send(MatchingList::with_destination_domain( destination_domain_2.id(), )) .unwrap(); diff --git a/rust/agents/relayer/src/msg/op_submitter.rs b/rust/main/agents/relayer/src/msg/op_submitter.rs similarity index 88% rename from rust/agents/relayer/src/msg/op_submitter.rs rename to rust/main/agents/relayer/src/msg/op_submitter.rs index 53dba8525..c1e295a24 100644 --- a/rust/agents/relayer/src/msg/op_submitter.rs +++ b/rust/main/agents/relayer/src/msg/op_submitter.rs @@ -1,3 +1,6 @@ +#![allow(clippy::doc_markdown)] // TODO: `rustc` 1.80.1 clippy issue +#![allow(clippy::doc_lazy_continuation)] // TODO: `rustc` 1.80.1 clippy issue + use std::sync::Arc; use std::time::Duration; @@ -9,6 +12,7 @@ use hyperlane_core::BatchResult; use hyperlane_core::ConfirmReason::*; use hyperlane_core::PendingOperation; use hyperlane_core::PendingOperationStatus; +use hyperlane_core::ReprepareReason; use itertools::Either; use itertools::Itertools; use prometheus::{IntCounter, IntGaugeVec}; @@ -28,7 +32,7 @@ use hyperlane_core::{ }; use crate::msg::pending_message::CONFIRM_DELAY; -use crate::server::MessageRetryRequest; +use crate::settings::matching_list::MatchingList; use super::op_queue::OpQueue; use super::op_queue::OperationPriorityQueue; @@ -101,7 +105,7 @@ impl SerialSubmitter { pub fn new( domain: HyperlaneDomain, rx: mpsc::UnboundedReceiver, - retry_op_transmitter: Sender, + retry_op_transmitter: Sender, metrics: SerialSubmitterMetrics, max_batch_size: u32, task_monitor: TaskMonitor, @@ -179,6 +183,7 @@ impl SerialSubmitter { &task_monitor, submit_task( domain.clone(), + prepare_queue.clone(), submit_queue, confirm_queue.clone(), max_batch_size, @@ -288,6 +293,7 @@ async fn prepare_task( } PendingOperationResult::Drop => { metrics.ops_dropped.inc(); + op.decrement_metric_if_exists(); } PendingOperationResult::Confirm(reason) => { debug!(?op, "Pushing operation to confirm queue"); @@ -307,6 +313,7 @@ async fn prepare_task( #[instrument(skip_all, fields(%domain))] async fn submit_task( domain: HyperlaneDomain, + mut prepare_queue: OpQueue, mut submit_queue: OpQueue, mut confirm_queue: OpQueue, max_batch_size: u32, @@ -324,25 +331,59 @@ async fn submit_task( } std::cmp::Ordering::Equal => { let op = batch.pop().unwrap(); - submit_single_operation(op, &mut confirm_queue, &metrics).await; + submit_single_operation(op, &mut prepare_queue, &mut confirm_queue, &metrics).await; } std::cmp::Ordering::Greater => { OperationBatch::new(batch, domain.clone()) - .submit(&mut confirm_queue, &metrics) + .submit(&mut prepare_queue, &mut confirm_queue, &metrics) .await; } } } } -#[instrument(skip(confirm_queue, metrics), ret, level = "debug")] +#[instrument(skip(prepare_queue, confirm_queue, metrics), ret, level = "debug")] async fn submit_single_operation( + mut op: QueueOperation, + prepare_queue: &mut OpQueue, + confirm_queue: &mut OpQueue, + metrics: &SerialSubmitterMetrics, +) { + let status = op.submit().await; + match status { + PendingOperationResult::Reprepare(reprepare_reason) => { + prepare_queue + .push(op, Some(PendingOperationStatus::Retry(reprepare_reason))) + .await; + } + PendingOperationResult::NotReady => { + // This `match` arm isn't expected to be hit, but it's here for completeness, + // hence the hardcoded `ReprepareReason` + prepare_queue + .push( + op, + Some(PendingOperationStatus::Retry( + ReprepareReason::ErrorSubmitting, + )), + ) + .await; + } + PendingOperationResult::Drop => { + // Not expected to hit this case in `submit`, but it's here for completeness + op.decrement_metric_if_exists(); + } + PendingOperationResult::Success | PendingOperationResult::Confirm(_) => { + confirm_op(op, confirm_queue, metrics).await + } + } +} + +async fn confirm_op( mut op: QueueOperation, confirm_queue: &mut OpQueue, metrics: &SerialSubmitterMetrics, ) { let destination = op.destination_domain().clone(); - op.submit().await; debug!(?op, "Operation submitted"); op.set_next_attempt_after(CONFIRM_DELAY); confirm_queue @@ -418,6 +459,7 @@ async fn confirm_operation( PendingOperationResult::Success => { debug!(?op, "Operation confirmed"); metrics.ops_confirmed.inc(); + op.decrement_metric_if_exists(); } PendingOperationResult::NotReady => { confirm_queue.push(op, None).await; @@ -436,6 +478,7 @@ async fn confirm_operation( } PendingOperationResult::Drop => { metrics.ops_dropped.inc(); + op.decrement_metric_if_exists(); } } operation_result @@ -483,7 +526,12 @@ struct OperationBatch { } impl OperationBatch { - async fn submit(self, confirm_queue: &mut OpQueue, metrics: &SerialSubmitterMetrics) { + async fn submit( + self, + prepare_queue: &mut OpQueue, + confirm_queue: &mut OpQueue, + metrics: &SerialSubmitterMetrics, + ) { let excluded_ops = match self.try_submit_as_batch(metrics).await { Ok(batch_result) => { Self::handle_batch_result(self.operations, batch_result, confirm_queue).await @@ -497,7 +545,7 @@ impl OperationBatch { if !excluded_ops.is_empty() { warn!(excluded_ops=?excluded_ops, "Either the batch tx would revert, or the operations would revert in the batch. Falling back to serial submission."); OperationBatch::new(excluded_ops, self.domain) - .submit_serially(confirm_queue, metrics) + .submit_serially(prepare_queue, confirm_queue, metrics) .await; } } @@ -562,9 +610,14 @@ impl OperationBatch { } } - async fn submit_serially(self, confirm_queue: &mut OpQueue, metrics: &SerialSubmitterMetrics) { + async fn submit_serially( + self, + prepare_queue: &mut OpQueue, + confirm_queue: &mut OpQueue, + metrics: &SerialSubmitterMetrics, + ) { for op in self.operations.into_iter() { - submit_single_operation(op, confirm_queue, metrics).await; + submit_single_operation(op, prepare_queue, confirm_queue, metrics).await; } } } diff --git a/rust/agents/relayer/src/msg/pending_message.rs b/rust/main/agents/relayer/src/msg/pending_message.rs similarity index 93% rename from rust/agents/relayer/src/msg/pending_message.rs rename to rust/main/agents/relayer/src/msg/pending_message.rs index 02ca48e13..cee00aed7 100644 --- a/rust/agents/relayer/src/msg/pending_message.rs +++ b/rust/main/agents/relayer/src/msg/pending_message.rs @@ -1,3 +1,5 @@ +#![allow(clippy::clone_on_ref_ptr)] // TODO: `rustc` 1.80.1 clippy issue + use std::{ fmt::{Debug, Formatter}, sync::Arc, @@ -7,7 +9,10 @@ use std::{ use async_trait::async_trait; use derive_new::new; use eyre::Result; -use hyperlane_base::{db::HyperlaneRocksDB, CoreMetrics}; +use hyperlane_base::{ + db::{HyperlaneDb, HyperlaneRocksDB}, + CoreMetrics, +}; use hyperlane_core::{ gas_used_by_operation, BatchItem, ChainCommunicationError, ChainResult, ConfirmReason, HyperlaneChain, HyperlaneDomain, HyperlaneMessage, Mailbox, MessageSubmissionData, @@ -28,7 +33,7 @@ pub const CONFIRM_DELAY: Duration = if cfg!(any(test, feature = "test-utils")) { Duration::from_secs(5) } else { // Wait 1 min after submitting the message before confirming in normal/production mode - Duration::from_secs(60) + Duration::from_secs(60 * 10) }; /// The message context contains the links needed to submit a message. Each @@ -74,6 +79,12 @@ pub struct PendingMessage { #[new(default)] #[serde(skip_serializing)] submission_outcome: Option, + #[new(default)] + #[serde(skip_serializing)] + metadata: Option>, + #[new(default)] + #[serde(skip_serializing)] + metric: Option>, } impl Debug for PendingMessage { @@ -156,6 +167,14 @@ impl PendingOperation for PendingMessage { self.ctx.destination_mailbox.domain() } + fn sender_address(&self) -> &H256 { + &self.message.sender + } + + fn recipient_address(&self) -> &H256 { + &self.message.recipient + } + fn retrieve_status_from_db(&self) -> Option { match self.ctx.origin_db.retrieve_status_by_message_id(&self.id()) { Ok(status) => status, @@ -252,6 +271,7 @@ impl PendingOperation for PendingMessage { return self.on_reprepare(Some(err), ReprepareReason::ErrorBuildingMetadata); } }; + self.metadata = metadata.clone(); let Some(metadata) = metadata else { return self.on_reprepare::(None, ReprepareReason::CouldNotFetchMetadata); @@ -267,7 +287,7 @@ impl PendingOperation for PendingMessage { .process_estimate_costs(&self.message, &metadata) .await { - Ok(metadata) => metadata, + Ok(tx_cost_estimate) => tx_cost_estimate, Err(err) => { return self.on_reprepare(Some(err), ReprepareReason::ErrorEstimatingGas); } @@ -319,10 +339,10 @@ impl PendingOperation for PendingMessage { } #[instrument] - async fn submit(&mut self) { + async fn submit(&mut self) -> PendingOperationResult { if self.submitted { // this message has already been submitted, possibly not by us - return; + return PendingOperationResult::Success; } let state = self @@ -330,6 +350,19 @@ impl PendingOperation for PendingMessage { .clone() .expect("Pending message must be prepared before it can be submitted"); + // To avoid spending gas on a tx that will revert, dry-run just before submitting. + if let Some(metadata) = self.metadata.as_ref() { + if self + .ctx + .destination_mailbox + .process_estimate_costs(&self.message, metadata) + .await + .is_err() + { + return self.on_reprepare::(None, ReprepareReason::ErrorEstimatingGas); + } + } + // We use the estimated gas limit from the prior call to // `process_estimate_costs` to avoid a second gas estimation. let tx_outcome = self @@ -340,9 +373,11 @@ impl PendingOperation for PendingMessage { match tx_outcome { Ok(outcome) => { self.set_operation_outcome(outcome, state.gas_limit); + PendingOperationResult::Confirm(ConfirmReason::SubmittedBySelf) } Err(e) => { error!(error=?e, "Error when processing message"); + return PendingOperationResult::Reprepare(ReprepareReason::ErrorSubmitting); } } } @@ -457,6 +492,14 @@ impl PendingOperation for PendingMessage { fn try_get_mailbox(&self) -> Option> { Some(self.ctx.destination_mailbox.clone()) } + + fn get_metric(&self) -> Option> { + self.metric.clone() + } + + fn set_metric(&mut self, metric: Arc) { + self.metric = Some(metric); + } } impl PendingMessage { @@ -510,9 +553,9 @@ impl PendingMessage { fn on_reconfirm(&mut self, err: Option, reason: &str) -> PendingOperationResult { self.inc_attempts(); if let Some(e) = err { - warn!(error = ?e, id = ?self.id(), "Reconfirming message: {}", reason.clone()); + warn!(error = ?e, id = ?self.id(), "Reconfirming message: {}", reason); } else { - warn!(id = ?self.id(), "Reconfirming message: {}", reason.clone()); + warn!(id = ?self.id(), "Reconfirming message: {}", reason); } PendingOperationResult::NotReady } diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/main/agents/relayer/src/msg/processor.rs similarity index 80% rename from rust/agents/relayer/src/msg/processor.rs rename to rust/main/agents/relayer/src/msg/processor.rs index 40808ad3d..59ec32cb6 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/main/agents/relayer/src/msg/processor.rs @@ -11,7 +11,7 @@ use derive_new::new; use ethers::utils::hex; use eyre::Result; use hyperlane_base::{ - db::{HyperlaneRocksDB, ProcessMessage}, + db::{HyperlaneDb, HyperlaneRocksDB}, CoreMetrics, }; use hyperlane_core::{HyperlaneDomain, HyperlaneMessage, QueueOperation}; @@ -52,7 +52,7 @@ struct ForwardBackwardIterator { impl ForwardBackwardIterator { #[instrument(skip(db), ret)] - fn new(db: Arc) -> Self { + fn new(db: Arc) -> Self { let high_nonce = db.retrieve_highest_seen_message_nonce().ok().flatten(); let domain = db.domain().name().to_owned(); let high_nonce_iter = DirectionalNonceIterator::new( @@ -125,7 +125,7 @@ enum NonceDirection { struct DirectionalNonceIterator { nonce: Option, direction: NonceDirection, - db: Arc, + db: Arc, domain_name: String, } @@ -163,7 +163,7 @@ impl DirectionalNonceIterator { if let Some(message) = self.indexed_message_with_nonce()? { Self::update_max_nonce_gauge(&message, metrics); if !self.is_message_processed()? { - debug!(?message, iterator=?self, "Found processable message"); + debug!(hyp_message=?message, iterator=?self, "Found processable message"); return Ok(MessageStatus::Processable(message)); } else { return Ok(MessageStatus::Processed); @@ -196,7 +196,10 @@ impl DirectionalNonceIterator { let Some(nonce) = self.nonce else { return Ok(false); }; - let processed = self.db.retrieve_processed_by_nonce(nonce)?.unwrap_or(false); + let processed = self + .db + .retrieve_processed_by_nonce(&nonce)? + .unwrap_or(false); if processed { trace!( nonce, @@ -258,8 +261,8 @@ impl ProcessorExt for MessageProcessor { } // Skip if the message is blacklisted - if self.message_whitelist.msg_matches(&msg, false) { - debug!(?msg, blacklist=?self.message_whitelist, "Message blacklisted, skipping"); + if self.message_blacklist.msg_matches(&msg, false) { + debug!(?msg, blacklist=?self.message_blacklist, "Message blacklisted, skipping"); return Ok(()); } @@ -326,7 +329,7 @@ impl MessageProcessor { send_channels, destination_ctxs, metric_app_contexts, - nonce_iterator: ForwardBackwardIterator::new(Arc::new(db) as Arc), + nonce_iterator: ForwardBackwardIterator::new(Arc::new(db) as Arc), } } @@ -394,9 +397,16 @@ mod test { use super::*; use hyperlane_base::{ - db::{test_utils, DbResult, HyperlaneRocksDB}, + db::{ + test_utils, DbResult, HyperlaneRocksDB, InterchainGasExpenditureData, + InterchainGasPaymentData, + }, settings::{ChainConf, ChainConnectionConf, Settings}, }; + use hyperlane_core::{ + test_utils::dummy_domain, GasPaymentKey, InterchainGasPayment, InterchainGasPaymentMeta, + MerkleTreeInsertion, PendingOperationStatus, H256, + }; use hyperlane_test::mocks::{MockMailboxContract, MockValidatorAnnounceContract}; use prometheus::{IntCounter, Registry}; use tokio::{ @@ -527,17 +537,6 @@ mod test { } } - fn dummy_domain(domain_id: u32, name: &str) -> HyperlaneDomain { - let test_domain = HyperlaneDomain::new_test_domain(name); - HyperlaneDomain::Unknown { - domain_id, - domain_name: name.to_owned(), - domain_type: test_domain.domain_type(), - domain_protocol: test_domain.domain_protocol(), - domain_technical_stack: test_domain.domain_technical_stack(), - } - } - /// Only adds database entries to the pending message prefix if the message's /// retry count is greater than zero fn persist_retried_messages( @@ -591,11 +590,153 @@ mod test { fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result; } - impl ProcessMessage for Db { + impl HyperlaneDb for Db { + /// Retrieve the nonce of the highest processed message we're aware of fn retrieve_highest_seen_message_nonce(&self) -> DbResult>; + + /// Retrieve a message by its nonce fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult>; - fn retrieve_processed_by_nonce(&self, nonce: u32) -> DbResult>; + + /// Retrieve whether a message has been processed + fn retrieve_processed_by_nonce(&self, nonce: &u32) -> DbResult>; + + /// Get the origin domain of the database fn domain(&self) -> &HyperlaneDomain; + + fn store_message_id_by_nonce(&self, nonce: &u32, id: &H256) -> DbResult<()>; + + fn retrieve_message_id_by_nonce(&self, nonce: &u32) -> DbResult>; + + fn store_message_by_id(&self, id: &H256, message: &HyperlaneMessage) -> DbResult<()>; + + fn retrieve_message_by_id(&self, id: &H256) -> DbResult>; + + fn store_dispatched_block_number_by_nonce( + &self, + nonce: &u32, + block_number: &u64, + ) -> DbResult<()>; + + fn retrieve_dispatched_block_number_by_nonce(&self, nonce: &u32) -> DbResult>; + + /// Store whether a message was processed by its nonce + fn store_processed_by_nonce(&self, nonce: &u32, processed: &bool) -> DbResult<()>; + + fn store_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + processed: &bool, + ) -> DbResult<()>; + + fn retrieve_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + ) -> DbResult>; + + fn store_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + data: &InterchainGasExpenditureData, + ) -> DbResult<()>; + + fn retrieve_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + + /// Store the status of an operation by its message id + fn store_status_by_message_id( + &self, + message_id: &H256, + status: &PendingOperationStatus, + ) -> DbResult<()>; + + /// Retrieve the status of an operation by its message id + fn retrieve_status_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + + fn store_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + data: &InterchainGasPaymentData, + ) -> DbResult<()>; + + fn retrieve_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + ) -> DbResult>; + + fn store_gas_payment_by_sequence( + &self, + sequence: &u32, + payment: &InterchainGasPayment, + ) -> DbResult<()>; + + fn retrieve_gas_payment_by_sequence( + &self, + sequence: &u32, + ) -> DbResult>; + + fn store_gas_payment_block_by_sequence( + &self, + sequence: &u32, + block_number: &u64, + ) -> DbResult<()>; + + fn retrieve_gas_payment_block_by_sequence(&self, sequence: &u32) -> DbResult>; + + /// Store the retry count for a pending message by its message id + fn store_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + count: &u32, + ) -> DbResult<()>; + + /// Retrieve the retry count for a pending message by its message id + fn retrieve_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + + fn store_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + insertion: &MerkleTreeInsertion, + ) -> DbResult<()>; + + /// Retrieve the merkle tree insertion event by its leaf index + fn retrieve_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult>; + + fn store_merkle_leaf_index_by_message_id( + &self, + message_id: &H256, + leaf_index: &u32, + ) -> DbResult<()>; + + /// Retrieve the merkle leaf index of a message in the merkle tree + fn retrieve_merkle_leaf_index_by_message_id(&self, message_id: &H256) -> DbResult>; + + fn store_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + block_number: &u64, + ) -> DbResult<()>; + + fn retrieve_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult>; + + fn store_highest_seen_message_nonce_number(&self, nonce: &u32) -> DbResult<()>; + + /// Retrieve the nonce of the highest processed message we're aware of + fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; + } } diff --git a/rust/agents/relayer/src/processor.rs b/rust/main/agents/relayer/src/processor.rs similarity index 100% rename from rust/agents/relayer/src/processor.rs rename to rust/main/agents/relayer/src/processor.rs diff --git a/rust/agents/relayer/src/prover.rs b/rust/main/agents/relayer/src/prover.rs similarity index 100% rename from rust/agents/relayer/src/prover.rs rename to rust/main/agents/relayer/src/prover.rs diff --git a/rust/agents/relayer/src/relayer.rs b/rust/main/agents/relayer/src/relayer.rs similarity index 99% rename from rust/agents/relayer/src/relayer.rs rename to rust/main/agents/relayer/src/relayer.rs index d5077d678..975f37242 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/main/agents/relayer/src/relayer.rs @@ -41,7 +41,7 @@ use crate::{ pending_message::{MessageContext, MessageSubmissionMetrics}, processor::{MessageProcessor, MessageProcessorMetrics}, }, - server::{self as relayer_server, MessageRetryRequest}, + server::{self as relayer_server}, settings::{matching_list::MatchingList, RelayerSettings}, }; use crate::{ @@ -311,7 +311,7 @@ impl BaseAgent for Relayer { })); tasks.push(console_server.instrument(info_span!("Tokio console server"))); } - let sender = BroadcastSender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); + let sender = BroadcastSender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); // send channels by destination chain let mut send_channels = HashMap::with_capacity(self.destination_chains.len()); let mut prep_queues = HashMap::with_capacity(self.destination_chains.len()); diff --git a/rust/agents/relayer/src/server/list_messages.rs b/rust/main/agents/relayer/src/server/list_messages.rs similarity index 54% rename from rust/agents/relayer/src/server/list_messages.rs rename to rust/main/agents/relayer/src/server/list_messages.rs index 9c851a623..e21f39a5d 100644 --- a/rust/agents/relayer/src/server/list_messages.rs +++ b/rust/main/agents/relayer/src/server/list_messages.rs @@ -3,8 +3,8 @@ use axum::{ routing, Router, }; use derive_new::new; -use hyperlane_core::QueueOperation; -use serde::Deserialize; +use hyperlane_core::{QueueOperation, H256}; +use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; @@ -39,12 +39,27 @@ async fn list_operations( format_queue(op_queue.clone()).await } +#[derive(Debug, Serialize)] +struct OperationWithId<'a> { + id: H256, + operation: &'a QueueOperation, +} + +impl<'a> OperationWithId<'a> { + fn new(operation: &'a QueueOperation) -> Self { + Self { + id: operation.id(), + operation, + } + } +} + pub async fn format_queue(queue: OperationPriorityQueue) -> String { let res: Result, _> = queue .lock() .await .iter() - .map(|reverse| serde_json::to_value(&reverse.0)) + .map(|reverse| serde_json::to_value(OperationWithId::new(&reverse.0))) .collect(); match res.and_then(|v| serde_json::to_string_pretty(&v)) { Ok(s) => s, @@ -64,8 +79,6 @@ impl ListOperationsApi { } } -// TODO: there's some duplication between the setup for these tests and the one in `message_retry.rs`, -// which should be refactored into a common test setup. #[cfg(test)] mod tests { use crate::msg::op_queue::{ @@ -94,6 +107,7 @@ mod tests { let list_operations_api = ListOperationsApi::new(op_queues_map); let (path, router) = list_operations_api.get_route(); + let app = Router::new().nest(path, router); // Running the app in the background using a test server @@ -108,15 +122,58 @@ mod tests { #[tokio::test] async fn test_message_id_retry() { let (addr, op_queue) = setup_test_server(); - let dummy_operation_1 = - Box::new(MockPendingOperation::new(1, DUMMY_DOMAIN.into())) as QueueOperation; - let dummy_operation_2 = - Box::new(MockPendingOperation::new(2, DUMMY_DOMAIN.into())) as QueueOperation; - let v = vec![ - serde_json::to_value(&dummy_operation_1).unwrap(), - serde_json::to_value(&dummy_operation_2).unwrap(), - ]; - let expected_response = serde_json::to_string_pretty(&v).unwrap(); + let id_1 = "0x1acbee9798118b11ebef0d94b0a2936eafd58e3bfab91b05da875825c4a1c39b"; + let id_2 = "0x51e7be221ce90a49dee46ca0d0270c48d338a7b9d85c2a89d83fac0816571914"; + let sender_address = "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08"; + let recipient_address = + "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08"; + let dummy_operation_1 = Box::new( + MockPendingOperation::new(1, DUMMY_DOMAIN.into()) + .with_id(id_1) + .with_sender_address(sender_address) + .with_recipient_address(recipient_address), + ) as QueueOperation; + let dummy_operation_2 = Box::new( + MockPendingOperation::new(2, DUMMY_DOMAIN.into()) + .with_id(id_2) + .with_sender_address(sender_address) + .with_recipient_address(recipient_address), + ) as QueueOperation; + + // The reason there already is an id inside `operation` here is because it's a field on `MockPendingOperation` - that field is + // missing on `PendingMessage` because it's derived, hence the need to hence the need to have it explicitly serialized alongside the operation. + let expected_response = r#"[ + { + "id": "0x1acbee9798118b11ebef0d94b0a2936eafd58e3bfab91b05da875825c4a1c39b", + "operation": { + "destination_domain": { + "Known": "Arbitrum" + }, + "destination_domain_id": 42161, + "id": "0x1acbee9798118b11ebef0d94b0a2936eafd58e3bfab91b05da875825c4a1c39b", + "origin_domain_id": 0, + "recipient_address": "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08", + "seconds_to_next_attempt": 1, + "sender_address": "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08", + "type": "MockPendingOperation" + } + }, + { + "id": "0x51e7be221ce90a49dee46ca0d0270c48d338a7b9d85c2a89d83fac0816571914", + "operation": { + "destination_domain": { + "Known": "Arbitrum" + }, + "destination_domain_id": 42161, + "id": "0x51e7be221ce90a49dee46ca0d0270c48d338a7b9d85c2a89d83fac0816571914", + "origin_domain_id": 0, + "recipient_address": "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08", + "seconds_to_next_attempt": 2, + "sender_address": "0x586d41b02fb35df0f84ecb2b73e076b40c929ee3e1ceeada9a078aa7b46d3b08", + "type": "MockPendingOperation" + } + } +]"#; op_queue.lock().await.push(Reverse(dummy_operation_1)); op_queue.lock().await.push(Reverse(dummy_operation_2)); @@ -130,6 +187,8 @@ mod tests { // Check that the response status code is OK assert_eq!(response.status(), StatusCode::OK); - assert_eq!(response.text().await.unwrap(), expected_response); + + let response_text = response.text().await.unwrap(); + assert_eq!(response_text, expected_response); } } diff --git a/rust/main/agents/relayer/src/server/message_retry.rs b/rust/main/agents/relayer/src/server/message_retry.rs new file mode 100644 index 000000000..6d04eed86 --- /dev/null +++ b/rust/main/agents/relayer/src/server/message_retry.rs @@ -0,0 +1,250 @@ +use crate::settings::matching_list::MatchingList; +use axum::{extract::State, routing, Json, Router}; +use derive_new::new; +use tokio::sync::broadcast::Sender; + +const MESSAGE_RETRY_API_BASE: &str = "/message_retry"; + +#[derive(new, Clone)] +pub struct MessageRetryApi { + tx: Sender, +} + +async fn retry_message( + State(tx): State>, + Json(retry_req_payload): Json, +) -> String { + match tx.send(retry_req_payload) { + Ok(_) => "Moved message(s) to the front of the queue".to_string(), + // Technically it's bad practice to print the error message to the user, but + // this endpoint is for debugging purposes only. + Err(err) => format!("Failed to send retry request to the queue: {}", err), + } +} + +impl MessageRetryApi { + pub fn router(&self) -> Router { + Router::new() + .route("/", routing::post(retry_message)) + .with_state(self.tx.clone()) + } + + pub fn get_route(&self) -> (&'static str, Router) { + (MESSAGE_RETRY_API_BASE, self.router()) + } +} + +#[cfg(test)] +mod tests { + use crate::{msg::op_queue::test::MockPendingOperation, server::ENDPOINT_MESSAGES_QUEUE_SIZE}; + + use super::*; + use axum::http::StatusCode; + use hyperlane_core::{HyperlaneMessage, QueueOperation}; + use serde_json::json; + use std::net::SocketAddr; + use tokio::sync::broadcast::{Receiver, Sender}; + + fn setup_test_server() -> (SocketAddr, Receiver) { + let broadcast_tx = Sender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); + let message_retry_api = MessageRetryApi::new(broadcast_tx.clone()); + let (path, retry_router) = message_retry_api.get_route(); + + let app = Router::new().nest(path, retry_router); + + // Running the app in the background using a test server + let server = + axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); + let addr = server.local_addr(); + tokio::spawn(server); + + (addr, broadcast_tx.subscribe()) + } + + #[tokio::test] + async fn test_message_id_retry() { + let (addr, mut rx) = setup_test_server(); + + let client = reqwest::Client::new(); + // Create a random message with a random message ID + let message = HyperlaneMessage::default(); + let pending_operation = MockPendingOperation::with_message_data(message.clone()); + let matching_list_body = json!([ + { + "messageid": message.id() + } + ]); + + // Send a POST request to the server + let response = client + .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) + .json(&matching_list_body) // Set the request body + .send() + .await + .unwrap(); + + // Check that the response status code is OK + assert_eq!(response.status(), StatusCode::OK); + + let list = rx.try_recv().unwrap(); + // Check that the list received by the server matches the pending operation + assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + } + + #[tokio::test] + async fn test_destination_domain_retry() { + let (addr, mut rx) = setup_test_server(); + + let client = reqwest::Client::new(); + let mut message = HyperlaneMessage::default(); + // Use a random destination domain + message.destination = 42; + let pending_operation = MockPendingOperation::with_message_data(message.clone()); + let matching_list_body = json!([ + { + "destinationdomain": message.destination + } + ]); + + // Send a POST request to the server + let response = client + .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) + .json(&matching_list_body) // Set the request body + .send() + .await + .unwrap(); + + // Check that the response status code is OK + assert_eq!(response.status(), StatusCode::OK); + + let list = rx.try_recv().unwrap(); + // Check that the list received by the server matches the pending operation + assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + } + + #[tokio::test] + async fn test_origin_domain_retry() { + let (addr, mut rx) = setup_test_server(); + + let client = reqwest::Client::new(); + let mut message = HyperlaneMessage::default(); + // Use a random origin domain + message.origin = 42; + let pending_operation = MockPendingOperation::with_message_data(message.clone()); + let matching_list_body = json!([ + { + "origindomain": message.origin + } + ]); + + // Send a POST request to the server + let response = client + .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) + .json(&matching_list_body) // Set the request body + .send() + .await + .unwrap(); + + // Check that the response status code is OK + assert_eq!(response.status(), StatusCode::OK); + + let list = rx.try_recv().unwrap(); + // Check that the list received by the server matches the pending operation + assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + } + + #[tokio::test] + async fn test_sender_address_retry() { + let (addr, mut rx) = setup_test_server(); + + let client = reqwest::Client::new(); + let message = HyperlaneMessage::default(); + let pending_operation = MockPendingOperation::with_message_data(message.clone()); + let matching_list_body = json!([ + { + "senderaddress": message.sender + } + ]); + + // Send a POST request to the server + let response = client + .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) + .json(&matching_list_body) // Set the request body + .send() + .await + .unwrap(); + + // Check that the response status code is OK + assert_eq!(response.status(), StatusCode::OK); + + let list = rx.try_recv().unwrap(); + // Check that the list received by the server matches the pending operation + assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + } + + #[tokio::test] + async fn test_recipient_address_retry() { + let (addr, mut rx) = setup_test_server(); + + let client = reqwest::Client::new(); + let message = HyperlaneMessage::default(); + let pending_operation = MockPendingOperation::with_message_data(message.clone()); + let matching_list_body = json!([ + { + "recipientaddress": message.recipient + } + ]); + + // Send a POST request to the server + let response = client + .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) + .json(&matching_list_body) // Set the request body + .send() + .await + .unwrap(); + + // Check that the response status code is OK + assert_eq!(response.status(), StatusCode::OK); + + let list = rx.try_recv().unwrap(); + // Check that the list received by the server matches the pending operation + assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + } + + #[tokio::test] + async fn test_multiple_retry() { + let (addr, mut rx) = setup_test_server(); + + let client = reqwest::Client::new(); + let mut message = HyperlaneMessage::default(); + // Use a random origin domain + message.origin = 42; + let pending_operation = MockPendingOperation::with_message_data(message.clone()); + let matching_list_body = json!([ + { + "origindomain": message.origin + }, + { + "destinationdomain": message.destination + }, + { + "messageid": message.id() + } + ]); + + // Send a POST request to the server + let response = client + .post(format!("http://{}{}", addr, MESSAGE_RETRY_API_BASE)) + .json(&matching_list_body) // Set the request body + .send() + .await + .unwrap(); + + // Check that the response status code is OK + assert_eq!(response.status(), StatusCode::OK); + + let list = rx.try_recv().unwrap(); + // Check that the list received by the server matches the pending operation + assert!(list.op_matches(&(Box::new(pending_operation) as QueueOperation))); + } +} diff --git a/rust/agents/relayer/src/server/mod.rs b/rust/main/agents/relayer/src/server/mod.rs similarity index 84% rename from rust/agents/relayer/src/server/mod.rs rename to rust/main/agents/relayer/src/server/mod.rs index 4b60e7bb5..083f8d94d 100644 --- a/rust/agents/relayer/src/server/mod.rs +++ b/rust/main/agents/relayer/src/server/mod.rs @@ -3,7 +3,7 @@ use derive_new::new; use std::collections::HashMap; use tokio::sync::broadcast::Sender; -use crate::msg::op_queue::OperationPriorityQueue; +use crate::{msg::op_queue::OperationPriorityQueue, settings::matching_list::MatchingList}; pub const ENDPOINT_MESSAGES_QUEUE_SIZE: usize = 100; @@ -16,13 +16,13 @@ mod message_retry; #[derive(new)] pub struct Server { #[new(default)] - retry_transmitter: Option>, + retry_transmitter: Option>, #[new(default)] op_queues: Option>, } impl Server { - pub fn with_op_retry(mut self, transmitter: Sender) -> Self { + pub fn with_op_retry(mut self, transmitter: Sender) -> Self { self.retry_transmitter = Some(transmitter); self } diff --git a/rust/agents/relayer/src/settings/matching_list.rs b/rust/main/agents/relayer/src/settings/matching_list.rs similarity index 83% rename from rust/agents/relayer/src/settings/matching_list.rs rename to rust/main/agents/relayer/src/settings/matching_list.rs index 6424283e3..ca65ed67f 100644 --- a/rust/agents/relayer/src/settings/matching_list.rs +++ b/rust/main/agents/relayer/src/settings/matching_list.rs @@ -8,7 +8,9 @@ use std::{ marker::PhantomData, }; -use hyperlane_core::{config::StrOrInt, utils::hex_or_base58_to_h256, HyperlaneMessage, H256}; +use hyperlane_core::{ + config::StrOrInt, utils::hex_or_base58_to_h256, HyperlaneMessage, QueueOperation, H256, +}; use serde::{ de::{Error, SeqAccess, Visitor}, Deserialize, Deserializer, @@ -223,6 +225,8 @@ impl<'de> Deserialize<'de> for Filter { #[derive(Debug, Deserialize, Clone)] #[serde(tag = "type")] struct ListElement { + #[serde(default, rename = "messageid")] + message_id: Filter, #[serde(default, rename = "origindomain")] origin_domain: Filter, #[serde(default, rename = "senderaddress")] @@ -237,7 +241,8 @@ impl Display for ListElement { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, - "{{originDomain: {}, senderAddress: {}, destinationDomain: {}, recipientAddress: {}}}", + "{{messageId: {}, originDomain: {}, senderAddress: {}, destinationDomain: {}, recipientAddress: {}}}", + self.message_id, self.origin_domain, self.sender_address, self.destination_domain, @@ -248,6 +253,7 @@ impl Display for ListElement { #[derive(Copy, Clone, Debug)] struct MatchInfo<'a> { + src_msg_id: H256, src_domain: u32, src_addr: &'a H256, dst_domain: u32, @@ -257,6 +263,7 @@ struct MatchInfo<'a> { impl<'a> From<&'a HyperlaneMessage> for MatchInfo<'a> { fn from(msg: &'a HyperlaneMessage) -> Self { Self { + src_msg_id: msg.id(), src_domain: msg.origin, src_addr: &msg.sender, dst_domain: msg.destination, @@ -265,13 +272,51 @@ impl<'a> From<&'a HyperlaneMessage> for MatchInfo<'a> { } } +impl<'a> From<&'a QueueOperation> for MatchInfo<'a> { + fn from(op: &'a QueueOperation) -> Self { + Self { + src_msg_id: op.id(), + src_domain: op.origin_domain_id(), + src_addr: op.sender_address(), + dst_domain: op.destination_domain().id(), + dst_addr: op.recipient_address(), + } + } +} + impl MatchingList { + pub fn with_message_id(message_id: H256) -> Self { + Self(Some(vec![ListElement { + message_id: Filter::Enumerated(vec![message_id]), + origin_domain: Default::default(), + sender_address: Default::default(), + destination_domain: Default::default(), + recipient_address: Default::default(), + }])) + } + + pub fn with_destination_domain(destination_domain: u32) -> Self { + Self(Some(vec![ListElement { + message_id: Default::default(), + origin_domain: Default::default(), + sender_address: Default::default(), + destination_domain: Filter::Enumerated(vec![destination_domain]), + recipient_address: Default::default(), + }])) + } + /// Check if a message matches any of the rules. /// - `default`: What to return if the matching list is empty. pub fn msg_matches(&self, msg: &HyperlaneMessage, default: bool) -> bool { self.matches(msg.into(), default) } + /// Check if queue operation matches any of the rules. + /// If the matching list is empty, we assume the queue operation does not match. + pub fn op_matches(&self, op: &QueueOperation) -> bool { + self.matches(op.into(), false) + } + /// Check if a message matches any of the rules. /// - `default`: What to return if the matching list is empty. fn matches(&self, info: MatchInfo, default: bool) -> bool { @@ -285,7 +330,8 @@ impl MatchingList { fn matches_any_rule<'a>(mut rules: impl Iterator, info: MatchInfo) -> bool { rules.any(|rule| { - rule.origin_domain.matches(&info.src_domain) + rule.message_id.matches(&info.src_msg_id) + && rule.origin_domain.matches(&info.src_domain) && rule.sender_address.matches(info.src_addr) && rule.destination_domain.matches(&info.dst_domain) && rule.recipient_address.matches(info.dst_addr) @@ -323,23 +369,26 @@ mod test { #[test] fn basic_config() { - let list: MatchingList = serde_json::from_str(r#"[{"origindomain": "*", "senderaddress": "*", "destinationdomain": "*", "recipientaddress": "*"}, {}]"#).unwrap(); + let list: MatchingList = serde_json::from_str(r#"[{"messageid": "*", "origindomain": "*", "senderaddress": "*", "destinationdomain": "*", "recipientaddress": "*"}, {}]"#).unwrap(); assert!(list.0.is_some()); assert_eq!(list.0.as_ref().unwrap().len(), 2); let elem = &list.0.as_ref().unwrap()[0]; assert_eq!(elem.destination_domain, Wildcard); + assert_eq!(elem.message_id, Wildcard); assert_eq!(elem.recipient_address, Wildcard); assert_eq!(elem.origin_domain, Wildcard); assert_eq!(elem.sender_address, Wildcard); let elem = &list.0.as_ref().unwrap()[1]; assert_eq!(elem.destination_domain, Wildcard); + assert_eq!(elem.message_id, Wildcard); assert_eq!(elem.recipient_address, Wildcard); assert_eq!(elem.origin_domain, Wildcard); assert_eq!(elem.sender_address, Wildcard); assert!(list.matches( MatchInfo { + src_msg_id: H256::random(), src_domain: 0, src_addr: &H256::default(), dst_domain: 0, @@ -350,6 +399,7 @@ mod test { assert!(list.matches( MatchInfo { + src_msg_id: H256::random(), src_domain: 34, src_addr: &"0x9d4454B023096f34B160D6B654540c56A1F81688" .parse::() @@ -369,6 +419,7 @@ mod test { assert_eq!(list.0.as_ref().unwrap().len(), 1); let elem = &list.0.as_ref().unwrap()[0]; assert_eq!(elem.destination_domain, Wildcard); + assert_eq!(elem.message_id, Wildcard); assert_eq!( elem.recipient_address, Enumerated(vec!["0x9d4454B023096f34B160D6B654540c56A1F81688" @@ -387,6 +438,7 @@ mod test { assert!(list.matches( MatchInfo { + src_msg_id: H256::default(), src_domain: 34, src_addr: &"0x9d4454B023096f34B160D6B654540c56A1F81688" .parse::() @@ -403,6 +455,7 @@ mod test { assert!(!list.matches( MatchInfo { + src_msg_id: H256::default(), src_domain: 34, src_addr: &"0x9d4454B023096f34B160D6B654540c56A1F81688" .parse::() @@ -423,6 +476,7 @@ mod test { assert_eq!(whitelist.0.as_ref().unwrap().len(), 1); let elem = &whitelist.0.as_ref().unwrap()[0]; assert_eq!(elem.destination_domain, Enumerated(vec![9913372, 9913373])); + assert_eq!(elem.message_id, Wildcard); assert_eq!(elem.recipient_address, Wildcard); assert_eq!(elem.origin_domain, Wildcard); assert_eq!(elem.sender_address, Wildcard); @@ -437,6 +491,7 @@ mod test { #[test] fn matches_empty_list() { let info = MatchInfo { + src_msg_id: H256::default(), src_domain: 0, src_addr: &H256::default(), dst_domain: 0, @@ -451,7 +506,7 @@ mod test { #[test] fn supports_base58() { serde_json::from_str::( - r#"[{"origindomain":1399811151,"senderaddress":"DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7","destinationdomain":11155111,"recipientaddress":"0x6AD4DEBA8A147d000C09de6465267a9047d1c217"}]"#, + r#"[{"messageid": "*", "origindomain":1399811151,"senderaddress":"DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7","destinationdomain":11155111,"recipientaddress":"0x6AD4DEBA8A147d000C09de6465267a9047d1c217"}]"#, ).unwrap(); } diff --git a/rust/agents/relayer/src/settings/mod.rs b/rust/main/agents/relayer/src/settings/mod.rs similarity index 100% rename from rust/agents/relayer/src/settings/mod.rs rename to rust/main/agents/relayer/src/settings/mod.rs diff --git a/rust/agents/scraper/Cargo.toml b/rust/main/agents/scraper/Cargo.toml similarity index 93% rename from rust/agents/scraper/Cargo.toml rename to rust/main/agents/scraper/Cargo.toml index 234813573..0b9e429b9 100644 --- a/rust/agents/scraper/Cargo.toml +++ b/rust/main/agents/scraper/Cargo.toml @@ -1,4 +1,3 @@ -cargo-features = ["workspace-inheritance"] [package] name = "scraper" @@ -30,7 +29,6 @@ tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] } tracing-futures.workspace = true tracing.workspace = true -hex = { path = "../../utils/hex" } hyperlane-base = { path = "../../hyperlane-base" } hyperlane-core = { path = "../../hyperlane-core", features = ["agent"] } migration = { path = "migration" } diff --git a/rust/agents/scraper/README.md b/rust/main/agents/scraper/README.md similarity index 100% rename from rust/agents/scraper/README.md rename to rust/main/agents/scraper/README.md diff --git a/rust/agents/scraper/migration/Cargo.toml b/rust/main/agents/scraper/migration/Cargo.toml similarity index 85% rename from rust/agents/scraper/migration/Cargo.toml rename to rust/main/agents/scraper/migration/Cargo.toml index ff9ea4d31..f7cb3a5ce 100644 --- a/rust/agents/scraper/migration/Cargo.toml +++ b/rust/main/agents/scraper/migration/Cargo.toml @@ -1,4 +1,3 @@ -cargo-features = ["workspace-inheritance"] [package] name = "migration" @@ -18,7 +17,12 @@ sea-orm.workspace = true sea-orm-migration.workspace = true serde.workspace = true time.workspace = true -tokio = { workspace = true, features = ["rt", "process", "macros", "parking_lot"] } +tokio = { workspace = true, features = [ + "rt", + "process", + "macros", + "parking_lot", +] } # bin-only deps tracing-subscriber.workspace = true diff --git a/rust/agents/scraper/migration/README.md b/rust/main/agents/scraper/migration/README.md similarity index 100% rename from rust/agents/scraper/migration/README.md rename to rust/main/agents/scraper/migration/README.md diff --git a/rust/agents/scraper/migration/bin/common.rs b/rust/main/agents/scraper/migration/bin/common.rs similarity index 100% rename from rust/agents/scraper/migration/bin/common.rs rename to rust/main/agents/scraper/migration/bin/common.rs diff --git a/rust/agents/scraper/migration/bin/down.rs b/rust/main/agents/scraper/migration/bin/down.rs similarity index 100% rename from rust/agents/scraper/migration/bin/down.rs rename to rust/main/agents/scraper/migration/bin/down.rs diff --git a/rust/agents/scraper/migration/bin/fresh.rs b/rust/main/agents/scraper/migration/bin/fresh.rs similarity index 100% rename from rust/agents/scraper/migration/bin/fresh.rs rename to rust/main/agents/scraper/migration/bin/fresh.rs diff --git a/rust/agents/scraper/migration/bin/generate_entities.rs b/rust/main/agents/scraper/migration/bin/generate_entities.rs similarity index 100% rename from rust/agents/scraper/migration/bin/generate_entities.rs rename to rust/main/agents/scraper/migration/bin/generate_entities.rs diff --git a/rust/agents/scraper/migration/bin/init_db.rs b/rust/main/agents/scraper/migration/bin/init_db.rs similarity index 100% rename from rust/agents/scraper/migration/bin/init_db.rs rename to rust/main/agents/scraper/migration/bin/init_db.rs diff --git a/rust/agents/scraper/migration/bin/recreate_db.rs b/rust/main/agents/scraper/migration/bin/recreate_db.rs similarity index 100% rename from rust/agents/scraper/migration/bin/recreate_db.rs rename to rust/main/agents/scraper/migration/bin/recreate_db.rs diff --git a/rust/agents/scraper/migration/src/l20230309_types.rs b/rust/main/agents/scraper/migration/src/l20230309_types.rs similarity index 100% rename from rust/agents/scraper/migration/src/l20230309_types.rs rename to rust/main/agents/scraper/migration/src/l20230309_types.rs diff --git a/rust/agents/scraper/migration/src/lib.rs b/rust/main/agents/scraper/migration/src/lib.rs similarity index 100% rename from rust/agents/scraper/migration/src/lib.rs rename to rust/main/agents/scraper/migration/src/lib.rs diff --git a/rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs b/rust/main/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs similarity index 98% rename from rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs rename to rust/main/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs index 80dcfc622..3a89e313d 100644 --- a/rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs +++ b/rust/main/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs @@ -438,6 +438,14 @@ const DOMAINS: &[RawDomain] = &[ is_test_net: false, is_deprecated: false, }, + RawDomain { + name: "stride", + token: "STRD", + domain: 745, + chain_id: 745, + is_test_net: false, + is_deprecated: false, + }, RawDomain { name: "cosmostest99990", token: "OSMO", diff --git a/rust/agents/scraper/migration/src/m20230309_000002_create_table_block.rs b/rust/main/agents/scraper/migration/src/m20230309_000002_create_table_block.rs similarity index 100% rename from rust/agents/scraper/migration/src/m20230309_000002_create_table_block.rs rename to rust/main/agents/scraper/migration/src/m20230309_000002_create_table_block.rs diff --git a/rust/agents/scraper/migration/src/m20230309_000003_create_table_cursor.rs b/rust/main/agents/scraper/migration/src/m20230309_000003_create_table_cursor.rs similarity index 100% rename from rust/agents/scraper/migration/src/m20230309_000003_create_table_cursor.rs rename to rust/main/agents/scraper/migration/src/m20230309_000003_create_table_cursor.rs diff --git a/rust/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs b/rust/main/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs similarity index 100% rename from rust/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs rename to rust/main/agents/scraper/migration/src/m20230309_000003_create_table_transaction.rs diff --git a/rust/agents/scraper/migration/src/m20230309_000004_create_table_delivered_message.rs b/rust/main/agents/scraper/migration/src/m20230309_000004_create_table_delivered_message.rs similarity index 100% rename from rust/agents/scraper/migration/src/m20230309_000004_create_table_delivered_message.rs rename to rust/main/agents/scraper/migration/src/m20230309_000004_create_table_delivered_message.rs diff --git a/rust/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs b/rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs similarity index 100% rename from rust/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs rename to rust/main/agents/scraper/migration/src/m20230309_000004_create_table_gas_payment.rs diff --git a/rust/agents/scraper/migration/src/m20230309_000005_create_table_message.rs b/rust/main/agents/scraper/migration/src/m20230309_000005_create_table_message.rs similarity index 100% rename from rust/agents/scraper/migration/src/m20230309_000005_create_table_message.rs rename to rust/main/agents/scraper/migration/src/m20230309_000005_create_table_message.rs diff --git a/rust/agents/scraper/src/agent.rs b/rust/main/agents/scraper/src/agent.rs similarity index 100% rename from rust/agents/scraper/src/agent.rs rename to rust/main/agents/scraper/src/agent.rs diff --git a/rust/agents/scraper/src/chain_scraper/mod.rs b/rust/main/agents/scraper/src/chain_scraper/mod.rs similarity index 67% rename from rust/agents/scraper/src/chain_scraper/mod.rs rename to rust/main/agents/scraper/src/chain_scraper/mod.rs index 813f11967..0be5b2ef1 100644 --- a/rust/agents/scraper/src/chain_scraper/mod.rs +++ b/rust/main/agents/scraper/src/chain_scraper/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::unnecessary_fallible_conversions)] // TODO: `rustc` 1.80.1 clippy issue + //! This module (and children) are responsible for scraping blockchain data and //! keeping things updated. @@ -5,14 +7,15 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use eyre::Result; +use itertools::Itertools; +use tracing::{trace, warn}; + use hyperlane_base::settings::IndexSettings; use hyperlane_core::{ - unwrap_or_none_result, BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, + unwrap_or_none_result, BlockId, BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, HyperlaneProvider, HyperlaneSequenceAwareIndexerStoreReader, - HyperlaneWatermarkedLogStore, Indexed, InterchainGasPayment, LogMeta, H256, + HyperlaneWatermarkedLogStore, Indexed, InterchainGasPayment, LogMeta, H256, H512, }; -use itertools::Itertools; -use tracing::trace; use crate::db::{ BasicBlock, BlockCursor, ScraperDb, StorableDelivery, StorableMessage, StorablePayment, @@ -76,13 +79,11 @@ impl HyperlaneSqlDb { &self, log_meta: impl Iterator, ) -> Result> { - let block_hash_by_txn_hash: HashMap = log_meta + let block_id_by_txn_hash: HashMap = log_meta .map(|meta| { ( - meta.transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - meta.block_hash, + meta.transaction_id, + BlockId::new(meta.block_hash, meta.block_number), ) }) .collect(); @@ -90,24 +91,18 @@ impl HyperlaneSqlDb { // all blocks we care about // hash of block maps to the block id and timestamp let blocks: HashMap<_, _> = self - .ensure_blocks(block_hash_by_txn_hash.values().copied()) + .ensure_blocks(block_id_by_txn_hash.values().copied()) .await? .map(|block| (block.hash, block)) .collect(); trace!(?blocks, "Ensured blocks"); - // all txns we care about - let txns_with_ids = - self.ensure_txns(block_hash_by_txn_hash.into_iter().map( - move |(txn_hash, block_hash)| { - let block_info = *blocks.get(&block_hash).as_ref().unwrap(); - TxnWithBlockId { - txn_hash, - block_id: block_info.id, - } - }, - )) - .await?; + // We ensure transactions only from blocks which are inserted into database + let txn_hash_with_block_ids = block_id_by_txn_hash + .into_iter() + .filter_map(move |(txn, block)| blocks.get(&block.hash).map(|b| (txn, b.id))) + .map(|(txn_hash, block_id)| TxnWithBlockId { txn_hash, block_id }); + let txns_with_ids = self.ensure_txns(txn_hash_with_block_ids).await?; Ok(txns_with_ids.map(move |TxnWithId { hash, id: txn_id }| TxnWithId { hash, id: txn_id })) } @@ -116,14 +111,16 @@ impl HyperlaneSqlDb { /// in. if it is in the database already: /// Fetches its associated database id /// if it is not in the database already: - /// Looks up its data with ethers and then returns the database id after + /// Looks up its data with the chain and then returns the database id after /// inserting it into the database. + /// if it cannot fetch and parse transaction, the transaction will be skipped and not returned + /// from this method. async fn ensure_txns( &self, txns: impl Iterator, ) -> Result> { // mapping of txn hash to (txn_id, block_id). - let mut txns: HashMap, i64)> = txns + let mut txns: HashMap, i64)> = txns .map(|TxnWithBlockId { txn_hash, block_id }| (txn_hash, (None, block_id))) .collect(); @@ -147,11 +144,17 @@ impl HyperlaneSqlDb { let mut txns_to_fetch = txns.iter_mut().filter(|(_, id)| id.0.is_none()); let mut txns_to_insert: Vec = Vec::with_capacity(CHUNK_SIZE); - let mut hashes_to_insert: Vec<&H256> = Vec::with_capacity(CHUNK_SIZE); + let mut hashes_to_insert: Vec<&H512> = Vec::with_capacity(CHUNK_SIZE); - for mut chunk in as_chunks::<(&H256, &mut (Option, i64))>(txns_to_fetch, CHUNK_SIZE) { + for mut chunk in as_chunks::<(&H512, &mut (Option, i64))>(txns_to_fetch, CHUNK_SIZE) { for (hash, (_, block_id)) in chunk.iter() { - let info = self.provider.get_txn_by_hash(hash).await?; + let info = match self.provider.get_txn_by_hash(hash).await { + Ok(info) => info, + Err(e) => { + warn!(?hash, ?e, "error fetching and parsing transaction"); + continue; + } + }; hashes_to_insert.push(*hash); txns_to_insert.push(StorableTxn { info, @@ -159,37 +162,49 @@ impl HyperlaneSqlDb { }); } + // If we have no transactions to insert, we don't need to store them and update + // database transaction ids. + if txns_to_insert.is_empty() { + continue; + } + self.db.store_txns(txns_to_insert.drain(..)).await?; let ids = self.db.get_txn_ids(hashes_to_insert.drain(..)).await?; for (hash, (txn_id, _block_id)) in chunk.iter_mut() { - let _ = txn_id.insert(ids[hash]); + *txn_id = ids.get(hash).copied(); } } - Ok(txns + let ensured_txns = txns .into_iter() - .map(|(hash, (txn_id, _block_id))| TxnWithId { - hash, - id: txn_id.unwrap(), - })) + .filter_map(|(hash, (txn_id, _))| txn_id.map(|id| (hash, id))) + .map(|(hash, id)| TxnWithId { hash, id }); + + Ok(ensured_txns) } /// Takes a list of block hashes for each block /// if it is in the database already: /// Fetches its associated database id /// if it is not in the database already: - /// Looks up its data with ethers and then returns the database id after + /// Looks up its data with the chain and then returns the database id after /// inserting it into the database. + /// if it cannot fetch and parse block, the block will be skipped and not returned from + /// this method. async fn ensure_blocks( &self, - block_hashes: impl Iterator, + block_ids: impl Iterator, ) -> Result> { - // mapping of block hash to the database id and block timestamp. Optionals are - // in place because we will find the timestamp first if the block was not - // already in the db. - let mut blocks: HashMap> = - block_hashes.map(|b| (b, None)).collect(); + // Mapping from block hash to block ids (hash and height) + let block_hash_to_block_id_map: HashMap = + block_ids.map(|b| (b.hash, b)).collect(); + + // Mapping of block hash to `BasicBlock` which contains database block id and block hash. + let mut blocks: HashMap> = block_hash_to_block_id_map + .keys() + .map(|hash| (*hash, None)) + .collect(); let db_blocks: Vec = if !blocks.is_empty() { // check database to see which blocks we already know and fetch their IDs @@ -220,7 +235,17 @@ impl HyperlaneSqlDb { for chunk in as_chunks(blocks_to_fetch, CHUNK_SIZE) { debug_assert!(!chunk.is_empty()); for (hash, block_info) in chunk { - let info = self.provider.get_block_by_hash(hash).await?; + // We should have block_id in this map for every hashes + let block_id = block_hash_to_block_id_map[hash]; + let block_height = block_id.height; + + let info = match self.provider.get_block_by_height(block_height).await { + Ok(info) => info, + Err(e) => { + warn!(block_hash = ?hash, ?block_height, ?e, "error fetching and parsing block"); + continue; + } + }; let basic_info_ref = block_info.insert(BasicBlock { id: -1, hash: *hash, @@ -229,6 +254,12 @@ impl HyperlaneSqlDb { hashes_to_insert.push(hash); } + // If we have no blocks to insert, we don't store them and we don't update + // database block ids. + if blocks_to_insert.is_empty() { + continue; + } + self.db .store_blocks( self.domain().id(), @@ -247,51 +278,41 @@ impl HyperlaneSqlDb { .collect::>(); for (block_ref, _) in blocks_to_insert.drain(..) { - block_ref.id = hashes[&block_ref.hash]; + if let Some(id) = hashes.get(&block_ref.hash) { + block_ref.id = *id; + } } } - // ensure we have updated all the block ids and that we have info for all of - // them. - #[cfg(debug_assertions)] - for (hash, block) in blocks.iter() { - let block = block.as_ref().unwrap(); - assert_eq!(hash, &block.hash); - assert!(block.id > 0); - } - - Ok(blocks + let ensured_blocks = blocks .into_iter() - .map(|(hash, block_info)| block_info.unwrap())) + .filter_map(|(hash, block_info)| block_info.filter(|b| b.id != -1)); + + Ok(ensured_blocks) } } #[async_trait] impl HyperlaneLogStore for HyperlaneSqlDb { - /// Store messages from the origin mailbox into the database. + /// Store dispatched messages from the origin mailbox into the database. + /// We store only messages from blocks and transaction which we could successfully insert + /// into database. async fn store_logs(&self, messages: &[(Indexed, LogMeta)]) -> Result { if messages.is_empty() { return Ok(0); } - let txns: HashMap = self + let txns: HashMap = self .ensure_blocks_and_txns(messages.iter().map(|r| &r.1)) .await? .map(|t| (t.hash, t)) .collect(); - let storable = messages.iter().map(|m| { - let txn = txns - .get( - &m.1.transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - ) - .unwrap(); - StorableMessage { - msg: m.0.inner().clone(), - meta: &m.1, - txn_id: txn.id, - } - }); + let storable = messages + .iter() + .filter_map(|(message, meta)| { + txns.get(&meta.transaction_id) + .map(|t| (message.inner().clone(), meta, t.id)) + }) + .map(|(msg, meta, txn_id)| StorableMessage { msg, meta, txn_id }); let stored = self .db .store_dispatched_messages(self.domain().id(), &self.mailbox_address, storable) @@ -302,31 +323,29 @@ impl HyperlaneLogStore for HyperlaneSqlDb { #[async_trait] impl HyperlaneLogStore for HyperlaneSqlDb { + /// Store delivered message ids from the destination mailbox into the database. + /// We store only delivered messages ids from blocks and transaction which we could successfully + /// insert into database. async fn store_logs(&self, deliveries: &[(Indexed, LogMeta)]) -> Result { if deliveries.is_empty() { return Ok(0); } - let txns: HashMap = self + let txns: HashMap = self .ensure_blocks_and_txns(deliveries.iter().map(|r| &r.1)) .await? .map(|t| (t.hash, t)) .collect(); - let storable = deliveries.iter().map(|(message_id, meta)| { - let txn_id = txns - .get( - &meta - .transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - ) - .unwrap() - .id; - StorableDelivery { - message_id: *message_id.inner(), + let storable = deliveries + .iter() + .filter_map(|(message_id, meta)| { + txns.get(&meta.transaction_id) + .map(|txn| (*message_id.inner(), meta, txn.id)) + }) + .map(|(message_id, meta, txn_id)| StorableDelivery { + message_id, meta, txn_id, - } - }); + }); let stored = self .db @@ -338,6 +357,9 @@ impl HyperlaneLogStore for HyperlaneSqlDb { #[async_trait] impl HyperlaneLogStore for HyperlaneSqlDb { + /// Store interchain gas payments into the database. + /// We store only interchain gas payments from blocks and transaction which we could + /// successfully insert into database. async fn store_logs( &self, payments: &[(Indexed, LogMeta)], @@ -345,27 +367,22 @@ impl HyperlaneLogStore for HyperlaneSqlDb { if payments.is_empty() { return Ok(0); } - let txns: HashMap = self + let txns: HashMap = self .ensure_blocks_and_txns(payments.iter().map(|r| &r.1)) .await? .map(|t| (t.hash, t)) .collect(); - let storable = payments.iter().map(|(payment, meta)| { - let txn_id = txns - .get( - &meta - .transaction_id - .try_into() - .expect("256-bit transaction ids are the maximum supported at this time"), - ) - .unwrap() - .id; - StorablePayment { - payment: payment.inner(), + let storable = payments + .iter() + .filter_map(|(payment, meta)| { + txns.get(&meta.transaction_id) + .map(|txn| (payment.inner(), meta, txn.id)) + }) + .map(|(payment, meta, txn_id)| StorablePayment { + payment, meta, txn_id, - } - }); + }); let stored = self.db.store_payments(self.domain().id(), storable).await?; Ok(stored as u32) @@ -413,13 +430,13 @@ where #[derive(Debug, Clone)] struct TxnWithId { - hash: H256, + hash: H512, id: i64, } #[derive(Debug, Clone)] struct TxnWithBlockId { - txn_hash: H256, + txn_hash: H512, block_id: i64, } diff --git a/rust/main/agents/scraper/src/conversions.rs b/rust/main/agents/scraper/src/conversions.rs new file mode 100644 index 000000000..9252d5cd4 --- /dev/null +++ b/rust/main/agents/scraper/src/conversions.rs @@ -0,0 +1,10 @@ +use num_bigint::{BigInt, Sign}; +use sea_orm::prelude::BigDecimal; + +use hyperlane_core::U256; + +pub fn u256_to_decimal(v: U256) -> BigDecimal { + let mut buf = [0u8; 32]; + v.to_little_endian(&mut buf); + BigDecimal::from(BigInt::from_bytes_le(Sign::Plus, &buf as &[u8])) +} diff --git a/rust/agents/scraper/src/date_time.rs b/rust/main/agents/scraper/src/date_time.rs similarity index 100% rename from rust/agents/scraper/src/date_time.rs rename to rust/main/agents/scraper/src/date_time.rs diff --git a/rust/agents/scraper/src/db/block.rs b/rust/main/agents/scraper/src/db/block.rs similarity index 97% rename from rust/agents/scraper/src/db/block.rs rename to rust/main/agents/scraper/src/db/block.rs index 1af05cd8c..45ff0bf7c 100644 --- a/rust/agents/scraper/src/db/block.rs +++ b/rust/main/agents/scraper/src/db/block.rs @@ -5,10 +5,9 @@ use sea_orm::{ }; use tracing::{debug, trace}; -use hyperlane_core::{BlockInfo, H256}; +use hyperlane_core::{address_to_bytes, h256_to_bytes, BlockInfo, H256}; use migration::OnConflict; -use crate::conversions::{address_to_bytes, h256_to_bytes}; use crate::date_time; use crate::db::ScraperDb; diff --git a/rust/agents/scraper/src/db/block_cursor.rs b/rust/main/agents/scraper/src/db/block_cursor.rs similarity index 100% rename from rust/agents/scraper/src/db/block_cursor.rs rename to rust/main/agents/scraper/src/db/block_cursor.rs diff --git a/rust/agents/scraper/src/db/generated/block.rs b/rust/main/agents/scraper/src/db/generated/block.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/block.rs rename to rust/main/agents/scraper/src/db/generated/block.rs diff --git a/rust/agents/scraper/src/db/generated/cursor.rs b/rust/main/agents/scraper/src/db/generated/cursor.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/cursor.rs rename to rust/main/agents/scraper/src/db/generated/cursor.rs diff --git a/rust/agents/scraper/src/db/generated/delivered_message.rs b/rust/main/agents/scraper/src/db/generated/delivered_message.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/delivered_message.rs rename to rust/main/agents/scraper/src/db/generated/delivered_message.rs diff --git a/rust/agents/scraper/src/db/generated/domain.rs b/rust/main/agents/scraper/src/db/generated/domain.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/domain.rs rename to rust/main/agents/scraper/src/db/generated/domain.rs diff --git a/rust/agents/scraper/src/db/generated/gas_payment.rs b/rust/main/agents/scraper/src/db/generated/gas_payment.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/gas_payment.rs rename to rust/main/agents/scraper/src/db/generated/gas_payment.rs diff --git a/rust/agents/scraper/src/db/generated/message.rs b/rust/main/agents/scraper/src/db/generated/message.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/message.rs rename to rust/main/agents/scraper/src/db/generated/message.rs diff --git a/rust/agents/scraper/src/db/generated/mod.rs b/rust/main/agents/scraper/src/db/generated/mod.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/mod.rs rename to rust/main/agents/scraper/src/db/generated/mod.rs diff --git a/rust/agents/scraper/src/db/generated/prelude.rs b/rust/main/agents/scraper/src/db/generated/prelude.rs similarity index 92% rename from rust/agents/scraper/src/db/generated/prelude.rs rename to rust/main/agents/scraper/src/db/generated/prelude.rs index 2bee035aa..1280bf70d 100644 --- a/rust/agents/scraper/src/db/generated/prelude.rs +++ b/rust/main/agents/scraper/src/db/generated/prelude.rs @@ -1,5 +1,5 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 - +#[allow(unused_imports)] pub use super::{ block::Entity as Block, cursor::Entity as Cursor, delivered_message::Entity as DeliveredMessage, domain::Entity as Domain, diff --git a/rust/agents/scraper/src/db/generated/transaction.rs b/rust/main/agents/scraper/src/db/generated/transaction.rs similarity index 100% rename from rust/agents/scraper/src/db/generated/transaction.rs rename to rust/main/agents/scraper/src/db/generated/transaction.rs diff --git a/rust/agents/scraper/src/db/message.rs b/rust/main/agents/scraper/src/db/message.rs similarity index 94% rename from rust/agents/scraper/src/db/message.rs rename to rust/main/agents/scraper/src/db/message.rs index fdf034c51..423c24058 100644 --- a/rust/agents/scraper/src/db/message.rs +++ b/rust/main/agents/scraper/src/db/message.rs @@ -1,12 +1,15 @@ +#![allow(dead_code)] // TODO: `rustc` 1.80.1 clippy issue + use eyre::Result; use itertools::Itertools; use sea_orm::{prelude::*, ActiveValue::*, DeriveColumn, EnumIter, Insert, QuerySelect}; use tracing::{debug, instrument, trace}; -use hyperlane_core::{HyperlaneMessage, LogMeta, H256}; +use hyperlane_core::{ + address_to_bytes, bytes_to_address, h256_to_bytes, HyperlaneMessage, LogMeta, H256, +}; use migration::OnConflict; -use crate::conversions::{address_to_bytes, bytes_to_address, h256_to_bytes}; use crate::date_time; use crate::db::ScraperDb; @@ -177,9 +180,13 @@ impl ScraperDb { }) .collect_vec(); - debug_assert!(!models.is_empty()); trace!(?models, "Writing delivered messages to database"); + if models.is_empty() { + debug!("Wrote zero new delivered messages to database"); + return Ok(0); + } + Insert::many(models) .on_conflict( OnConflict::columns([delivered_message::Column::MsgId]) @@ -196,12 +203,10 @@ impl ScraperDb { .deliveries_count_since_id(domain, destination_mailbox, latest_id_before) .await?; - if new_deliveries_count > 0 { - debug!( - messages = new_deliveries_count, - "Wrote new delivered messages to database" - ); - } + debug!( + messages = new_deliveries_count, + "Wrote new delivered messages to database" + ); Ok(new_deliveries_count) } @@ -271,9 +276,13 @@ impl ScraperDb { }) .collect_vec(); - debug_assert!(!models.is_empty()); trace!(?models, "Writing messages to database"); + if models.is_empty() { + debug!("Wrote zero new messages to database"); + return Ok(0); + } + Insert::many(models) .on_conflict( OnConflict::columns([ @@ -298,12 +307,10 @@ impl ScraperDb { .dispatch_count_since_id(domain, origin_mailbox, latest_id_before) .await?; - if new_dispatch_count > 0 { - debug!( - messages = new_dispatch_count, - "Wrote new messages to database" - ); - } + debug!( + messages = new_dispatch_count, + "Wrote new messages to database" + ); Ok(new_dispatch_count) } } diff --git a/rust/agents/scraper/src/db/mod.rs b/rust/main/agents/scraper/src/db/mod.rs similarity index 100% rename from rust/agents/scraper/src/db/mod.rs rename to rust/main/agents/scraper/src/db/mod.rs diff --git a/rust/agents/scraper/src/db/payment.rs b/rust/main/agents/scraper/src/db/payment.rs similarity index 90% rename from rust/agents/scraper/src/db/payment.rs rename to rust/main/agents/scraper/src/db/payment.rs index 57c63b3e3..498128c34 100644 --- a/rust/agents/scraper/src/db/payment.rs +++ b/rust/main/agents/scraper/src/db/payment.rs @@ -3,10 +3,10 @@ use itertools::Itertools; use sea_orm::{prelude::*, ActiveValue::*, Insert, QuerySelect}; use tracing::{debug, instrument, trace}; -use hyperlane_core::{InterchainGasPayment, LogMeta}; +use hyperlane_core::{h256_to_bytes, InterchainGasPayment, LogMeta}; use migration::OnConflict; -use crate::conversions::{h256_to_bytes, u256_to_decimal}; +use crate::conversions::u256_to_decimal; use crate::date_time; use crate::db::ScraperDb; @@ -42,9 +42,13 @@ impl ScraperDb { }) .collect_vec(); - debug_assert!(!models.is_empty()); trace!(?models, "Writing gas payments to database"); + if models.is_empty() { + debug!("Wrote zero new gas payments to database"); + return Ok(0); + } + Insert::many(models) .on_conflict( OnConflict::columns([ @@ -67,12 +71,10 @@ impl ScraperDb { .payments_count_since_id(domain, latest_id_before) .await?; - if new_payments_count > 0 { - debug!( - payments = new_payments_count, - "Wrote new gas payments to database" - ); - } + debug!( + payments = new_payments_count, + "Wrote new gas payments to database" + ); Ok(new_payments_count) } diff --git a/rust/agents/scraper/src/db/txn.rs b/rust/main/agents/scraper/src/db/txn.rs similarity index 91% rename from rust/agents/scraper/src/db/txn.rs rename to rust/main/agents/scraper/src/db/txn.rs index ff0e70f2b..d5cec03dc 100644 --- a/rust/agents/scraper/src/db/txn.rs +++ b/rust/main/agents/scraper/src/db/txn.rs @@ -2,19 +2,17 @@ use std::collections::HashMap; use derive_more::Deref; use eyre::{eyre, Context, Result}; -use hyperlane_core::{TxnInfo, H256}; use sea_orm::{ prelude::*, sea_query::OnConflict, ActiveValue::*, DeriveColumn, EnumIter, Insert, NotSet, QuerySelect, }; use tracing::{debug, instrument, trace}; +use hyperlane_core::{address_to_bytes, bytes_to_h512, h512_to_bytes, TxnInfo, H512}; + use super::generated::transaction; -use crate::{ - conversions::{address_to_bytes, h256_to_bytes, u256_to_decimal}, - date_time, - db::ScraperDb, -}; + +use crate::{conversions::u256_to_decimal, date_time, db::ScraperDb}; #[derive(Debug, Clone, Deref)] pub struct StorableTxn { @@ -43,8 +41,8 @@ impl ScraperDb { /// found be excluded from the hashmap. pub async fn get_txn_ids( &self, - hashes: impl Iterator, - ) -> Result> { + hashes: impl Iterator, + ) -> Result> { #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] enum QueryAs { Id, @@ -53,7 +51,7 @@ impl ScraperDb { // check database to see which txns we already know and fetch their IDs let txns = transaction::Entity::find() - .filter(transaction::Column::Hash.is_in(hashes.map(h256_to_bytes))) + .filter(transaction::Column::Hash.is_in(hashes.map(h512_to_bytes))) .select_only() .column_as(transaction::Column::Id, QueryAs::Id) .column_as(transaction::Column::Hash, QueryAs::Hash) @@ -62,7 +60,7 @@ impl ScraperDb { .await .context("When querying transactions")? .into_iter() - .map(|(id, hash)| Ok((H256::from_slice(&hash), id))) + .map(|(id, hash)| Ok((bytes_to_h512(&hash), id))) .collect::>>()?; trace!(?txns, "Queried transaction info for hashes"); @@ -86,7 +84,7 @@ impl ScraperDb { max_priority_fee_per_gas: Set(txn .max_priority_fee_per_gas .map(u256_to_decimal)), - hash: Unchanged(h256_to_bytes(&txn.hash)), + hash: Unchanged(h512_to_bytes(&txn.hash)), time_created: Set(date_time::now()), gas_used: Set(u256_to_decimal(receipt.gas_used)), gas_price: Set(txn.gas_price.map(u256_to_decimal)), diff --git a/rust/agents/scraper/src/main.rs b/rust/main/agents/scraper/src/main.rs similarity index 100% rename from rust/agents/scraper/src/main.rs rename to rust/main/agents/scraper/src/main.rs diff --git a/rust/agents/scraper/src/settings.rs b/rust/main/agents/scraper/src/settings.rs similarity index 100% rename from rust/agents/scraper/src/settings.rs rename to rust/main/agents/scraper/src/settings.rs diff --git a/rust/agents/validator/Cargo.toml b/rust/main/agents/validator/Cargo.toml similarity index 83% rename from rust/agents/validator/Cargo.toml rename to rust/main/agents/validator/Cargo.toml index e9e66eb30..7228ad69c 100644 --- a/rust/agents/validator/Cargo.toml +++ b/rust/main/agents/validator/Cargo.toml @@ -1,4 +1,3 @@ -cargo-features = ["workspace-inheritance"] [package] name = "validator" @@ -12,6 +11,7 @@ version.workspace = true [dependencies] async-trait.workspace = true axum.workspace = true +chrono.workspace = true config.workspace = true console-subscriber.workspace = true derive_more.workspace = true @@ -28,16 +28,21 @@ tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] } tracing-futures.workspace = true tracing.workspace = true -hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async"] } +hyperlane-core = { path = "../../hyperlane-core", features = [ + "agent", + "async", +] } hyperlane-base = { path = "../../hyperlane-base" } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } [dev-dependencies] +mockall.workspace = true tokio-test.workspace = true reqwest.workspace = true hyperlane-test = { path = "../../hyperlane-test" } k256.workspace = true +hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum", features = ["test-utils"] } [features] default = ["color-eyre", "oneline-errors"] diff --git a/rust/agents/validator/src/main.rs b/rust/main/agents/validator/src/main.rs similarity index 100% rename from rust/agents/validator/src/main.rs rename to rust/main/agents/validator/src/main.rs diff --git a/rust/agents/validator/src/server/eigen_node.rs b/rust/main/agents/validator/src/server/eigen_node.rs similarity index 99% rename from rust/agents/validator/src/server/eigen_node.rs rename to rust/main/agents/validator/src/server/eigen_node.rs index 90bcd5e98..7a927d727 100644 --- a/rust/agents/validator/src/server/eigen_node.rs +++ b/rust/main/agents/validator/src/server/eigen_node.rs @@ -1,3 +1,5 @@ +// TODO: `rustc` 1.80.1 clippy issue +#![allow(clippy::doc_lazy_continuation)] //! A server that serves EigenLayer specific routes //! compliant with the spec here https://eigen.nethermind.io/docs/spec/api/ //! diff --git a/rust/agents/validator/src/server/mod.rs b/rust/main/agents/validator/src/server/mod.rs similarity index 100% rename from rust/agents/validator/src/server/mod.rs rename to rust/main/agents/validator/src/server/mod.rs diff --git a/rust/agents/validator/src/settings.rs b/rust/main/agents/validator/src/settings.rs similarity index 96% rename from rust/agents/validator/src/settings.rs rename to rust/main/agents/validator/src/settings.rs index 02dc87cb1..d464be5a9 100644 --- a/rust/agents/validator/src/settings.rs +++ b/rust/main/agents/validator/src/settings.rs @@ -15,7 +15,9 @@ use hyperlane_base::{ CheckpointSyncerConf, Settings, SignerConf, }, }; -use hyperlane_core::{cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol}; +use hyperlane_core::{ + cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, ReorgPeriod, +}; use serde::Deserialize; use serde_json::Value; @@ -36,8 +38,8 @@ pub struct ValidatorSettings { pub validator: SignerConf, /// The checkpoint syncer configuration pub checkpoint_syncer: CheckpointSyncerConf, - /// The reorg_period in blocks - pub reorg_period: u64, + /// The reorg configuration + pub reorg_period: ReorgPeriod, /// How frequently to check for new checkpoints pub interval: Duration, } @@ -122,8 +124,8 @@ impl FromRawConf for ValidatorSettings { .get_key(origin_chain_name) .get_opt_key("blocks") .get_opt_key("reorgPeriod") - .parse_u64() - .unwrap_or(1); + .parse_value("Invalid reorgPeriod") + .unwrap_or(ReorgPeriod::from_blocks(1)); cfg_unwrap_all!(cwp, err: [base, origin_chain, validator, checkpoint_syncer]); diff --git a/rust/main/agents/validator/src/submit.rs b/rust/main/agents/validator/src/submit.rs new file mode 100644 index 000000000..954b8d0d9 --- /dev/null +++ b/rust/main/agents/validator/src/submit.rs @@ -0,0 +1,653 @@ +use std::sync::Arc; +use std::time::{Duration, Instant}; +use std::vec; + +use prometheus::IntGauge; +use tokio::time::sleep; +use tracing::{debug, error, info}; + +use hyperlane_base::db::HyperlaneDb; +use hyperlane_base::{CheckpointSyncer, CoreMetrics}; +use hyperlane_core::rpc_clients::call_and_retry_indefinitely; +use hyperlane_core::{ + accumulator::incremental::IncrementalMerkle, Checkpoint, CheckpointWithMessageId, + HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSignerExt, +}; +use hyperlane_core::{ChainResult, MerkleTreeHook, ReorgEvent, ReorgPeriod}; +use hyperlane_ethereum::SingletonSignerHandle; + +#[derive(Clone)] +pub(crate) struct ValidatorSubmitter { + interval: Duration, + reorg_period: ReorgPeriod, + signer: SingletonSignerHandle, + merkle_tree_hook: Arc, + checkpoint_syncer: Arc, + db: Arc, + metrics: ValidatorSubmitterMetrics, +} + +impl ValidatorSubmitter { + pub(crate) fn new( + interval: Duration, + reorg_period: ReorgPeriod, + merkle_tree_hook: Arc, + signer: SingletonSignerHandle, + checkpoint_syncer: Arc, + db: Arc, + metrics: ValidatorSubmitterMetrics, + ) -> Self { + Self { + reorg_period, + interval, + merkle_tree_hook, + signer, + checkpoint_syncer, + db, + metrics, + } + } + + pub(crate) fn checkpoint(&self, tree: &IncrementalMerkle) -> Checkpoint { + Checkpoint { + root: tree.root(), + index: tree.index(), + merkle_tree_hook_address: self.merkle_tree_hook.address(), + mailbox_domain: self.merkle_tree_hook.domain().id(), + } + } + + /// Submits signed checkpoints from index 0 until the target checkpoint (inclusive). + /// Runs idly forever once the target checkpoint is reached to avoid exiting the task. + pub(crate) async fn backfill_checkpoint_submitter(self, target_checkpoint: Checkpoint) { + let mut tree = IncrementalMerkle::default(); + self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &target_checkpoint) + .await; + + info!( + ?target_checkpoint, + "Backfill checkpoint submitter successfully reached target checkpoint" + ); + } + + /// Submits signed checkpoints indefinitely, starting from the `tree`. + pub(crate) async fn checkpoint_submitter(self, mut tree: IncrementalMerkle) { + // How often to log checkpoint info - once every minute + let checkpoint_info_log_period = Duration::from_secs(60); + // The instant in which we last logged checkpoint info, if at all + let mut latest_checkpoint_info_log: Option = None; + // Returns whether checkpoint info should be logged based off the + // checkpoint_info_log_period having elapsed since the last log. + // Sets latest_checkpoint_info_log to the current instant if true. + let mut should_log_checkpoint_info = || { + if let Some(instant) = latest_checkpoint_info_log { + if instant.elapsed() < checkpoint_info_log_period { + return false; + } + } + latest_checkpoint_info_log = Some(Instant::now()); + true + }; + + loop { + // Lag by reorg period because this is our correctness checkpoint. + let latest_checkpoint = call_and_retry_indefinitely(|| { + let merkle_tree_hook = self.merkle_tree_hook.clone(); + let reorg_period = self.reorg_period.clone(); + Box::pin(async move { merkle_tree_hook.latest_checkpoint(&reorg_period).await }) + }) + .await; + + self.metrics + .latest_checkpoint_observed + .set(latest_checkpoint.index as i64); + + if should_log_checkpoint_info() { + info!( + ?latest_checkpoint, + tree_count = tree.count(), + "Latest checkpoint" + ); + } + + // This may occur e.g. if RPC providers are unreliable and make calls against + // inconsistent block tips. + // + // In this case, we just sleep a bit until we fetch a new latest checkpoint + // that at least meets the tree. + if tree_exceeds_checkpoint(&latest_checkpoint, &tree) { + debug!( + ?latest_checkpoint, + tree_count = tree.count(), + "Latest checkpoint is behind tree, sleeping briefly" + ); + sleep(self.interval).await; + continue; + } + self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &latest_checkpoint) + .await; + + self.metrics + .latest_checkpoint_processed + .set(latest_checkpoint.index as i64); + + sleep(self.interval).await; + } + } + + /// Submits signed checkpoints relating to the given tree until the correctness checkpoint (inclusive). + /// Only submits the signed checkpoints once the correctness checkpoint is reached. + async fn submit_checkpoints_until_correctness_checkpoint( + &self, + tree: &mut IncrementalMerkle, + correctness_checkpoint: &Checkpoint, + ) { + // This should never be called with a tree that is ahead of the correctness checkpoint. + assert!( + !tree_exceeds_checkpoint(correctness_checkpoint, tree), + "tree (count: {}) is ahead of correctness checkpoint {:?}", + tree.count(), + correctness_checkpoint, + ); + + // All intermediate checkpoints will be stored here and signed once the correctness + // checkpoint is reached. + let mut checkpoint_queue = vec![]; + + // If the correctness checkpoint is ahead of the tree, we need to ingest more messages. + // + // tree.index() will panic if the tree is empty, so we use tree.count() instead + // and convert the correctness_checkpoint.index to a count by adding 1. + while tree.count() as u32 <= correctness_checkpoint.index { + if let Some(insertion) = self + .db + .retrieve_merkle_tree_insertion_by_leaf_index(&(tree.count() as u32)) + .unwrap_or_else(|err| { + panic!( + "Error fetching merkle tree insertion for leaf index {}: {}", + tree.count(), + err + ) + }) + { + debug!( + index = insertion.index(), + queue_length = checkpoint_queue.len(), + "Ingesting leaf to tree" + ); + let message_id = insertion.message_id(); + tree.ingest(message_id); + + let checkpoint = self.checkpoint(tree); + + checkpoint_queue.push(CheckpointWithMessageId { + checkpoint, + message_id, + }); + } else { + // If we haven't yet indexed the next merkle tree insertion but know that + // it will soon exist (because we know the correctness checkpoint), wait a bit and + // try again. + sleep(Duration::from_millis(100)).await + } + } + + // At this point we know that correctness_checkpoint.index == tree.index(). + assert_eq!( + correctness_checkpoint.index, + tree.index(), + "correctness checkpoint index {} != tree index {}", + correctness_checkpoint.index, + tree.index(), + ); + + let checkpoint = self.checkpoint(tree); + + // If the tree's checkpoint doesn't match the correctness checkpoint, something went wrong + // and we bail loudly. + if checkpoint != *correctness_checkpoint { + let reorg_event = ReorgEvent::new( + tree.root(), + correctness_checkpoint.root, + checkpoint.index, + chrono::Utc::now().timestamp() as u64, + self.reorg_period.clone(), + ); + error!( + ?checkpoint, + ?correctness_checkpoint, + ?reorg_event, + "Incorrect tree root, something went wrong" + ); + + let mut panic_message = "Incorrect tree root, something went wrong.".to_owned(); + if let Err(e) = self + .checkpoint_syncer + .write_reorg_status(&reorg_event) + .await + { + panic_message.push_str(&format!( + " Reorg troubleshooting details couldn't be written to checkpoint storage: {}", + e + )); + } + panic!("{panic_message}"); + } + + if !checkpoint_queue.is_empty() { + info!( + index = checkpoint.index, + queue_len = checkpoint_queue.len(), + "Reached tree consistency" + ); + self.sign_and_submit_checkpoints(checkpoint_queue).await; + + info!( + index = checkpoint.index, + "Signed all queued checkpoints until index" + ); + } + } + + async fn sign_and_submit_checkpoint( + &self, + checkpoint: CheckpointWithMessageId, + ) -> ChainResult<()> { + let existing = self + .checkpoint_syncer + .fetch_checkpoint(checkpoint.index) + .await?; + if existing.is_some() { + debug!(index = checkpoint.index, "Checkpoint already submitted"); + return Ok(()); + } + let signed_checkpoint = self.signer.sign(checkpoint).await?; + self.checkpoint_syncer + .write_checkpoint(&signed_checkpoint) + .await?; + debug!(index = checkpoint.index, "Signed and submitted checkpoint"); + + // TODO: move these into S3 implementations + // small sleep before signing next checkpoint to avoid rate limiting + sleep(Duration::from_millis(100)).await; + Ok(()) + } + + /// Signs and submits any previously unsubmitted checkpoints. + async fn sign_and_submit_checkpoints(&self, checkpoints: Vec) { + let last_checkpoint = checkpoints.as_slice()[checkpoints.len() - 1]; + // Submits checkpoints to the store in reverse order. This speeds up processing historic checkpoints (those before the validator is spun up), + // since those are the most likely to make messages become processable. + // A side effect is that new checkpoints will also be submitted in reverse order. + for queued_checkpoint in checkpoints.into_iter().rev() { + // certain checkpoint stores rate limit very aggressively, so we retry indefinitely + call_and_retry_indefinitely(|| { + let self_clone = self.clone(); + Box::pin(async move { + self_clone + .sign_and_submit_checkpoint(queued_checkpoint) + .await?; + Ok(()) + }) + }) + .await; + } + + call_and_retry_indefinitely(|| { + let self_clone = self.clone(); + Box::pin(async move { + self_clone + .checkpoint_syncer + .update_latest_index(last_checkpoint.index) + .await?; + Ok(()) + }) + }) + .await; + } +} + +/// Returns whether the tree exceeds the checkpoint. +fn tree_exceeds_checkpoint(checkpoint: &Checkpoint, tree: &IncrementalMerkle) -> bool { + // tree.index() will panic if the tree is empty, so we use tree.count() instead + // and convert the correctness_checkpoint.index to a count by adding 1. + checkpoint.index + 1 < tree.count() as u32 +} + +#[derive(Clone)] +pub(crate) struct ValidatorSubmitterMetrics { + latest_checkpoint_observed: IntGauge, + latest_checkpoint_processed: IntGauge, +} + +impl ValidatorSubmitterMetrics { + pub fn new(metrics: &CoreMetrics, mailbox_chain: &HyperlaneDomain) -> Self { + let chain_name = mailbox_chain.name(); + Self { + latest_checkpoint_observed: metrics + .latest_checkpoint() + .with_label_values(&["validator_observed", chain_name]), + latest_checkpoint_processed: metrics + .latest_checkpoint() + .with_label_values(&["validator_processed", chain_name]), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use async_trait::async_trait; + use eyre::Result; + use hyperlane_base::{ + db::{DbResult, HyperlaneDb, InterchainGasExpenditureData, InterchainGasPaymentData}, + AgentMetadata, + }; + use hyperlane_core::{ + test_utils::dummy_domain, GasPaymentKey, HyperlaneChain, HyperlaneContract, + HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, InterchainGasPayment, + InterchainGasPaymentMeta, MerkleTreeHook, MerkleTreeInsertion, PendingOperationStatus, + ReorgEvent, SignedAnnouncement, SignedCheckpointWithMessageId, H160, H256, + }; + use prometheus::Registry; + use std::{fmt::Debug, sync::Arc, time::Duration}; + use tokio::sync::mpsc; + + mockall::mock! { + pub Db { + fn provider(&self) -> Box; + } + + impl Debug for Db { + fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result; + } + + impl HyperlaneDb for Db { + fn retrieve_highest_seen_message_nonce(&self) -> DbResult>; + fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult>; + fn retrieve_processed_by_nonce(&self, nonce: &u32) -> DbResult>; + fn domain(&self) -> &HyperlaneDomain; + fn store_message_id_by_nonce(&self, nonce: &u32, id: &H256) -> DbResult<()>; + fn retrieve_message_id_by_nonce(&self, nonce: &u32) -> DbResult>; + fn store_message_by_id(&self, id: &H256, message: &HyperlaneMessage) -> DbResult<()>; + fn retrieve_message_by_id(&self, id: &H256) -> DbResult>; + fn store_dispatched_block_number_by_nonce( + &self, + nonce: &u32, + block_number: &u64, + ) -> DbResult<()>; + fn retrieve_dispatched_block_number_by_nonce(&self, nonce: &u32) -> DbResult>; + fn store_processed_by_nonce(&self, nonce: &u32, processed: &bool) -> DbResult<()>; + fn store_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + processed: &bool, + ) -> DbResult<()>; + fn retrieve_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + ) -> DbResult>; + fn store_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + data: &InterchainGasExpenditureData, + ) -> DbResult<()>; + fn retrieve_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + fn store_status_by_message_id( + &self, + message_id: &H256, + status: &PendingOperationStatus, + ) -> DbResult<()>; + fn retrieve_status_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + fn store_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + data: &InterchainGasPaymentData, + ) -> DbResult<()>; + fn retrieve_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + ) -> DbResult>; + fn store_gas_payment_by_sequence( + &self, + sequence: &u32, + payment: &InterchainGasPayment, + ) -> DbResult<()>; + fn retrieve_gas_payment_by_sequence( + &self, + sequence: &u32, + ) -> DbResult>; + fn store_gas_payment_block_by_sequence( + &self, + sequence: &u32, + block_number: &u64, + ) -> DbResult<()>; + fn retrieve_gas_payment_block_by_sequence(&self, sequence: &u32) -> DbResult>; + fn store_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + count: &u32, + ) -> DbResult<()>; + fn retrieve_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + fn store_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + insertion: &MerkleTreeInsertion, + ) -> DbResult<()>; + fn retrieve_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult>; + fn store_merkle_leaf_index_by_message_id( + &self, + message_id: &H256, + leaf_index: &u32, + ) -> DbResult<()>; + fn retrieve_merkle_leaf_index_by_message_id(&self, message_id: &H256) -> DbResult>; + fn store_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + block_number: &u64, + ) -> DbResult<()>; + fn retrieve_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult>; + fn store_highest_seen_message_nonce_number(&self, nonce: &u32) -> DbResult<()>; + fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; + + } + } + + mockall::mock! { + pub MerkleTreeHook {} + + impl Debug for MerkleTreeHook { + fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result; + } + + impl HyperlaneChain for MerkleTreeHook { + fn domain(&self) -> &HyperlaneDomain; + fn provider(&self) -> Box; + } + + impl HyperlaneContract for MerkleTreeHook { + fn address(&self) -> H256; + } + + #[async_trait] + impl MerkleTreeHook for MerkleTreeHook { + async fn tree(&self, reorg_period: &ReorgPeriod) -> ChainResult; + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult; + async fn latest_checkpoint(&self, reorg_period: &ReorgPeriod) -> ChainResult; + } + } + + mockall::mock! { + pub CheckpointSyncer {} + + impl Debug for CheckpointSyncer { + fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result; + } + + #[async_trait] + impl CheckpointSyncer for CheckpointSyncer { + async fn latest_index(&self) -> Result>; + async fn write_latest_index(&self, index: u32) -> Result<()>; + async fn update_latest_index(&self, index: u32) -> Result<()>; + async fn fetch_checkpoint(&self, index: u32) -> Result>; + async fn write_checkpoint( + &self, + signed_checkpoint: &SignedCheckpointWithMessageId, + ) -> Result<()>; + async fn write_metadata(&self, metadata: &AgentMetadata) -> Result<()>; + async fn write_announcement(&self, signed_announcement: &SignedAnnouncement) -> Result<()>; + fn announcement_location(&self) -> String; + async fn write_reorg_status(&self, reorg_event: &ReorgEvent) -> Result<()>; + async fn reorg_status(&self) -> Result>; + } + } + + fn dummy_metrics() -> ValidatorSubmitterMetrics { + let origin_domain = dummy_domain(0, "dummy_origin_domain"); + let core_metrics = CoreMetrics::new("dummy_relayer", 37582, Registry::new()).unwrap(); + ValidatorSubmitterMetrics::new(&core_metrics, &origin_domain) + } + + fn dummy_singleton_handle() -> SingletonSignerHandle { + SingletonSignerHandle::new(H160::from_low_u64_be(0), mpsc::unbounded_channel().0) + } + + fn reorg_event_is_correct( + reorg_event: &ReorgEvent, + expected_local_merkle_tree: &IncrementalMerkle, + mock_onchain_merkle_tree: &IncrementalMerkle, + unix_timestamp: u64, + expected_reorg_period: ReorgPeriod, + ) { + assert_eq!( + reorg_event.canonical_merkle_root, + mock_onchain_merkle_tree.root() + ); + assert_eq!( + reorg_event.local_merkle_root, + expected_local_merkle_tree.root() + ); + assert_eq!( + reorg_event.checkpoint_index, + expected_local_merkle_tree.index() + ); + // timestamp diff should be less than 1 second + let timestamp_diff = reorg_event.unix_timestamp as i64 - unix_timestamp as i64; + assert!(timestamp_diff.abs() < 1); + + assert_eq!(reorg_event.reorg_period, expected_reorg_period); + } + + #[tokio::test] + #[should_panic(expected = "Incorrect tree root, something went wrong.")] + async fn reorg_is_detected_and_persisted_to_checkpoint_storage() { + let unix_timestamp = chrono::Utc::now().timestamp() as u64; + let expected_reorg_period = 12; + + let pre_reorg_merke_insertions = vec![ + MerkleTreeInsertion::new(0, H256::random()), + MerkleTreeInsertion::new(1, H256::random()), + MerkleTreeInsertion::new(2, H256::random()), + ]; + let mut expected_local_merkle_tree = IncrementalMerkle::default(); + for insertion in pre_reorg_merke_insertions.iter() { + expected_local_merkle_tree.ingest(insertion.message_id()); + } + + // the last leaf is different post-reorg + let post_reorg_merkle_insertions = vec![ + pre_reorg_merke_insertions[0].clone(), + pre_reorg_merke_insertions[1].clone(), + MerkleTreeInsertion::new(2, H256::random()), + ]; + let mut mock_onchain_merkle_tree = IncrementalMerkle::default(); + for insertion in post_reorg_merkle_insertions.iter() { + mock_onchain_merkle_tree.ingest(insertion.message_id()); + } + + // assert the reorg resulted in different merkle tree roots + assert_ne!( + mock_onchain_merkle_tree.root(), + expected_local_merkle_tree.root() + ); + + // the db returns the pre-reorg merkle tree insertions + let mut db = MockDb::new(); + db.expect_retrieve_merkle_tree_insertion_by_leaf_index() + .returning(move |sequence| { + Ok(Some(pre_reorg_merke_insertions[*sequence as usize].clone())) + }); + + // boilerplate mocks + let mut mock_merkle_tree_hook = MockMerkleTreeHook::new(); + mock_merkle_tree_hook + .expect_address() + .returning(|| H256::from_low_u64_be(0)); + let dummy_domain = dummy_domain(0, "dummy_domain"); + mock_merkle_tree_hook + .expect_domain() + .return_const(dummy_domain.clone()); + + // expect the checkpoint syncer to post the reorg event to the checkpoint storage + // and not submit any checkpoints (this is checked implicitly, by not setting any `expect`s) + let mut mock_checkpoint_syncer = MockCheckpointSyncer::new(); + let mock_onchain_merkle_tree_clone = mock_onchain_merkle_tree.clone(); + mock_checkpoint_syncer + .expect_write_reorg_status() + .once() + .returning(move |reorg_event| { + // unit test correctness criteria + reorg_event_is_correct( + reorg_event, + &expected_local_merkle_tree, + &mock_onchain_merkle_tree_clone, + unix_timestamp, + ReorgPeriod::from_blocks(expected_reorg_period), + ); + Ok(()) + }); + + // instantiate the validator submitter + let validator_submitter = ValidatorSubmitter::new( + Duration::from_secs(1), + ReorgPeriod::from_blocks(expected_reorg_period), + Arc::new(mock_merkle_tree_hook), + dummy_singleton_handle(), + Arc::new(mock_checkpoint_syncer), + Arc::new(db), + dummy_metrics(), + ); + + // mock the correctness checkpoint response + let mock_onchain_checkpoint = Checkpoint { + root: mock_onchain_merkle_tree.root(), + index: mock_onchain_merkle_tree.index(), + merkle_tree_hook_address: H256::from_low_u64_be(0), + mailbox_domain: dummy_domain.id(), + }; + + // Start the submitter with an empty merkle tree, so it gets rebuilt from the db. + // A panic is expected here, as the merkle root inconsistency is a critical error that may indicate fraud. + validator_submitter + .submit_checkpoints_until_correctness_checkpoint( + &mut IncrementalMerkle::default(), + &mock_onchain_checkpoint, + ) + .await; + } +} diff --git a/rust/agents/validator/src/validator.rs b/rust/main/agents/validator/src/validator.rs similarity index 96% rename from rust/agents/validator/src/validator.rs rename to rust/main/agents/validator/src/validator.rs index a4f9a9e00..2d09bd93f 100644 --- a/rust/agents/validator/src/validator.rs +++ b/rust/main/agents/validator/src/validator.rs @@ -1,4 +1,4 @@ -use std::{num::NonZeroU64, sync::Arc, time::Duration}; +use std::{sync::Arc, time::Duration}; use crate::server as validator_server; use async_trait::async_trait; @@ -10,7 +10,7 @@ use tokio::{task::JoinHandle, time::sleep}; use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument}; use hyperlane_base::{ - db::{HyperlaneRocksDB, DB}, + db::{HyperlaneDb, HyperlaneRocksDB, DB}, metrics::AgentMetrics, settings::ChainConf, AgentMetadata, BaseAgent, ChainMetrics, CheckpointSyncer, ContractSyncMetrics, ContractSyncer, @@ -19,8 +19,8 @@ use hyperlane_base::{ use hyperlane_core::{ Announcement, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneSigner, - HyperlaneSignerExt, Mailbox, MerkleTreeHook, MerkleTreeInsertion, TxOutcome, ValidatorAnnounce, - H256, U256, + HyperlaneSignerExt, Mailbox, MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, TxOutcome, + ValidatorAnnounce, H256, U256, }; use hyperlane_ethereum::{SingletonSigner, SingletonSignerHandle}; @@ -44,7 +44,7 @@ pub struct Validator { signer: SingletonSignerHandle, // temporary holder until `run` is called signer_instance: Option>, - reorg_period: u64, + reorg_period: ReorgPeriod, interval: Duration, checkpoint_syncer: Arc, core_metrics: Arc, @@ -77,7 +77,11 @@ impl BaseAgent for Validator { let (signer_instance, signer) = SingletonSigner::new(settings.validator.build().await?); let core = settings.build_hyperlane_core(metrics.clone()); - let checkpoint_syncer = settings.checkpoint_syncer.build(None).await?.into(); + let checkpoint_syncer = settings + .checkpoint_syncer + .build_and_validate(None) + .await? + .into(); let mailbox = settings .build_mailbox(&settings.origin_chain, &metrics) @@ -180,12 +184,10 @@ impl BaseAgent for Validator { // announce the validator after spawning the signer task self.announce().await.expect("Failed to announce validator"); - let reorg_period = NonZeroU64::new(self.reorg_period); - // Ensure that the merkle tree hook has count > 0 before we begin indexing // messages or submitting checkpoints. loop { - match self.merkle_tree_hook.count(reorg_period).await { + match self.merkle_tree_hook.count(&self.reorg_period).await { Ok(0) => { info!("Waiting for first message in merkle tree hook"); sleep(self.interval).await; @@ -237,18 +239,17 @@ impl Validator { async fn run_checkpoint_submitters(&self) -> Vec>> { let submitter = ValidatorSubmitter::new( self.interval, - self.reorg_period, + self.reorg_period.clone(), self.merkle_tree_hook.clone(), self.signer.clone(), self.checkpoint_syncer.clone(), - self.db.clone(), + Arc::new(self.db.clone()) as Arc, ValidatorSubmitterMetrics::new(&self.core.metrics, &self.origin_chain), ); - let reorg_period = NonZeroU64::new(self.reorg_period); let tip_tree = self .merkle_tree_hook - .tree(reorg_period) + .tree(&self.reorg_period) .await .expect("failed to get merkle tree"); // This function is only called after we have already checked that the diff --git a/rust/chains/hyperlane-cosmos/Cargo.toml b/rust/main/chains/hyperlane-cosmos/Cargo.toml similarity index 89% rename from rust/chains/hyperlane-cosmos/Cargo.toml rename to rust/main/chains/hyperlane-cosmos/Cargo.toml index f1caaeec0..ce936ec1f 100644 --- a/rust/chains/hyperlane-cosmos/Cargo.toml +++ b/rust/main/chains/hyperlane-cosmos/Cargo.toml @@ -1,4 +1,3 @@ -cargo-features = ["workspace-inheritance"] [package] name = "hyperlane-cosmos" @@ -15,11 +14,12 @@ base64 = { workspace = true } bech32 = { workspace = true } cosmrs = { workspace = true, features = ["cosmwasm", "tokio", "grpc", "rpc"] } cosmwasm-std = { workspace = true } +crypto = { path = "../../utils/crypto" } derive-new = { workspace = true } futures = { workspace = true } hex = { workspace = true } http = { workspace = true } -hyperlane-core = { path = "../../hyperlane-core", features = ["async"]} +hyperlane-core = { path = "../../hyperlane-core", features = ["async"] } hyperlane-cosmwasm-interface.workspace = true hyper = { workspace = true } hyper-tls = { workspace = true } @@ -38,7 +38,12 @@ tendermint-rpc = { workspace = true } time = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } -tonic = { workspace = true, features = ["transport", "tls", "tls-roots","tls-roots-common"] } +tonic = { workspace = true, features = [ + "transport", + "tls", + "tls-roots", + "tls-roots-common", +] } tracing = { workspace = true } tracing-futures = { workspace = true } url = { workspace = true } diff --git a/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs b/rust/main/chains/hyperlane-cosmos/src/aggregation_ism.rs similarity index 96% rename from rust/chains/hyperlane-cosmos/src/aggregation_ism.rs rename to rust/main/chains/hyperlane-cosmos/src/aggregation_ism.rs index df41c440b..d9a1f5391 100644 --- a/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs +++ b/rust/main/chains/hyperlane-cosmos/src/aggregation_ism.rs @@ -33,7 +33,7 @@ impl CosmosAggregationIsm { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; @@ -63,6 +63,7 @@ impl HyperlaneChain for CosmosAggregationIsm { #[async_trait] impl AggregationIsm for CosmosAggregationIsm { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue #[instrument(err)] async fn modules_and_threshold( &self, diff --git a/rust/chains/hyperlane-cosmos/src/error.rs b/rust/main/chains/hyperlane-cosmos/src/error.rs similarity index 75% rename from rust/chains/hyperlane-cosmos/src/error.rs rename to rust/main/chains/hyperlane-cosmos/src/error.rs index abe61fd92..de5e2f019 100644 --- a/rust/chains/hyperlane-cosmos/src/error.rs +++ b/rust/main/chains/hyperlane-cosmos/src/error.rs @@ -1,6 +1,9 @@ +use std::fmt::Debug; + use cosmrs::proto::prost; + +use crypto::PublicKeyError; use hyperlane_core::ChainCommunicationError; -use std::fmt::Debug; /// Errors from the crates specific to the hyperlane-cosmos /// implementation. @@ -50,9 +53,24 @@ pub enum HyperlaneCosmosError { /// Public key error #[error("{0}")] PublicKeyError(String), + /// Address error + #[error("{0}")] + AddressError(String), /// Signer info error #[error("{0}")] SignerInfoError(String), + /// Serde error + #[error("{0}")] + SerdeError(#[from] serde_json::Error), + /// Empty error + #[error("{0}")] + UnparsableEmptyField(String), + /// Parsing error + #[error("{0}")] + ParsingFailed(String), + /// Parsing attempt failed + #[error("Parsing attempt failed. (Errors: {0:?})")] + ParsingAttemptsFailed(Vec), } impl From for ChainCommunicationError { @@ -60,3 +78,9 @@ impl From for ChainCommunicationError { ChainCommunicationError::from_other(value) } } + +impl From for HyperlaneCosmosError { + fn from(value: PublicKeyError) -> Self { + HyperlaneCosmosError::PublicKeyError(value.to_string()) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs b/rust/main/chains/hyperlane-cosmos/src/interchain_gas.rs similarity index 89% rename from rust/chains/hyperlane-cosmos/src/interchain_gas.rs rename to rust/main/chains/hyperlane-cosmos/src/interchain_gas.rs index 7b9e68af8..1e1cb2b32 100644 --- a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs +++ b/rust/main/chains/hyperlane-cosmos/src/interchain_gas.rs @@ -1,25 +1,24 @@ +use std::ops::RangeInclusive; + use async_trait::async_trait; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use once_cell::sync::Lazy; +use tendermint::abci::EventAttribute; +use tracing::instrument; + use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, InterchainGasPaymaster, - InterchainGasPayment, LogMeta, SequenceAwareIndexer, H256, U256, + InterchainGasPayment, LogMeta, SequenceAwareIndexer, H256, H512, U256, }; -use once_cell::sync::Lazy; -use std::ops::RangeInclusive; -use tendermint::abci::EventAttribute; -use tracing::instrument; -use crate::utils::parse_logs_in_range; -use crate::{ - rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}, - signers::Signer, - utils::{ - execute_and_parse_log_futures, CONTRACT_ADDRESS_ATTRIBUTE_KEY, - CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, - }, - ConnectionConf, CosmosProvider, HyperlaneCosmosError, +use crate::rpc::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; +use crate::signers::Signer; +use crate::utils::{ + execute_and_parse_log_futures, parse_logs_in_range, parse_logs_in_tx, + CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, }; +use crate::{ConnectionConf, CosmosProvider, HyperlaneCosmosError}; /// A reference to a InterchainGasPaymaster contract on some Cosmos chain #[derive(Debug)] @@ -57,7 +56,7 @@ impl CosmosInterchainGasPaymaster { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; @@ -90,12 +89,12 @@ static DESTINATION_ATTRIBUTE_KEY_BASE64: Lazy = /// A reference to a InterchainGasPaymasterIndexer contract on some Cosmos chain #[derive(Debug, Clone)] pub struct CosmosInterchainGasPaymasterIndexer { - indexer: Box, + provider: Box, } impl CosmosInterchainGasPaymasterIndexer { /// The interchain gas payment event type from the CW contract. - const INTERCHAIN_GAS_PAYMENT_EVENT_TYPE: &str = "igp-core-pay-for-gas"; + const INTERCHAIN_GAS_PAYMENT_EVENT_TYPE: &'static str = "igp-core-pay-for-gas"; /// create new Cosmos InterchainGasPaymasterIndexer agent pub fn new( @@ -103,7 +102,7 @@ impl CosmosInterchainGasPaymasterIndexer { locator: ContractLocator, reorg_period: u32, ) -> ChainResult { - let indexer = CosmosWasmIndexer::new( + let provider = CosmosWasmRpcProvider::new( conf, locator, Self::INTERCHAIN_GAS_PAYMENT_EVENT_TYPE.into(), @@ -111,7 +110,7 @@ impl CosmosInterchainGasPaymasterIndexer { )?; Ok(Self { - indexer: Box::new(indexer), + provider: Box::new(provider), }) } @@ -211,7 +210,7 @@ impl Indexer for CosmosInterchainGasPaymasterIndexer { ) -> ChainResult, LogMeta)>> { let logs_futures = parse_logs_in_range( range, - self.indexer.clone(), + self.provider.clone(), Self::interchain_gas_payment_parser, "InterchainGasPaymentCursor", ); @@ -220,7 +219,21 @@ impl Indexer for CosmosInterchainGasPaymasterIndexer { } async fn get_finalized_block_number(&self) -> ChainResult { - self.indexer.get_finalized_block_number().await + self.provider.get_finalized_block_number().await + } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + parse_logs_in_tx( + &tx_hash.into(), + self.provider.clone(), + Self::interchain_gas_payment_parser, + "InterchainGasPaymentReceiver", + ) + .await + .map(|v| v.into_iter().map(|(m, l)| (m.into(), l)).collect()) } } @@ -269,10 +282,12 @@ impl TryInto for IncompleteInterchainGasPayment { #[cfg(test)] mod tests { - use hyperlane_core::{InterchainGasPayment, H256, U256}; use std::str::FromStr; - use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; + use hyperlane_core::{InterchainGasPayment, H256, U256}; + + use crate::providers::rpc::ParsedEvent; + use crate::utils::event_attributes_from_str; use super::*; diff --git a/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs b/rust/main/chains/hyperlane-cosmos/src/interchain_security_module.rs similarity index 99% rename from rust/chains/hyperlane-cosmos/src/interchain_security_module.rs rename to rust/main/chains/hyperlane-cosmos/src/interchain_security_module.rs index dd495be89..e23807a58 100644 --- a/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs +++ b/rust/main/chains/hyperlane-cosmos/src/interchain_security_module.rs @@ -38,7 +38,7 @@ impl CosmosInterchainSecurityModule { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; diff --git a/rust/chains/hyperlane-cosmos/src/lib.rs b/rust/main/chains/hyperlane-cosmos/src/lib.rs similarity index 92% rename from rust/chains/hyperlane-cosmos/src/lib.rs rename to rust/main/chains/hyperlane-cosmos/src/lib.rs index c0ce3ad54..64f9c96b8 100644 --- a/rust/chains/hyperlane-cosmos/src/lib.rs +++ b/rust/main/chains/hyperlane-cosmos/src/lib.rs @@ -4,6 +4,7 @@ #![warn(missing_docs)] // TODO: Remove once we start filling things in #![allow(unused_variables)] +#![allow(unused_imports)] // TODO: `rustc` 1.80.1 clippy issue mod aggregation_ism; mod error; diff --git a/rust/main/chains/hyperlane-cosmos/src/libs/account.rs b/rust/main/chains/hyperlane-cosmos/src/libs/account.rs new file mode 100644 index 000000000..d29afd1d0 --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/libs/account.rs @@ -0,0 +1,94 @@ +use cosmrs::{crypto::PublicKey, AccountId}; +use hyperlane_cosmwasm_interface::types::keccak256_hash; +use tendermint::account::Id as TendermintAccountId; +use tendermint::public_key::PublicKey as TendermintPublicKey; + +use crypto::decompress_public_key; +use hyperlane_core::Error::Overflow; +use hyperlane_core::{AccountAddressType, ChainCommunicationError, ChainResult, H256}; + +use crate::HyperlaneCosmosError; + +pub(crate) struct CosmosAccountId<'a> { + account_id: &'a AccountId, +} + +impl<'a> CosmosAccountId<'a> { + pub fn new(account_id: &'a AccountId) -> Self { + Self { account_id } + } + + /// Calculate AccountId from public key depending on provided prefix + pub fn account_id_from_pubkey( + pub_key: PublicKey, + prefix: &str, + account_address_type: &AccountAddressType, + ) -> ChainResult { + match account_address_type { + AccountAddressType::Bitcoin => Self::bitcoin_style(pub_key, prefix), + AccountAddressType::Ethereum => Self::ethereum_style(pub_key, prefix), + } + } + + /// Returns a Bitcoin style address: RIPEMD160(SHA256(pubkey)) + /// Source: `` + fn bitcoin_style(pub_key: PublicKey, prefix: &str) -> ChainResult { + // Get the inner type + let tendermint_pub_key = TendermintPublicKey::from(pub_key); + // Get the RIPEMD160(SHA256(pub_key)) + let tendermint_id = TendermintAccountId::from(tendermint_pub_key); + // Bech32 encoding + let account_id = AccountId::new(prefix, tendermint_id.as_bytes()) + .map_err(Into::::into)?; + + Ok(account_id) + } + + /// Returns an Ethereum style address: KECCAK256(pubkey)[20] + /// Parameter `pub_key` is a compressed public key. + fn ethereum_style(pub_key: PublicKey, prefix: &str) -> ChainResult { + let decompressed_public_key = decompress_public_key(&pub_key.to_bytes()) + .map_err(Into::::into)?; + + let hash = keccak256_hash(&decompressed_public_key[1..]); + + let mut bytes = [0u8; 20]; + bytes.copy_from_slice(&hash.as_slice()[12..]); + + let account_id = + AccountId::new(prefix, bytes.as_slice()).map_err(Into::::into)?; + + Ok(account_id) + } +} + +impl TryFrom<&CosmosAccountId<'_>> for H256 { + type Error = HyperlaneCosmosError; + + /// Builds a H256 digest from a cosmos AccountId (Bech32 encoding) + fn try_from(account_id: &CosmosAccountId) -> Result { + let bytes = account_id.account_id.to_bytes(); + let h256_len = H256::len_bytes(); + let Some(start_point) = h256_len.checked_sub(bytes.len()) else { + // input is too large to fit in a H256 + let msg = "account address is too large to fit it a H256"; + return Err(HyperlaneCosmosError::AddressError(msg.to_owned())); + }; + let mut empty_hash = H256::default(); + let result = empty_hash.as_bytes_mut(); + result[start_point..].copy_from_slice(bytes.as_slice()); + Ok(H256::from_slice(result)) + } +} + +impl TryFrom> for H256 { + type Error = HyperlaneCosmosError; + + /// Builds a H256 digest from a cosmos AccountId (Bech32 encoding) + fn try_from(account_id: CosmosAccountId) -> Result { + (&account_id).try_into() + } +} + +#[cfg(test)] +mod tests; diff --git a/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs b/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs new file mode 100644 index 000000000..0ba8f73d7 --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/libs/account/tests.rs @@ -0,0 +1,74 @@ +use cosmrs::crypto::PublicKey; +use cosmwasm_std::HexBinary; + +use crypto::decompress_public_key; +use hyperlane_core::AccountAddressType; +use AccountAddressType::{Bitcoin, Ethereum}; + +use crate::CosmosAccountId; + +const COMPRESSED_PUBLIC_KEY: &str = + "02962d010010b6eec66846322704181570d89e28236796579c535d2e44d20931f4"; +const INJECTIVE_ADDRESS: &str = "inj1m6ada382hfuxvuke4h9p4uswhn2qcca7mlg0dr"; +const NEUTRON_ADDRESS: &str = "neutron1mydju5alsmhnfsawy0j4lyns70l7qukgdgy45w"; + +#[test] +fn test_account_id() { + // given + let pub_key = compressed_public_key(); + + // when + let neutron_account_id = + CosmosAccountId::account_id_from_pubkey(pub_key, "neutron", &Bitcoin).unwrap(); + let injective_account_id = + CosmosAccountId::account_id_from_pubkey(pub_key, "inj", &Ethereum).unwrap(); + + // then + assert_eq!(neutron_account_id.as_ref(), NEUTRON_ADDRESS); + assert_eq!(injective_account_id.as_ref(), INJECTIVE_ADDRESS); +} + +#[test] +fn test_bitcoin_style() { + // given + let compressed = compressed_public_key(); + let decompressed = decompressed_public_key(); + + // when + let from_compressed = CosmosAccountId::bitcoin_style(compressed, "neutron").unwrap(); + let from_decompressed = CosmosAccountId::bitcoin_style(decompressed, "neutron").unwrap(); + + // then + assert_eq!(from_compressed.as_ref(), NEUTRON_ADDRESS); + assert_eq!(from_decompressed.as_ref(), NEUTRON_ADDRESS); +} + +#[test] +fn test_ethereum_style() { + // given + let compressed = compressed_public_key(); + let decompressed = decompressed_public_key(); + + // when + let from_compressed = CosmosAccountId::ethereum_style(compressed, "inj").unwrap(); + let from_decompressed = CosmosAccountId::ethereum_style(decompressed, "inj").unwrap(); + + // then + assert_eq!(from_compressed.as_ref(), INJECTIVE_ADDRESS); + assert_eq!(from_decompressed.as_ref(), INJECTIVE_ADDRESS); +} + +fn compressed_public_key() -> PublicKey { + let hex = hex::decode(COMPRESSED_PUBLIC_KEY).unwrap(); + let tendermint = tendermint::PublicKey::from_raw_secp256k1(&hex).unwrap(); + let pub_key = PublicKey::from(tendermint); + pub_key +} + +fn decompressed_public_key() -> PublicKey { + let hex = hex::decode(COMPRESSED_PUBLIC_KEY).unwrap(); + let decompressed = decompress_public_key(&hex).unwrap(); + let tendermint = tendermint::PublicKey::from_raw_secp256k1(&decompressed).unwrap(); + let pub_key = PublicKey::from(tendermint); + pub_key +} diff --git a/rust/chains/hyperlane-cosmos/src/libs/address.rs b/rust/main/chains/hyperlane-cosmos/src/libs/address.rs similarity index 81% rename from rust/chains/hyperlane-cosmos/src/libs/address.rs rename to rust/main/chains/hyperlane-cosmos/src/libs/address.rs index 41a5ab6ab..e538e1c3b 100644 --- a/rust/chains/hyperlane-cosmos/src/libs/address.rs +++ b/rust/main/chains/hyperlane-cosmos/src/libs/address.rs @@ -5,10 +5,11 @@ use cosmrs::{ AccountId, }; use derive_new::new; -use hyperlane_core::{ChainCommunicationError, ChainResult, Error::Overflow, H256}; +use hyperlane_core::{ + AccountAddressType, ChainCommunicationError, ChainResult, Error::Overflow, H256, +}; -use crate::libs::account::CosmosAccountId; -use crate::HyperlaneCosmosError; +use crate::{CosmosAccountId, HyperlaneCosmosError}; /// Wrapper around the cosmrs AccountId type that abstracts bech32 encoding #[derive(new, Debug, Clone)] @@ -20,23 +21,19 @@ pub struct CosmosAddress { } impl CosmosAddress { - /// Returns a Bitcoin style address: RIPEMD160(SHA256(pubkey)) - /// Source: `` - pub fn from_pubkey(pubkey: PublicKey, prefix: &str) -> ChainResult { - let account_id = CosmosAccountId::account_id_from_pubkey(pubkey, prefix)?; - Self::from_account_id(account_id) - } - /// Creates a wrapper around a cosmrs AccountId from a private key byte array - pub fn from_privkey(priv_key: &[u8], prefix: &str) -> ChainResult { + pub fn from_privkey( + priv_key: &[u8], + prefix: &str, + account_address_type: &AccountAddressType, + ) -> ChainResult { let pubkey = SigningKey::from_slice(priv_key) .map_err(Into::::into)? .public_key(); - Self::from_pubkey(pubkey, prefix) + Self::from_pubkey(pubkey, prefix, account_address_type) } - /// Returns a Bitcoin style address calculated from Bech32 encoding - /// Source: `` + /// Returns an account address calculated from Bech32 encoding pub fn from_account_id(account_id: AccountId) -> ChainResult { // Hex digest let digest = H256::try_from(&CosmosAccountId::new(&account_id))?; @@ -77,13 +74,25 @@ impl CosmosAddress { pub fn digest(&self) -> H256 { self.digest } + + /// Calculates an account address depending on prefix and account address type + fn from_pubkey( + pubkey: PublicKey, + prefix: &str, + account_address_type: &AccountAddressType, + ) -> ChainResult { + let account_id = + CosmosAccountId::account_id_from_pubkey(pubkey, prefix, account_address_type)?; + Self::from_account_id(account_id) + } } impl TryFrom<&CosmosAddress> for H256 { type Error = ChainCommunicationError; fn try_from(cosmos_address: &CosmosAddress) -> Result { - CosmosAccountId::new(&cosmos_address.account_id).try_into() + H256::try_from(CosmosAccountId::new(&cosmos_address.account_id)) + .map_err(Into::::into) } } @@ -119,8 +128,9 @@ pub mod test { let hex_key = "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26"; let key = hex_or_base58_to_h256(hex_key).unwrap(); let prefix = "neutron"; - let addr = CosmosAddress::from_privkey(key.as_bytes(), prefix) - .expect("Cosmos address creation failed"); + let addr = + CosmosAddress::from_privkey(key.as_bytes(), prefix, &AccountAddressType::Bitcoin) + .expect("Cosmos address creation failed"); assert_eq!( addr.address(), "neutron1kknekjxg0ear00dky5ykzs8wwp2gz62z9s6aaj" diff --git a/rust/chains/hyperlane-cosmos/src/libs/mod.rs b/rust/main/chains/hyperlane-cosmos/src/libs/mod.rs similarity index 61% rename from rust/chains/hyperlane-cosmos/src/libs/mod.rs rename to rust/main/chains/hyperlane-cosmos/src/libs/mod.rs index d641b041a..0ee55478e 100644 --- a/rust/chains/hyperlane-cosmos/src/libs/mod.rs +++ b/rust/main/chains/hyperlane-cosmos/src/libs/mod.rs @@ -1,4 +1,8 @@ +pub(crate) use account::CosmosAccountId; +pub(crate) use address::CosmosAddress; + /// This module contains conversions from Cosmos AccountId to H56 -pub(crate) mod account; +mod account; + /// This module contains all the verification variables the libraries used by the Hyperlane Cosmos chain. -pub mod address; +mod address; diff --git a/rust/chains/hyperlane-cosmos/src/mailbox.rs b/rust/main/chains/hyperlane-cosmos/src/mailbox.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/mailbox.rs rename to rust/main/chains/hyperlane-cosmos/src/mailbox.rs diff --git a/rust/chains/hyperlane-cosmos/src/mailbox/contract.rs b/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs similarity index 86% rename from rust/chains/hyperlane-cosmos/src/mailbox/contract.rs rename to rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs index df63f0c06..5a998aac5 100644 --- a/rust/chains/hyperlane-cosmos/src/mailbox/contract.rs +++ b/rust/main/chains/hyperlane-cosmos/src/mailbox/contract.rs @@ -8,18 +8,17 @@ use tracing::instrument; use hyperlane_core::{ utils::bytes_to_hex, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Mailbox, RawHyperlaneMessage, - TxCostEstimate, TxOutcome, H256, U256, + ReorgPeriod, TxCostEstimate, TxOutcome, H256, U256, }; -use crate::address::CosmosAddress; use crate::grpc::WasmProvider; use crate::payloads::general; use crate::payloads::mailbox::{ GeneralMailboxQuery, ProcessMessageRequest, ProcessMessageRequestInner, }; use crate::types::tx_response_to_outcome; -use crate::utils::get_block_height_for_lag; -use crate::{payloads, ConnectionConf, CosmosProvider, Signer}; +use crate::utils::get_block_height_for_reorg_period; +use crate::{payloads, ConnectionConf, CosmosAddress, CosmosProvider, Signer}; #[derive(Clone, Debug)] /// A reference to a Mailbox contract on some Cosmos chain @@ -41,7 +40,7 @@ impl CosmosMailbox { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; @@ -82,12 +81,15 @@ impl HyperlaneChain for CosmosMailbox { #[async_trait] impl Mailbox for CosmosMailbox { #[instrument(level = "debug", err, ret, skip(self))] - async fn count(&self, lag: Option) -> ChainResult { - let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + let block_height = + get_block_height_for_reorg_period(self.provider.grpc(), reorg_period).await?; self.nonce_at_block(block_height).await } #[instrument(level = "debug", err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn delivered(&self, id: H256) -> ChainResult { let id = hex::encode(id); let payload = payloads::mailbox::DeliveredRequest { @@ -105,6 +107,7 @@ impl Mailbox for CosmosMailbox { } #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn default_ism(&self) -> ChainResult { let payload = payloads::mailbox::DefaultIsmRequest { default_ism: general::EmptyStruct {}, @@ -123,6 +126,7 @@ impl Mailbox for CosmosMailbox { } #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn recipient_ism(&self, recipient: H256) -> ChainResult { let address = CosmosAddress::from_h256( recipient, @@ -150,6 +154,7 @@ impl Mailbox for CosmosMailbox { } #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn process( &self, message: &HyperlaneMessage, @@ -172,7 +177,8 @@ impl Mailbox for CosmosMailbox { Ok(tx_response_to_outcome(response)?) } - #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] + #[instrument(err, ret, skip(self), fields(hyp_message=%message, metadata=%bytes_to_hex(metadata)))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn process_estimate_costs( &self, message: &HyperlaneMessage, diff --git a/rust/chains/hyperlane-cosmos/src/mailbox/delivery_indexer.rs b/rust/main/chains/hyperlane-cosmos/src/mailbox/delivery_indexer.rs similarity index 81% rename from rust/chains/hyperlane-cosmos/src/mailbox/delivery_indexer.rs rename to rust/main/chains/hyperlane-cosmos/src/mailbox/delivery_indexer.rs index 7279c67fe..ccc92df70 100644 --- a/rust/chains/hyperlane-cosmos/src/mailbox/delivery_indexer.rs +++ b/rust/main/chains/hyperlane-cosmos/src/mailbox/delivery_indexer.rs @@ -9,14 +9,14 @@ use tendermint::abci::EventAttribute; use tracing::instrument; use hyperlane_core::{ - ChainCommunicationError, ChainResult, ContractLocator, Delivery, Indexed, Indexer, LogMeta, - SequenceAwareIndexer, H256, + ChainCommunicationError, ChainResult, ContractLocator, Delivery, HyperlaneMessage, Indexed, + Indexer, LogMeta, SequenceAwareIndexer, H256, H512, }; -use crate::rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}; +use crate::rpc::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; use crate::utils::{ - execute_and_parse_log_futures, parse_logs_in_range, CONTRACT_ADDRESS_ATTRIBUTE_KEY, - CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, + execute_and_parse_log_futures, parse_logs_in_range, parse_logs_in_tx, + CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, }; use crate::{ConnectionConf, HyperlaneCosmosError, Signer}; @@ -28,7 +28,7 @@ static MESSAGE_ID_ATTRIBUTE_KEY_BASE64: Lazy = /// Struct that retrieves delivery event data for a Cosmos Mailbox contract pub struct CosmosMailboxDeliveryIndexer { - indexer: Box, + provider: Box, } impl CosmosMailboxDeliveryIndexer { @@ -40,7 +40,7 @@ impl CosmosMailboxDeliveryIndexer { signer: Option, reorg_period: u32, ) -> ChainResult { - let indexer = CosmosWasmIndexer::new( + let provider = CosmosWasmRpcProvider::new( conf, locator, MESSAGE_DELIVERY_EVENT_TYPE.to_owned(), @@ -48,7 +48,7 @@ impl CosmosMailboxDeliveryIndexer { )?; Ok(Self { - indexer: Box::new(indexer), + provider: Box::new(provider), }) } @@ -114,7 +114,7 @@ impl Indexer for CosmosMailboxDeliveryIndexer { ) -> ChainResult, LogMeta)>> { let logs_futures = parse_logs_in_range( range, - self.indexer.clone(), + self.provider.clone(), Self::hyperlane_delivery_parser, "DeliveryCursor", ); @@ -123,7 +123,21 @@ impl Indexer for CosmosMailboxDeliveryIndexer { } async fn get_finalized_block_number(&self) -> ChainResult { - self.indexer.get_finalized_block_number().await + self.provider.get_finalized_block_number().await + } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + parse_logs_in_tx( + &tx_hash.into(), + self.provider.clone(), + Self::hyperlane_delivery_parser, + "DeliveryReceiver", + ) + .await + .map(|v| v.into_iter().map(|(m, l)| (m.into(), l)).collect()) } } diff --git a/rust/chains/hyperlane-cosmos/src/mailbox/dispatch_indexer.rs b/rust/main/chains/hyperlane-cosmos/src/mailbox/dispatch_indexer.rs similarity index 88% rename from rust/chains/hyperlane-cosmos/src/mailbox/dispatch_indexer.rs rename to rust/main/chains/hyperlane-cosmos/src/mailbox/dispatch_indexer.rs index 77fc6579c..433ea661e 100644 --- a/rust/chains/hyperlane-cosmos/src/mailbox/dispatch_indexer.rs +++ b/rust/main/chains/hyperlane-cosmos/src/mailbox/dispatch_indexer.rs @@ -9,13 +9,13 @@ use tracing::instrument; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, Decode, HyperlaneMessage, Indexed, - Indexer, LogMeta, SequenceAwareIndexer, + Indexer, LogMeta, SequenceAwareIndexer, H512, }; -use crate::rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}; +use crate::rpc::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; use crate::utils::{ - execute_and_parse_log_futures, parse_logs_in_range, CONTRACT_ADDRESS_ATTRIBUTE_KEY, - CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, + execute_and_parse_log_futures, parse_logs_in_range, parse_logs_in_tx, + CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, }; use crate::{ConnectionConf, CosmosMailbox, HyperlaneCosmosError, Signer}; @@ -29,7 +29,7 @@ static MESSAGE_ATTRIBUTE_KEY_BASE64: Lazy = #[derive(Debug, Clone)] pub struct CosmosMailboxDispatchIndexer { mailbox: CosmosMailbox, - indexer: Box, + provider: Box, } impl CosmosMailboxDispatchIndexer { @@ -42,7 +42,7 @@ impl CosmosMailboxDispatchIndexer { reorg_period: u32, ) -> ChainResult { let mailbox = CosmosMailbox::new(conf.clone(), locator.clone(), signer.clone())?; - let indexer = CosmosWasmIndexer::new( + let provider = CosmosWasmRpcProvider::new( conf, locator, MESSAGE_DISPATCH_EVENT_TYPE.into(), @@ -51,7 +51,7 @@ impl CosmosMailboxDispatchIndexer { Ok(Self { mailbox, - indexer: Box::new(indexer), + provider: Box::new(provider), }) } @@ -116,7 +116,7 @@ impl Indexer for CosmosMailboxDispatchIndexer { ) -> ChainResult, LogMeta)>> { let logs_futures = parse_logs_in_range( range, - self.indexer.clone(), + self.provider.clone(), Self::hyperlane_message_parser, "HyperlaneMessageCursor", ); @@ -125,7 +125,21 @@ impl Indexer for CosmosMailboxDispatchIndexer { } async fn get_finalized_block_number(&self) -> ChainResult { - self.indexer.get_finalized_block_number().await + self.provider.get_finalized_block_number().await + } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + parse_logs_in_tx( + &tx_hash.into(), + self.provider.clone(), + Self::hyperlane_message_parser, + "HyperlaneMessageReceiver", + ) + .await + .map(|v| v.into_iter().map(|(m, l)| (m.into(), l)).collect()) } } @@ -144,7 +158,8 @@ impl SequenceAwareIndexer for CosmosMailboxDispatchIndexer { mod tests { use hyperlane_core::HyperlaneMessage; - use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; + use crate::providers::rpc::ParsedEvent; + use crate::utils::event_attributes_from_str; use super::*; diff --git a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs b/rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs similarity index 82% rename from rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs rename to rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs index 35fe73a23..b9acdd357 100644 --- a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-cosmos/src/merkle_tree_hook.rs @@ -2,27 +2,26 @@ use std::{fmt::Debug, num::NonZeroU64, ops::RangeInclusive, str::FromStr}; use async_trait::async_trait; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; -use hyperlane_core::{ - accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, - ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, - Indexed, Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, -}; use once_cell::sync::Lazy; use tendermint::abci::EventAttribute; use tracing::instrument; -use crate::utils::parse_logs_in_range; -use crate::{ - grpc::WasmProvider, - payloads::{general, merkle_tree_hook}, - rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}, - utils::{ - execute_and_parse_log_futures, get_block_height_for_lag, CONTRACT_ADDRESS_ATTRIBUTE_KEY, - CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, - }, - ConnectionConf, CosmosProvider, HyperlaneCosmosError, Signer, +use hyperlane_core::accumulator::incremental::IncrementalMerkle; +use hyperlane_core::{ + ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, + HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, LogMeta, + MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, SequenceAwareIndexer, H256, H512, }; +use crate::grpc::WasmProvider; +use crate::payloads::{general, merkle_tree_hook}; +use crate::rpc::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; +use crate::utils::{ + execute_and_parse_log_futures, get_block_height_for_reorg_period, parse_logs_in_range, + parse_logs_in_tx, CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64, +}; +use crate::{ConnectionConf, CosmosProvider, HyperlaneCosmosError, Signer}; + #[derive(Debug, Clone)] /// A reference to a MerkleTreeHook contract on some Cosmos chain pub struct CosmosMerkleTreeHook { @@ -44,7 +43,7 @@ impl CosmosMerkleTreeHook { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; @@ -76,12 +75,14 @@ impl HyperlaneChain for CosmosMerkleTreeHook { impl MerkleTreeHook for CosmosMerkleTreeHook { /// Return the incremental merkle tree in storage #[instrument(level = "debug", err, ret, skip(self))] - async fn tree(&self, lag: Option) -> ChainResult { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn tree(&self, reorg_period: &ReorgPeriod) -> ChainResult { let payload = merkle_tree_hook::MerkleTreeRequest { tree: general::EmptyStruct {}, }; - let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; + let block_height = + get_block_height_for_reorg_period(self.provider.grpc(), reorg_period).await?; let data = self .provider @@ -110,23 +111,26 @@ impl MerkleTreeHook for CosmosMerkleTreeHook { } /// Gets the current leaf count of the merkle tree - async fn count(&self, lag: Option) -> ChainResult { + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { let payload = merkle_tree_hook::MerkleTreeCountRequest { count: general::EmptyStruct {}, }; - let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; + let block_height = + get_block_height_for_reorg_period(self.provider.grpc(), reorg_period).await?; self.count_at_block(block_height).await } #[instrument(level = "debug", err, ret, skip(self))] - async fn latest_checkpoint(&self, lag: Option) -> ChainResult { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn latest_checkpoint(&self, reorg_period: &ReorgPeriod) -> ChainResult { let payload = merkle_tree_hook::CheckPointRequest { check_point: general::EmptyStruct {}, }; - let block_height = get_block_height_for_lag(self.provider.grpc(), lag).await?; + let block_height = + get_block_height_for_reorg_period(self.provider.grpc(), reorg_period).await?; let data = self .provider @@ -187,13 +191,13 @@ pub(crate) static MESSAGE_ID_ATTRIBUTE_KEY_BASE64: Lazy = pub struct CosmosMerkleTreeHookIndexer { /// The CosmosMerkleTreeHook merkle_tree_hook: CosmosMerkleTreeHook, - /// Cosmwasm indexer instance - indexer: Box, + /// Cosmwasm RPC provider instance + provider: Box, } impl CosmosMerkleTreeHookIndexer { /// The message dispatch event type from the CW contract. - const MERKLE_TREE_INSERTION_EVENT_TYPE: &str = "hpl_hook_merkle::post_dispatch"; + const MERKLE_TREE_INSERTION_EVENT_TYPE: &'static str = "hpl_hook_merkle::post_dispatch"; /// create new Cosmos MerkleTreeHookIndexer agent pub fn new( @@ -202,7 +206,7 @@ impl CosmosMerkleTreeHookIndexer { signer: Option, reorg_period: u32, ) -> ChainResult { - let indexer = CosmosWasmIndexer::new( + let provider = CosmosWasmRpcProvider::new( conf.clone(), locator.clone(), Self::MERKLE_TREE_INSERTION_EVENT_TYPE.into(), @@ -211,7 +215,7 @@ impl CosmosMerkleTreeHookIndexer { Ok(Self { merkle_tree_hook: CosmosMerkleTreeHook::new(conf, locator, signer)?, - indexer: Box::new(indexer), + provider: Box::new(provider), }) } @@ -286,7 +290,7 @@ impl Indexer for CosmosMerkleTreeHookIndexer { ) -> ChainResult, LogMeta)>> { let logs_futures = parse_logs_in_range( range, - self.indexer.clone(), + self.provider.clone(), Self::merkle_tree_insertion_parser, "MerkleTreeInsertionCursor", ); @@ -296,7 +300,21 @@ impl Indexer for CosmosMerkleTreeHookIndexer { /// Get the chain's latest block number that has reached finality async fn get_finalized_block_number(&self) -> ChainResult { - self.indexer.get_finalized_block_number().await + self.provider.get_finalized_block_number().await + } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + parse_logs_in_tx( + &tx_hash.into(), + self.provider.clone(), + Self::merkle_tree_insertion_parser, + "MerkleTreeInsertionReceiver", + ) + .await + .map(|v| v.into_iter().map(|(m, l)| (m.into(), l)).collect()) } } @@ -336,10 +354,12 @@ impl TryInto for IncompleteMerkleTreeInsertion { #[cfg(test)] mod tests { - use hyperlane_core::H256; use std::str::FromStr; - use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; + use hyperlane_core::H256; + + use crate::providers::rpc::ParsedEvent; + use crate::utils::event_attributes_from_str; use super::*; diff --git a/rust/chains/hyperlane-cosmos/src/multisig_ism.rs b/rust/main/chains/hyperlane-cosmos/src/multisig_ism.rs similarity index 98% rename from rust/chains/hyperlane-cosmos/src/multisig_ism.rs rename to rust/main/chains/hyperlane-cosmos/src/multisig_ism.rs index d558acfa3..0b7f49234 100644 --- a/rust/chains/hyperlane-cosmos/src/multisig_ism.rs +++ b/rust/main/chains/hyperlane-cosmos/src/multisig_ism.rs @@ -30,7 +30,7 @@ impl CosmosMultisigIsm { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; diff --git a/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/general.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/general.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/general.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/general.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/ism_routes.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/ism_routes.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/mailbox.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/mailbox.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/mod.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/mod.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/mod.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/mod.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs diff --git a/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs b/rust/main/chains/hyperlane-cosmos/src/payloads/validator_announce.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs rename to rust/main/chains/hyperlane-cosmos/src/payloads/validator_announce.rs diff --git a/rust/main/chains/hyperlane-cosmos/src/providers.rs b/rust/main/chains/hyperlane-cosmos/src/providers.rs new file mode 100644 index 000000000..3c3a79fd8 --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers.rs @@ -0,0 +1,8 @@ +pub use cosmos::CosmosProvider; + +/// cosmos provider +mod cosmos; +/// cosmos grpc provider +pub mod grpc; +/// cosmos rpc provider +pub mod rpc; diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos.rs b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos.rs new file mode 100644 index 000000000..9fea74725 --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos.rs @@ -0,0 +1,3 @@ +pub use provider::CosmosProvider; + +mod provider; diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs new file mode 100644 index 000000000..2ab0388be --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider.rs @@ -0,0 +1,466 @@ +use std::str::FromStr; + +use async_trait::async_trait; +use cosmrs::cosmwasm::MsgExecuteContract; +use cosmrs::crypto::PublicKey; +use cosmrs::proto::traits::Message; +use cosmrs::tx::{MessageExt, SequenceNumber, SignerInfo, SignerPublicKey}; +use cosmrs::{proto, AccountId, Any, Coin, Tx}; +use hyperlane_core::rpc_clients::FallbackProvider; +use itertools::{any, cloned, Itertools}; +use once_cell::sync::Lazy; +use serde::{Deserialize, Serialize}; +use tendermint::hash::Algorithm; +use tendermint::Hash; +use tendermint_rpc::{client::CompatMode, Client, HttpClient}; +use time::OffsetDateTime; +use tracing::{error, warn}; + +use crypto::decompress_public_key; +use hyperlane_core::{ + bytes_to_h512, h512_to_bytes, AccountAddressType, BlockInfo, ChainCommunicationError, + ChainInfo, ChainResult, ContractLocator, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, + HyperlaneProviderError, TxnInfo, TxnReceiptInfo, H256, H512, U256, +}; + +use crate::grpc::{WasmGrpcProvider, WasmProvider}; +use crate::providers::cosmos::provider::parse::PacketData; +use crate::providers::rpc::CosmosRpcClient; +use crate::rpc_clients::CosmosFallbackProvider; +use crate::{ + ConnectionConf, CosmosAccountId, CosmosAddress, CosmosAmount, HyperlaneCosmosError, Signer, +}; + +mod parse; + +/// Exponent value for atto units (10^-18). +const ATTO_EXPONENT: u32 = 18; + +/// Injective public key type URL for protobuf Any +const INJECTIVE_PUBLIC_KEY_TYPE_URL: &str = "/injective.crypto.v1beta1.ethsecp256k1.PubKey"; + +/// Abstraction over a connection to a Cosmos chain +#[derive(Debug, Clone)] +pub struct CosmosProvider { + domain: HyperlaneDomain, + connection_conf: ConnectionConf, + grpc_provider: WasmGrpcProvider, + rpc_client: CosmosFallbackProvider, +} + +impl CosmosProvider { + /// Create a reference to a Cosmos chain + pub fn new( + domain: HyperlaneDomain, + conf: ConnectionConf, + locator: ContractLocator, + signer: Option, + ) -> ChainResult { + let gas_price = CosmosAmount::try_from(conf.get_minimum_gas_price().clone())?; + let grpc_provider = WasmGrpcProvider::new( + domain.clone(), + conf.clone(), + gas_price.clone(), + locator, + signer, + )?; + + let providers = conf + .get_rpc_urls() + .iter() + .map(CosmosRpcClient::new) + .collect::, _>>()?; + let provider = CosmosFallbackProvider::new( + FallbackProvider::builder().add_providers(providers).build(), + ); + + Ok(Self { + domain, + connection_conf: conf, + grpc_provider, + rpc_client: provider, + }) + } + + /// Get a grpc client + pub fn grpc(&self) -> &WasmGrpcProvider { + &self.grpc_provider + } + + fn search_payer_in_signer_infos( + &self, + signer_infos: &[SignerInfo], + payer: &AccountId, + ) -> ChainResult<(AccountId, SequenceNumber)> { + signer_infos + .iter() + .map(|si| self.convert_signer_info_into_account_id_and_nonce(si)) + // After the following we have a single Ok entry and, possibly, many Err entries + .filter_ok(|(a, s)| payer == a) + // If we have Ok entry, use it since it is the payer, if not, use the first entry with error + .find_or_first(|r| match r { + Ok((a, s)) => payer == a, + Err(e) => false, + }) + // If there were not any signer info with non-empty public key or no signers for the transaction, + // we get None here + .unwrap_or_else(|| Err(ChainCommunicationError::from_other_str("no signer info"))) + } + + fn convert_signer_info_into_account_id_and_nonce( + &self, + signer_info: &SignerInfo, + ) -> ChainResult<(AccountId, SequenceNumber)> { + let signer_public_key = signer_info.public_key.clone().ok_or_else(|| { + HyperlaneCosmosError::PublicKeyError("no public key for default signer".to_owned()) + })?; + + let (key, account_address_type) = self.normalize_public_key(signer_public_key)?; + let public_key = PublicKey::try_from(key)?; + + let account_id = CosmosAccountId::account_id_from_pubkey( + public_key, + &self.connection_conf.get_bech32_prefix(), + &account_address_type, + )?; + + Ok((account_id, signer_info.sequence)) + } + + fn normalize_public_key( + &self, + signer_public_key: SignerPublicKey, + ) -> ChainResult<(SignerPublicKey, AccountAddressType)> { + let public_key_and_account_address_type = match signer_public_key { + SignerPublicKey::Single(pk) => (SignerPublicKey::from(pk), AccountAddressType::Bitcoin), + SignerPublicKey::LegacyAminoMultisig(pk) => { + (SignerPublicKey::from(pk), AccountAddressType::Bitcoin) + } + SignerPublicKey::Any(pk) => { + if pk.type_url != PublicKey::ED25519_TYPE_URL + && pk.type_url != PublicKey::SECP256K1_TYPE_URL + && pk.type_url != INJECTIVE_PUBLIC_KEY_TYPE_URL + { + let msg = format!( + "can only normalize public keys with a known TYPE_URL: {}, {}, {}", + PublicKey::ED25519_TYPE_URL, + PublicKey::SECP256K1_TYPE_URL, + INJECTIVE_PUBLIC_KEY_TYPE_URL + ); + warn!(pk.type_url, msg); + Err(HyperlaneCosmosError::PublicKeyError(msg.to_owned()))? + } + + let (pub_key, account_address_type) = + if pk.type_url == INJECTIVE_PUBLIC_KEY_TYPE_URL { + let any = Any { + type_url: PublicKey::SECP256K1_TYPE_URL.to_owned(), + value: pk.value, + }; + + let proto = proto::cosmos::crypto::secp256k1::PubKey::from_any(&any) + .map_err(Into::::into)?; + + let decompressed = decompress_public_key(&proto.key) + .map_err(|e| HyperlaneCosmosError::PublicKeyError(e.to_string()))?; + + let tendermint = tendermint::PublicKey::from_raw_secp256k1(&decompressed) + .ok_or_else(|| { + HyperlaneCosmosError::PublicKeyError( + "cannot create tendermint public key".to_owned(), + ) + })?; + + (PublicKey::from(tendermint), AccountAddressType::Ethereum) + } else { + (PublicKey::try_from(pk)?, AccountAddressType::Bitcoin) + }; + + (SignerPublicKey::Single(pub_key), account_address_type) + } + }; + + Ok(public_key_and_account_address_type) + } + + /// Calculates the sender and the nonce for the transaction. + /// We use `payer` of the fees as the sender of the transaction, and we search for `payer` + /// signature information to find the nonce. + /// If `payer` is not specified, we use the account which signed the transaction first, as + /// the sender. + fn sender_and_nonce(&self, tx: &Tx) -> ChainResult<(H256, SequenceNumber)> { + let (sender, nonce) = tx + .auth_info + .fee + .payer + .as_ref() + .map(|payer| self.search_payer_in_signer_infos(&tx.auth_info.signer_infos, payer)) + .map_or_else( + || { + #[allow(clippy::get_first)] // TODO: `rustc` 1.80.1 clippy issue + let signer_info = tx.auth_info.signer_infos.get(0).ok_or_else(|| { + HyperlaneCosmosError::SignerInfoError( + "no signer info in default signer".to_owned(), + ) + })?; + self.convert_signer_info_into_account_id_and_nonce(signer_info) + }, + |p| p, + ) + .map(|(a, n)| CosmosAddress::from_account_id(a).map(|a| (a.digest(), n)))??; + Ok((sender, nonce)) + } + + /// Extract contract address from transaction. + fn contract(tx: &Tx, tx_hash: &H256) -> ChainResult { + // We merge two error messages together so that both of them are reported + match Self::contract_address_from_msg_execute_contract(tx) { + Ok(contract) => Ok(contract), + Err(msg_execute_contract_error) => { + match Self::contract_address_from_msg_recv_packet(tx) { + Ok(contract) => Ok(contract), + Err(msg_recv_packet_error) => { + let errors = vec![msg_execute_contract_error, msg_recv_packet_error]; + let error = HyperlaneCosmosError::ParsingAttemptsFailed(errors); + warn!(?tx_hash, ?error); + Err(ChainCommunicationError::from_other(error))? + } + } + } + } + } + + /// Assumes that there is only one `MsgExecuteContract` message in the transaction + fn contract_address_from_msg_execute_contract(tx: &Tx) -> Result { + use cosmrs::proto::cosmwasm::wasm::v1::MsgExecuteContract as ProtoMsgExecuteContract; + + let contract_execution_messages = tx + .body + .messages + .iter() + .filter(|a| a.type_url == "/cosmwasm.wasm.v1.MsgExecuteContract") + .cloned() + .collect::>(); + + let contract_execution_messages_len = contract_execution_messages.len(); + if contract_execution_messages_len > 1 { + let msg = "transaction contains multiple contract execution messages"; + Err(HyperlaneCosmosError::ParsingFailed(msg.to_owned()))? + } + + let any = contract_execution_messages.first().ok_or_else(|| { + let msg = "could not find contract execution message"; + HyperlaneCosmosError::ParsingFailed(msg.to_owned()) + })?; + let proto = + ProtoMsgExecuteContract::from_any(any).map_err(Into::::into)?; + let msg = MsgExecuteContract::try_from(proto)?; + let contract = H256::try_from(CosmosAccountId::new(&msg.contract))?; + + Ok(contract) + } + + fn contract_address_from_msg_recv_packet(tx: &Tx) -> Result { + let packet_data = tx + .body + .messages + .iter() + .filter(|a| a.type_url == "/ibc.core.channel.v1.MsgRecvPacket") + .map(PacketData::try_from) + .flat_map(|r| r.ok()) + .next() + .ok_or_else(|| { + let msg = "could not find IBC receive packets message containing receiver address"; + HyperlaneCosmosError::ParsingFailed(msg.to_owned()) + })?; + + let account_id = AccountId::from_str(&packet_data.receiver)?; + let address = H256::try_from(CosmosAccountId::new(&account_id))?; + + Ok(address) + } + + /// Reports if transaction contains fees expressed in unsupported denominations + /// The only denomination we support at the moment is the one we express gas minimum price + /// in the configuration of a chain. If fees contain an entry in a different denomination, + /// we report it in the logs. + fn report_unsupported_denominations(&self, tx: &Tx, tx_hash: &H256) -> ChainResult<()> { + let supported_denomination = self.connection_conf.get_minimum_gas_price().denom; + let unsupported_denominations = tx + .auth_info + .fee + .amount + .iter() + .filter(|c| c.denom.as_ref() != supported_denomination) + .map(|c| c.denom.as_ref()) + .fold("".to_string(), |acc, denom| acc + ", " + denom); + + if !unsupported_denominations.is_empty() { + let msg = "transaction contains fees in unsupported denominations, manual intervention is required"; + warn!( + ?tx_hash, + ?supported_denomination, + ?unsupported_denominations, + msg, + ); + Err(ChainCommunicationError::CustomError(msg.to_owned()))? + } + + Ok(()) + } + + /// Converts fees to a common denomination if necessary. + /// + /// Currently, we support Injective, Neutron and Osmosis. Fees in Injective are usually + /// expressed in `inj` which is 10^-18 of `INJ`, while fees in Neutron and Osmosis are + /// usually expressed in `untrn` and `uosmo`, respectively, which are 10^-6 of corresponding + /// `NTRN` and `OSMO`. + /// + /// This function will convert fees expressed in `untrn` and `uosmo` to 10^-18 of `NTRN` and + /// `OSMO` and it will keep fees expressed in `inj` as is. + /// + /// If fees are expressed in an unsupported denomination, they will be ignored. + fn convert_fee(&self, coin: &Coin) -> U256 { + let native_token = self.connection_conf.get_native_token(); + + if coin.denom.as_ref() != native_token.denom { + return U256::zero(); + } + + let exponent = ATTO_EXPONENT - native_token.decimals; + let coefficient = U256::from(10u128.pow(exponent)); + + let amount_in_native_denom = U256::from(coin.amount); + + amount_in_native_denom * coefficient + } + + fn calculate_gas_price(&self, hash: &H256, tx: &Tx) -> U256 { + // TODO support multiple denominations for amount + let supported = self.report_unsupported_denominations(tx, hash); + if supported.is_err() { + return U256::max_value(); + } + + let gas_limit = U256::from(tx.auth_info.fee.gas_limit); + let fee = tx + .auth_info + .fee + .amount + .iter() + .map(|c| self.convert_fee(c)) + .fold(U256::zero(), |acc, v| acc + v); + + if fee < gas_limit { + warn!(tx_hash = ?hash, ?fee, ?gas_limit, "calculated fee is less than gas limit. it will result in zero gas price"); + } + + fee / gas_limit + } +} + +impl HyperlaneChain for CosmosProvider { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(self.clone()) + } +} + +#[async_trait] +impl HyperlaneProvider for CosmosProvider { + async fn get_block_by_height(&self, height: u64) -> ChainResult { + let response = self + .rpc_client + .call(|provider| Box::pin(async move { provider.get_block(height as u32).await })) + .await?; + + let block = response.block; + let block_height = block.header.height.value(); + + if block_height != height { + Err(HyperlaneProviderError::IncorrectBlockByHeight( + height, + block_height, + ))? + } + + let hash = H256::from_slice(response.block_id.hash.as_bytes()); + let time: OffsetDateTime = block.header.time.into(); + + let block_info = BlockInfo { + hash: hash.to_owned(), + timestamp: time.unix_timestamp() as u64, + number: block_height, + }; + + Ok(block_info) + } + + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { + let hash: H256 = H256::from_slice(&h512_to_bytes(hash)); + + let tendermint_hash = Hash::from_bytes(Algorithm::Sha256, hash.as_bytes()) + .expect("transaction hash should be of correct size"); + + let response = self + .rpc_client + .call(|provider| { + Box::pin(async move { provider.get_tx_by_hash(tendermint_hash).await }) + }) + .await?; + + let received_hash = H256::from_slice(response.hash.as_bytes()); + + if received_hash != hash { + return Err(ChainCommunicationError::from_other_str(&format!( + "received incorrect transaction, expected hash: {:?}, received hash: {:?}", + hash, received_hash, + ))); + } + + let tx = Tx::from_bytes(&response.tx)?; + + let contract = Self::contract(&tx, &hash)?; + let (sender, nonce) = self.sender_and_nonce(&tx)?; + let gas_price = self.calculate_gas_price(&hash, &tx); + + let tx_info = TxnInfo { + hash: hash.into(), + gas_limit: U256::from(response.tx_result.gas_wanted), + max_priority_fee_per_gas: None, + max_fee_per_gas: None, + gas_price: Some(gas_price), + nonce, + sender, + recipient: Some(contract), + receipt: Some(TxnReceiptInfo { + gas_used: U256::from(response.tx_result.gas_used), + cumulative_gas_used: U256::from(response.tx_result.gas_used), + effective_gas_price: Some(gas_price), + }), + }; + + Ok(tx_info) + } + + async fn is_contract(&self, address: &H256) -> ChainResult { + match self.grpc_provider.wasm_contract_info().await { + Ok(c) => Ok(true), + Err(e) => Ok(false), + } + } + + async fn get_balance(&self, address: String) -> ChainResult { + Ok(self + .grpc_provider + .get_balance(address, self.connection_conf.get_canonical_asset()) + .await?) + } + + async fn get_chain_metrics(&self) -> ChainResult> { + Ok(None) + } +} diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs new file mode 100644 index 000000000..aac9b7ce5 --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/cosmos/provider/parse.rs @@ -0,0 +1,163 @@ +use cosmrs::proto::ibc::core::channel::v1::MsgRecvPacket; +use cosmrs::proto::prost::Message; +use cosmrs::Any; +use serde::{Deserialize, Serialize}; + +use crate::HyperlaneCosmosError; + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct PacketData { + pub amount: String, + pub denom: String, + pub memo: String, + pub receiver: String, + pub sender: String, +} + +impl TryFrom<&Any> for PacketData { + type Error = HyperlaneCosmosError; + + fn try_from(any: &Any) -> Result { + let vec = any.value.as_slice(); + let msg = MsgRecvPacket::decode(vec).map_err(Into::::into)?; + let packet = msg + .packet + .ok_or(HyperlaneCosmosError::UnparsableEmptyField( + "MsgRecvPacket packet is empty".to_owned(), + ))?; + let data = serde_json::from_slice::(&packet.data)?; + Ok(data) + } +} + +impl TryFrom for PacketData { + type Error = HyperlaneCosmosError; + + fn try_from(any: Any) -> Result { + Self::try_from(&any) + } +} + +#[cfg(test)] +mod tests { + use cosmrs::proto::ibc::core::channel::v1::MsgRecvPacket; + use cosmrs::proto::ibc::core::channel::v1::Packet; + use cosmrs::proto::prost::Message; + use cosmrs::Any; + + use crate::providers::cosmos::provider::parse::PacketData; + use crate::HyperlaneCosmosError; + + #[test] + fn success() { + // given + let json = r#"{"amount":"59743800","denom":"utia","memo":"{\"wasm\":{\"contract\":\"neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq\",\"msg\":{\"transfer_remote\":{\"dest_domain\":42161,\"recipient\":\"0000000000000000000000008784aca75a95696fec93184b1c7b2d3bf5838df9\",\"amount\":\"59473800\"}},\"funds\":[{\"amount\":\"59743800\",\"denom\":\"ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7\"}]}}","receiver":"neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq","sender":"celestia19ns7dd07g5vvrueyqlkvn4dmxt957zcdzemvj6"}"#; + let any = any(json); + + // when + let data = PacketData::try_from(&any); + + // then + assert!(data.is_ok()); + } + + #[test] + fn fail_json() { + // given + let json = r#"{"amount":"27000000","denom":"utia","receiver":"neutron13uuq6vgenxan43ngscjlew8lc2z32znx9qfk0n","sender":"celestia1rh4gplea4gzvaaejew8jfvp9r0qkdmfgkf55qy"}"#; + let any = any(json); + + // when + let data = PacketData::try_from(&any); + + // then + assert!(data.is_err()); + assert!(matches!( + data.err().unwrap(), + HyperlaneCosmosError::SerdeError(_), + )); + } + + #[test] + fn fail_empty() { + // given + let any = empty(); + + // when + let data = PacketData::try_from(&any); + + // then + assert!(data.is_err()); + assert!(matches!( + data.err().unwrap(), + HyperlaneCosmosError::UnparsableEmptyField(_), + )); + } + + #[test] + fn fail_decode() { + // given + let any = wrong_encoding(); + + // when + let data = PacketData::try_from(&any); + + // then + assert!(data.is_err()); + assert!(matches!( + data.err().unwrap(), + HyperlaneCosmosError::Prost(_), + )); + } + + fn any(json: &str) -> Any { + let packet = Packet { + sequence: 0, + source_port: "".to_string(), + source_channel: "".to_string(), + destination_port: "".to_string(), + destination_channel: "".to_string(), + data: json.as_bytes().to_vec(), + timeout_height: None, + timeout_timestamp: 0, + }; + + let msg = MsgRecvPacket { + packet: Option::from(packet), + proof_commitment: vec![], + proof_height: None, + signer: "".to_string(), + }; + + encode_proto(&msg) + } + + fn empty() -> Any { + let msg = MsgRecvPacket { + packet: None, + proof_commitment: vec![], + proof_height: None, + signer: "".to_string(), + }; + + encode_proto(&msg) + } + + fn wrong_encoding() -> Any { + let buf = vec![1, 2, 3]; + Any { + type_url: "".to_string(), + value: buf, + } + } + + fn encode_proto(msg: &MsgRecvPacket) -> Any { + let mut buf = Vec::with_capacity(msg.encoded_len()); + MsgRecvPacket::encode(&msg, &mut buf).unwrap(); + + Any { + type_url: "".to_string(), + value: buf, + } + } +} diff --git a/rust/chains/hyperlane-cosmos/src/providers/grpc.rs b/rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs similarity index 95% rename from rust/chains/hyperlane-cosmos/src/providers/grpc.rs rename to rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs index e0a6388a4..c2e05acf4 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/grpc.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/grpc.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + use async_trait::async_trait; use cosmrs::{ proto::{ @@ -25,13 +27,8 @@ use cosmrs::{ Any, Coin, }; use derive_new::new; -use hyperlane_core::{ - rpc_clients::{BlockNumberGetter, FallbackProvider}, - ChainCommunicationError, ChainResult, ContractLocator, FixedPointNumber, HyperlaneDomain, U256, -}; use protobuf::Message as _; use serde::Serialize; -use std::fmt::Debug; use tonic::{ transport::{Channel, Endpoint}, GrpcMethod, IntoRequest, @@ -39,9 +36,14 @@ use tonic::{ use tracing::{debug, instrument}; use url::Url; -use crate::{address::CosmosAddress, CosmosAmount}; +use hyperlane_core::{ + rpc_clients::{BlockNumberGetter, FallbackProvider}, + ChainCommunicationError, ChainResult, ContractLocator, FixedPointNumber, HyperlaneDomain, U256, +}; + use crate::{rpc_clients::CosmosFallbackProvider, HyperlaneCosmosError}; use crate::{signers::Signer, ConnectionConf}; +use crate::{CosmosAddress, CosmosAmount}; /// A multiplier applied to a simulated transaction's gas usage to /// calculate the estimated gas. @@ -123,7 +125,7 @@ pub struct WasmGrpcProvider { conf: ConnectionConf, /// A contract address that can be used as the default /// for queries / sends / estimates. - contract_address: Option, + contract_address: CosmosAddress, /// Signer for transactions. signer: Option, /// GRPC Channel that can be cheaply cloned. @@ -138,7 +140,7 @@ impl WasmGrpcProvider { domain: HyperlaneDomain, conf: ConnectionConf, gas_price: CosmosAmount, - locator: Option, + locator: ContractLocator, signer: Option, ) -> ChainResult { // get all the configured grpc urls and convert them to a Vec @@ -156,15 +158,11 @@ impl WasmGrpcProvider { let fallback_provider = builder.build(); let provider = CosmosFallbackProvider::new(fallback_provider); - let contract_address = locator - .map(|l| { - CosmosAddress::from_h256( - l.address, - &conf.get_bech32_prefix(), - conf.get_contract_address_bytes(), - ) - }) - .transpose()?; + let contract_address = CosmosAddress::from_h256( + locator.address, + &conf.get_bech32_prefix(), + conf.get_contract_address_bytes(), + )?; Ok(Self { domain, @@ -446,11 +444,8 @@ impl WasmGrpcProvider { }) } - fn get_contract_address(&self) -> Result<&CosmosAddress, ChainCommunicationError> { - let contract_address = self.contract_address.as_ref().ok_or_else(|| { - ChainCommunicationError::from_other_str("No contract address available") - })?; - Ok(contract_address) + fn get_contract_address(&self) -> &CosmosAddress { + &self.contract_address } } @@ -488,7 +483,7 @@ impl WasmProvider for WasmGrpcProvider { where T: Serialize + Send + Sync + Clone + Debug, { - let contract_address = self.get_contract_address()?; + let contract_address = self.get_contract_address(); let query_data = serde_json::to_string(&payload)?.as_bytes().to_vec(); let response = self .provider @@ -522,7 +517,7 @@ impl WasmProvider for WasmGrpcProvider { } async fn wasm_contract_info(&self) -> ChainResult { - let contract_address = self.get_contract_address()?; + let contract_address = self.get_contract_address(); let response = self .provider .call(move |provider| { @@ -557,7 +552,7 @@ impl WasmProvider for WasmGrpcProvider { T: Serialize + Send + Sync + Clone + Debug, { let signer = self.get_signer()?; - let contract_address = self.get_contract_address()?; + let contract_address = self.get_contract_address(); let msgs = vec![MsgExecuteContract { sender: signer.address.clone(), contract: contract_address.address(), @@ -625,7 +620,7 @@ impl WasmProvider for WasmGrpcProvider { // Estimating gas requires a signer, which we can reasonably expect to have // since we need one to send a tx with the estimated gas anyways. let signer = self.get_signer()?; - let contract_address = self.get_contract_address()?; + let contract_address = self.get_contract_address(); let msg = MsgExecuteContract { sender: signer.address.clone(), contract: contract_address.address(), diff --git a/rust/chains/hyperlane-cosmos/src/providers/grpc/tests.rs b/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs similarity index 84% rename from rust/chains/hyperlane-cosmos/src/providers/grpc/tests.rs rename to rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs index 9a59b6007..392ab713d 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/grpc/tests.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/grpc/tests.rs @@ -5,9 +5,8 @@ use url::Url; use hyperlane_core::config::OperationBatchConfig; use hyperlane_core::{ContractLocator, HyperlaneDomain, KnownHyperlaneDomain}; -use crate::address::CosmosAddress; use crate::grpc::{WasmGrpcProvider, WasmProvider}; -use crate::{ConnectionConf, CosmosAmount, RawCosmosAmount}; +use crate::{ConnectionConf, CosmosAddress, CosmosAmount, NativeToken, RawCosmosAmount}; #[ignore] #[tokio::test] @@ -49,13 +48,13 @@ async fn test_wasm_contract_info_no_contract() { fn provider(address: &str) -> WasmGrpcProvider { let domain = HyperlaneDomain::Known(KnownHyperlaneDomain::Neutron); let address = CosmosAddress::from_str(address).unwrap(); - let locator = Some(ContractLocator::new(&domain, address.digest())); + let locator = ContractLocator::new(&domain, address.digest()); WasmGrpcProvider::new( domain.clone(), ConnectionConf::new( vec![Url::parse("http://grpc-kralum.neutron-1.neutron.org:80").unwrap()], - "https://rpc-kralum.neutron-1.neutron.org".to_owned(), + vec![Url::parse("https://rpc-kralum.neutron-1.neutron.org").unwrap()], "neutron-1".to_owned(), "neutron".to_owned(), "untrn".to_owned(), @@ -65,6 +64,10 @@ fn provider(address: &str) -> WasmGrpcProvider { batch_contract_address: None, max_batch_size: 1, }, + NativeToken { + decimals: 6, + denom: "untrn".to_owned(), + }, ), CosmosAmount { denom: "untrn".to_owned(), diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/rpc.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc.rs new file mode 100644 index 000000000..08864b26c --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc.rs @@ -0,0 +1,5 @@ +pub use client::CosmosRpcClient; +pub use provider::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; + +mod client; +mod provider; diff --git a/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs new file mode 100644 index 000000000..102c04fac --- /dev/null +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/client.rs @@ -0,0 +1,90 @@ +use cosmrs::proto::tendermint::blocksync::BlockResponse; +use hyperlane_core::rpc_clients::BlockNumberGetter; +use tendermint::Hash; +use tendermint_rpc::client::CompatMode; +use tendermint_rpc::endpoint::{block, block_by_hash, block_results, tx}; +use tendermint_rpc::{Client, HttpClient, HttpClientUrl, Url as TendermintUrl}; + +use hyperlane_core::{ChainCommunicationError, ChainResult}; +use tonic::async_trait; +use url::Url; + +use crate::{ConnectionConf, HyperlaneCosmosError}; + +/// Thin wrapper around Cosmos RPC client with error mapping +#[derive(Clone, Debug)] +pub struct CosmosRpcClient { + client: HttpClient, +} + +impl CosmosRpcClient { + /// Create new `CosmosRpcClient` + pub fn new(url: &Url) -> ChainResult { + let tendermint_url = tendermint_rpc::Url::try_from(url.to_owned()) + .map_err(Into::::into)?; + let url = tendermint_rpc::HttpClientUrl::try_from(tendermint_url) + .map_err(Into::::into)?; + + let client = HttpClient::builder(url) + // Consider supporting different compatibility modes. + .compat_mode(CompatMode::latest()) + .build() + .map_err(Into::::into)?; + + Ok(Self { client }) + } + + /// Request block by block height + pub async fn get_block(&self, height: u32) -> ChainResult { + Ok(self + .client + .block(height) + .await + .map_err(Into::::into)?) + } + + /// Request block results by block height + pub async fn get_block_results(&self, height: u32) -> ChainResult { + Ok(self + .client + .block_results(height) + .await + .map_err(Into::::into)?) + } + + /// Request block by block hash + pub async fn get_block_by_hash(&self, hash: Hash) -> ChainResult { + Ok(self + .client + .block_by_hash(hash) + .await + .map_err(Into::::into)?) + } + + /// Request the latest block + pub async fn get_latest_block(&self) -> ChainResult { + Ok(self + .client + .latest_block() + .await + .map_err(Into::::into)?) + } + + /// Request transaction by transaction hash + pub async fn get_tx_by_hash(&self, hash: Hash) -> ChainResult { + Ok(self + .client + .tx(hash, false) + .await + .map_err(Into::::into)?) + } +} + +#[async_trait] +impl BlockNumberGetter for CosmosRpcClient { + async fn get_block_number(&self) -> Result { + self.get_latest_block() + .await + .map(|block| block.block.header.height.value()) + } +} diff --git a/rust/chains/hyperlane-cosmos/src/providers/rpc.rs b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs similarity index 55% rename from rust/chains/hyperlane-cosmos/src/providers/rpc.rs rename to rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs index b5b924def..599a75116 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/rpc.rs +++ b/rust/main/chains/hyperlane-cosmos/src/providers/rpc/provider.rs @@ -1,22 +1,33 @@ +use std::fmt::Debug; + use async_trait::async_trait; +use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::rpc::client::Client; -use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator, LogMeta, H256, U256}; +use futures::StreamExt; +use hyperlane_core::rpc_clients::{BlockNumberGetter, FallbackProvider}; use sha256::digest; -use std::fmt::Debug; use tendermint::abci::{Event, EventAttribute}; use tendermint::hash::Algorithm; use tendermint::Hash; +use tendermint_rpc::client::CompatMode; use tendermint_rpc::endpoint::block::Response as BlockResponse; -use tendermint_rpc::endpoint::block_results::Response as BlockResultsResponse; +use tendermint_rpc::endpoint::block_results::{self, Response as BlockResultsResponse}; +use tendermint_rpc::endpoint::tx; use tendermint_rpc::HttpClient; -use tracing::{debug, instrument, trace}; +use time::OffsetDateTime; +use tracing::{debug, info, instrument, trace}; -use crate::address::CosmosAddress; -use crate::{ConnectionConf, CosmosProvider, HyperlaneCosmosError}; +use hyperlane_core::{ + ChainCommunicationError, ChainResult, ContractLocator, HyperlaneDomain, LogMeta, H256, U256, +}; + +use crate::rpc::CosmosRpcClient; +use crate::rpc_clients::CosmosFallbackProvider; +use crate::{ConnectionConf, CosmosAddress, CosmosProvider, HyperlaneCosmosError}; #[async_trait] /// Trait for wasm indexer. Use rpc provider -pub trait WasmIndexer: Send + Sync { +pub trait WasmRpcProvider: Send + Sync { /// Get the finalized block height. async fn get_finalized_block_number(&self) -> ChainResult; @@ -29,6 +40,16 @@ pub trait WasmIndexer: Send + Sync { ) -> ChainResult> where T: Send + Sync + PartialEq + Debug + 'static; + + /// Get logs for the given transaction using the given parser. + async fn get_logs_in_tx( + &self, + tx_hash: Hash, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + cursor_label: &'static str, + ) -> ChainResult> + where + T: Send + Sync + PartialEq + Debug + 'static; } #[derive(Debug, Eq, PartialEq)] @@ -55,15 +76,16 @@ impl ParsedEvent { #[derive(Debug, Clone)] /// Cosmwasm RPC Provider -pub struct CosmosWasmIndexer { - provider: CosmosProvider, +pub struct CosmosWasmRpcProvider { + domain: HyperlaneDomain, contract_address: CosmosAddress, target_event_kind: String, reorg_period: u32, + rpc_client: CosmosFallbackProvider, } -impl CosmosWasmIndexer { - const WASM_TYPE: &str = "wasm"; +impl CosmosWasmRpcProvider { + const WASM_TYPE: &'static str = "wasm"; /// create new Cosmwasm RPC Provider pub fn new( @@ -72,14 +94,18 @@ impl CosmosWasmIndexer { event_type: String, reorg_period: u32, ) -> ChainResult { - let provider = CosmosProvider::new( - locator.domain.clone(), - conf.clone(), - Some(locator.clone()), - None, - )?; + let providers = conf + .get_rpc_urls() + .iter() + .map(CosmosRpcClient::new) + .collect::, _>>()?; + let mut builder = FallbackProvider::builder(); + builder = builder.add_providers(providers); + let fallback_provider = builder.build(); + let provider = CosmosFallbackProvider::new(fallback_provider); + Ok(Self { - provider, + domain: locator.domain.clone(), contract_address: CosmosAddress::from_h256( locator.address, conf.get_bech32_prefix().as_str(), @@ -87,35 +113,18 @@ impl CosmosWasmIndexer { )?, target_event_kind: format!("{}-{}", Self::WASM_TYPE, event_type), reorg_period, + rpc_client: provider, }) } - async fn get_block(client: HttpClient, block_number: u32) -> ChainResult { - Ok(client - .block(block_number) - .await - .map_err(Into::::into)?) - } - - async fn get_block_results( - client: HttpClient, - block_number: u32, - ) -> ChainResult { - Ok(client - .block_results(block_number) - .await - .map_err(Into::::into)?) - } - - async fn get_latest_block(client: HttpClient) -> ChainResult { - Ok(client - .latest_block() + async fn get_block(&self, height: u32) -> ChainResult { + self.rpc_client + .call(|provider| Box::pin(async move { provider.get_block(height).await })) .await - .map_err(Into::::into)?) } } -impl CosmosWasmIndexer { +impl CosmosWasmRpcProvider { // Iterate through all txs, filter out failed txs, find target events // in successful txs, and parse them. fn handle_txs( @@ -132,17 +141,13 @@ impl CosmosWasmIndexer { return vec![]; }; - let tx_hashes: Vec = block + let tx_hashes: Vec = block .clone() .block .data .into_iter() .filter_map(|tx| hex::decode(digest(tx.as_slice())).ok()) - .filter_map(|hash| { - Hash::from_bytes(Algorithm::Sha256, hash.as_slice()) - .ok() - .map(|hash| H256::from_slice(hash.as_bytes())) - }) + .filter_map(|hash| Hash::from_bytes(Algorithm::Sha256, hash.as_slice()).ok()) .collect(); tx_results @@ -157,7 +162,21 @@ impl CosmosWasmIndexer { debug!(?tx_hash, "Not indexing failed transaction"); return None; } - Some(self.handle_tx(block.clone(), tx.events, *tx_hash, idx, parser)) + + // We construct a simplified structure `tx::Response` here so that we can + // reuse `handle_tx` method below. + let tx_response = tx::Response { + hash: *tx_hash, + height: block_results.height, + index: idx as u32, + tx_result: tx, + tx: vec![], + proof: None, + }; + + let block_hash = H256::from_slice(block.block_id.hash.as_bytes()); + + Some(self.handle_tx(tx_response, block_hash, parser)) }) .flatten() .collect() @@ -167,15 +186,18 @@ impl CosmosWasmIndexer { // made by the contract we are indexing. fn handle_tx( &self, - block: BlockResponse, - tx_events: Vec, - tx_hash: H256, - transaction_index: usize, + tx: tx::Response, + block_hash: H256, parser: for<'a> fn(&'a Vec) -> ChainResult>, ) -> impl Iterator + '_ where T: PartialEq + 'static, { + let tx_events = tx.tx_result.events; + let tx_hash = tx.hash; + let tx_index = tx.index; + let block_height = tx.height; + tx_events.into_iter().enumerate().filter_map(move |(log_idx, event)| { if event.kind.as_str() != self.target_event_kind { return None; @@ -185,7 +207,7 @@ impl CosmosWasmIndexer { .map_err(|err| { // This can happen if we attempt to parse an event that just happens // to have the same name but a different structure. - tracing::trace!(?err, tx_hash=?tx_hash, log_idx, ?event, "Failed to parse event attributes"); + trace!(?err, tx_hash=?tx_hash, log_idx, ?event, "Failed to parse event attributes"); }) .ok() .and_then(|parsed_event| { @@ -200,22 +222,26 @@ impl CosmosWasmIndexer { Some((parsed_event.event, LogMeta { address: self.contract_address.digest(), - block_number: block.block.header.height.into(), - block_hash: H256::from_slice(block.block_id.hash.as_bytes()), + block_number: block_height.value(), + block_hash, transaction_id: H256::from_slice(tx_hash.as_bytes()).into(), - transaction_index: transaction_index as u64, + transaction_index: tx_index as u64, log_index: U256::from(log_idx), })) }) - }) + }) } } #[async_trait] -impl WasmIndexer for CosmosWasmIndexer { +impl WasmRpcProvider for CosmosWasmRpcProvider { #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_finalized_block_number(&self) -> ChainResult { - let latest_block = Self::get_latest_block(self.provider.rpc().clone()).await?; + let latest_block = self + .rpc_client + .call(|provider| Box::pin(async move { provider.get_latest_block().await })) + .await?; let latest_height: u32 = latest_block .block .header @@ -227,6 +253,7 @@ impl WasmIndexer for CosmosWasmIndexer { } #[instrument(err, skip(self, parser))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_logs_in_block( &self, block_number: u32, @@ -236,14 +263,41 @@ impl WasmIndexer for CosmosWasmIndexer { where T: Send + Sync + PartialEq + Debug + 'static, { - let client = self.provider.rpc().clone(); - debug!(?block_number, cursor_label, domain=?self.provider.domain, "Getting logs in block"); - // The two calls below could be made in parallel, but on cosmos rate limiting is a bigger problem // than indexing latency, so we do them sequentially. - let block = Self::get_block(client.clone(), block_number).await?; - let block_results = Self::get_block_results(client.clone(), block_number).await?; + let block = self.get_block(block_number).await?; + debug!(?block_number, block_hash = ?block.block_id.hash, cursor_label, domain=?self.domain, "Getting logs in block with hash"); + let block_results = self + .rpc_client + .call(|provider| { + Box::pin(async move { provider.get_block_results(block_number).await }) + }) + .await?; Ok(self.handle_txs(block, block_results, parser, cursor_label)) } + + #[instrument(err, skip(self, parser))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn get_logs_in_tx( + &self, + hash: Hash, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + cursor_label: &'static str, + ) -> ChainResult> + where + T: Send + Sync + PartialEq + Debug + 'static, + { + let tx = self + .rpc_client + .call(|provider| Box::pin(async move { provider.get_tx_by_hash(hash).await })) + .await?; + let block_number = tx.height.value() as u32; + let block = self.get_block(block_number).await?; + let block_hash = H256::from_slice(block.block_id.hash.as_bytes()); + + debug!(?block_number, block_hash = ?block.block_id.hash, cursor_label, domain=?self.domain, "Getting logs in transaction: block info"); + + Ok(self.handle_tx(tx, block_hash, parser).collect()) + } } diff --git a/rust/chains/hyperlane-cosmos/src/routing_ism.rs b/rust/main/chains/hyperlane-cosmos/src/routing_ism.rs similarity index 95% rename from rust/chains/hyperlane-cosmos/src/routing_ism.rs rename to rust/main/chains/hyperlane-cosmos/src/routing_ism.rs index 63b759f1b..c68b2cf59 100644 --- a/rust/chains/hyperlane-cosmos/src/routing_ism.rs +++ b/rust/main/chains/hyperlane-cosmos/src/routing_ism.rs @@ -8,13 +8,12 @@ use hyperlane_core::{ }; use crate::{ - address::CosmosAddress, grpc::WasmProvider, payloads::ism_routes::{ IsmRouteRequest, IsmRouteRequestInner, IsmRouteRespnose, QueryRoutingIsmGeneralRequest, }, signers::Signer, - ConnectionConf, CosmosProvider, + ConnectionConf, CosmosAddress, CosmosProvider, }; /// A reference to a RoutingIsm contract on some Cosmos chain @@ -35,7 +34,7 @@ impl CosmosRoutingIsm { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; diff --git a/rust/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs b/rust/main/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs rename to rust/main/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs diff --git a/rust/chains/hyperlane-cosmos/src/rpc_clients/mod.rs b/rust/main/chains/hyperlane-cosmos/src/rpc_clients/mod.rs similarity index 100% rename from rust/chains/hyperlane-cosmos/src/rpc_clients/mod.rs rename to rust/main/chains/hyperlane-cosmos/src/rpc_clients/mod.rs diff --git a/rust/chains/hyperlane-cosmos/src/signers.rs b/rust/main/chains/hyperlane-cosmos/src/signers.rs similarity index 73% rename from rust/chains/hyperlane-cosmos/src/signers.rs rename to rust/main/chains/hyperlane-cosmos/src/signers.rs index 60870fc92..1cd0dd8ec 100644 --- a/rust/chains/hyperlane-cosmos/src/signers.rs +++ b/rust/main/chains/hyperlane-cosmos/src/signers.rs @@ -1,7 +1,7 @@ use cosmrs::crypto::{secp256k1::SigningKey, PublicKey}; -use hyperlane_core::ChainResult; +use hyperlane_core::{AccountAddressType, ChainResult}; -use crate::{address::CosmosAddress, HyperlaneCosmosError}; +use crate::{CosmosAddress, HyperlaneCosmosError}; #[derive(Clone, Debug)] /// Signer for cosmos chain @@ -22,8 +22,14 @@ impl Signer { /// # Arguments /// * `private_key` - private key for signer /// * `prefix` - prefix for signer address - pub fn new(private_key: Vec, prefix: String) -> ChainResult { - let address = CosmosAddress::from_privkey(&private_key, &prefix)?.address(); + /// * `account_address_type` - the type of account address used for signer + pub fn new( + private_key: Vec, + prefix: String, + account_address_type: &AccountAddressType, + ) -> ChainResult { + let address = + CosmosAddress::from_privkey(&private_key, &prefix, account_address_type)?.address(); let signing_key = Self::build_signing_key(&private_key)?; let public_key = signing_key.public_key(); Ok(Self { diff --git a/rust/chains/hyperlane-cosmos/src/trait_builder.rs b/rust/main/chains/hyperlane-cosmos/src/trait_builder.rs similarity index 84% rename from rust/chains/hyperlane-cosmos/src/trait_builder.rs rename to rust/main/chains/hyperlane-cosmos/src/trait_builder.rs index a6463654f..c96d73c47 100644 --- a/rust/chains/hyperlane-cosmos/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-cosmos/src/trait_builder.rs @@ -1,16 +1,17 @@ use std::str::FromStr; use derive_new::new; -use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, FixedPointNumber}; use url::Url; +use hyperlane_core::{config::OperationBatchConfig, ChainCommunicationError, FixedPointNumber}; + /// Cosmos connection configuration #[derive(Debug, Clone)] pub struct ConnectionConf { - /// The GRPC url to connect to + /// The GRPC urls to connect to grpc_urls: Vec, /// The RPC url to connect to - rpc_url: String, + rpc_urls: Vec, /// The chain ID chain_id: String, /// The human readable address prefix for the chains using bech32. @@ -27,6 +28,8 @@ pub struct ConnectionConf { contract_address_bytes: usize, /// Operation batching configuration pub operation_batch: OperationBatchConfig, + /// Native Token + native_token: NativeToken, } /// Untyped cosmos amount @@ -57,6 +60,15 @@ impl TryFrom for CosmosAmount { } } +/// Chain native token denomination and number of decimal places +#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] +pub struct NativeToken { + /// The number of decimal places in token which can be expressed by denomination + pub decimals: u32, + /// Denomination of the token + pub denom: String, +} + /// An error type when parsing a connection configuration. #[derive(thiserror::Error, Debug)] pub enum ConnectionConfError { @@ -83,9 +95,9 @@ impl ConnectionConf { self.grpc_urls.clone() } - /// Get the RPC url - pub fn get_rpc_url(&self) -> String { - self.rpc_url.clone() + /// Get the RPC urls + pub fn get_rpc_urls(&self) -> Vec { + self.rpc_urls.clone() } /// Get the chain ID @@ -108,6 +120,11 @@ impl ConnectionConf { self.gas_price.clone() } + /// Get the native token + pub fn get_native_token(&self) -> &NativeToken { + &self.native_token + } + /// Get the number of bytes used to represent a contract address pub fn get_contract_address_bytes(&self) -> usize { self.contract_address_bytes @@ -117,23 +134,25 @@ impl ConnectionConf { #[allow(clippy::too_many_arguments)] pub fn new( grpc_urls: Vec, - rpc_url: String, + rpc_urls: Vec, chain_id: String, bech32_prefix: String, canonical_asset: String, minimum_gas_price: RawCosmosAmount, contract_address_bytes: usize, operation_batch: OperationBatchConfig, + native_token: NativeToken, ) -> Self { Self { grpc_urls, - rpc_url, + rpc_urls, chain_id, bech32_prefix, canonical_asset, gas_price: minimum_gas_price, contract_address_bytes, operation_batch, + native_token, } } } diff --git a/rust/chains/hyperlane-cosmos/src/types.rs b/rust/main/chains/hyperlane-cosmos/src/types.rs similarity index 94% rename from rust/chains/hyperlane-cosmos/src/types.rs rename to rust/main/chains/hyperlane-cosmos/src/types.rs index aa5a95465..bba61fb01 100644 --- a/rust/chains/hyperlane-cosmos/src/types.rs +++ b/rust/main/chains/hyperlane-cosmos/src/types.rs @@ -1,5 +1,6 @@ -use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; +use cosmrs::proto::{cosmos::base::abci::v1beta1::TxResponse, tendermint::Error}; use hyperlane_core::{ChainResult, ModuleType, TxOutcome, H256, U256}; +use url::Url; pub struct IsmType(pub hyperlane_cosmwasm_interface::ism::IsmType); diff --git a/rust/chains/hyperlane-cosmos/src/utils.rs b/rust/main/chains/hyperlane-cosmos/src/utils.rs similarity index 66% rename from rust/chains/hyperlane-cosmos/src/utils.rs rename to rust/main/chains/hyperlane-cosmos/src/utils.rs index 032700785..f792090e6 100644 --- a/rust/chains/hyperlane-cosmos/src/utils.rs +++ b/rust/main/chains/hyperlane-cosmos/src/utils.rs @@ -6,13 +6,15 @@ use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; use futures::future; use once_cell::sync::Lazy; use tendermint::abci::EventAttribute; +use tendermint::hash::Algorithm; +use tendermint::Hash; use tokio::task::JoinHandle; use tracing::warn; -use hyperlane_core::{ChainCommunicationError, ChainResult, Indexed, LogMeta}; +use hyperlane_core::{ChainCommunicationError, ChainResult, Indexed, LogMeta, ReorgPeriod, H256}; use crate::grpc::{WasmGrpcProvider, WasmProvider}; -use crate::rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}; +use crate::rpc::{CosmosWasmRpcProvider, ParsedEvent, WasmRpcProvider}; type FutureChainResults = Vec>, u32)>>; @@ -22,20 +24,25 @@ pub(crate) const CONTRACT_ADDRESS_ATTRIBUTE_KEY: &str = "_contract_address"; pub(crate) static CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64: Lazy = Lazy::new(|| BASE64.encode(CONTRACT_ADDRESS_ATTRIBUTE_KEY)); -/// Given a lag, returns the block height at the moment. -/// If the lag is None, a block height of None is given, indicating that the -/// tip directly can be used. -pub(crate) async fn get_block_height_for_lag( +/// Given a `reorg_period`, returns the block height at the moment. +/// If the `reorg_period` is None, a block height of None is given, +/// indicating that the tip directly can be used. +pub(crate) async fn get_block_height_for_reorg_period( provider: &WasmGrpcProvider, - lag: Option, + reorg_period: &ReorgPeriod, ) -> ChainResult> { - let block_height = match lag { - Some(lag) => { + let block_height = match reorg_period { + ReorgPeriod::Blocks(blocks) => { let tip = provider.latest_block_height().await?; - let block_height = tip - lag.get(); + let block_height = tip - blocks.get() as u64; Some(block_height) } - None => None, + ReorgPeriod::None => None, + ReorgPeriod::Tag(_) => { + return Err(ChainCommunicationError::InvalidReorgPeriod( + reorg_period.clone(), + )) + } }; Ok(block_height) @@ -43,21 +50,37 @@ pub(crate) async fn get_block_height_for_lag( pub(crate) fn parse_logs_in_range( range: RangeInclusive, - indexer: Box, + provider: Box, parser: for<'a> fn(&'a Vec) -> ChainResult>, label: &'static str, ) -> FutureChainResults { range .map(|block_number| { - let indexer = indexer.clone(); + let provider = provider.clone(); tokio::spawn(async move { - let logs = indexer.get_logs_in_block(block_number, parser, label).await; + let logs = provider + .get_logs_in_block(block_number, parser, label) + .await; (logs, block_number) }) }) .collect() } +pub(crate) async fn parse_logs_in_tx( + hash: &H256, + provider: Box, + parser: for<'a> fn(&'a Vec) -> ChainResult>, + label: &'static str, +) -> ChainResult> { + let tendermint_hash = Hash::from_bytes(Algorithm::Sha256, hash.as_bytes()) + .expect("transaction hash should be of correct size"); + + provider + .get_logs_in_tx(tendermint_hash, parser, label) + .await +} + #[allow(clippy::type_complexity)] pub(crate) async fn execute_and_parse_log_futures>>( logs_futures: Vec, ChainCommunicationError>, u32)>>, diff --git a/rust/chains/hyperlane-cosmos/src/validator_announce.rs b/rust/main/chains/hyperlane-cosmos/src/validator_announce.rs similarity index 99% rename from rust/chains/hyperlane-cosmos/src/validator_announce.rs rename to rust/main/chains/hyperlane-cosmos/src/validator_announce.rs index 9ead17f8f..d82b0e829 100644 --- a/rust/chains/hyperlane-cosmos/src/validator_announce.rs +++ b/rust/main/chains/hyperlane-cosmos/src/validator_announce.rs @@ -35,7 +35,7 @@ impl CosmosValidatorAnnounce { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), signer, )?; diff --git a/rust/chains/hyperlane-ethereum/.gitignore b/rust/main/chains/hyperlane-ethereum/.gitignore similarity index 100% rename from rust/chains/hyperlane-ethereum/.gitignore rename to rust/main/chains/hyperlane-ethereum/.gitignore diff --git a/rust/chains/hyperlane-ethereum/Cargo.toml b/rust/main/chains/hyperlane-ethereum/Cargo.toml similarity index 95% rename from rust/chains/hyperlane-ethereum/Cargo.toml rename to rust/main/chains/hyperlane-ethereum/Cargo.toml index b1f75335f..07e624e1e 100644 --- a/rust/chains/hyperlane-ethereum/Cargo.toml +++ b/rust/main/chains/hyperlane-ethereum/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "hyperlane-ethereum" documentation.workspace = true @@ -32,9 +30,13 @@ tracing-futures.workspace = true tracing.workspace = true url.workspace = true -hyperlane-core = { path = "../../hyperlane-core", features = ["async"]} +hyperlane-core = { path = "../../hyperlane-core", features = ["async"] } ethers-prometheus = { path = "../../ethers-prometheus", features = ["serde"] } [build-dependencies] abigen = { path = "../../utils/abigen", features = ["ethers"] } hyperlane-core = { path = "../../hyperlane-core", features = ["test-utils"] } + +[features] +default = [] +test-utils = [] \ No newline at end of file diff --git a/rust/chains/hyperlane-ethereum/abis/ArbitrumNodeInterface.abi.json b/rust/main/chains/hyperlane-ethereum/abis/ArbitrumNodeInterface.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/ArbitrumNodeInterface.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/ArbitrumNodeInterface.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IAggregationIsm.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IAggregationIsm.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IAggregationIsm.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IAggregationIsm.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/ICcipReadIsm.abi.json b/rust/main/chains/hyperlane-ethereum/abis/ICcipReadIsm.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/ICcipReadIsm.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/ICcipReadIsm.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IInterchainGasPaymaster.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IInterchainGasPaymaster.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IInterchainGasPaymaster.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IInterchainGasPaymaster.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IInterchainSecurityModule.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IInterchainSecurityModule.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IInterchainSecurityModule.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IInterchainSecurityModule.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IMailbox.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IMailbox.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IMailbox.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IMailbox.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IMultisigIsm.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IMultisigIsm.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IMultisigIsm.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IMultisigIsm.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IRoutingIsm.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IRoutingIsm.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IRoutingIsm.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IRoutingIsm.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/IValidatorAnnounce.abi.json b/rust/main/chains/hyperlane-ethereum/abis/IValidatorAnnounce.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/IValidatorAnnounce.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/IValidatorAnnounce.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/Mailbox.abi.json b/rust/main/chains/hyperlane-ethereum/abis/Mailbox.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/Mailbox.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/Mailbox.abi.json diff --git a/rust/chains/hyperlane-ethereum/abis/MerkleTreeHook.abi.json b/rust/main/chains/hyperlane-ethereum/abis/MerkleTreeHook.abi.json similarity index 100% rename from rust/chains/hyperlane-ethereum/abis/MerkleTreeHook.abi.json rename to rust/main/chains/hyperlane-ethereum/abis/MerkleTreeHook.abi.json diff --git a/rust/chains/hyperlane-ethereum/build.rs b/rust/main/chains/hyperlane-ethereum/build.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/build.rs rename to rust/main/chains/hyperlane-ethereum/build.rs diff --git a/rust/main/chains/hyperlane-ethereum/src/config.rs b/rust/main/chains/hyperlane-ethereum/src/config.rs new file mode 100644 index 000000000..e1375e790 --- /dev/null +++ b/rust/main/chains/hyperlane-ethereum/src/config.rs @@ -0,0 +1,106 @@ +use ethers::providers::Middleware; +use ethers_core::types::{BlockId, BlockNumber}; +use hyperlane_core::{ + config::OperationBatchConfig, ChainCommunicationError, ChainResult, ReorgPeriod, U256, +}; +use url::Url; + +/// Ethereum RPC connection configuration +#[derive(Debug, Clone)] +pub enum RpcConnectionConf { + /// An HTTP-only quorum. + HttpQuorum { + /// List of urls to connect to + urls: Vec, + }, + /// An HTTP-only fallback set. + HttpFallback { + /// List of urls to connect to in order of priority + urls: Vec, + }, + /// HTTP connection details + Http { + /// Url to connect to + url: Url, + }, + /// Websocket connection details + Ws { + /// Url to connect to + url: Url, + }, +} + +/// Ethereum connection configuration +#[derive(Debug, Clone)] +pub struct ConnectionConf { + /// RPC connection configuration + pub rpc_connection: RpcConnectionConf, + /// Transaction overrides to use when sending transactions. + pub transaction_overrides: TransactionOverrides, + /// Operation batching configuration + pub operation_batch: OperationBatchConfig, +} + +/// Ethereum transaction overrides. +#[derive(Debug, Clone, Default)] +pub struct TransactionOverrides { + /// Gas price to use for transactions, in wei. + /// If specified, non-1559 transactions will be used with this gas price. + pub gas_price: Option, + /// Gas limit to use for transactions. + /// If unspecified, the gas limit will be estimated. + /// If specified, transactions will use `max(estimated_gas, gas_limit)` + pub gas_limit: Option, + /// Max fee per gas to use for EIP-1559 transactions. + pub max_fee_per_gas: Option, + /// Max priority fee per gas to use for EIP-1559 transactions. + pub max_priority_fee_per_gas: Option, +} + +/// Ethereum reorg period +#[derive(Copy, Clone, Debug)] +pub enum EthereumReorgPeriod { + /// Number of blocks + Blocks(u32), + /// A block tag + Tag(BlockId), +} + +impl TryFrom<&ReorgPeriod> for EthereumReorgPeriod { + type Error = ChainCommunicationError; + + fn try_from(value: &ReorgPeriod) -> Result { + match value { + ReorgPeriod::None => Ok(EthereumReorgPeriod::Blocks(0)), + ReorgPeriod::Blocks(blocks) => Ok(EthereumReorgPeriod::Blocks(blocks.get())), + ReorgPeriod::Tag(tag) => { + let tag = match tag.as_str() { + "latest" => BlockNumber::Latest, + "finalized" => BlockNumber::Finalized, + "safe" => BlockNumber::Safe, + "earliest" => BlockNumber::Earliest, + "pending" => BlockNumber::Pending, + _ => return Err(ChainCommunicationError::InvalidReorgPeriod(value.clone())), + }; + Ok(EthereumReorgPeriod::Tag(tag.into())) + } + } + } +} + +impl EthereumReorgPeriod { + /// Converts the reorg period into a block id + pub async fn into_block_id( + &self, + provider: &M, + ) -> ChainResult { + let block_id = match self { + EthereumReorgPeriod::Blocks(_) => { + (crate::get_finalized_block_number(provider, self).await? as u64).into() + } + // no need to fetch the block number for the `tag` + EthereumReorgPeriod::Tag(tag) => *tag, + }; + Ok(block_id) + } +} diff --git a/rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs similarity index 90% rename from rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs index e6f2c8a4a..d7cf37e81 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs @@ -9,18 +9,18 @@ use async_trait::async_trait; use ethers::prelude::Middleware; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use hyperlane_core::{ - ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, - HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, - InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H160, H256, H512, + ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneProvider, Indexed, Indexer, InterchainGasPaymaster, InterchainGasPayment, LogMeta, + SequenceAwareIndexer, H160, H256, H512, }; use tracing::instrument; -use super::utils::fetch_raw_logs_and_meta; +use super::utils::{fetch_raw_logs_and_meta, get_finalized_block_number}; use crate::interfaces::i_interchain_gas_paymaster::{ GasPaymentFilter, IInterchainGasPaymaster as EthereumInterchainGasPaymasterInternal, IINTERCHAINGASPAYMASTER_ABI, }; -use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider}; +use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod}; impl Display for EthereumInterchainGasPaymasterInternal where @@ -33,7 +33,7 @@ where pub struct InterchainGasPaymasterIndexerBuilder { pub mailbox_address: H160, - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -63,7 +63,7 @@ where { contract: Arc>, provider: Arc, - reorg_period: u32, + reorg_period: EthereumReorgPeriod, } impl EthereumInterchainGasPaymasterIndexer @@ -71,7 +71,11 @@ where M: Middleware + 'static, { /// Create new EthereumInterchainGasPaymasterIndexer - pub fn new(provider: Arc, locator: &ContractLocator, reorg_period: u32) -> Self { + pub fn new( + provider: Arc, + locator: &ContractLocator, + reorg_period: EthereumReorgPeriod, + ) -> Self { Self { contract: Arc::new(EthereumInterchainGasPaymasterInternal::new( locator.address, @@ -90,6 +94,7 @@ where { /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn fetch_logs_in_range( &self, range: RangeInclusive, @@ -119,14 +124,9 @@ where } #[instrument(level = "debug", err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_finalized_block_number(&self) -> ChainResult { - Ok(self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .as_u32() - .saturating_sub(self.reorg_period)) + get_finalized_block_number(&self.provider, &self.reorg_period).await } async fn fetch_logs_by_tx_hash( diff --git a/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs similarity index 84% rename from rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs index e751ab9b7..a9141e7b7 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -2,7 +2,6 @@ #![allow(missing_docs)] use std::collections::HashMap; -use std::num::NonZeroU64; use std::ops::RangeInclusive; use std::sync::Arc; @@ -14,7 +13,7 @@ use ethers_contract::builders::ContractCall; use ethers_contract::{Multicall, MulticallResult}; use futures_util::future::join_all; use hyperlane_core::rpc_clients::call_and_retry_indefinitely; -use hyperlane_core::{BatchResult, QueueOperation, H512}; +use hyperlane_core::{BatchResult, QueueOperation, ReorgPeriod, H512}; use itertools::Itertools; use tracing::instrument; @@ -31,11 +30,14 @@ use crate::interfaces::i_mailbox::{ IMailbox as EthereumMailboxInternal, ProcessCall, IMAILBOX_ABI, }; use crate::interfaces::mailbox::DispatchFilter; -use crate::tx::{call_with_lag, fill_tx_gas_params, report_tx}; -use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, TransactionOverrides}; +use crate::tx::{call_with_reorg_period, fill_tx_gas_params, report_tx}; +use crate::{ + BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod, + TransactionOverrides, +}; use super::multicall::{self, build_multicall}; -use super::utils::fetch_raw_logs_and_meta; +use super::utils::{fetch_raw_logs_and_meta, get_finalized_block_number}; impl std::fmt::Display for EthereumMailboxInternal where @@ -47,7 +49,7 @@ where } pub struct SequenceIndexerBuilder { - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -70,7 +72,7 @@ impl BuildableWithProvider for SequenceIndexerBuilder { } pub struct DeliveryIndexerBuilder { - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -100,7 +102,7 @@ where { contract: Arc>, provider: Arc, - reorg_period: u32, + reorg_period: EthereumReorgPeriod, } impl EthereumMailboxIndexer @@ -108,7 +110,11 @@ where M: Middleware + 'static, { /// Create new EthereumMailboxIndexer - pub fn new(provider: Arc, locator: &ContractLocator, reorg_period: u32) -> Self { + pub fn new( + provider: Arc, + locator: &ContractLocator, + reorg_period: EthereumReorgPeriod, + ) -> Self { let contract = Arc::new(EthereumMailboxInternal::new( locator.address, provider.clone(), @@ -122,13 +128,7 @@ where #[instrument(level = "debug", err, ret, skip(self))] async fn get_finalized_block_number(&self) -> ChainResult { - Ok(self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .as_u32() - .saturating_sub(self.reorg_period)) + get_finalized_block_number(&self.provider, &self.reorg_period).await } } @@ -143,6 +143,7 @@ where /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn fetch_logs_in_range( &self, range: RangeInclusive, @@ -198,6 +199,7 @@ where M: Middleware + 'static, { #[instrument(err, skip(self), ret)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = Indexer::::get_finalized_block_number(self).await?; let sequence = self.contract.nonce().block(u64::from(tip)).call().await?; @@ -216,6 +218,7 @@ where /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn fetch_logs_in_range( &self, range: RangeInclusive, @@ -457,8 +460,9 @@ where M: Middleware + 'static, { #[instrument(skip(self))] - async fn count(&self, maybe_lag: Option) -> ChainResult { - let call = call_with_lag(self.contract.nonce(), &self.provider, maybe_lag).await?; + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + let call = + call_with_reorg_period(self.contract.nonce(), &self.provider, reorg_period).await?; let nonce = call.call().await?; Ok(nonce) } @@ -616,8 +620,12 @@ mod test { /// An amount of gas to add to the estimated gas const GAS_ESTIMATE_BUFFER: u32 = 75_000; - #[tokio::test] - async fn test_process_estimate_costs_sets_l2_gas_limit_for_arbitrum() { + fn get_test_mailbox( + domain: HyperlaneDomain, + ) -> ( + EthereumMailbox>>, + Arc, + ) { let mock_provider = Arc::new(MockProvider::new()); let provider = Arc::new(Provider::new(mock_provider.clone())); let connection_conf = ConnectionConf { @@ -632,12 +640,19 @@ mod test { provider.clone(), &connection_conf, &ContractLocator { - // An Arbitrum Nitro chain - domain: &HyperlaneDomain::Known(KnownHyperlaneDomain::PlumeTestnet), + domain: &domain, // Address doesn't matter because we're using a MockProvider address: H256::default(), }, ); + (mailbox, mock_provider) + } + + #[tokio::test] + async fn test_process_estimate_costs_sets_l2_gas_limit_for_arbitrum() { + // An Arbitrum Nitro chain + let (mailbox, mock_provider) = + get_test_mailbox(HyperlaneDomain::Known(KnownHyperlaneDomain::PlumeTestnet)); let message = HyperlaneMessage::default(); let metadata: Vec = vec![]; @@ -659,12 +674,17 @@ mod test { EthersU256::from(ethers::utils::parse_units("15", "gwei").unwrap()).into(); mock_provider.push(gas_price).unwrap(); - // RPC 3: eth_estimateGas to the ArbitrumNodeInterface's estimateRetryableTicket function by process_estimate_costs + // RPC 4: eth_estimateGas to the ArbitrumNodeInterface's estimateRetryableTicket function by process_estimate_costs let l2_gas_limit = U256::from(200000); // 200k gas mock_provider.push(l2_gas_limit).unwrap(); - // RPC 2: eth_getBlockByNumber from the estimate_eip1559_fees call in process_contract_call - mock_provider.push(Block::::default()).unwrap(); + let latest_block: Block = Block { + gas_limit: ethers::types::U256::MAX, + ..Block::::default() + }; + // RPC 3: eth_getBlockByNumber from the fill_tx_gas_params call in process_contract_call + // to get the latest block gas limit and for eip 1559 fee estimation + mock_provider.push(latest_block).unwrap(); // RPC 1: eth_estimateGas from the estimate_gas call in process_contract_call // Return 1M gas @@ -676,7 +696,7 @@ mod test { .await .unwrap(); - // The TxCostEstimat's gas limit includes the buffer + // The TxCostEstimate's gas limit includes the buffer let estimated_gas_limit = gas_limit.saturating_add(GAS_ESTIMATE_BUFFER.into()); assert_eq!( @@ -688,4 +708,52 @@ mod test { }, ); } + + #[tokio::test] + async fn test_tx_gas_limit_caps_at_block_gas_limit() { + let (mailbox, mock_provider) = + get_test_mailbox(HyperlaneDomain::Known(KnownHyperlaneDomain::Ethereum)); + + let message = HyperlaneMessage::default(); + let metadata: Vec = vec![]; + + // The MockProvider responses we push are processed in LIFO + // order, so we start with the final RPCs and work toward the first + // RPCs + + // RPC 4: eth_gasPrice by process_estimate_costs + // Return 15 gwei + let gas_price: U256 = + EthersU256::from(ethers::utils::parse_units("15", "gwei").unwrap()).into(); + mock_provider.push(gas_price).unwrap(); + + let latest_block_gas_limit = U256::from(12345u32); + let latest_block: Block = Block { + gas_limit: latest_block_gas_limit.into(), + ..Block::::default() + }; + // RPC 3: eth_getBlockByNumber from the fill_tx_gas_params call in process_contract_call + // to get the latest block gas limit and for eip 1559 fee estimation + mock_provider.push(latest_block).unwrap(); + + // RPC 1: eth_estimateGas from the estimate_gas call in process_contract_call + // Return 1M gas + let gas_limit = U256::from(1000000u32); + mock_provider.push(gas_limit).unwrap(); + + let tx_cost_estimate = mailbox + .process_estimate_costs(&message, &metadata) + .await + .unwrap(); + + assert_eq!( + tx_cost_estimate, + TxCostEstimate { + // The block gas limit is the cap + gas_limit: latest_block_gas_limit, + gas_price: gas_price.try_into().unwrap(), + l2_gas_limit: None, + }, + ); + } } diff --git a/rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs similarity index 82% rename from rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs index c307621c2..f098d75ca 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs @@ -1,5 +1,4 @@ #![allow(missing_docs)] -use std::num::NonZeroU64; use std::ops::RangeInclusive; use std::sync::Arc; @@ -10,18 +9,18 @@ use hyperlane_core::rpc_clients::call_and_retry_indefinitely; use tracing::instrument; use hyperlane_core::{ - ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, - HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, LogMeta, - MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, H512, + ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + HyperlaneProvider, Indexed, Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, ReorgPeriod, + SequenceAwareIndexer, H256, H512, }; use crate::interfaces::merkle_tree_hook::{ InsertedIntoTreeFilter, MerkleTreeHook as MerkleTreeHookContract, Tree, }; -use crate::tx::call_with_lag; -use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider}; +use crate::tx::call_with_reorg_period; +use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, EthereumReorgPeriod}; -use super::utils::fetch_raw_logs_and_meta; +use super::utils::{fetch_raw_logs_and_meta, get_finalized_block_number}; // We don't need the reverse of this impl, so it's ok to disable the clippy lint #[allow(clippy::from_over_into)] @@ -58,7 +57,7 @@ impl BuildableWithProvider for MerkleTreeHookBuilder { } pub struct MerkleTreeHookIndexerBuilder { - pub reorg_period: u32, + pub reorg_period: EthereumReorgPeriod, } #[async_trait] @@ -88,7 +87,7 @@ where { contract: Arc>, provider: Arc, - reorg_period: u32, + reorg_period: EthereumReorgPeriod, } impl EthereumMerkleTreeHookIndexer @@ -96,7 +95,11 @@ where M: Middleware + 'static, { /// Create new EthereumMerkleTreeHookIndexer - pub fn new(provider: Arc, locator: &ContractLocator, reorg_period: u32) -> Self { + pub fn new( + provider: Arc, + locator: &ContractLocator, + reorg_period: EthereumReorgPeriod, + ) -> Self { Self { contract: Arc::new(MerkleTreeHookContract::new( locator.address, @@ -115,6 +118,7 @@ where { /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn fetch_logs_in_range( &self, range: RangeInclusive, @@ -140,14 +144,9 @@ where } #[instrument(level = "debug", err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_finalized_block_number(&self) -> ChainResult { - Ok(self - .provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .as_u32() - .saturating_sub(self.reorg_period)) + get_finalized_block_number(&self.provider, &self.reorg_period).await } async fn fetch_logs_by_tx_hash( @@ -251,9 +250,13 @@ where M: Middleware + 'static, { #[instrument(skip(self))] - async fn latest_checkpoint(&self, maybe_lag: Option) -> ChainResult { - let call = - call_with_lag(self.contract.latest_checkpoint(), &self.provider, maybe_lag).await?; + async fn latest_checkpoint(&self, reorg_period: &ReorgPeriod) -> ChainResult { + let call = call_with_reorg_period( + self.contract.latest_checkpoint(), + &self.provider, + reorg_period, + ) + .await?; let (root, index) = call.call().await?; Ok(Checkpoint { @@ -266,15 +269,17 @@ where #[instrument(skip(self))] #[allow(clippy::needless_range_loop)] - async fn tree(&self, maybe_lag: Option) -> ChainResult { - let call = call_with_lag(self.contract.tree(), &self.provider, maybe_lag).await?; + async fn tree(&self, reorg_period: &ReorgPeriod) -> ChainResult { + let call = + call_with_reorg_period(self.contract.tree(), &self.provider, reorg_period).await?; Ok(call.call().await?.into()) } #[instrument(skip(self))] - async fn count(&self, maybe_lag: Option) -> ChainResult { - let call = call_with_lag(self.contract.count(), &self.provider, maybe_lag).await?; + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + let call = + call_with_reorg_period(self.contract.count(), &self.provider, reorg_period).await?; let count = call.call().await?; Ok(count) } diff --git a/rust/chains/hyperlane-ethereum/src/contracts/mod.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/mod.rs similarity index 78% rename from rust/chains/hyperlane-ethereum/src/contracts/mod.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/mod.rs index 1a39fae07..f8475f9e7 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/mod.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/mod.rs @@ -1,5 +1,7 @@ pub use {interchain_gas::*, mailbox::*, merkle_tree_hook::*, validator_announce::*}; +pub(crate) use utils::get_finalized_block_number; + mod interchain_gas; mod mailbox; mod merkle_tree_hook; diff --git a/rust/chains/hyperlane-ethereum/src/contracts/multicall.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/contracts/multicall.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/multicall.rs diff --git a/rust/chains/hyperlane-ethereum/src/contracts/utils.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs similarity index 57% rename from rust/chains/hyperlane-ethereum/src/contracts/utils.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs index 290519918..5e9c78201 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/utils.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/utils.rs @@ -6,7 +6,10 @@ use ethers::{ types::{H160 as EthersH160, H256 as EthersH256}, }; use ethers_contract::{ContractError, EthEvent, LogMeta as EthersLogMeta}; -use hyperlane_core::{ChainResult, LogMeta, H512}; +use hyperlane_core::{ChainCommunicationError, ChainResult, LogMeta, H512}; +use tracing::instrument; + +use crate::EthereumReorgPeriod; pub async fn fetch_raw_logs_and_meta( tx_hash: H512, @@ -44,3 +47,33 @@ where .collect(); Ok(logs) } + +#[instrument(level = "trace", err, ret, skip(provider))] +pub async fn get_finalized_block_number( + provider: &M, + reorg_period: &EthereumReorgPeriod, +) -> ChainResult +where + M: Middleware + 'static, +{ + let number = match *reorg_period { + EthereumReorgPeriod::Blocks(blocks) => provider + .get_block_number() + .await + .map_err(ChainCommunicationError::from_other)? + .as_u32() + .saturating_sub(blocks), + + EthereumReorgPeriod::Tag(tag) => provider + .get_block(tag) + .await + .map_err(ChainCommunicationError::from_other)? + .and_then(|block| block.number) + .ok_or(ChainCommunicationError::CustomError( + "Unable to get finalized block number".into(), + ))? + .as_u32(), + }; + + Ok(number) +} diff --git a/rust/chains/hyperlane-ethereum/src/contracts/validator_announce.rs b/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs similarity index 98% rename from rust/chains/hyperlane-ethereum/src/contracts/validator_announce.rs rename to rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs index 2479f5a19..2da1078f9 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/validator_announce.rs +++ b/rust/main/chains/hyperlane-ethereum/src/contracts/validator_announce.rs @@ -163,6 +163,7 @@ where } #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn announce(&self, announcement: SignedType) -> ChainResult { let contract_call = self.announce_contract_call(announcement).await?; let receipt = report_tx(contract_call).await?; diff --git a/rust/chains/hyperlane-ethereum/src/error.rs b/rust/main/chains/hyperlane-ethereum/src/error.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/error.rs rename to rust/main/chains/hyperlane-ethereum/src/error.rs diff --git a/rust/chains/hyperlane-ethereum/src/ism/aggregation_ism.rs b/rust/main/chains/hyperlane-ethereum/src/ism/aggregation_ism.rs similarity index 97% rename from rust/chains/hyperlane-ethereum/src/ism/aggregation_ism.rs rename to rust/main/chains/hyperlane-ethereum/src/ism/aggregation_ism.rs index 5f685cf26..61b371f3b 100644 --- a/rust/chains/hyperlane-ethereum/src/ism/aggregation_ism.rs +++ b/rust/main/chains/hyperlane-ethereum/src/ism/aggregation_ism.rs @@ -93,6 +93,7 @@ where M: Middleware + 'static, { #[instrument(err)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn modules_and_threshold( &self, message: &HyperlaneMessage, diff --git a/rust/chains/hyperlane-ethereum/src/ism/ccip_read_ism.rs b/rust/main/chains/hyperlane-ethereum/src/ism/ccip_read_ism.rs similarity index 97% rename from rust/chains/hyperlane-ethereum/src/ism/ccip_read_ism.rs rename to rust/main/chains/hyperlane-ethereum/src/ism/ccip_read_ism.rs index 8f0ef6b91..dc76d43fa 100644 --- a/rust/chains/hyperlane-ethereum/src/ism/ccip_read_ism.rs +++ b/rust/main/chains/hyperlane-ethereum/src/ism/ccip_read_ism.rs @@ -90,6 +90,7 @@ where M: Middleware + 'static, { #[instrument(err)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_offchain_verify_info(&self, message: Vec) -> ChainResult<()> { self.contract .get_offchain_verify_info(message.into()) diff --git a/rust/chains/hyperlane-ethereum/src/ism/interchain_security_module.rs b/rust/main/chains/hyperlane-ethereum/src/ism/interchain_security_module.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/ism/interchain_security_module.rs rename to rust/main/chains/hyperlane-ethereum/src/ism/interchain_security_module.rs diff --git a/rust/chains/hyperlane-ethereum/src/ism/mod.rs b/rust/main/chains/hyperlane-ethereum/src/ism/mod.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/ism/mod.rs rename to rust/main/chains/hyperlane-ethereum/src/ism/mod.rs diff --git a/rust/chains/hyperlane-ethereum/src/ism/multisig_ism.rs b/rust/main/chains/hyperlane-ethereum/src/ism/multisig_ism.rs similarity index 97% rename from rust/chains/hyperlane-ethereum/src/ism/multisig_ism.rs rename to rust/main/chains/hyperlane-ethereum/src/ism/multisig_ism.rs index 87e6b030b..2c71c900c 100644 --- a/rust/chains/hyperlane-ethereum/src/ism/multisig_ism.rs +++ b/rust/main/chains/hyperlane-ethereum/src/ism/multisig_ism.rs @@ -99,6 +99,7 @@ where M: Middleware + 'static, { #[instrument(err)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn validators_and_threshold( &self, message: &HyperlaneMessage, diff --git a/rust/chains/hyperlane-ethereum/src/ism/routing_ism.rs b/rust/main/chains/hyperlane-ethereum/src/ism/routing_ism.rs similarity index 97% rename from rust/chains/hyperlane-ethereum/src/ism/routing_ism.rs rename to rust/main/chains/hyperlane-ethereum/src/ism/routing_ism.rs index d23d06ca6..39aaf7ecd 100644 --- a/rust/chains/hyperlane-ethereum/src/ism/routing_ism.rs +++ b/rust/main/chains/hyperlane-ethereum/src/ism/routing_ism.rs @@ -90,6 +90,7 @@ where M: Middleware + 'static, { #[instrument(err)] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn route(&self, message: &HyperlaneMessage) -> ChainResult { let ism = self .contract diff --git a/rust/chains/hyperlane-ethereum/src/lib.rs b/rust/main/chains/hyperlane-ethereum/src/lib.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/lib.rs rename to rust/main/chains/hyperlane-ethereum/src/lib.rs diff --git a/rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs similarity index 99% rename from rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs rename to rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs index 6d57a10e2..9c52a6bb3 100644 --- a/rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs @@ -32,6 +32,7 @@ impl Debug for EthereumFallbackProvider where C: JsonRpcClient + PrometheusJsonRpcClientConfigExt, { + #[allow(clippy::get_first)] // TODO: `rustc` 1.80.1 clippy issue fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("FallbackProvider") .field( diff --git a/rust/chains/hyperlane-ethereum/src/rpc_clients/mod.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/mod.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/rpc_clients/mod.rs rename to rust/main/chains/hyperlane-ethereum/src/rpc_clients/mod.rs diff --git a/rust/chains/hyperlane-ethereum/src/rpc_clients/provider.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs similarity index 76% rename from rust/chains/hyperlane-ethereum/src/rpc_clients/provider.rs rename to rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs index a5e333342..d89cf9f43 100644 --- a/rust/chains/hyperlane-ethereum/src/rpc_clients/provider.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/provider.rs @@ -7,7 +7,7 @@ use async_trait::async_trait; use derive_new::new; use ethers::prelude::Middleware; use ethers_core::{abi::Address, types::BlockNumber}; -use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, U256}; +use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, H512, U256}; use tokio::time::sleep; use tracing::instrument; @@ -48,25 +48,50 @@ where M: Middleware + 'static, { #[instrument(err, skip(self))] - async fn get_block_by_hash(&self, hash: &H256) -> ChainResult { - let block = get_with_retry_on_none(hash, |h| { - let eth_h256: ethers_core_types::H256 = h.into(); - self.provider.get_block(eth_h256) - }) + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn get_block_by_height(&self, height: u64) -> ChainResult { + let block = get_with_retry_on_none( + &height, + |h| self.provider.get_block(*h), + |h| HyperlaneProviderError::CouldNotFindBlockByHeight(*h), + ) .await?; - Ok(BlockInfo { - hash: *hash, + + let block_height = block + .number + .ok_or(HyperlaneProviderError::CouldNotFindBlockByHeight(height))? + .as_u64(); + + if block_height != height { + Err(HyperlaneProviderError::IncorrectBlockByHeight( + height, + block_height, + ))?; + } + + let block_hash = block + .hash + .ok_or(HyperlaneProviderError::BlockWithoutHash(height))?; + + let block_info = BlockInfo { + hash: block_hash.into(), timestamp: block.timestamp.as_u64(), - number: block - .number - .ok_or(HyperlaneProviderError::BlockIsNotPartOfChainYet(*hash))? - .as_u64(), - }) + number: block_height, + }; + + Ok(block_info) } #[instrument(err, skip(self))] - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult { - let txn = get_with_retry_on_none(hash, |h| self.provider.get_transaction(*h)).await?; + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { + let txn = get_with_retry_on_none( + hash, + |h| self.provider.get_transaction(*h), + |h| HyperlaneProviderError::CouldNotFindTransactionByHash(*h), + ) + .await?; + let receipt = self .provider .get_transaction_receipt(*hash) @@ -95,6 +120,7 @@ where } #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn is_contract(&self, address: &H256) -> ChainResult { let code = self .provider @@ -105,6 +131,7 @@ where } #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_balance(&self, address: String) -> ChainResult { // Can't use the address directly as a string, because ethers interprets it // as an ENS name rather than an address. @@ -189,22 +216,24 @@ impl BuildableWithProvider for HyperlaneProviderBuilder { /// Call a get function that returns a Result> and retry if the inner /// option is None. This can happen because the provider has not discovered the /// object we are looking for yet. -async fn get_with_retry_on_none(hash: &H256, get: F) -> ChainResult +async fn get_with_retry_on_none( + id: &I, + get: F, + not_found_error: N, +) -> ChainResult where - F: Fn(&H256) -> O, + F: Fn(&I) -> O, O: Future, E>>, E: std::error::Error + Send + Sync + 'static, + N: Fn(&I) -> HyperlaneProviderError, { for _ in 0..3 { - if let Some(t) = get(hash) - .await - .map_err(ChainCommunicationError::from_other)? - { + if let Some(t) = get(id).await.map_err(ChainCommunicationError::from_other)? { return Ok(t); } else { sleep(Duration::from_secs(5)).await; continue; }; } - Err(HyperlaneProviderError::CouldNotFindObjectByHash(*hash).into()) + Err(not_found_error(id).into()) } diff --git a/rust/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs rename to rust/main/chains/hyperlane-ethereum/src/rpc_clients/retrying.rs diff --git a/rust/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs similarity index 99% rename from rust/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs rename to rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs index 440e46368..67bad0893 100644 --- a/rust/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs +++ b/rust/main/chains/hyperlane-ethereum/src/rpc_clients/trait_builder.rs @@ -213,13 +213,11 @@ pub trait BuildableWithProvider { M: Middleware + 'static, { Ok(if let Some(signer) = signer { - println!("Building provider with signer"); let signing_provider = wrap_with_signer(provider, signer) .await .map_err(ChainCommunicationError::from_other)?; self.build_with_provider(signing_provider, conn, locator) } else { - println!("Building provider without signer"); self.build_with_provider(provider, conn, locator) } .await) diff --git a/rust/chains/hyperlane-ethereum/src/signer/mod.rs b/rust/main/chains/hyperlane-ethereum/src/signer/mod.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/src/signer/mod.rs rename to rust/main/chains/hyperlane-ethereum/src/signer/mod.rs diff --git a/rust/chains/hyperlane-ethereum/src/signer/singleton.rs b/rust/main/chains/hyperlane-ethereum/src/signer/singleton.rs similarity index 94% rename from rust/chains/hyperlane-ethereum/src/signer/singleton.rs rename to rust/main/chains/hyperlane-ethereum/src/signer/singleton.rs index 790c8e73a..7925d1ec9 100644 --- a/rust/chains/hyperlane-ethereum/src/signer/singleton.rs +++ b/rust/main/chains/hyperlane-ethereum/src/signer/singleton.rs @@ -38,6 +38,14 @@ pub struct SingletonSignerHandle { tx: mpsc::UnboundedSender, } +#[cfg(feature = "test-utils")] +impl SingletonSignerHandle { + /// Create a new handle for testing purposes + pub fn new(address: H160, tx: mpsc::UnboundedSender) -> Self { + Self { address, tx } + } +} + impl fmt::Debug for SingletonSignerHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("SingletonSignerHandle") diff --git a/rust/chains/hyperlane-ethereum/src/tx.rs b/rust/main/chains/hyperlane-ethereum/src/tx.rs similarity index 85% rename from rust/chains/hyperlane-ethereum/src/tx.rs rename to rust/main/chains/hyperlane-ethereum/src/tx.rs index 290260711..05cc4da44 100644 --- a/rust/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/main/chains/hyperlane-ethereum/src/tx.rs @@ -1,4 +1,3 @@ -use std::num::NonZeroU64; use std::sync::Arc; use std::time::Duration; @@ -6,7 +5,7 @@ use ethers::{ abi::Detokenize, prelude::{NameOrAddress, TransactionReceipt}, providers::{JsonRpcClient, PendingTransaction, ProviderError}, - types::Eip1559TransactionRequest, + types::{Block, Eip1559TransactionRequest, TxHash}, }; use ethers_contract::builders::ContractCall; use ethers_core::{ @@ -16,10 +15,12 @@ use ethers_core::{ EIP1559_FEE_ESTIMATION_REWARD_PERCENTILE, }, }; -use hyperlane_core::{utils::bytes_to_hex, ChainCommunicationError, ChainResult, H256, U256}; -use tracing::{debug, error, info}; +use hyperlane_core::{ + utils::bytes_to_hex, ChainCommunicationError, ChainResult, ReorgPeriod, H256, U256, +}; +use tracing::{debug, error, info, warn}; -use crate::{Middleware, TransactionOverrides}; +use crate::{EthereumReorgPeriod, Middleware, TransactionOverrides}; /// An amount of gas to add to the estimated gas pub const GAS_ESTIMATE_BUFFER: u32 = 75_000; @@ -107,6 +108,24 @@ where } else { estimated_gas_limit }; + + // Cap the gas limit to the block gas limit + let latest_block = provider + .get_block(BlockNumber::Latest) + .await + .map_err(ChainCommunicationError::from_other)? + .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))?; + let block_gas_limit: U256 = latest_block.gas_limit.into(); + let gas_limit = if gas_limit > block_gas_limit { + warn!( + ?gas_limit, + ?block_gas_limit, + "Gas limit for transaction is higher than the block gas limit. Capping it to the block gas limit." + ); + block_gas_limit + } else { + gas_limit + }; debug!(?estimated_gas_limit, gas_override=?transaction_overrides.gas_limit, used_gas_limit=?gas_limit, "Gas limit set for transaction"); if let Some(gas_price) = transaction_overrides.gas_price { @@ -114,7 +133,8 @@ where return Ok(tx.gas_price(gas_price).gas(gas_limit)); } - let Ok((base_fee, max_fee, max_priority_fee)) = estimate_eip1559_fees(provider, None).await + let Ok((base_fee, max_fee, max_priority_fee)) = + estimate_eip1559_fees(provider, None, &latest_block).await else { // Is not EIP 1559 chain return Ok(tx.gas(gas_limit)); @@ -169,15 +189,12 @@ type FeeEstimator = fn(EthersU256, Vec>) -> (EthersU256, EthersU async fn estimate_eip1559_fees( provider: Arc, estimator: Option, + latest_block: &Block, ) -> ChainResult<(EthersU256, EthersU256, EthersU256)> where M: Middleware + 'static, { - let base_fee_per_gas = provider - .get_block(BlockNumber::Latest) - .await - .map_err(ChainCommunicationError::from_other)? - .ok_or_else(|| ProviderError::CustomError("Latest block not found".into()))? + let base_fee_per_gas = latest_block .base_fee_per_gas .ok_or_else(|| ProviderError::CustomError("EIP-1559 not activated".into()))?; @@ -200,23 +217,20 @@ where Ok((base_fee_per_gas, max_fee_per_gas, max_priority_fee_per_gas)) } -pub(crate) async fn call_with_lag( +pub(crate) async fn call_with_reorg_period( call: ethers::contract::builders::ContractCall, provider: &M, - maybe_lag: Option, + reorg_period: &ReorgPeriod, ) -> ChainResult> where M: Middleware + 'static, T: Detokenize, { - if let Some(lag) = maybe_lag { - let fixed_block_number: BlockNumber = provider - .get_block_number() - .await - .map_err(ChainCommunicationError::from_other)? - .saturating_sub(lag.get().into()) - .into(); - Ok(call.block(fixed_block_number)) + if !reorg_period.is_none() { + let block_id = EthereumReorgPeriod::try_from(reorg_period)? + .into_block_id(provider) + .await?; + Ok(call.block(block_id)) } else { Ok(call) } diff --git a/rust/chains/hyperlane-ethereum/tests/signer_output.rs b/rust/main/chains/hyperlane-ethereum/tests/signer_output.rs similarity index 100% rename from rust/chains/hyperlane-ethereum/tests/signer_output.rs rename to rust/main/chains/hyperlane-ethereum/tests/signer_output.rs diff --git a/rust/chains/hyperlane-fuel/.gitignore b/rust/main/chains/hyperlane-fuel/.gitignore similarity index 100% rename from rust/chains/hyperlane-fuel/.gitignore rename to rust/main/chains/hyperlane-fuel/.gitignore diff --git a/rust/chains/hyperlane-fuel/Cargo.toml b/rust/main/chains/hyperlane-fuel/Cargo.toml similarity index 91% rename from rust/chains/hyperlane-fuel/Cargo.toml rename to rust/main/chains/hyperlane-fuel/Cargo.toml index d2a711feb..bb82a9c07 100644 --- a/rust/chains/hyperlane-fuel/Cargo.toml +++ b/rust/main/chains/hyperlane-fuel/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "hyperlane-fuel" documentation.workspace = true @@ -13,13 +11,14 @@ version.workspace = true anyhow.workspace = true async-trait.workspace = true fuels.workspace = true +futures.workspace = true serde.workspace = true thiserror.workspace = true tracing-futures.workspace = true tracing.workspace = true url.workspace = true -hyperlane-core = { path = "../../hyperlane-core", features = ["async"]} +hyperlane-core = { path = "../../hyperlane-core", features = ["async"] } [build-dependencies] abigen = { path = "../../utils/abigen", features = ["fuels"] } diff --git a/rust/main/chains/hyperlane-fuel/abis/Mailbox.abi.json b/rust/main/chains/hyperlane-fuel/abis/Mailbox.abi.json new file mode 100644 index 000000000..5c357aa4b --- /dev/null +++ b/rust/main/chains/hyperlane-fuel/abis/Mailbox.abi.json @@ -0,0 +1,1282 @@ +{ + "encoding": "1", + "types": [ + { + "typeId": 0, + "type": "()", + "components": [], + "typeParameters": null + }, + { + "typeId": 1, + "type": "b256", + "components": null, + "typeParameters": null + }, + { + "typeId": 2, + "type": "bool", + "components": null, + "typeParameters": null + }, + { + "typeId": 3, + "type": "enum AccessError", + "components": [ + { + "name": "NotOwner", + "type": 0, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 4, + "type": "enum Identity", + "components": [ + { + "name": "Address", + "type": 11, + "typeArguments": null + }, + { + "name": "ContractId", + "type": 13, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 5, + "type": "enum InitializationError", + "components": [ + { + "name": "CannotReinitialized", + "type": 0, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 6, + "type": "enum MailboxError", + "components": [ + { + "name": "InvalidISMAddress", + "type": 0, + "typeArguments": null + }, + { + "name": "InvalidHookAddress", + "type": 0, + "typeArguments": null + }, + { + "name": "InvalidProtocolVersion", + "type": 27, + "typeArguments": null + }, + { + "name": "InvalidMessageOrigin", + "type": 25, + "typeArguments": null + }, + { + "name": "MessageAlreadyDelivered", + "type": 0, + "typeArguments": null + }, + { + "name": "MessageVerificationFailed", + "type": 0, + "typeArguments": null + }, + { + "name": "AlreadyInitialized", + "type": 0, + "typeArguments": null + }, + { + "name": "MessageTooLarge", + "type": 26, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 7, + "type": "enum PauseError", + "components": [ + { + "name": "Paused", + "type": 0, + "typeArguments": null + }, + { + "name": "NotPaused", + "type": 0, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 8, + "type": "enum ReentrancyError", + "components": [ + { + "name": "NonReentrant", + "type": 0, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 9, + "type": "enum State", + "components": [ + { + "name": "Uninitialized", + "type": 0, + "typeArguments": null + }, + { + "name": "Initialized", + "type": 4, + "typeArguments": null + }, + { + "name": "Revoked", + "type": 0, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 10, + "type": "raw untyped ptr", + "components": null, + "typeParameters": null + }, + { + "typeId": 11, + "type": "struct Address", + "components": [ + { + "name": "bits", + "type": 1, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 12, + "type": "struct Bytes", + "components": [ + { + "name": "buf", + "type": 23, + "typeArguments": null + }, + { + "name": "len", + "type": 26, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 13, + "type": "struct ContractId", + "components": [ + { + "name": "bits", + "type": 1, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 14, + "type": "struct DefaultHookSetEvent", + "components": [ + { + "name": "module", + "type": 13, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 15, + "type": "struct DefaultIsmSetEvent", + "components": [ + { + "name": "module", + "type": 13, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 16, + "type": "struct DispatchEvent", + "components": [ + { + "name": "message_id", + "type": 1, + "typeArguments": null + }, + { + "name": "destination_domain", + "type": 25, + "typeArguments": null + }, + { + "name": "recipient_address", + "type": 1, + "typeArguments": null + }, + { + "name": "message", + "type": 18, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 17, + "type": "struct DispatchIdEvent", + "components": [ + { + "name": "message_id", + "type": 1, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 18, + "type": "struct EncodedMessage", + "components": [ + { + "name": "bytes", + "type": 12, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 19, + "type": "struct OwnershipRenounced", + "components": [ + { + "name": "previous_owner", + "type": 4, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 20, + "type": "struct OwnershipSet", + "components": [ + { + "name": "new_owner", + "type": 4, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 21, + "type": "struct OwnershipTransferred", + "components": [ + { + "name": "new_owner", + "type": 4, + "typeArguments": null + }, + { + "name": "previous_owner", + "type": 4, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 22, + "type": "struct ProcessEvent", + "components": [ + { + "name": "message_id", + "type": 1, + "typeArguments": null + }, + { + "name": "origin", + "type": 25, + "typeArguments": null + }, + { + "name": "sender", + "type": 1, + "typeArguments": null + }, + { + "name": "recipient", + "type": 1, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 23, + "type": "struct RawBytes", + "components": [ + { + "name": "ptr", + "type": 10, + "typeArguments": null + }, + { + "name": "cap", + "type": 26, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 24, + "type": "struct RequiredHookSetEvent", + "components": [ + { + "name": "module", + "type": 13, + "typeArguments": null + } + ], + "typeParameters": null + }, + { + "typeId": 25, + "type": "u32", + "components": null, + "typeParameters": null + }, + { + "typeId": 26, + "type": "u64", + "components": null, + "typeParameters": null + }, + { + "typeId": 27, + "type": "u8", + "components": null, + "typeParameters": null + } + ], + "functions": [ + { + "inputs": [], + "name": "default_hook", + "output": { + "name": "", + "type": 13, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Gets the default hook used for message processing." + ] + }, + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "default_ism", + "output": { + "name": "", + "type": 13, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Gets the default ISM used for message verification." + ] + }, + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [ + { + "name": "message_id", + "type": 1, + "typeArguments": null + } + ], + "name": "delivered", + "output": { + "name": "", + "type": 2, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Returns true if the message has been processed." + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " ### Arguments" + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `message_id` - The unique identifier of the message." + ] + }, + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [ + { + "name": "destination_domain", + "type": 25, + "typeArguments": null + }, + { + "name": "recipient_address", + "type": 1, + "typeArguments": null + }, + { + "name": "message_body", + "type": 12, + "typeArguments": null + }, + { + "name": "metadata", + "type": 12, + "typeArguments": null + }, + { + "name": "hook", + "type": 13, + "typeArguments": null + } + ], + "name": "dispatch", + "output": { + "name": "", + "type": 1, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Dispatches a message to the destination domain and recipient." + ] + }, + { + "name": "doc-comment", + "arguments": [ + " Returns the message's ID." + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " ### Arguments" + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `destination_domain` - The domain of the destination chain." + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `recipient` - Address of the recipient on the destination chain." + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `message_body` - Raw bytes content of the message body." + ] + }, + { + "name": "payable", + "arguments": [] + }, + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [ + { + "name": "owner", + "type": 1, + "typeArguments": null + }, + { + "name": "default_ism", + "type": 1, + "typeArguments": null + }, + { + "name": "default_hook", + "type": 1, + "typeArguments": null + }, + { + "name": "required_hook", + "type": 1, + "typeArguments": null + } + ], + "name": "initialize", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Initializes the contract." + ] + }, + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "latest_dispatched_id", + "output": { + "name": "", + "type": 1, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "local_domain", + "output": { + "name": "", + "type": 25, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Returns the domain of the chain where the contract is deployed." + ] + }, + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "nonce", + "output": { + "name": "", + "type": 25, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [ + { + "name": "metadata", + "type": 12, + "typeArguments": null + }, + { + "name": "message", + "type": 12, + "typeArguments": null + } + ], + "name": "process", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Processes a message." + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " ### Arguments" + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `metadata` - The metadata for ISM verification." + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `message` - The message as emitted by dispatch." + ] + }, + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [ + { + "name": "destination_domain", + "type": 25, + "typeArguments": null + }, + { + "name": "recipient_address", + "type": 1, + "typeArguments": null + }, + { + "name": "message_body", + "type": 12, + "typeArguments": null + }, + { + "name": "metadata", + "type": 12, + "typeArguments": null + }, + { + "name": "hook", + "type": 13, + "typeArguments": null + } + ], + "name": "quote_dispatch", + "output": { + "name": "", + "type": 26, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Quotes the cost of dispatching a message to the destination domain and recipient." + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " ### Arguments" + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `destination_domain` - The domain of the destination chain." + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `recipient` - Address of the recipient on the destination chain." + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `message_body` - Raw bytes content of the message body." + ] + }, + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [ + { + "name": "recipient", + "type": 13, + "typeArguments": null + } + ], + "name": "recipient_ism", + "output": { + "name": "", + "type": 13, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "required_hook", + "output": { + "name": "", + "type": 13, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Gets the required hook used for message processing." + ] + }, + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [ + { + "name": "module", + "type": 13, + "typeArguments": null + } + ], + "name": "set_default_hook", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Sets the default hook used for message processing." + ] + }, + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + }, + { + "inputs": [ + { + "name": "module", + "type": 13, + "typeArguments": null + } + ], + "name": "set_default_ism", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Sets the default ISM used for message verification." + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " ### Arguments" + ] + }, + { + "name": "doc-comment", + "arguments": [ + "" + ] + }, + { + "name": "doc-comment", + "arguments": [ + " * `module` - Address implementing ISM interface." + ] + }, + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [ + { + "name": "module", + "type": 13, + "typeArguments": null + } + ], + "name": "set_required_hook", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "doc-comment", + "arguments": [ + " Sets the required hook used for message processing." + ] + }, + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "is_paused", + "output": { + "name": "", + "type": 2, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "pause", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "unpause", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + }, + { + "inputs": [ + { + "name": "new_owner", + "type": 4, + "typeArguments": null + } + ], + "name": "initialize_ownership", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [], + "name": "only_owner", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "owner", + "output": { + "name": "", + "type": 9, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read" + ] + } + ] + }, + { + "inputs": [], + "name": "renounce_ownership", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "read", + "write" + ] + } + ] + }, + { + "inputs": [ + { + "name": "new_owner", + "type": 4, + "typeArguments": null + } + ], + "name": "transfer_ownership", + "output": { + "name": "", + "type": 0, + "typeArguments": null + }, + "attributes": [ + { + "name": "storage", + "arguments": [ + "write" + ] + } + ] + } + ], + "loggedTypes": [ + { + "logId": "5557842539076482339", + "loggedType": { + "name": "", + "type": 8, + "typeArguments": [] + } + }, + { + "logId": "10032608944051208538", + "loggedType": { + "name": "", + "type": 7, + "typeArguments": [] + } + }, + { + "logId": "4904025822840310122", + "loggedType": { + "name": "", + "type": 6, + "typeArguments": [] + } + }, + { + "logId": "10811788483172643035", + "loggedType": { + "name": "", + "type": 16, + "typeArguments": [] + } + }, + { + "logId": "2522729423758891677", + "loggedType": { + "name": "", + "type": 17, + "typeArguments": [] + } + }, + { + "logId": "2161305517876418151", + "loggedType": { + "name": "", + "type": 5, + "typeArguments": [] + } + }, + { + "logId": "16280289466020123285", + "loggedType": { + "name": "", + "type": 20, + "typeArguments": [] + } + }, + { + "logId": "7929134096091764817", + "loggedType": { + "name": "", + "type": 22, + "typeArguments": [] + } + }, + { + "logId": "4571204900286667806", + "loggedType": { + "name": "", + "type": 3, + "typeArguments": [] + } + }, + { + "logId": "14400248731700551312", + "loggedType": { + "name": "", + "type": 14, + "typeArguments": [] + } + }, + { + "logId": "1889958695533330661", + "loggedType": { + "name": "", + "type": 15, + "typeArguments": [] + } + }, + { + "logId": "1134555198745859881", + "loggedType": { + "name": "", + "type": 24, + "typeArguments": [] + } + }, + { + "logId": "4883303303013154842", + "loggedType": { + "name": "", + "type": 19, + "typeArguments": [] + } + }, + { + "logId": "12970362301975156672", + "loggedType": { + "name": "", + "type": 21, + "typeArguments": [] + } + } + ], + "messagesTypes": [], + "configurables": [ + { + "name": "LOCAL_DOMAIN", + "configurableType": { + "name": "", + "type": 25, + "typeArguments": null + }, + "offset": 51792 + } + ] +} \ No newline at end of file diff --git a/rust/chains/hyperlane-fuel/build.rs b/rust/main/chains/hyperlane-fuel/build.rs similarity index 100% rename from rust/chains/hyperlane-fuel/build.rs rename to rust/main/chains/hyperlane-fuel/build.rs diff --git a/rust/chains/hyperlane-fuel/src/conversions.rs b/rust/main/chains/hyperlane-fuel/src/conversions.rs similarity index 93% rename from rust/chains/hyperlane-fuel/src/conversions.rs rename to rust/main/chains/hyperlane-fuel/src/conversions.rs index fb420b3ef..af16b8067 100644 --- a/rust/chains/hyperlane-fuel/src/conversions.rs +++ b/rust/main/chains/hyperlane-fuel/src/conversions.rs @@ -52,3 +52,9 @@ impl_h256!( |v| fuels::prelude::ContractId::new(v.0), |v| H256::from(<[u8; 32]>::from(v)) ); + +impl_h256!( + fuels::types::Bytes32, + |v| fuels::types::Bytes32::new(v.0), + |v| H256::from(*v) +); diff --git a/rust/chains/hyperlane-fuel/src/interchain_gas.rs b/rust/main/chains/hyperlane-fuel/src/interchain_gas.rs similarity index 100% rename from rust/chains/hyperlane-fuel/src/interchain_gas.rs rename to rust/main/chains/hyperlane-fuel/src/interchain_gas.rs diff --git a/rust/chains/hyperlane-fuel/src/lib.rs b/rust/main/chains/hyperlane-fuel/src/lib.rs similarity index 100% rename from rust/chains/hyperlane-fuel/src/lib.rs rename to rust/main/chains/hyperlane-fuel/src/lib.rs diff --git a/rust/main/chains/hyperlane-fuel/src/mailbox.rs b/rust/main/chains/hyperlane-fuel/src/mailbox.rs new file mode 100644 index 000000000..fbe951abe --- /dev/null +++ b/rust/main/chains/hyperlane-fuel/src/mailbox.rs @@ -0,0 +1,315 @@ +use crate::{ + contracts::mailbox::Mailbox as FuelMailboxInner, conversions::*, ConnectionConf, FuelProvider, +}; +use async_trait::async_trait; +use fuels::{ + prelude::{Bech32ContractId, WalletUnlocked}, + tx::{Receipt, ScriptExecutionResult}, + types::{transaction::TxPolicies, Bytes}, +}; +use hyperlane_core::{ + utils::bytes_to_hex, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, + HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, + Indexed, Indexer, LogMeta, Mailbox, RawHyperlaneMessage, ReorgPeriod, SequenceAwareIndexer, + TxCostEstimate, TxOutcome, H256, H512, U256, +}; +use std::{ + collections::HashMap, + fmt::{Debug, Formatter}, + ops::RangeInclusive, +}; +use tracing::{instrument, warn}; + +/// A reference to a Mailbox contract on some Fuel chain +pub struct FuelMailbox { + contract: FuelMailboxInner, + provider: FuelProvider, + domain: HyperlaneDomain, +} + +impl FuelMailbox { + /// Create a new fuel mailbox + pub async fn new( + conf: &ConnectionConf, + locator: ContractLocator<'_>, + mut wallet: WalletUnlocked, + ) -> ChainResult { + let fuel_provider = FuelProvider::new(locator.domain.clone(), conf).await; + + wallet.set_provider(fuel_provider.provider().clone()); + let address = Bech32ContractId::from_h256(&locator.address); + + Ok(FuelMailbox { + contract: FuelMailboxInner::new(address, wallet), + domain: locator.domain.clone(), + provider: fuel_provider, + }) + } +} + +impl HyperlaneContract for FuelMailbox { + fn address(&self) -> H256 { + self.contract.contract_id().into_h256() + } +} + +impl HyperlaneChain for FuelMailbox { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(self.provider.clone()) + } +} + +impl Debug for FuelMailbox { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self as &dyn HyperlaneContract) + } +} + +#[async_trait] +impl Mailbox for FuelMailbox { + #[instrument(level = "debug", err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + assert!( + reorg_period.is_none(), + "Fuel does not support querying point-in-time" + ); + self.contract + .methods() + .nonce() + .simulate() + .await + .map(|r| r.value) + .map_err(ChainCommunicationError::from_other) + } + + #[instrument(level = "debug", err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn delivered(&self, id: H256) -> ChainResult { + self.contract + .methods() + .delivered(fuels::types::Bits256::from_h256(&id)) + .simulate() + .await + .map(|r| r.value) + .map_err(ChainCommunicationError::from_other) + } + + #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn default_ism(&self) -> ChainResult { + self.contract + .methods() + .default_ism() + .simulate() + .await + .map(|r| r.value.into_h256()) + .map_err(ChainCommunicationError::from_other) + } + + #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn recipient_ism(&self, recipient: H256) -> ChainResult { + self.contract + .methods() + .recipient_ism(Bech32ContractId::from_h256(&recipient)) + .simulate() + .await + .map(|r| r.value.into_h256()) + .map_err(ChainCommunicationError::from_other) + } + + #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn process( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + tx_gas_limit: Option, + ) -> ChainResult { + // The max gas limit per transaction is 30000000 so it should always be safe to convert to u64 + let tx_policies = match tx_gas_limit { + Some(gas_limit) if gas_limit <= U256::from(u64::MAX) => { + match u64::try_from(gas_limit) { + Ok(parsed_gas_limit) => { + TxPolicies::default().with_script_gas_limit(parsed_gas_limit) + } + Err(_) => { + warn!("Failed to convert U256 to u64 during process call"); + TxPolicies::default() + } + } + } + _ => TxPolicies::default(), + }; + + let gas_price = self.provider.get_gas_price().await?; + + let call_res = self + .contract + .methods() + .process( + Bytes(metadata.to_vec()), + Bytes(RawHyperlaneMessage::from(message)), + ) + .with_tx_policies(tx_policies) + .determine_missing_contracts(Some(3)) + .await + .map_err(ChainCommunicationError::from_other)? + .call() + .await + .map_err(ChainCommunicationError::from_other)?; + + // Extract transaction success from the receipts + let success = call_res + .receipts + .iter() + .filter_map(|r| match r { + Receipt::ScriptResult { result, .. } => Some(result), + _ => None, + }) + .any(|result| matches!(result, ScriptExecutionResult::Success)); + + let tx_id = H512::from(call_res.tx_id.unwrap().into_h256()); + Ok(TxOutcome { + transaction_id: tx_id, + executed: success, + gas_used: call_res.gas_used.into(), + gas_price: gas_price.into(), + }) + } + + // Process cost of the `process` method + #[instrument(err, ret, skip(self), fields(hyp_message=%message, metadata=%bytes_to_hex(metadata)))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn process_estimate_costs( + &self, + message: &HyperlaneMessage, + metadata: &[u8], + ) -> ChainResult { + let call_res = self + .contract + .methods() + .process( + Bytes(metadata.to_vec()), + Bytes(RawHyperlaneMessage::from(message)), + ) + .determine_missing_contracts(Some(3)) + .await + .map_err(ChainCommunicationError::from_other)? + .estimate_transaction_cost(None, None) + .await + .map_err(ChainCommunicationError::from_other)?; + + Ok(TxCostEstimate { + gas_limit: call_res.total_fee.into(), + gas_price: call_res.gas_price.into(), + l2_gas_limit: None, + }) + } + + fn process_calldata(&self, message: &HyperlaneMessage, metadata: &[u8]) -> Vec { + // Seems like this is not needed for Fuel, as it's only used in mocks + todo!() + } +} + +/// Struct that retrieves event data for a Fuel Mailbox contract +#[derive(Debug)] +pub struct FuelMailboxIndexer { + contract: FuelMailboxInner, + provider: FuelProvider, +} + +impl FuelMailboxIndexer { + /// Create a new FuelMailboxIndexer + pub async fn new( + conf: &ConnectionConf, + locator: ContractLocator<'_>, + wallet: WalletUnlocked, + ) -> ChainResult { + let fuel_provider = FuelProvider::new(locator.domain.clone(), conf).await; + + let address = Bech32ContractId::from_h256(&locator.address); + let contract = FuelMailboxInner::new(address, wallet); + + Ok(FuelMailboxIndexer { + contract, + provider: fuel_provider, + }) + } +} + +#[async_trait] +impl Indexer for FuelMailboxIndexer { + async fn fetch_logs_in_range( + &self, + range: RangeInclusive, + ) -> ChainResult, LogMeta)>> { + let mailbox_address = self.contract.contract_id().clone(); + self.provider + .index_logs_in_range(range, mailbox_address) + .await + } + + async fn get_finalized_block_number(&self) -> ChainResult { + self.provider.get_finalized_block_number().await + } +} + +#[async_trait] +impl Indexer for FuelMailboxIndexer { + async fn fetch_logs_in_range( + &self, + range: RangeInclusive, + ) -> ChainResult, LogMeta)>> { + todo!() // Needed for scraper + } + + async fn get_finalized_block_number(&self) -> ChainResult { + self.provider.get_finalized_block_number().await + } +} + +#[async_trait] +impl SequenceAwareIndexer for FuelMailboxIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { + let tip = Indexer::::get_finalized_block_number(&self).await?; + + // No sequence for message deliveries. + Ok((None, tip)) + } +} + +#[async_trait] +impl SequenceAwareIndexer for FuelMailboxIndexer { + #[allow(clippy::unnecessary_cast)] // TODO: `rustc` 1.80.1 clippy issue + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { + let tip = Indexer::::get_finalized_block_number(&self).await?; + + self.contract + .methods() + .nonce() + .simulate() + .await + .map(|r| r.value) + .map_err(ChainCommunicationError::from_other) + .map(|sequence| (Some(sequence as u32), tip)) + } +} + +#[allow(dead_code)] // TODO: Remove this once the FuelMailboxAbi is implemented +struct FuelMailboxAbi; + +impl HyperlaneAbi for FuelMailboxAbi { + const SELECTOR_SIZE_BYTES: usize = 8; + + fn fn_map() -> HashMap, &'static str> { + // Can't support this without Fuels exporting it in the generated code + todo!() + } +} diff --git a/rust/chains/hyperlane-fuel/src/multisig_ism.rs b/rust/main/chains/hyperlane-fuel/src/multisig_ism.rs similarity index 100% rename from rust/chains/hyperlane-fuel/src/multisig_ism.rs rename to rust/main/chains/hyperlane-fuel/src/multisig_ism.rs diff --git a/rust/main/chains/hyperlane-fuel/src/provider.rs b/rust/main/chains/hyperlane-fuel/src/provider.rs new file mode 100644 index 000000000..cdd32650e --- /dev/null +++ b/rust/main/chains/hyperlane-fuel/src/provider.rs @@ -0,0 +1,422 @@ +use std::{collections::HashMap, ops::Deref}; + +use async_trait::async_trait; +use fuels::{ + client::{FuelClient, PageDirection, PaginationRequest}, + prelude::Provider, + tx::Receipt, + types::{ + bech32::Bech32ContractId, + block::Block, + gas_price::LatestGasPrice, + transaction::{Transaction, TransactionType}, + transaction_response::TransactionResponse, + tx_status::TxStatus, + Address, BlockHeight, Bytes32, ContractId, + }, +}; +use futures::future::join_all; +use hyperlane_core::{ + h512_to_bytes, BlockInfo, ChainCommunicationError, ChainInfo, ChainResult, HyperlaneChain, + HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, HyperlaneProviderError, Indexed, LogMeta, + TxnInfo, H256, H512, U256, +}; + +use crate::{make_client, make_provider, prelude::FuelIntoH256, ConnectionConf}; + +/// A wrapper around a fuel provider to get generic blockchain information. +#[derive(Debug, Clone)] +pub struct FuelProvider { + domain: HyperlaneDomain, + provider: Provider, + client: FuelClient, +} + +impl FuelProvider { + /// Create a new fuel provider + pub async fn new(domain: HyperlaneDomain, conf: &ConnectionConf) -> Self { + let provider = make_provider(conf).await.unwrap(); + let client = make_client(conf).unwrap(); + + Self { + domain, + provider, + client, + } + } + + /// Get the inner provider + pub fn provider(&self) -> &Provider { + &self.provider + } + + /// Get the latest gas price + pub async fn get_gas_price(&self) -> ChainResult { + let LatestGasPrice { gas_price, .. } = self + .provider() + .latest_gas_price() + .await + .map_err(ChainCommunicationError::from_other)?; + + Ok(gas_price) + } + + /// Check if a transaction is from a contract + /// @note: Only works for checking script transactions + /// Assumes that the first input is the contract id + #[allow(clippy::get_first)] // TODO: `rustc` 1.80.1 clippy issue + fn is_transaction_from_contract( + res: &TransactionResponse, + contract: &Bech32ContractId, + ) -> bool { + if let TransactionType::Script(script_transaction) = &res.transaction { + if script_transaction.inputs().get(0).is_some_and(|input| { + input + .contract_id() + .is_some_and(|id| id == &ContractId::from(&contract.into())) + }) { + return true; + } + } + false + } + + /// Check if a transaction is a call to the dispatch function of the Mailbox contract + #[allow(clippy::match_like_matches_macro)] // TODO: `rustc` 1.80.1 clippy issue + #[allow(clippy::into_iter_on_ref)] // TODO: `rustc` 1.80.1 clippy issue + fn is_dispatch_call(res: &TransactionResponse) -> bool { + // let selector = encode_fn_selector("dispatch"); + // println!("selector: {:?}", selector); // XXX see if we can get the correct txn by selector + // Apparently we should be able to see it in the call logs + + let receipts = match &res.status { + TxStatus::Success { receipts } => receipts, + _ => return false, + }; + let log_data_receipts = receipts + .into_iter() + .filter(|rec| { + // if let Receipt::Call { param1, param2, .. } = rec { + // print!( + // "param1: {:?}, param2: {:?}", + // param1.to_be_bytes(), + // param2.to_be_bytes() + // ); + // } + + match rec { + Receipt::LogData { .. } => true, + _ => false, + } + }) + .collect::>(); + + // Dispatch is the only call that has 2 log data receipts + match log_data_receipts.len() { + 2 => true, + _ => false, + } + } + + #[allow(clippy::clone_on_copy)] // TODO: `rustc` 1.80.1 clippy issue + async fn get_block_data( + &self, + range: std::ops::RangeInclusive, + ) -> ChainResult<(Vec, HashMap)> { + let result_amount = range.end() - range.start() + 1; + let req = PaginationRequest { + cursor: Some(range.start().to_string()), + results: i32::try_from(result_amount).expect("Invalid range"), + direction: PageDirection::Forward, + }; + + let blocks = self + .provider + .get_blocks(req) + .await + .map_err(ChainCommunicationError::from_other)?; + + let mut transaction_map: HashMap = HashMap::new(); + blocks.results.iter().for_each(|block| { + block + .transactions + .iter() + .enumerate() + .for_each(|(index, tx)| { + transaction_map.insert(tx.clone(), (block.id, index as u64)); + }); + }); + Ok((blocks.results, transaction_map)) + } + + /// Get the finalized block number + /// XXX might be inaccurate as we do not know the block finality + pub async fn get_finalized_block_number(&self) -> ChainResult { + self.provider + .latest_block_height() + .await + .map_err(ChainCommunicationError::from_other) + } + + /// index logs in a range + #[allow(clippy::clone_on_copy)] // TODO: `rustc` 1.80.1 clippy issue + #[allow(clippy::manual_map)] // TODO: `rustc` 1.80.1 clippy issue + #[allow(clippy::into_iter_on_ref)] // TODO: `rustc` 1.80.1 clippy issue + #[allow(clippy::needless_borrow)] // TODO: `rustc` 1.80.1 clippy issue + pub async fn index_logs_in_range( + &self, + range: std::ops::RangeInclusive, + mailbox_contract: Bech32ContractId, + ) -> ChainResult, LogMeta)>> { + let (blocks, transaction_map) = self.get_block_data(range.clone()).await.unwrap(); + + // Transaction ids from selected blocks + let transaction_ids = blocks + .into_iter() + .map(|block| block.transactions) + .flat_map(|txs| txs.into_iter()) + .collect::>(); + + let futures = transaction_ids + .into_iter() + .map(|tx_id| { + let provider = self.provider.clone(); + let tx_clone = tx_id.clone(); + async move { + let result = provider.get_transaction_by_id(&tx_id).await.unwrap(); + (tx_clone, result) + } + }) + .collect::>(); + + // Filter transactions + // 1. Transaction type is Script + // 2. Transaction status is Success + // 3. Transaction is from mailbox contract + // 4. Transaction is a dispatch call + // 5. Transaction data is valid + let transaction_data = join_all(futures) + .await + .into_iter() + .filter_map(|(tx_id, tx_data)| match tx_data { + Some(tx_data) => Some((tx_id, tx_data)), + _ => None, + }) + .filter(|(_, tx_data)| { + matches!(tx_data.transaction, TransactionType::Script(_)) + && matches!(tx_data.status, TxStatus::Success { .. }) + && Self::is_transaction_from_contract(&tx_data, &mailbox_contract) + && Self::is_dispatch_call(&tx_data) + }) + .collect::>(); + + // Full data needed to construct the logs + let full_tx_data = transaction_data + .into_iter() + .filter_map(|(tx_id, tx_data)| { + let receipts = match &tx_data.status { + TxStatus::Success { receipts } => receipts, + _ => return None, + }; + + let (log_index, mut receipt_log_data) = receipts + .into_iter() + .enumerate() + .filter_map(|(log_index, rec)| { + // We only care about LogData receipts with data length greater than 32 bytes + match rec { + Receipt::LogData { .. } + if rec.data().is_some_and(|data| data.len() > 32) => + { + let data = rec.data().map(|data| data.to_owned()); + + match data { + Some(data) => Some((U256::from(log_index), data)), + _ => None, + } + } + _ => None, + } + }) + .next()?; // Each dispatch call should have only one log data receipt + + if !receipt_log_data.is_empty() { + // We cut out the message id, recipient and domain which are encoded in the first 76 bytes + receipt_log_data.drain(0..76); + let encoded_message = HyperlaneMessage::from(receipt_log_data); + Some((tx_id, tx_data, encoded_message, log_index)) + } else { + None + } + }) + .collect::>(); // Collect all Vec from each transaction into a Vec> + + let indexed_logs: Vec<(Indexed, LogMeta)> = full_tx_data + .into_iter() + .map(|(tx_id, tx, message, log_index)| { + let (block_hash, transaction_index) = transaction_map.get(&tx_id).unwrap(); + + let log_meta = LogMeta { + address: mailbox_contract.clone().into_h256(), + block_number: *tx.block_height.unwrap().deref() as u64, + block_hash: block_hash.into_h256(), + transaction_id: H512::from(tx_id.into_h256()), + transaction_index: transaction_index.clone(), + log_index, + }; + (message.into(), log_meta) + }) + .collect::>(); + Ok(indexed_logs) + } +} + +impl HyperlaneChain for FuelProvider { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(self.clone()) + } +} + +#[async_trait] +impl HyperlaneProvider for FuelProvider { + /// Used by scraper + #[allow(clippy::clone_on_copy)] // TODO: `rustc` 1.80.1 clippy issue + async fn get_block_by_height(&self, height: u64) -> ChainResult { + let block_res = self + .provider + .block_by_height(BlockHeight::new(height as u32)) + .await + .map_err(|e| HyperlaneProviderError::CouldNotFindBlockByHeight(height))?; + + let block_info = match block_res { + Some(block) => BlockInfo { + hash: H256::from_slice(block.id.as_slice()), + timestamp: block.header.time.map_or(0, |t| t.timestamp() as u64), + number: block.header.height.into(), + }, + None => Err(HyperlaneProviderError::CouldNotFindBlockByHeight(height))?, + }; + + if block_info.number != height { + Err(HyperlaneProviderError::IncorrectBlockByHeight( + height, + block_info.number, + ))?; + } + + Ok(block_info) + } + + /// Used by scraper + #[allow(clippy::clone_on_copy)] // TODO: `rustc` 1.80.1 clippy issue + #[allow(clippy::match_like_matches_macro)] // TODO: `rustc` 1.80.1 clippy issue + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { + let hash = H256::from_slice(&h512_to_bytes(hash)); + + let transaction_res = self + .provider + .get_transaction_by_id(&hash.0.into()) + .await + .map_err(|e| { + ChainCommunicationError::CustomError(format!("Failed to get transaction: {}", e)) + })?; + + match transaction_res { + Some(transaction) => { + let block_number = transaction.block_height.unwrap(); + + let gas_price = self + .provider + .estimate_gas_price(block_number.into()) + .await + .map_or(0, |estimate| estimate.gas_price); + + let gas_limit = match transaction.transaction { + TransactionType::Script(tx) => tx.gas_limit(), + _ => 0, + }; + + let (sender, recipient, nonce) = match transaction.status { + TxStatus::Success { receipts } => { + let valid_receipt = receipts.into_iter().find(|receipt| match receipt { + Receipt::MessageOut { .. } => true, + _ => false, + }); + + match valid_receipt { + Some(Receipt::MessageOut { + sender, + recipient, + nonce, + .. + }) => { + let mut arr = [0u8; 8]; + let nonce_bytes = <[u8; 32]>::from(nonce); + arr.copy_from_slice(&nonce_bytes[0..8]); + let parsed_nonce = u64::from_be_bytes(arr); + + ( + <[u8; 32]>::from(sender).into(), + Some(<[u8; 32]>::from(recipient).into()), + parsed_nonce, + ) + } + _ => (H256::zero(), None, 0), + } + } + _ => (H256::zero(), None, 0), + }; + + Ok(TxnInfo { + hash: hash.into(), + gas_limit: gas_limit.into(), + max_priority_fee_per_gas: None, + max_fee_per_gas: None, + nonce, + sender, + gas_price: Some(gas_price.into()), + recipient, + receipt: None, + }) + } + None => Err(ChainCommunicationError::CustomError(format!( + "Transaction not found: {}", + hash + ))), + } + } + + async fn is_contract(&self, address: &H256) -> ChainResult { + let contract_res = self.client.contract(&ContractId::from(address.0)).await; + + match contract_res { + Ok(contract) => Ok(contract.is_some()), + Err(e) => Err(ChainCommunicationError::CustomError(format!( + "Failed to get contract: {}", + e + ))), + } + } + + async fn get_balance(&self, address: String) -> ChainResult { + let base = self.provider.base_asset_id(); + let asset = *Address::from_bytes_ref_checked(address.as_bytes()).expect("Invalid address"); + + self.provider + .get_asset_balance(&asset.into(), *base) + .await + .map(|balance| Ok(U256::from(balance))) + .map_err(|e| { + ChainCommunicationError::CustomError(format!("Failed to get balance: {}", e)) + })? + } + + /// Used by hyperlane base metrics (scraper) + async fn get_chain_metrics(&self) -> ChainResult> { + Ok(None) + } +} diff --git a/rust/chains/hyperlane-fuel/src/routing_ism.rs b/rust/main/chains/hyperlane-fuel/src/routing_ism.rs similarity index 100% rename from rust/chains/hyperlane-fuel/src/routing_ism.rs rename to rust/main/chains/hyperlane-fuel/src/routing_ism.rs diff --git a/rust/chains/hyperlane-fuel/src/trait_builder.rs b/rust/main/chains/hyperlane-fuel/src/trait_builder.rs similarity index 79% rename from rust/chains/hyperlane-fuel/src/trait_builder.rs rename to rust/main/chains/hyperlane-fuel/src/trait_builder.rs index b056a17ee..b9efd9060 100644 --- a/rust/chains/hyperlane-fuel/src/trait_builder.rs +++ b/rust/main/chains/hyperlane-fuel/src/trait_builder.rs @@ -30,11 +30,14 @@ impl From for ChainCommunicationError { } } -fn make_client(conf: &ConnectionConf) -> ChainResult { +/// Create a new Fuel client +pub fn make_client(conf: &ConnectionConf) -> ChainResult { FuelClient::new(&conf.url).map_err(|e| FuelNewConnectionError(e).into()) } /// Create a new fuel provider and connection -pub fn make_provider(conf: &ConnectionConf) -> ChainResult { - Ok(Provider::new(make_client(conf)?)) +pub async fn make_provider(conf: &ConnectionConf) -> ChainResult { + Provider::connect(&conf.url) + .await + .map_err(|e| FuelNewConnectionError(e.into()).into()) } diff --git a/rust/chains/hyperlane-fuel/src/validator_announce.rs b/rust/main/chains/hyperlane-fuel/src/validator_announce.rs similarity index 100% rename from rust/chains/hyperlane-fuel/src/validator_announce.rs rename to rust/main/chains/hyperlane-fuel/src/validator_announce.rs diff --git a/rust/main/chains/hyperlane-sealevel/Cargo.toml b/rust/main/chains/hyperlane-sealevel/Cargo.toml new file mode 100644 index 000000000..3a1bda12f --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/Cargo.toml @@ -0,0 +1,48 @@ +[package] +name = "hyperlane-sealevel" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow.workspace = true +async-trait.workspace = true +base64.workspace = true +bincode.workspace = true +borsh.workspace = true +derive-new.workspace = true +jsonrpc-core.workspace = true +num-traits.workspace = true +reqwest.workspace = true +serde.workspace = true +serde_json.workspace = true +solana-account-decoder.workspace = true +solana-client.workspace = true +solana-sdk.workspace = true +solana-transaction-status.workspace = true +thiserror.workspace = true +tokio.workspace = true +tracing-futures.workspace = true +tracing.workspace = true +url.workspace = true + +account-utils = { path = "../../../sealevel/libraries/account-utils" } +hyperlane-core = { path = "../../hyperlane-core", features = [ + "solana", + "async", +] } +hyperlane-sealevel-interchain-security-module-interface = { path = "../../../sealevel/libraries/interchain-security-module-interface" } +hyperlane-sealevel-mailbox = { path = "../../../sealevel/programs/mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../../../sealevel/programs/hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-message-recipient-interface = { path = "../../../sealevel/libraries/message-recipient-interface" } +hyperlane-sealevel-multisig-ism-message-id = { path = "../../../sealevel/programs/ism/multisig-ism-message-id", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-validator-announce = { path = "../../../sealevel/programs/validator-announce", features = [ + "no-entrypoint", +] } +multisig-ism = { path = "../../../sealevel/libraries/multisig-ism" } +serializable-account-meta = { path = "../../../sealevel/libraries/serializable-account-meta" } diff --git a/rust/main/chains/hyperlane-sealevel/src/account.rs b/rust/main/chains/hyperlane-sealevel/src/account.rs new file mode 100644 index 000000000..f671c432e --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/account.rs @@ -0,0 +1,68 @@ +use base64::{engine::general_purpose::STANDARD as Base64, Engine}; +use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}; +use solana_client::{ + rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, + rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, +}; +use solana_sdk::{account::Account, commitment_config::CommitmentConfig, pubkey::Pubkey}; + +use hyperlane_core::{ChainCommunicationError, ChainResult}; + +use crate::rpc::SealevelRpcClient; + +pub async fn search_accounts_by_discriminator( + client: &SealevelRpcClient, + program_id: &Pubkey, + discriminator: &[u8; 8], + nonce_bytes: &[u8], + offset: usize, + length: usize, +) -> ChainResult> { + let target_message_account_bytes = &[discriminator, nonce_bytes].concat(); + let target_message_account_bytes = Base64.encode(target_message_account_bytes); + + // First, find all accounts with the matching account data. + // To keep responses small in case there is ever more than 1 + // match, we don't request the full account data, and just request + // the field which was used to generate account id + #[allow(deprecated)] + let memcmp = RpcFilterType::Memcmp(Memcmp { + // Ignore the first byte, which is the `initialized` bool flag. + offset: 1, + bytes: MemcmpEncodedBytes::Base64(target_message_account_bytes), + encoding: None, + }); + let config = RpcProgramAccountsConfig { + filters: Some(vec![memcmp]), + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: Some(UiDataSliceConfig { offset, length }), + commitment: Some(CommitmentConfig::finalized()), + min_context_slot: None, + }, + with_context: Some(false), + }; + let accounts = client + .get_program_accounts_with_config(program_id, config) + .await?; + Ok(accounts) +} + +pub fn search_and_validate_account( + accounts: Vec<(Pubkey, Account)>, + message_account: F, +) -> ChainResult +where + F: Fn(&Account) -> ChainResult, +{ + for (pubkey, account) in accounts { + let expected_pubkey = message_account(&account)?; + if expected_pubkey == pubkey { + return Ok(pubkey); + } + } + + Err(ChainCommunicationError::from_other_str( + "Could not find valid storage PDA pubkey", + )) +} diff --git a/rust/main/chains/hyperlane-sealevel/src/error.rs b/rust/main/chains/hyperlane-sealevel/src/error.rs new file mode 100644 index 000000000..4ba217119 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/error.rs @@ -0,0 +1,42 @@ +use hyperlane_core::{ChainCommunicationError, H512}; +use solana_client::client_error::ClientError; +use solana_sdk::pubkey::ParsePubkeyError; +use solana_transaction_status::EncodedTransaction; + +/// Errors from the crates specific to the hyperlane-sealevel +/// implementation. +/// This error can then be converted into the broader error type +/// in hyperlane-core using the `From` trait impl +#[derive(Debug, thiserror::Error)] +pub enum HyperlaneSealevelError { + /// ParsePubkeyError error + #[error("{0}")] + ParsePubkeyError(#[from] ParsePubkeyError), + /// ClientError error + #[error("{0}")] + ClientError(#[from] ClientError), + /// Decoding error + #[error("{0}")] + Decoding(#[from] solana_sdk::bs58::decode::Error), + /// No transaction in block error + #[error("{0}")] + NoTransactions(String), + /// Too many transactions of particular content in block + #[error("{0}")] + TooManyTransactions(String), + /// Unsupported transaction encoding + #[error("{0:?}")] + UnsupportedTransactionEncoding(EncodedTransaction), + /// Unsigned transaction + #[error("{0}")] + UnsignedTransaction(H512), + /// Incorrect transaction + #[error("received incorrect transaction, expected hash: {0:?}, received hash: {1:?}")] + IncorrectTransaction(Box, Box), +} + +impl From for ChainCommunicationError { + fn from(value: HyperlaneSealevelError) -> Self { + ChainCommunicationError::from_other(value) + } +} diff --git a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs b/rust/main/chains/hyperlane-sealevel/src/interchain_gas.rs similarity index 64% rename from rust/chains/hyperlane-sealevel/src/interchain_gas.rs rename to rust/main/chains/hyperlane-sealevel/src/interchain_gas.rs index beebcb9db..3c2cbd1c1 100644 --- a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs +++ b/rust/main/chains/hyperlane-sealevel/src/interchain_gas.rs @@ -1,27 +1,22 @@ +use std::ops::RangeInclusive; + use async_trait::async_trait; -use hyperlane_core::{ - config::StrOrIntParseError, ChainCommunicationError, ChainResult, ContractLocator, - HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, - InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H256, H512, -}; +use derive_new::new; use hyperlane_sealevel_igp::{ accounts::{GasPaymentAccount, ProgramDataAccount}, igp_gas_payment_pda_seeds, igp_program_data_pda_seeds, }; -use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}; -use solana_client::{ - rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, - rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, -}; -use std::ops::RangeInclusive; +use solana_sdk::{account::Account, pubkey::Pubkey}; use tracing::{info, instrument}; -use crate::{ - client::RpcClientWithDebug, utils::get_finalized_block_number, ConnectionConf, SealevelProvider, +use hyperlane_core::{ + config::StrOrIntParseError, ChainCommunicationError, ChainResult, ContractLocator, + HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, + InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H256, H512, }; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; -use derive_new::new; +use crate::account::{search_accounts_by_discriminator, search_and_validate_account}; +use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; /// The offset to get the `unique_gas_payment_pubkey` field from the serialized GasPaymentData. /// The account data includes prefixes that are accounted for here: a 1 byte initialized flag @@ -60,20 +55,14 @@ impl SealevelInterchainGasPaymaster { } async fn determine_igp_program_id( - rpc_client: &RpcClientWithDebug, + rpc_client: &SealevelRpcClient, igp_account_pubkey: &H256, ) -> ChainResult { let account = rpc_client - .get_account_with_commitment( - &Pubkey::from(<[u8; 32]>::from(*igp_account_pubkey)), - CommitmentConfig::finalized(), - ) - .await - .map_err(ChainCommunicationError::from_other)? - .value - .ok_or_else(|| { - ChainCommunicationError::from_other_str("Could not find IGP account for pubkey") - })?; + .get_account_with_finalized_commitment(&Pubkey::from(<[u8; 32]>::from( + *igp_account_pubkey, + ))) + .await?; Ok(account.owner) } } @@ -99,7 +88,7 @@ impl InterchainGasPaymaster for SealevelInterchainGasPaymaster {} /// Struct that retrieves event data for a Sealevel IGP contract #[derive(Debug)] pub struct SealevelInterchainGasPaymasterIndexer { - rpc_client: RpcClientWithDebug, + rpc_client: SealevelRpcClient, igp: SealevelInterchainGasPaymaster, } @@ -118,10 +107,7 @@ impl SealevelInterchainGasPaymasterIndexer { igp_account_locator: ContractLocator<'_>, ) -> ChainResult { // Set the `processed` commitment at rpc level - let rpc_client = RpcClientWithDebug::new_with_commitment( - conf.url.to_string(), - CommitmentConfig::processed(), - ); + let rpc_client = SealevelRpcClient::new(conf.url.to_string()); let igp = SealevelInterchainGasPaymaster::new(conf, &igp_account_locator).await?; Ok(Self { rpc_client, igp }) @@ -132,83 +118,30 @@ impl SealevelInterchainGasPaymasterIndexer { &self, sequence_number: u64, ) -> ChainResult { - let payment_bytes = &[ - &hyperlane_sealevel_igp::accounts::GAS_PAYMENT_DISCRIMINATOR[..], - &sequence_number.to_le_bytes()[..], - ] - .concat(); - #[allow(deprecated)] - let payment_bytes: String = base64::encode(payment_bytes); - - // First, find all accounts with the matching gas payment data. - // To keep responses small in case there is ever more than 1 - // match, we don't request the full account data, and just request - // the `unique_gas_payment_pubkey` field. - #[allow(deprecated)] - let memcmp = RpcFilterType::Memcmp(Memcmp { - // Ignore the first byte, which is the `initialized` bool flag. - offset: 1, - bytes: MemcmpEncodedBytes::Base64(payment_bytes), - encoding: None, - }); - let config = RpcProgramAccountsConfig { - filters: Some(vec![memcmp]), - account_config: RpcAccountInfoConfig { - encoding: Some(UiAccountEncoding::Base64), - // Don't return any data - data_slice: Some(UiDataSliceConfig { - offset: UNIQUE_GAS_PAYMENT_PUBKEY_OFFSET, - length: 32, // the length of the `unique_gas_payment_pubkey` field - }), - commitment: Some(CommitmentConfig::finalized()), - min_context_slot: None, - }, - with_context: Some(false), - }; - tracing::debug!(config=?config, "Fetching program accounts"); - let accounts = self - .rpc_client - .get_program_accounts_with_config(&self.igp.program_id, config) - .await - .map_err(ChainCommunicationError::from_other)?; + let discriminator = hyperlane_sealevel_igp::accounts::GAS_PAYMENT_DISCRIMINATOR; + let sequence_number_bytes = sequence_number.to_le_bytes(); + let unique_gas_payment_pubkey_length = 32; // the length of the `unique_gas_payment_pubkey` field + let accounts = search_accounts_by_discriminator( + &self.rpc_client, + &self.igp.program_id, + discriminator, + &sequence_number_bytes, + UNIQUE_GAS_PAYMENT_PUBKEY_OFFSET, + unique_gas_payment_pubkey_length, + ) + .await?; tracing::debug!(accounts=?accounts, "Fetched program accounts"); - // Now loop through matching accounts and find the one with a valid account pubkey - // that proves it's an actual gas payment PDA. - let mut valid_payment_pda_pubkey = Option::::None; - - for (pubkey, account) in accounts { - let unique_gas_payment_pubkey = Pubkey::new(&account.data); - let (expected_pubkey, _bump) = Pubkey::try_find_program_address( - igp_gas_payment_pda_seeds!(unique_gas_payment_pubkey), - &self.igp.program_id, - ) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find program address for unique_gas_payment_pubkey", - ) - })?; - if expected_pubkey == pubkey { - valid_payment_pda_pubkey = Some(pubkey); - break; - } - } - - let valid_payment_pda_pubkey = valid_payment_pda_pubkey.ok_or_else(|| { - ChainCommunicationError::from_other_str("Could not find valid gas payment PDA pubkey") + let valid_payment_pda_pubkey = search_and_validate_account(accounts, |account| { + self.interchain_payment_account(account) })?; // Now that we have the valid gas payment PDA pubkey, we can get the full account data. let account = self .rpc_client - .get_account_with_commitment(&valid_payment_pda_pubkey, CommitmentConfig::finalized()) - .await - .map_err(ChainCommunicationError::from_other)? - .value - .ok_or_else(|| { - ChainCommunicationError::from_other_str("Could not find account data") - })?; + .get_account_with_finalized_commitment(&valid_payment_pda_pubkey) + .await?; let gas_payment_account = GasPaymentAccount::fetch(&mut account.data.as_ref()) .map_err(ChainCommunicationError::from_other)? .into_inner(); @@ -241,10 +174,25 @@ impl SealevelInterchainGasPaymasterIndexer { H256::from(gas_payment_account.igp.to_bytes()), )) } + + fn interchain_payment_account(&self, account: &Account) -> ChainResult { + let unique_gas_payment_pubkey = Pubkey::new(&account.data); + let (expected_pubkey, _bump) = Pubkey::try_find_program_address( + igp_gas_payment_pda_seeds!(unique_gas_payment_pubkey), + &self.igp.program_id, + ) + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Could not find program address for unique_gas_payment_pubkey", + ) + })?; + Ok(expected_pubkey) + } } #[async_trait] impl Indexer for SealevelInterchainGasPaymasterIndexer { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue #[instrument(err, skip(self))] async fn fetch_logs_in_range( &self, @@ -271,24 +219,21 @@ impl Indexer for SealevelInterchainGasPaymasterIndexer { } #[instrument(level = "debug", err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn get_finalized_block_number(&self) -> ChainResult { - get_finalized_block_number(&self.rpc_client).await + self.rpc_client.get_block_height().await } } #[async_trait] impl SequenceAwareIndexer for SealevelInterchainGasPaymasterIndexer { #[instrument(err, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let program_data_account = self .rpc_client - .get_account_with_commitment(&self.igp.data_pda_pubkey, CommitmentConfig::finalized()) - .await - .map_err(ChainCommunicationError::from_other)? - .value - .ok_or_else(|| { - ChainCommunicationError::from_other_str("Could not find account data") - })?; + .get_account_with_finalized_commitment(&self.igp.data_pda_pubkey) + .await?; let program_data = ProgramDataAccount::fetch(&mut program_data_account.data.as_ref()) .map_err(ChainCommunicationError::from_other)? .into_inner(); @@ -296,7 +241,7 @@ impl SequenceAwareIndexer for SealevelInterchainGasPaymast .payment_count .try_into() .map_err(StrOrIntParseError::from)?; - let tip = get_finalized_block_number(&self.rpc_client).await?; + let tip = self.rpc_client.get_block_height().await?; Ok((Some(payment_count), tip)) } } diff --git a/rust/chains/hyperlane-sealevel/src/interchain_security_module.rs b/rust/main/chains/hyperlane-sealevel/src/interchain_security_module.rs similarity index 81% rename from rust/chains/hyperlane-sealevel/src/interchain_security_module.rs rename to rust/main/chains/hyperlane-sealevel/src/interchain_security_module.rs index 0f92432eb..aaf2683fd 100644 --- a/rust/chains/hyperlane-sealevel/src/interchain_security_module.rs +++ b/rust/main/chains/hyperlane-sealevel/src/interchain_security_module.rs @@ -10,7 +10,7 @@ use hyperlane_core::{ use hyperlane_sealevel_interchain_security_module_interface::InterchainSecurityModuleInstruction; use serializable_account_meta::SimulationReturnData; -use crate::{utils::simulate_instruction, ConnectionConf, RpcClientWithDebug, SealevelProvider}; +use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; /// A reference to an InterchainSecurityModule contract on some Sealevel chain #[derive(Debug)] @@ -32,7 +32,7 @@ impl SealevelInterchainSecurityModule { } } - fn rpc(&self) -> &RpcClientWithDebug { + fn rpc(&self) -> &SealevelRpcClient { self.provider.rpc() } } @@ -64,18 +64,19 @@ impl InterchainSecurityModule for SealevelInterchainSecurityModule { vec![], ); - let module = simulate_instruction::>( - self.rpc(), - self.payer - .as_ref() - .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, - instruction, - ) - .await? - .ok_or_else(|| { - ChainCommunicationError::from_other_str("No return data was returned from the ISM") - })? - .return_data; + let module = self + .rpc() + .simulate_instruction::>( + self.payer + .as_ref() + .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, + instruction, + ) + .await? + .ok_or_else(|| { + ChainCommunicationError::from_other_str("No return data was returned from the ISM") + })? + .return_data; if let Some(module_type) = ModuleType::from_u32(module) { Ok(module_type) diff --git a/rust/chains/hyperlane-sealevel/src/lib.rs b/rust/main/chains/hyperlane-sealevel/src/lib.rs similarity index 87% rename from rust/chains/hyperlane-sealevel/src/lib.rs rename to rust/main/chains/hyperlane-sealevel/src/lib.rs index 8cd8830f5..941c64a7b 100644 --- a/rust/chains/hyperlane-sealevel/src/lib.rs +++ b/rust/main/chains/hyperlane-sealevel/src/lib.rs @@ -5,16 +5,17 @@ #![deny(warnings)] pub use crate::multisig_ism::*; -pub(crate) use client::RpcClientWithDebug; pub use interchain_gas::*; pub use interchain_security_module::*; pub use mailbox::*; pub use merkle_tree_hook::*; pub use provider::*; +pub(crate) use rpc::SealevelRpcClient; pub use solana_sdk::signer::keypair::Keypair; pub use trait_builder::*; pub use validator_announce::*; +mod account; mod error; mod interchain_gas; mod interchain_security_module; @@ -22,8 +23,8 @@ mod mailbox; mod merkle_tree_hook; mod multisig_ism; mod provider; +mod rpc; mod trait_builder; +mod transaction; mod utils; - -mod client; mod validator_announce; diff --git a/rust/chains/hyperlane-sealevel/src/mailbox.rs b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs similarity index 58% rename from rust/chains/hyperlane-sealevel/src/mailbox.rs rename to rust/main/chains/hyperlane-sealevel/src/mailbox.rs index beb4e86c3..45e446127 100644 --- a/rust/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/main/chains/hyperlane-sealevel/src/mailbox.rs @@ -4,21 +4,15 @@ use std::{collections::HashMap, num::NonZeroU64, ops::RangeInclusive, str::FromS use async_trait::async_trait; use borsh::{BorshDeserialize, BorshSerialize}; -use jsonrpc_core::futures_util::TryFutureExt; -use tracing::{debug, info, instrument, warn}; - -use hyperlane_core::{ - accumulator::incremental::IncrementalMerkle, BatchItem, ChainCommunicationError, ChainResult, - Checkpoint, ContractLocator, Decode as _, Encode as _, FixedPointNumber, HyperlaneAbi, - HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, - Indexed, Indexer, LogMeta, Mailbox, MerkleTreeHook, SequenceAwareIndexer, TxCostEstimate, - TxOutcome, H256, H512, U256, -}; use hyperlane_sealevel_interchain_security_module_interface::{ InterchainSecurityModuleInstruction, VerifyInstruction, }; use hyperlane_sealevel_mailbox::{ - accounts::{DispatchedMessageAccount, InboxAccount, OutboxAccount}, + accounts::{ + DispatchedMessageAccount, InboxAccount, OutboxAccount, ProcessedMessage, + ProcessedMessageAccount, DISPATCHED_MESSAGE_DISCRIMINATOR, PROCESSED_MESSAGE_DISCRIMINATOR, + }, + instruction, instruction::InboxProcess, mailbox_dispatched_message_pda_seeds, mailbox_inbox_pda_seeds, mailbox_outbox_pda_seeds, mailbox_process_authority_pda_seeds, mailbox_processed_message_pda_seeds, @@ -26,20 +20,23 @@ use hyperlane_sealevel_mailbox::{ use hyperlane_sealevel_message_recipient_interface::{ HandleInstruction, MessageRecipientInstruction, }; +use jsonrpc_core::futures_util::TryFutureExt; use serializable_account_meta::SimulationReturnData; use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}; use solana_client::{ nonblocking::rpc_client::RpcClient, + rpc_client::SerializableTransaction, rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig}, rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, + rpc_response::Response, }; use solana_sdk::{ account::Account, + bs58, commitment_config::CommitmentConfig, compute_budget::ComputeBudgetInstruction, hash::Hash, - instruction::AccountMeta, - instruction::Instruction, + instruction::{AccountMeta, Instruction}, message::Message, pubkey::Pubkey, signature::Signature, @@ -47,17 +44,27 @@ use solana_sdk::{ transaction::{Transaction, VersionedTransaction}, }; use solana_transaction_status::{ - EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta, - UiInnerInstructions, UiInstruction, UiMessage, UiParsedInstruction, UiReturnDataEncoding, - UiTransaction, UiTransactionReturnData, UiTransactionStatusMeta, + EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta, TransactionStatus, + UiCompiledInstruction, UiInnerInstructions, UiInstruction, UiMessage, UiParsedInstruction, + UiReturnDataEncoding, UiTransaction, UiTransactionReturnData, UiTransactionStatusMeta, }; +use tracing::{debug, info, instrument, warn}; -use crate::RpcClientWithDebug; -use crate::{ - utils::{get_account_metas, get_finalized_block_number, simulate_instruction}, - ConnectionConf, SealevelProvider, +use hyperlane_core::{ + accumulator::incremental::IncrementalMerkle, BatchItem, ChainCommunicationError, + ChainCommunicationError::ContractError, ChainResult, Checkpoint, ContractLocator, Decode as _, + Encode as _, FixedPointNumber, HyperlaneAbi, HyperlaneChain, HyperlaneContract, + HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexed, Indexer, KnownHyperlaneDomain, + LogMeta, Mailbox, MerkleTreeHook, ReorgPeriod, SequenceAwareIndexer, TxCostEstimate, TxOutcome, + H256, H512, U256, }; +use crate::account::{search_accounts_by_discriminator, search_and_validate_account}; +use crate::error::HyperlaneSealevelError; +use crate::transaction::search_dispatched_message_transactions; +use crate::utils::{decode_h256, decode_h512, from_base58}; +use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; + const SYSTEM_PROGRAM: &str = "11111111111111111111111111111111"; const SPL_NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; @@ -65,6 +72,24 @@ const SPL_NOOP: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; // TODO: consider a more sane value and/or use IGP gas payments instead. const PROCESS_COMPUTE_UNITS: u32 = 1_400_000; +/// 0.0005 SOL, in lamports. +/// A typical tx fee without a prioritization fee is 0.000005 SOL, or +/// 5000 lamports. (Example: https://explorer.solana.com/tx/fNd3xVeBzFHeuzr8dXQxLGiHMzTeYpykSV25xWzNRaHtzzjvY9A3MzXh1ZsK2JncRHkwtuWrGEwGXVhFaUCYhtx) +/// See average priority fees here https://solanacompass.com/statistics/fees +/// to inform what to spend here. +const PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX: u64 = 500000; + +/// In micro-lamports. Multiply this by the compute units to figure out +/// the additional cost of processing a message, in addition to the mandatory +/// "base" cost of signature verification. +const PROCESS_COMPUTE_UNIT_PRICE_MICRO_LAMPORTS: u64 = + ( + // Convert to micro-lamports + (PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX * 1_000_000) + // Divide by the max compute units + / PROCESS_COMPUTE_UNITS as u64 + ); + /// A reference to a Mailbox contract on some Sealevel chain pub struct SealevelMailbox { pub(crate) program_id: Pubkey, @@ -108,7 +133,7 @@ impl SealevelMailbox { self.outbox } - pub fn rpc(&self) -> &RpcClientWithDebug { + pub fn rpc(&self) -> &SealevelRpcClient { self.provider.rpc() } @@ -120,14 +145,14 @@ impl SealevelMailbox { &self, instruction: Instruction, ) -> ChainResult> { - simulate_instruction( - &self.rpc(), - self.payer - .as_ref() - .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, - instruction, - ) - .await + self.rpc() + .simulate_instruction( + self.payer + .as_ref() + .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, + instruction, + ) + .await } /// Simulates an Instruction that will return a list of AccountMetas. @@ -135,14 +160,14 @@ impl SealevelMailbox { &self, instruction: Instruction, ) -> ChainResult> { - get_account_metas( - &self.rpc(), - self.payer - .as_ref() - .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, - instruction, - ) - .await + self.rpc() + .get_account_metas( + self.payer + .as_ref() + .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, + instruction, + ) + .await } /// Gets the recipient ISM given a recipient program id and the ISM getter account metas. @@ -253,6 +278,123 @@ impl SealevelMailbox { self.get_account_metas(instruction).await } + + fn use_jito(&self) -> bool { + matches!( + self.domain(), + HyperlaneDomain::Known(KnownHyperlaneDomain::SolanaMainnet) + ) + } + + async fn send_and_confirm_transaction( + &self, + transaction: &Transaction, + ) -> ChainResult { + if self.use_jito() { + self.send_and_confirm_transaction_with_jito(transaction) + .await + } else { + self.provider + .rpc() + .send_and_confirm_transaction(transaction) + .await + } + } + + // Stolen from Solana's non-blocking client, but with Jito! + pub async fn send_and_confirm_transaction_with_jito( + &self, + transaction: &impl SerializableTransaction, + ) -> ChainResult { + let signature = transaction.get_signature(); + + let base58_txn = bs58::encode( + bincode::serialize(&transaction).map_err(ChainCommunicationError::from_other)?, + ) + .into_string(); + + const SEND_RETRIES: usize = 1; + const GET_STATUS_RETRIES: usize = usize::MAX; + + 'sending: for _ in 0..SEND_RETRIES { + let jito_request_body = serde_json::json!({ + "jsonrpc": "2.0", + "id": 1, + "method": "sendBundle", + "params": [ + [base58_txn] + ], + }); + + tracing::info!( + ?jito_request_body, + ?signature, + "Sending sealevel transaction to Jito as bundle" + ); + + let jito_response = reqwest::Client::new() + .post("https://mainnet.block-engine.jito.wtf:443/api/v1/bundles") + .json(&jito_request_body) + .send() + .await + .map_err(ChainCommunicationError::from_other)?; + let jito_response_text = jito_response.text().await; + + tracing::info!( + ?signature, + ?jito_response_text, + "Got Jito response for sealevel transaction bundle" + ); + + let recent_blockhash = if transaction.uses_durable_nonce() { + self.provider + .rpc() + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) + .await? + } else { + *transaction.get_recent_blockhash() + }; + + for status_retry in 0..GET_STATUS_RETRIES { + let signature_statuses: Response>> = self + .provider + .rpc() + .get_signature_statuses(&[*signature]) + .await?; + let signature_status = signature_statuses.value.first().cloned().flatten(); + match signature_status { + Some(_) => return Ok(*signature), + None => { + if !self + .provider + .rpc() + .is_blockhash_valid(&recent_blockhash) + .await? + { + // Block hash is not found by some reason + break 'sending; + } else if cfg!(not(test)) + // Ignore sleep at last step. + && status_retry < GET_STATUS_RETRIES + { + // Retry twice a second + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + continue; + } + } + } + } + } + + Err(ChainCommunicationError::from_other( + solana_client::rpc_request::RpcError::ForUser( + "unable to confirm transaction. \ + This can happen in situations such as transaction expiration \ + and insufficient fee-payer funds" + .to_string(), + ), + )) + } } impl HyperlaneContract for SealevelMailbox { @@ -282,8 +424,8 @@ impl std::fmt::Debug for SealevelMailbox { #[async_trait] impl Mailbox for SealevelMailbox { #[instrument(err, ret, skip(self))] - async fn count(&self, _maybe_lag: Option) -> ChainResult { - ::count(self, _maybe_lag).await + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + ::count(self, reorg_period).await } #[instrument(err, ret, skip(self))] @@ -296,23 +438,15 @@ impl Mailbox for SealevelMailbox { let account = self .rpc() - .get_account_with_commitment( - &processed_message_account_key, - CommitmentConfig::finalized(), - ) - .await - .map_err(ChainCommunicationError::from_other)?; + .get_account_option_with_finalized_commitment(&processed_message_account_key) + .await?; - Ok(account.value.is_some()) + Ok(account.is_some()) } #[instrument(err, ret, skip(self))] async fn default_ism(&self) -> ChainResult { - let inbox_account = self - .rpc() - .get_account(&self.inbox.0) - .await - .map_err(ChainCommunicationError::from_other)?; + let inbox_account = self.rpc().get_account(&self.inbox.0).await?; let inbox = InboxAccount::fetch(&mut inbox_account.data.as_ref()) .map_err(ChainCommunicationError::from_other)? .into_inner(); @@ -353,12 +487,25 @@ impl Mailbox for SealevelMailbox { .as_ref() .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?; - let mut instructions = Vec::with_capacity(2); + let mut instructions = Vec::with_capacity(3); // Set the compute unit limit. instructions.push(ComputeBudgetInstruction::set_compute_unit_limit( PROCESS_COMPUTE_UNITS, )); + // If we're using Jito, we need to send a tip to the Jito fee account. + // Otherwise, we need to set the compute unit price. + if self.use_jito() { + // The tip is a standalone transfer to a Jito fee account. + // See https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#sendbundle. + instructions.push(solana_sdk::system_instruction::transfer( + &payer.pubkey(), + // A random Jito fee account, taken from the getFeeAccount RPC response: + // https://github.com/jito-labs/mev-protos/blob/master/json_rpc/http.md#gettipaccounts + &solana_sdk::pubkey!("DfXygSm4jCyNCybVYYK6DwvWqjKee8pbDmJGcLWNDXjh"), + PROCESS_DESIRED_PRIORITIZATION_FEE_LAMPORTS_PER_TX, + )); + } // "processed" level commitment does not guarantee finality. // roughly 5% of blocks end up on a dropped fork. // However we don't want this function to be a bottleneck and there already @@ -435,11 +582,10 @@ impl Mailbox for SealevelMailbox { accounts, }; instructions.push(inbox_instruction); - let (recent_blockhash, _) = self + let recent_blockhash = self .rpc() .get_latest_blockhash_with_commitment(commitment) - .await - .map_err(ChainCommunicationError::from_other)?; + .await?; let txn = Transaction::new_signed_with_payer( &instructions, @@ -450,11 +596,7 @@ impl Mailbox for SealevelMailbox { tracing::info!(?txn, "Created sealevel transaction to process message"); - let signature = self - .rpc() - .send_and_confirm_transaction(&txn) - .await - .map_err(ChainCommunicationError::from_other)?; + let signature = self.send_and_confirm_transaction(&txn).await?; tracing::info!(?txn, ?signature, "Sealevel transaction sent"); @@ -463,7 +605,6 @@ impl Mailbox for SealevelMailbox { .confirm_transaction_with_commitment(&signature, commitment) .await .map_err(|err| warn!("Failed to confirm inbox process transaction: {}", err)) - .map(|ctx| ctx.value) .unwrap_or(false); let txid = signature.into(); @@ -512,104 +653,40 @@ impl SealevelMailboxIndexer { }) } - fn rpc(&self) -> &RpcClientWithDebug { + fn rpc(&self) -> &SealevelRpcClient { &self.mailbox.rpc() } async fn get_finalized_block_number(&self) -> ChainResult { - let height = self - .rpc() - .get_block_height() - .await - .map_err(ChainCommunicationError::from_other)? - .try_into() - // FIXME solana block height is u64... - .expect("sealevel block height exceeds u32::MAX"); - Ok(height) + self.rpc().get_block_height().await } - async fn get_message_with_nonce( + async fn get_dispatched_message_with_nonce( &self, nonce: u32, ) -> ChainResult<(Indexed, LogMeta)> { - let target_message_account_bytes = &[ - &hyperlane_sealevel_mailbox::accounts::DISPATCHED_MESSAGE_DISCRIMINATOR[..], - &nonce.to_le_bytes()[..], - ] - .concat(); - let target_message_account_bytes = base64::encode(target_message_account_bytes); - - // First, find all accounts with the matching account data. - // To keep responses small in case there is ever more than 1 - // match, we don't request the full account data, and just request - // the `unique_message_pubkey` field. - let memcmp = RpcFilterType::Memcmp(Memcmp { - // Ignore the first byte, which is the `initialized` bool flag. - offset: 1, - bytes: MemcmpEncodedBytes::Base64(target_message_account_bytes), - encoding: None, - }); - let config = RpcProgramAccountsConfig { - filters: Some(vec![memcmp]), - account_config: RpcAccountInfoConfig { - encoding: Some(UiAccountEncoding::Base64), - // Don't return any data - data_slice: Some(UiDataSliceConfig { - offset: 1 + 8 + 4 + 8, // the offset to get the `unique_message_pubkey` field - length: 32, // the length of the `unique_message_pubkey` field - }), - commitment: Some(CommitmentConfig::finalized()), - min_context_slot: None, - }, - with_context: Some(false), - }; - let accounts = self - .rpc() - .get_program_accounts_with_config(&self.mailbox.program_id, config) - .await - .map_err(ChainCommunicationError::from_other)?; - - // Now loop through matching accounts and find the one with a valid account pubkey - // that proves it's an actual message storage PDA. - let mut valid_message_storage_pda_pubkey = Option::::None; - - for (pubkey, account) in accounts { - let unique_message_pubkey = Pubkey::new(&account.data); - let (expected_pubkey, _bump) = Pubkey::try_find_program_address( - mailbox_dispatched_message_pda_seeds!(unique_message_pubkey), - &self.mailbox.program_id, - ) - .ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find program address for unique_message_pubkey", - ) - })?; - if expected_pubkey == pubkey { - valid_message_storage_pda_pubkey = Some(pubkey); - break; - } - } + let nonce_bytes = nonce.to_le_bytes(); + let unique_dispatched_message_pubkey_offset = 1 + 8 + 4 + 8; // the offset to get the `unique_message_pubkey` field + let unique_dispatch_message_pubkey_length = 32; // the length of the `unique_message_pubkey` field + let accounts = search_accounts_by_discriminator( + self.rpc(), + &self.program_id, + &DISPATCHED_MESSAGE_DISCRIMINATOR, + &nonce_bytes, + unique_dispatched_message_pubkey_offset, + unique_dispatch_message_pubkey_length, + ) + .await?; - let valid_message_storage_pda_pubkey = - valid_message_storage_pda_pubkey.ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Could not find valid message storage PDA pubkey", - ) - })?; + let valid_message_storage_pda_pubkey = search_and_validate_account(accounts, |account| { + self.dispatched_message_account(&account) + })?; // Now that we have the valid message storage PDA pubkey, we can get the full account data. let account = self .rpc() - .get_account_with_commitment( - &valid_message_storage_pda_pubkey, - CommitmentConfig::finalized(), - ) - .await - .map_err(ChainCommunicationError::from_other)? - .value - .ok_or_else(|| { - ChainCommunicationError::from_other_str("Could not find account data") - })?; + .get_account_with_finalized_commitment(&valid_message_storage_pda_pubkey) + .await?; let dispatched_message_account = DispatchedMessageAccount::fetch(&mut account.data.as_ref()) .map_err(ChainCommunicationError::from_other)? @@ -617,11 +694,99 @@ impl SealevelMailboxIndexer { let hyperlane_message = HyperlaneMessage::read_from(&mut &dispatched_message_account.encoded_message[..])?; + let block = self + .mailbox + .provider + .rpc() + .get_block(dispatched_message_account.slot) + .await?; + let block_hash = decode_h256(&block.blockhash)?; + + let transactions = + block.transactions.ok_or(HyperlaneSealevelError::NoTransactions("block which should contain message dispatch transaction does not contain any transaction".to_owned()))?; + + let transaction_hashes = search_dispatched_message_transactions( + &self.mailbox.program_id, + &valid_message_storage_pda_pubkey, + transactions, + ); + + // We expect to see that there is only one message dispatch transaction + if transaction_hashes.len() > 1 { + Err(HyperlaneSealevelError::TooManyTransactions("Block contains more than one dispatch message transaction operating on the same dispatch message store PDA".to_owned()))? + } + + let (transaction_index, transaction_hash) = transaction_hashes + .into_iter() + .next() + .ok_or(HyperlaneSealevelError::NoTransactions("block which should contain message dispatch transaction does not contain any after filtering".to_owned()))?; + Ok(( hyperlane_message.into(), LogMeta { address: self.mailbox.program_id.to_bytes().into(), block_number: dispatched_message_account.slot, + block_hash, + transaction_id: transaction_hash, + transaction_index: transaction_index as u64, + log_index: U256::from(nonce), + }, + )) + } + + fn dispatched_message_account(&self, account: &Account) -> ChainResult { + let unique_message_pubkey = Pubkey::new(&account.data); + let (expected_pubkey, _bump) = Pubkey::try_find_program_address( + mailbox_dispatched_message_pda_seeds!(unique_message_pubkey), + &self.mailbox.program_id, + ) + .ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Could not find program address for unique message pubkey", + ) + })?; + Ok(expected_pubkey) + } + + async fn get_delivered_message_with_nonce( + &self, + nonce: u32, + ) -> ChainResult<(Indexed, LogMeta)> { + let nonce_bytes = nonce.to_le_bytes(); + let delivered_message_id_offset = 1 + 8 + 8; // the offset to get the `message_id` field + let delivered_message_id_length = 32; + let accounts = search_accounts_by_discriminator( + self.rpc(), + &self.program_id, + &PROCESSED_MESSAGE_DISCRIMINATOR, + &nonce_bytes, + delivered_message_id_offset, + delivered_message_id_length, + ) + .await?; + + debug!(account_len = ?accounts.len(), "Found accounts with processed message discriminator"); + + let valid_message_storage_pda_pubkey = search_and_validate_account(accounts, |account| { + self.delivered_message_account(&account) + })?; + + // Now that we have the valid delivered message storage PDA pubkey, + // we can get the full account data. + let account = self + .rpc() + .get_account_with_finalized_commitment(&valid_message_storage_pda_pubkey) + .await?; + let delivered_message_account = ProcessedMessageAccount::fetch(&mut account.data.as_ref()) + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + let message_id = delivered_message_account.message_id; + + Ok(( + message_id.into(), + LogMeta { + address: self.mailbox.program_id.to_bytes().into(), + block_number: delivered_message_account.slot, // TODO: get these when building out scraper support. // It's inconvenient to get these :| block_hash: H256::zero(), @@ -631,6 +796,18 @@ impl SealevelMailboxIndexer { }, )) } + + fn delivered_message_account(&self, account: &Account) -> ChainResult { + let message_id = H256::from_slice(&account.data); + let (expected_pubkey, _bump) = Pubkey::try_find_program_address( + mailbox_processed_message_pda_seeds!(message_id), + &self.mailbox.program_id, + ) + .ok_or_else(|| { + ChainCommunicationError::from_other_str("Could not find program address for message id") + })?; + Ok(expected_pubkey) + } } #[async_trait] @@ -639,7 +816,7 @@ impl SequenceAwareIndexer for SealevelMailboxIndexer { async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = Indexer::::get_finalized_block_number(self).await?; // TODO: need to make sure the call and tip are at the same height? - let count = Mailbox::count(&self.mailbox, None).await?; + let count = Mailbox::count(&self.mailbox, &ReorgPeriod::None).await?; Ok((Some(count), tip)) } } @@ -658,13 +835,13 @@ impl Indexer for SealevelMailboxIndexer { let message_capacity = range.end().saturating_sub(*range.start()); let mut messages = Vec::with_capacity(message_capacity as usize); for nonce in range { - messages.push(self.get_message_with_nonce(nonce).await?); + messages.push(self.get_dispatched_message_with_nonce(nonce).await?); } Ok(messages) } async fn get_finalized_block_number(&self) -> ChainResult { - get_finalized_block_number(&self.rpc()).await + self.get_finalized_block_number().await } } @@ -672,9 +849,19 @@ impl Indexer for SealevelMailboxIndexer { impl Indexer for SealevelMailboxIndexer { async fn fetch_logs_in_range( &self, - _range: RangeInclusive, + range: RangeInclusive, ) -> ChainResult, LogMeta)>> { - todo!() + info!( + ?range, + "Fetching SealevelMailboxIndexer HyperlaneMessage Delivery logs" + ); + + let message_capacity = range.end().saturating_sub(*range.start()); + let mut message_ids = Vec::with_capacity(message_capacity as usize); + for nonce in range { + message_ids.push(self.get_delivered_message_with_nonce(nonce).await?); + } + Ok(message_ids) } async fn get_finalized_block_number(&self) -> ChainResult { diff --git a/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs b/rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs similarity index 78% rename from rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs rename to rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs index 8c1132add..a0813bfba 100644 --- a/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs +++ b/rust/main/chains/hyperlane-sealevel/src/merkle_tree_hook.rs @@ -1,14 +1,13 @@ -use std::{num::NonZeroU64, ops::RangeInclusive}; +use std::ops::RangeInclusive; use async_trait::async_trait; use derive_new::new; use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, HyperlaneChain, HyperlaneMessage, Indexed, Indexer, LogMeta, MerkleTreeHook, - MerkleTreeInsertion, SequenceAwareIndexer, + MerkleTreeInsertion, ReorgPeriod, SequenceAwareIndexer, }; use hyperlane_sealevel_mailbox::accounts::OutboxAccount; -use solana_sdk::commitment_config::CommitmentConfig; use tracing::instrument; use crate::{SealevelMailbox, SealevelMailboxIndexer}; @@ -16,21 +15,17 @@ use crate::{SealevelMailbox, SealevelMailboxIndexer}; #[async_trait] impl MerkleTreeHook for SealevelMailbox { #[instrument(err, ret, skip(self))] - async fn tree(&self, lag: Option) -> ChainResult { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn tree(&self, reorg_period: &ReorgPeriod) -> ChainResult { assert!( - lag.is_none(), + reorg_period.is_none(), "Sealevel does not support querying point-in-time" ); let outbox_account = self .rpc() - .get_account_with_commitment(&self.outbox.0, CommitmentConfig::finalized()) - .await - .map_err(ChainCommunicationError::from_other)? - .value - .ok_or_else(|| { - ChainCommunicationError::from_other_str("Could not find account data") - })?; + .get_account_with_finalized_commitment(&self.outbox.0) + .await?; let outbox = OutboxAccount::fetch(&mut outbox_account.data.as_ref()) .map_err(ChainCommunicationError::from_other)? .into_inner(); @@ -39,13 +34,14 @@ impl MerkleTreeHook for SealevelMailbox { } #[instrument(err, ret, skip(self))] - async fn latest_checkpoint(&self, lag: Option) -> ChainResult { + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn latest_checkpoint(&self, reorg_period: &ReorgPeriod) -> ChainResult { assert!( - lag.is_none(), + reorg_period.is_none(), "Sealevel does not support querying point-in-time" ); - let tree = self.tree(lag).await?; + let tree = self.tree(reorg_period).await?; let root = tree.root(); let count: u32 = tree @@ -67,8 +63,9 @@ impl MerkleTreeHook for SealevelMailbox { } #[instrument(err, ret, skip(self))] - async fn count(&self, _maybe_lag: Option) -> ChainResult { - let tree = self.tree(_maybe_lag).await?; + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + let tree = self.tree(reorg_period).await?; tree.count() .try_into() diff --git a/rust/chains/hyperlane-sealevel/src/multisig_ism.rs b/rust/main/chains/hyperlane-sealevel/src/multisig_ism.rs similarity index 87% rename from rust/chains/hyperlane-sealevel/src/multisig_ism.rs rename to rust/main/chains/hyperlane-sealevel/src/multisig_ism.rs index 794e19c14..a3cdb1273 100644 --- a/rust/chains/hyperlane-sealevel/src/multisig_ism.rs +++ b/rust/main/chains/hyperlane-sealevel/src/multisig_ism.rs @@ -1,9 +1,9 @@ use async_trait::async_trait; - use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, MultisigIsm, RawHyperlaneMessage, H256, }; +use hyperlane_sealevel_multisig_ism_message_id::instruction::ValidatorsAndThreshold; use serializable_account_meta::SimulationReturnData; use solana_sdk::{ instruction::{AccountMeta, Instruction}, @@ -11,12 +11,8 @@ use solana_sdk::{ signature::Keypair, }; -use crate::{ - utils::{get_account_metas, simulate_instruction}, - ConnectionConf, RpcClientWithDebug, SealevelProvider, -}; +use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; -use hyperlane_sealevel_multisig_ism_message_id::instruction::ValidatorsAndThreshold; use multisig_ism::interface::{ MultisigIsmInstruction, VALIDATORS_AND_THRESHOLD_ACCOUNT_METAS_PDA_SEEDS, }; @@ -44,7 +40,7 @@ impl SealevelMultisigIsm { } } - fn rpc(&self) -> &RpcClientWithDebug { + fn rpc(&self) -> &SealevelRpcClient { self.provider.rpc() } } @@ -86,9 +82,9 @@ impl MultisigIsm for SealevelMultisigIsm { account_metas, ); - let validators_and_threshold = - simulate_instruction::>( - self.rpc(), + let validators_and_threshold = self + .rpc() + .simulate_instruction::>( self.payer .as_ref() .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, @@ -135,13 +131,13 @@ impl SealevelMultisigIsm { vec![AccountMeta::new_readonly(account_metas_pda_key, false)], ); - get_account_metas( - self.rpc(), - self.payer - .as_ref() - .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, - instruction, - ) - .await + self.rpc() + .get_account_metas( + self.payer + .as_ref() + .ok_or_else(|| ChainCommunicationError::SignerUnavailable)?, + instruction, + ) + .await } } diff --git a/rust/main/chains/hyperlane-sealevel/src/provider.rs b/rust/main/chains/hyperlane-sealevel/src/provider.rs new file mode 100644 index 000000000..a0c5a41ea --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/provider.rs @@ -0,0 +1,135 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use solana_sdk::signature::Signature; +use solana_transaction_status::EncodedTransaction; + +use hyperlane_core::{ + BlockInfo, ChainCommunicationError, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, + HyperlaneProvider, HyperlaneProviderError, TxnInfo, TxnReceiptInfo, H256, H512, U256, +}; + +use crate::error::HyperlaneSealevelError; +use crate::utils::{decode_h256, decode_h512, decode_pubkey}; +use crate::{ConnectionConf, SealevelRpcClient}; + +/// A wrapper around a Sealevel provider to get generic blockchain information. +#[derive(Debug)] +pub struct SealevelProvider { + domain: HyperlaneDomain, + rpc_client: Arc, +} + +impl SealevelProvider { + /// Create a new Sealevel provider. + pub fn new(domain: HyperlaneDomain, conf: &ConnectionConf) -> Self { + // Set the `processed` commitment at rpc level + let rpc_client = Arc::new(SealevelRpcClient::new(conf.url.to_string())); + + SealevelProvider { domain, rpc_client } + } + + /// Get an rpc client + pub fn rpc(&self) -> &SealevelRpcClient { + &self.rpc_client + } +} + +impl HyperlaneChain for SealevelProvider { + fn domain(&self) -> &HyperlaneDomain { + &self.domain + } + + fn provider(&self) -> Box { + Box::new(SealevelProvider { + domain: self.domain.clone(), + rpc_client: self.rpc_client.clone(), + }) + } +} + +#[async_trait] +impl HyperlaneProvider for SealevelProvider { + async fn get_block_by_height(&self, slot: u64) -> ChainResult { + let confirmed_block = self.rpc_client.get_block(slot).await?; + + let block_hash = decode_h256(&confirmed_block.blockhash)?; + + let block_time = confirmed_block + .block_time + .ok_or(HyperlaneProviderError::CouldNotFindBlockByHeight(slot))?; + + let block_info = BlockInfo { + hash: block_hash, + timestamp: block_time as u64, + number: slot, + }; + + Ok(block_info) + } + + /// TODO This method is superfluous for Solana. + /// Since we have to request full block to find transaction hash and transaction index + /// for Solana, we have all the data about transaction mach earlier before this + /// method is invoked. + /// We can refactor abstractions so that our chain-agnostic code is more suitable + /// for all chains, not only Ethereum-like chains. + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult { + let signature = Signature::new(hash.as_bytes()); + let transaction = self.rpc_client.get_transaction(&signature).await?; + + let ui_transaction = match transaction.transaction.transaction { + EncodedTransaction::Json(t) => t, + t => Err(Into::::into( + HyperlaneSealevelError::UnsupportedTransactionEncoding(t), + ))?, + }; + + let received_signature = ui_transaction + .signatures + .first() + .ok_or(HyperlaneSealevelError::UnsignedTransaction(*hash))?; + let received_hash = decode_h512(received_signature)?; + + if &received_hash != hash { + Err(Into::::into( + HyperlaneSealevelError::IncorrectTransaction( + Box::new(*hash), + Box::new(received_hash), + ), + ))?; + } + + let receipt = TxnReceiptInfo { + gas_used: Default::default(), + cumulative_gas_used: Default::default(), + effective_gas_price: None, + }; + + Ok(TxnInfo { + hash: *hash, + gas_limit: Default::default(), + max_priority_fee_per_gas: None, + max_fee_per_gas: None, + gas_price: None, + nonce: 0, + sender: Default::default(), + recipient: None, + receipt: Some(receipt), + }) + } + + async fn is_contract(&self, _address: &H256) -> ChainResult { + // FIXME + Ok(true) + } + + async fn get_balance(&self, address: String) -> ChainResult { + let pubkey = decode_pubkey(&address)?; + self.rpc_client.get_balance(&pubkey).await + } + + async fn get_chain_metrics(&self) -> ChainResult> { + Ok(None) + } +} diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc.rs b/rust/main/chains/hyperlane-sealevel/src/rpc.rs new file mode 100644 index 000000000..1c82b77c0 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/rpc.rs @@ -0,0 +1,3 @@ +pub use client::SealevelRpcClient; + +mod client; diff --git a/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs new file mode 100644 index 000000000..88a474cbb --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/rpc/client.rs @@ -0,0 +1,274 @@ +use base64::Engine; +use borsh::{BorshDeserialize, BorshSerialize}; +use serializable_account_meta::{SerializableAccountMeta, SimulationReturnData}; +use solana_client::{ + nonblocking::rpc_client::RpcClient, rpc_config::RpcBlockConfig, + rpc_config::RpcProgramAccountsConfig, rpc_config::RpcTransactionConfig, rpc_response::Response, +}; +use solana_sdk::{ + account::Account, + commitment_config::CommitmentConfig, + hash::Hash, + instruction::{AccountMeta, Instruction}, + message::Message, + pubkey::Pubkey, + signature::{Keypair, Signature, Signer}, + transaction::Transaction, +}; +use solana_transaction_status::{ + EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, UiConfirmedBlock, + UiReturnDataEncoding, UiTransactionReturnData, +}; + +use hyperlane_core::{ChainCommunicationError, ChainResult, U256}; + +use crate::error::HyperlaneSealevelError; + +pub struct SealevelRpcClient(RpcClient); + +impl SealevelRpcClient { + pub fn new(rpc_endpoint: String) -> Self { + Self(RpcClient::new_with_commitment( + rpc_endpoint, + CommitmentConfig::processed(), + )) + } + + pub async fn confirm_transaction_with_commitment( + &self, + signature: &Signature, + commitment: CommitmentConfig, + ) -> ChainResult { + self.0 + .confirm_transaction_with_commitment(signature, commitment) + .await + .map(|ctx| ctx.value) + .map_err(HyperlaneSealevelError::ClientError) + .map_err(Into::into) + } + + pub async fn get_account(&self, pubkey: &Pubkey) -> ChainResult { + self.0 + .get_account(pubkey) + .await + .map_err(ChainCommunicationError::from_other) + } + + /// Simulates an Instruction that will return a list of AccountMetas. + pub async fn get_account_metas( + &self, + payer: &Keypair, + instruction: Instruction, + ) -> ChainResult> { + // If there's no data at all, default to an empty vec. + let account_metas = self + .simulate_instruction::>>( + payer, + instruction, + ) + .await? + .map(|serializable_account_metas| { + serializable_account_metas + .return_data + .into_iter() + .map(|serializable_account_meta| serializable_account_meta.into()) + .collect() + }) + .unwrap_or_else(Vec::new); + + Ok(account_metas) + } + + pub async fn get_account_with_finalized_commitment( + &self, + pubkey: &Pubkey, + ) -> ChainResult { + self.get_account_option_with_finalized_commitment(pubkey) + .await? + .ok_or_else(|| ChainCommunicationError::from_other_str("Could not find account data")) + } + + pub async fn get_account_option_with_finalized_commitment( + &self, + pubkey: &Pubkey, + ) -> ChainResult> { + let account = self + .0 + .get_account_with_commitment(pubkey, CommitmentConfig::finalized()) + .await + .map_err(ChainCommunicationError::from_other)? + .value; + Ok(account) + } + + pub async fn get_balance(&self, pubkey: &Pubkey) -> ChainResult { + let balance = self + .0 + .get_balance(pubkey) + .await + .map_err(Into::::into) + .map_err(ChainCommunicationError::from)?; + + Ok(balance.into()) + } + + pub async fn get_block(&self, height: u64) -> ChainResult { + let config = RpcBlockConfig { + commitment: Some(CommitmentConfig::finalized()), + max_supported_transaction_version: Some(0), + ..Default::default() + }; + self.0 + .get_block_with_config(height, config) + .await + .map_err(HyperlaneSealevelError::ClientError) + .map_err(Into::into) + } + + pub async fn get_block_height(&self) -> ChainResult { + let height = self + .0 + .get_block_height_with_commitment(CommitmentConfig::finalized()) + .await + .map_err(ChainCommunicationError::from_other)? + .try_into() + // FIXME solana block height is u64... + .expect("sealevel block height exceeds u32::MAX"); + Ok(height) + } + + pub async fn get_multiple_accounts_with_finalized_commitment( + &self, + pubkeys: &[Pubkey], + ) -> ChainResult>> { + let accounts = self + .0 + .get_multiple_accounts_with_commitment(pubkeys, CommitmentConfig::finalized()) + .await + .map_err(ChainCommunicationError::from_other)? + .value; + + Ok(accounts) + } + + pub async fn get_latest_blockhash_with_commitment( + &self, + commitment: CommitmentConfig, + ) -> ChainResult { + self.0 + .get_latest_blockhash_with_commitment(commitment) + .await + .map_err(ChainCommunicationError::from_other) + .map(|(blockhash, _)| blockhash) + } + + pub async fn get_program_accounts_with_config( + &self, + pubkey: &Pubkey, + config: RpcProgramAccountsConfig, + ) -> ChainResult> { + self.0 + .get_program_accounts_with_config(pubkey, config) + .await + .map_err(ChainCommunicationError::from_other) + } + + pub async fn get_signature_statuses( + &self, + signatures: &[Signature], + ) -> ChainResult>>> { + self.0 + .get_signature_statuses(signatures) + .await + .map_err(ChainCommunicationError::from_other) + } + + pub async fn get_transaction( + &self, + signature: &Signature, + ) -> ChainResult { + let config = RpcTransactionConfig { + commitment: Some(CommitmentConfig::finalized()), + ..Default::default() + }; + self.0 + .get_transaction_with_config(signature, config) + .await + .map_err(HyperlaneSealevelError::ClientError) + .map_err(Into::into) + } + + pub async fn is_blockhash_valid(&self, hash: &Hash) -> ChainResult { + self.0 + .is_blockhash_valid(hash, CommitmentConfig::processed()) + .await + .map_err(ChainCommunicationError::from_other) + } + + pub async fn send_and_confirm_transaction( + &self, + transaction: &Transaction, + ) -> ChainResult { + self.0 + .send_and_confirm_transaction(transaction) + .await + .map_err(ChainCommunicationError::from_other) + } + + /// Simulates an instruction, and attempts to deserialize it into a T. + /// If no return data at all was returned, returns Ok(None). + /// If some return data was returned but deserialization was unsuccessful, + /// an Err is returned. + pub async fn simulate_instruction( + &self, + payer: &Keypair, + instruction: Instruction, + ) -> ChainResult> { + let commitment = CommitmentConfig::finalized(); + let recent_blockhash = self + .get_latest_blockhash_with_commitment(commitment) + .await?; + let transaction = Transaction::new_unsigned(Message::new_with_blockhash( + &[instruction], + Some(&payer.pubkey()), + &recent_blockhash, + )); + let return_data = self.simulate_transaction(&transaction).await?; + + if let Some(return_data) = return_data { + let bytes = match return_data.data.1 { + UiReturnDataEncoding::Base64 => base64::engine::general_purpose::STANDARD + .decode(return_data.data.0) + .map_err(ChainCommunicationError::from_other)?, + }; + + let decoded_data = + T::try_from_slice(bytes.as_slice()).map_err(ChainCommunicationError::from_other)?; + + return Ok(Some(decoded_data)); + } + + Ok(None) + } + + async fn simulate_transaction( + &self, + transaction: &Transaction, + ) -> ChainResult> { + let return_data = self + .0 + .simulate_transaction(transaction) + .await + .map_err(ChainCommunicationError::from_other)? + .value + .return_data; + + Ok(return_data) + } +} + +impl std::fmt::Debug for SealevelRpcClient { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("RpcClient { ... }") + } +} diff --git a/rust/chains/hyperlane-sealevel/src/trait_builder.rs b/rust/main/chains/hyperlane-sealevel/src/trait_builder.rs similarity index 100% rename from rust/chains/hyperlane-sealevel/src/trait_builder.rs rename to rust/main/chains/hyperlane-sealevel/src/trait_builder.rs diff --git a/rust/main/chains/hyperlane-sealevel/src/transaction.rs b/rust/main/chains/hyperlane-sealevel/src/transaction.rs new file mode 100644 index 000000000..26a0722cf --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/transaction.rs @@ -0,0 +1,188 @@ +use std::collections::HashMap; + +use hyperlane_sealevel_mailbox::instruction::Instruction; +use solana_sdk::pubkey::Pubkey; +use solana_transaction_status::option_serializer::OptionSerializer; +use solana_transaction_status::{ + EncodedTransaction, EncodedTransactionWithStatusMeta, UiCompiledInstruction, UiInstruction, + UiMessage, UiTransaction, UiTransactionStatusMeta, +}; +use tracing::warn; + +use hyperlane_core::H512; + +use crate::utils::{decode_h512, from_base58}; + +/// This function searches for a transaction which dispatches Hyperlane message and returns +/// list of hashes of such transactions. +/// +/// This function takes the mailbox program identifier and the identifier for PDA for storing +/// a dispatched message and searches a message dispatch transaction in a list of transaction. +/// The list of transaction is usually comes from a block. The function returns list of hashes +/// of such transactions. +/// +/// The transaction will be searched with the following criteria: +/// 1. Transaction contains Mailbox program id in the list of accounts. +/// 2. Transaction contains dispatched message PDA in the list of accounts. +/// 3. Transaction is performing message dispatch (OutboxDispatch). +/// +/// * `mailbox_program_id` - Identifier of Mailbox program +/// * `message_storage_pda_pubkey` - Identifier for dispatch message store PDA +/// * `transactions` - List of transactions +pub fn search_dispatched_message_transactions( + mailbox_program_id: &Pubkey, + message_storage_pda_pubkey: &Pubkey, + transactions: Vec, +) -> Vec<(usize, H512)> { + transactions + .into_iter() + .enumerate() + .filter_map(|(index, tx)| filter_by_encoding(tx).map(|(tx, meta)| (index, tx, meta))) + .filter_map(|(index, tx, meta)| { + filter_by_validity(tx, meta) + .map(|(hash, account_keys, instructions)| (index, hash, account_keys, instructions)) + }) + .filter_map(|(index, hash, account_keys, instructions)| { + filter_not_relevant( + mailbox_program_id, + message_storage_pda_pubkey, + hash, + account_keys, + instructions, + ) + .map(|hash| (index, hash)) + }) + .collect::>() +} + +fn filter_not_relevant( + mailbox_program_id: &Pubkey, + message_storage_pda_pubkey: &Pubkey, + hash: H512, + account_keys: Vec, + instructions: Vec, +) -> Option { + let account_index_map = account_index_map(account_keys); + + let mailbox_program_id_str = mailbox_program_id.to_string(); + let mailbox_program_index = match account_index_map.get(&mailbox_program_id_str) { + Some(i) => *i as u8, + None => return None, // If account keys do not contain Mailbox program, transaction is not message dispatch. + }; + + let message_storage_pda_pubkey_str = message_storage_pda_pubkey.to_string(); + let dispatch_message_pda_account_index = + match account_index_map.get(&message_storage_pda_pubkey_str) { + Some(i) => *i as u8, + None => return None, // If account keys do not contain dispatch message store PDA account, transaction is not message dispatch. + }; + + let mailbox_program_maybe = instructions + .into_iter() + .find(|instruction| instruction.program_id_index == mailbox_program_index); + + let mailbox_program = match mailbox_program_maybe { + Some(p) => p, + None => return None, // If transaction does not contain call into Mailbox, transaction is not message dispatch. + }; + + // If Mailbox program does not operate on dispatch message store PDA account, transaction is not message dispatch. + if !mailbox_program + .accounts + .contains(&dispatch_message_pda_account_index) + { + return None; + } + + let instruction_data = match from_base58(&mailbox_program.data) { + Ok(d) => d, + Err(_) => return None, // If we cannot decode instruction data, transaction is not message dispatch. + }; + + let instruction = match Instruction::from_instruction_data(&instruction_data) { + Ok(ii) => ii, + Err(_) => return None, // If we cannot parse instruction data, transaction is not message dispatch. + }; + + // If the call into Mailbox program is not OutboxDispatch, transaction is not message dispatch. + if !matches!(instruction, Instruction::OutboxDispatch(_)) { + return None; + } + + Some(hash) +} + +fn filter_by_validity( + tx: UiTransaction, + meta: UiTransactionStatusMeta, +) -> Option<(H512, Vec, Vec)> { + let Some(transaction_hash) = tx + .signatures + .first() + .map(|signature| decode_h512(signature)) + .and_then(|r| r.ok()) + else { + warn!( + transaction = ?tx, + "transaction does not have any signatures or signatures cannot be decoded", + ); + return None; + }; + + let UiMessage::Raw(message) = tx.message else { + warn!(message = ?tx.message, "we expect messages in Raw format"); + return None; + }; + + let instructions = instructions(message.instructions, meta); + + Some((transaction_hash, message.account_keys, instructions)) +} + +fn filter_by_encoding( + tx: EncodedTransactionWithStatusMeta, +) -> Option<(UiTransaction, UiTransactionStatusMeta)> { + match (tx.transaction, tx.meta) { + // We support only transactions encoded as JSON + // We need none-empty metadata as well + (EncodedTransaction::Json(t), Some(m)) => Some((t, m)), + t => { + warn!( + ?t, + "transaction is not encoded as json or metadata is empty" + ); + None + } + } +} + +fn account_index_map(account_keys: Vec) -> HashMap { + account_keys + .into_iter() + .enumerate() + .map(|(index, key)| (key, index)) + .collect::>() +} + +/// Extract all instructions from transaction +fn instructions( + instruction: Vec, + meta: UiTransactionStatusMeta, +) -> Vec { + let inner_instructions = match meta.inner_instructions { + OptionSerializer::Some(ii) => ii + .into_iter() + .flat_map(|ii| ii.instructions) + .flat_map(|ii| match ii { + UiInstruction::Compiled(ci) => Some(ci), + _ => None, + }) + .collect::>(), + OptionSerializer::None | OptionSerializer::Skip => vec![], + }; + + [instruction, inner_instructions].concat() +} + +#[cfg(test)] +mod tests; diff --git a/rust/main/chains/hyperlane-sealevel/src/transaction/tests.rs b/rust/main/chains/hyperlane-sealevel/src/transaction/tests.rs new file mode 100644 index 000000000..759f15de9 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/transaction/tests.rs @@ -0,0 +1,329 @@ +use solana_transaction_status::EncodedTransactionWithStatusMeta; + +use crate::transaction::search_dispatched_message_transactions; +use crate::utils::decode_pubkey; + +#[test] +pub fn test_search_dispatched_message_transaction() { + // given + let mailbox_program_id = decode_pubkey("E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi").unwrap(); + let dispatched_message_pda_account = + decode_pubkey("6eG8PheL41qLFFUtPjSYMtsp4aoAQsMgcsYwkGCB8kwT").unwrap(); + let transaction = serde_json::from_str::(JSON).unwrap(); + let transactions = vec![transaction]; + + // when + let transaction_hashes = search_dispatched_message_transactions( + &mailbox_program_id, + &dispatched_message_pda_account, + transactions, + ); + + // then + assert!(!transaction_hashes.is_empty()); +} + +const JSON: &str = r#" +{ + "blockTime": 1729865514, + "meta": { + "computeUnitsConsumed": 171834, + "err": null, + "fee": 3564950, + "innerInstructions": [ + { + "index": 2, + "instructions": [ + { + "accounts": [ + 8, + 7, + 6, + 0 + ], + "data": "gCzo5F74HA9Pb", + "programIdIndex": 19, + "stackHeight": 2 + }, + { + "accounts": [ + 5, + 11, + 10, + 18, + 0, + 1, + 2 + ], + "data": "2Nsbnwq8JuYnSefHfRznxFtFqdPnbeydtt5kenfF8GR1ZU2XtF8jJDo4SUc2VY52V5C25WsKsQZBLsoCVQNzefgVj2bVznkThjuZuSKXJfZN9ADggiM2soRKVsAjf3xHm3CC3w3iyvK5U9LsjmYtiDNbJCFtEPRTDxsfvMS45Bg3q6EogmBN9JiZNLP", + "programIdIndex": 17, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 5 + ], + "data": "3Bxs3zrfFUZbEPqZ", + "programIdIndex": 10, + "stackHeight": 3 + }, + { + "accounts": [ + 0, + 2 + ], + "data": "11114XfZCGKrze4PNou1GXiYCJgiBCGpHks9hxjb8tFwYMjtgVtMzvriDxwYPdRqSoqztL", + "programIdIndex": 10, + "stackHeight": 3 + }, + { + "accounts": [ + 10, + 0, + 3, + 1, + 4, + 9, + 14 + ], + "data": "5MtKiLZhPB3NhS7Gus6CenAEMS2QBtpY9QtuLeVH4CkpUN7599vsYzZXhk8Vu", + "programIdIndex": 15, + "stackHeight": 2 + }, + { + "accounts": [ + 0, + 9 + ], + "data": "3Bxs4A3YxXXYy5gj", + "programIdIndex": 10, + "stackHeight": 3 + }, + { + "accounts": [ + 0, + 4 + ], + "data": "111158VjdPaAaGVkCbPZoXJqknHXBEqoypfVjf96mwePbKxAkrKfR2gUFyN7wD8ccc9g1z", + "programIdIndex": 10, + "stackHeight": 3 + } + ] + } + ], + "loadedAddresses": { + "readonly": [], + "writable": [] + }, + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success", + "Program 3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm invoke [1]", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [2]", + "Program log: Instruction: TransferChecked", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 6200 of 983051 compute units", + "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA success", + "Program E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi invoke [2]", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Protocol fee of 0 paid from FGyh1FfooV7AtVrYjFGmjMxbELC8RMxNp4xY5WY4L4md to BvZpTuYLAR77mPhH4GtvwEWUTs53GQqkgBNuXpCePVNk", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Dispatched message to 1408864445, ID 0x09c74f3e10d98c112696b72ba1609aae47616f64f28b4cb1ad8a4a710e93ee89", + "Program E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi consumed 86420 of 972001 compute units", + "Program return: E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi CcdPPhDZjBEmlrcroWCarkdhb2Tyi0yxrYpKcQ6T7ok=", + "Program E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi success", + "Program BhNcatUDC2D5JTyeaqrdSukiVFsEHK7e3hVmKMztwefv invoke [2]", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program 11111111111111111111111111111111 invoke [3]", + "Program 11111111111111111111111111111111 success", + "Program log: Paid IGP JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M for 431000 gas for message 0x09c7…ee89 to 1408864445", + "Program BhNcatUDC2D5JTyeaqrdSukiVFsEHK7e3hVmKMztwefv consumed 42792 of 882552 compute units", + "Program BhNcatUDC2D5JTyeaqrdSukiVFsEHK7e3hVmKMztwefv success", + "Program log: Warp route transfer completed to destination: 1408864445, recipient: 0xd41b…f050, remote_amount: 2206478600", + "Program 3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm consumed 171534 of 999700 compute units", + "Program 3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm success" + ], + "postBalances": [ + 12374928, + 0, + 2241120, + 1016160, + 1872240, + 8679120, + 2039280, + 319231603414, + 2039280, + 10172586528, + 1, + 890880, + 1141440, + 3361680, + 1830480, + 1141440, + 1, + 1141440, + 1141440, + 934087680 + ], + "postTokenBalances": [ + { + "accountIndex": 6, + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "owner": "CcquFeCYNZM48kLPyG3HWxdwgigmyxPBi6iHwve9Myhj", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "165697511204", + "decimals": 6, + "uiAmount": 165697.511204, + "uiAmountString": "165697.511204" + } + }, + { + "accountIndex": 8, + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "owner": "FGyh1FfooV7AtVrYjFGmjMxbELC8RMxNp4xY5WY4L4md", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "94", + "decimals": 6, + "uiAmount": 9.4E-5, + "uiAmountString": "0.000094" + } + } + ], + "preBalances": [ + 22211372, + 0, + 0, + 1016160, + 0, + 8679120, + 2039280, + 319231603414, + 2039280, + 10170428394, + 1, + 890880, + 1141440, + 3361680, + 1830480, + 1141440, + 1, + 1141440, + 1141440, + 934087680 + ], + "preTokenBalances": [ + { + "accountIndex": 6, + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "owner": "CcquFeCYNZM48kLPyG3HWxdwgigmyxPBi6iHwve9Myhj", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "163491032604", + "decimals": 6, + "uiAmount": 163491.032604, + "uiAmountString": "163491.032604" + } + }, + { + "accountIndex": 8, + "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "owner": "FGyh1FfooV7AtVrYjFGmjMxbELC8RMxNp4xY5WY4L4md", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "2206478694", + "decimals": 6, + "uiAmount": 2206.478694, + "uiAmountString": "2206.478694" + } + } + ], + "rewards": [], + "status": { + "Ok": null + } + }, + "slot": 297626301, + "transaction": { + "message": { + "accountKeys": [ + "FGyh1FfooV7AtVrYjFGmjMxbELC8RMxNp4xY5WY4L4md", + "8DqWVhEZcg4rDYwe5UFaopmGuEajiPz9L3A1ZnytMcUm", + "6eG8PheL41qLFFUtPjSYMtsp4aoAQsMgcsYwkGCB8kwT", + "8Cv4PHJ6Cf3xY7dse7wYeZKtuQv9SAN6ujt5w22a2uho", + "9yMwrDqHsbmmvYPS9h4MLPbe2biEykcL51W7qJSDL5hF", + "BvZpTuYLAR77mPhH4GtvwEWUTs53GQqkgBNuXpCePVNk", + "CcquFeCYNZM48kLPyG3HWxdwgigmyxPBi6iHwve9Myhj", + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "FDDbaNtod9pt7pmR8qtmRZJtEj9NViDA7J6cazqUjXQj", + "JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M", + "11111111111111111111111111111111", + "37N3sbyVAd3KvQsPw42i1LWkLahzL4ninVQ4n1NmnHjS", + "3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm", + "AHX3iiEPFMyygANrp15cyUr63o9qGkwkB6ki1pgpZ7gZ", + "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "BhNcatUDC2D5JTyeaqrdSukiVFsEHK7e3hVmKMztwefv", + "ComputeBudget111111111111111111111111111111", + "E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi", + "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV", + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + ], + "header": { + "numReadonlySignedAccounts": 1, + "numReadonlyUnsignedAccounts": 10, + "numRequiredSignatures": 2 + }, + "instructions": [ + { + "accounts": [], + "data": "FjL4FH", + "programIdIndex": 16, + "stackHeight": null + }, + { + "accounts": [], + "data": "3butUEijJrLf", + "programIdIndex": 16, + "stackHeight": null + }, + { + "accounts": [ + 10, + 18, + 13, + 17, + 5, + 11, + 0, + 1, + 2, + 15, + 3, + 4, + 14, + 9, + 19, + 7, + 8, + 6 + ], + "data": "RpjV6TtUSvt6UnMXdNo4h1Ze2VGVifo65r2jqRBUq6HJKhskSnwWybXyB4NxgfvedV9vhKdmDPg8sFT64JEZvxF8VfoGdqoAFt4WFLSB", + "programIdIndex": 12, + "stackHeight": null + } + ], + "recentBlockhash": "GHQhVUy7Eq3hcps8YoG9DCd1Tb6ccQZ9xhh81ju8ujHJ" + }, + "signatures": [ + "4nRGgV9tqCuiKUXeBzWdvdk6YC9BsGWUZurAVQLMX1NwNPpysbZNwXu97Sw4aM9REwaRmWS7gaiSKXbwtmw6oLRi", + "hXjvQbAuFH9vAxZMdGqfnSjN7t7Z7NLTzRq1SG8i6fLr9LS6XahTduPWqakiTsLDyWSofvq3MSncUAkbQLEj85f" + ] + } +} +"#; diff --git a/rust/main/chains/hyperlane-sealevel/src/utils.rs b/rust/main/chains/hyperlane-sealevel/src/utils.rs new file mode 100644 index 000000000..56b8202c6 --- /dev/null +++ b/rust/main/chains/hyperlane-sealevel/src/utils.rs @@ -0,0 +1,33 @@ +use std::str::FromStr; + +use solana_sdk::bs58; +use solana_sdk::pubkey::Pubkey; + +use hyperlane_core::{H256, H512}; + +use crate::error::HyperlaneSealevelError; + +pub fn from_base58(base58: &str) -> Result, HyperlaneSealevelError> { + let binary = bs58::decode(base58) + .into_vec() + .map_err(HyperlaneSealevelError::Decoding)?; + Ok(binary) +} + +pub fn decode_h256(base58: &str) -> Result { + let binary = from_base58(base58)?; + let hash = H256::from_slice(&binary); + + Ok(hash) +} + +pub fn decode_h512(base58: &str) -> Result { + let binary = from_base58(base58)?; + let hash = H512::from_slice(&binary); + + Ok(hash) +} + +pub fn decode_pubkey(address: &str) -> Result { + Pubkey::from_str(address).map_err(Into::::into) +} diff --git a/rust/chains/hyperlane-sealevel/src/validator_announce.rs b/rust/main/chains/hyperlane-sealevel/src/validator_announce.rs similarity index 87% rename from rust/chains/hyperlane-sealevel/src/validator_announce.rs rename to rust/main/chains/hyperlane-sealevel/src/validator_announce.rs index 9d22eb68c..3edfa0d06 100644 --- a/rust/chains/hyperlane-sealevel/src/validator_announce.rs +++ b/rust/main/chains/hyperlane-sealevel/src/validator_announce.rs @@ -1,17 +1,15 @@ use async_trait::async_trait; -use tracing::{info, instrument, warn}; - use hyperlane_core::{ - Announcement, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, - HyperlaneContract, HyperlaneDomain, SignedType, TxOutcome, ValidatorAnnounce, H160, H256, H512, - U256, + Announcement, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + SignedType, TxOutcome, ValidatorAnnounce, H160, H256, H512, U256, }; -use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; - -use crate::{ConnectionConf, RpcClientWithDebug, SealevelProvider}; use hyperlane_sealevel_validator_announce::{ accounts::ValidatorStorageLocationsAccount, validator_storage_locations_pda_seeds, }; +use solana_sdk::pubkey::Pubkey; +use tracing::{info, instrument, warn}; + +use crate::{ConnectionConf, SealevelProvider, SealevelRpcClient}; /// A reference to a ValidatorAnnounce contract on some Sealevel chain #[derive(Debug)] @@ -33,7 +31,7 @@ impl SealevelValidatorAnnounce { } } - fn rpc(&self) -> &RpcClientWithDebug { + fn rpc(&self) -> &SealevelRpcClient { self.provider.rpc() } } @@ -79,10 +77,8 @@ impl ValidatorAnnounce for SealevelValidatorAnnounce { // If an account doesn't exist, it will be returned as None. let accounts = self .rpc() - .get_multiple_accounts_with_commitment(&account_pubkeys, CommitmentConfig::finalized()) - .await - .map_err(ChainCommunicationError::from_other)? - .value; + .get_multiple_accounts_with_finalized_commitment(&account_pubkeys) + .await?; // Parse the storage locations from each account. // If a validator's account doesn't exist, its storage locations will @@ -116,6 +112,7 @@ impl ValidatorAnnounce for SealevelValidatorAnnounce { } #[instrument(err, ret, skip(self))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn announce(&self, _announcement: SignedType) -> ChainResult { warn!( "Announcing validator storage locations within the agents is not supported on Sealevel" diff --git a/rust/config/mainnet_config.json b/rust/main/config/mainnet_config.json similarity index 67% rename from rust/config/mainnet_config.json rename to rust/main/config/mainnet_config.json index ce6c300c1..b0404fee4 100644 --- a/rust/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -14,7 +14,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 888888888, "deployer": { @@ -34,7 +34,7 @@ "interchainAccountIsm": "0xd766e7C7517f2d0D92754b2fe4aE7AdEf7bDEC3e", "interchainAccountRouter": "0x25C87e735021F72d8728438C2130b02E3141f2cb", "interchainGasPaymaster": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", - "interchainSecurityModule": "0x6C73cA7bF00FFd3617eF28E26B28EbC1e53D6075", + "interchainSecurityModule": "0x4e1d2cdB48A2C2912b11801Eb1F1d5007474cA43", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162", @@ -60,7 +60,7 @@ "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "storageGasOracle": "0x59Bf7c7b458375b1A7c453aE70EaCb376E65CDAF", - "technicalStack": "other", + "technicalStack": "opstack", "testRecipient": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", @@ -80,7 +80,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 42161, "deployer": { @@ -100,7 +100,7 @@ "interchainAccountIsm": "0x2A7574358Ec53522CE2452887661AB4c86F7d400", "interchainAccountRouter": "0x91874Dbed74925dFe6059B90385EEb90DdE0B2E6", "interchainGasPaymaster": "0x3b6044acd6767f017e99318AA6Ef93b7B06A5a22", - "interchainSecurityModule": "0xC270F9DF86bb320A31757a64d1d5429B4D7a516e", + "interchainSecurityModule": "0x50d0b0E27B8B93119618f053A623886116dd3b6d", "mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9", "merkleTreeHook": "0x748040afB89B8FdBb992799808215419d36A0930", "name": "arbitrum", @@ -172,7 +172,7 @@ "interchainAccountIsm": "0x27a3233c05C1Df7c163123301D14bE9349E3Cb48", "interchainAccountRouter": "0xa82a0227e6d6db53AF4B264A852bfF91C6504a51", "interchainGasPaymaster": "0x95519ba800BBd0d34eeAE026fEc620AD978176C0", - "interchainSecurityModule": "0xCBcE96dAa697A2A51f488a47264D623c3593bb18", + "interchainSecurityModule": "0xbc803Da34A88E5f6B50dfc0CC9D924d9865c91C5", "mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6", "merkleTreeHook": "0x84eea61D679F42D92145fA052C89900CBAccE95A", "name": "avalanche", @@ -209,7 +209,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x9Cad0eC82328CEE2386Ec14a12E81d070a27712f", "staticMerkleRootWeightedMultisigIsmFactory": "0xEdF170Da58598955e9a63DA43885842108969129", - "staticMessageIdWeightedMultisigIsmFactory": "0xf44bae1e60bD5B895B2c5bAfF26C49B7e324E36C" + "staticMessageIdWeightedMultisigIsmFactory": "0xf44bae1e60bD5B895B2c5bAfF26C49B7e324E36C", + "technicalStack": "other" }, "base": { "aggregationHook": "0x13f3d4B0Ee0a713430fded9E18f7fb6c91A6E41F", @@ -224,7 +225,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 10 }, "chainId": 8453, "deployer": { @@ -244,7 +245,7 @@ "interchainAccountIsm": "0x223F7D3f27E6272266AE4B5B91Fd5C7A2d798cD8", "interchainAccountRouter": "0x4767D22117bBeeb295413000B620B93FD8522d53", "interchainGasPaymaster": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", - "interchainSecurityModule": "0xf1d67752c6399933d850dC27F41F581a198fbEdE", + "interchainSecurityModule": "0xB7fcb4665ace2B0d36fd92D26b4a8B516c0bFe5F", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x19dc38aeae620380430C200a6E990D5Af5480117", "name": "base", @@ -279,7 +280,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", "staticMerkleRootWeightedMultisigIsmFactory": "0x414B67F62b143d6db6E9b633168Dd6fd4DA20642", - "staticMessageIdWeightedMultisigIsmFactory": "0xcfacC141f090E5441D8F274659D43ec20F748b19" + "staticMessageIdWeightedMultisigIsmFactory": "0xcfacC141f090E5441D8F274659D43ec20F748b19", + "technicalStack": "opstack" }, "blast": { "aggregationHook": "0x012278333Ce0A845AE9bD7302867a59Bd5D3635d", @@ -294,7 +296,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 81457, "deployer": { @@ -314,7 +316,7 @@ "interchainAccountIsm": "0xe93f2f409ad8B5000431D234472973fe848dcBEC", "interchainAccountRouter": "0x2f4Eb04189e11Af642237Da62d163Ab714614498", "interchainGasPaymaster": "0xB3fCcD379ad66CED0c91028520C64226611A48c9", - "interchainSecurityModule": "0x0aa862a0039ed2d7908d749049ed2766Ff03AABb", + "interchainSecurityModule": "0xECa4a584E91867a72cd036DB7Db22Ad894a197B7", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "name": "blast", @@ -342,7 +344,7 @@ "staticMerkleRootMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", "staticMessageIdMultisigIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "storageGasOracle": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", - "technicalStack": "other", + "technicalStack": "opstack", "testRecipient": "0x17E216fBb22dF4ef8A6640ae9Cb147C92710ac84", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0xFC62DeF1f08793aBf0E67f69257c6be258194F72", @@ -362,7 +364,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 60808, "deployer": { @@ -382,7 +384,7 @@ "interchainAccountIsm": "0x451dF8AB0936D85526D816f0b4dCaDD934A034A4", "interchainAccountRouter": "0x5C02157068a52cEcfc98EDb6115DE6134EcB4764", "interchainGasPaymaster": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05", - "interchainSecurityModule": "0x83a33bc769aE0b830cdbACbC82a151A1217EbD60", + "interchainSecurityModule": "0x93E3e6CA295803417212421785606B1F7dDeaD8f", "mailbox": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "merkleTreeHook": "0x781bE492F1232E66990d83a9D3AC3Ec26f56DAfB", "name": "bob", @@ -407,7 +409,7 @@ "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "storageGasOracle": "0x2Fca7f6eC3d4A0408900f2BB30004d4616eE985E", - "technicalStack": "other", + "technicalStack": "opstack", "testRecipient": "0xe03dad16074BC5EEA9A9311257BF02Eb0B6AAA2b", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", @@ -427,7 +429,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 15 + "reorgPeriod": "finalized" }, "chainId": 56, "deployer": { @@ -448,7 +450,7 @@ "interchainAccountIsm": "0x9e22945bE593946618383B108CC5bce09eBA4C26", "interchainAccountRouter": "0x32A07c1B7a7fe8D4A0e44B0181873aB9d64C16c1", "interchainGasPaymaster": "0x78E25e7f84416e69b9339B0A6336EB6EFfF6b451", - "interchainSecurityModule": "0x6a46c4db32c06bF8D6A4E2e1ed311427E8087E47", + "interchainSecurityModule": "0xA0506B5b12770494740A4a7cc86C9A36Dc1Fc6Dc", "mailbox": "0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4", "merkleTreeHook": "0xFDb9Cd5f9daAA2E4474019405A328a88E7484f26", "name": "bsc", @@ -482,12 +484,13 @@ "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": 3000000000 - }, "validatorAnnounce": "0x7024078130D9c2100fEA474DAD009C2d1703aCcd", "staticMerkleRootWeightedMultisigIsmFactory": "0x6f72BF0018a93689D9CD6BF59C7AAeA66F578Fc1", - "staticMessageIdWeightedMultisigIsmFactory": "0x058C7458193f1b28e2bF7547E3f7a6A719Fc0f59" + "staticMessageIdWeightedMultisigIsmFactory": "0x058C7458193f1b28e2bF7547E3f7a6A719Fc0f59", + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 3000000000 + } }, "celo": { "aggregationHook": "0xc65890329066FB20c339Bc5C22f1756e9D3a4fF5", @@ -528,7 +531,7 @@ "interchainAccountIsm": "0xB732c83aeE29596E3163Da2260710eAB67Bc0B29", "interchainAccountRouter": "0x27a6cAe33378bB6A6663b382070427A01fc9cB37", "interchainGasPaymaster": "0x571f1435613381208477ac5d6974310d88AC7cB7", - "interchainSecurityModule": "0x8718d2867085C065BE7a65ec669ceE4a6d0902E1", + "interchainSecurityModule": "0xa6f4835940dbA46E295076D0CD0411349C33789f", "mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb", "merkleTreeHook": "0x04dB778f05854f26E67e0a66b740BBbE9070D366", "name": "celo", @@ -558,22 +561,23 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0xCeF677b65FDaA6804d4403083bb12B8dB3991FE1", "staticMerkleRootWeightedMultisigIsmFactory": "0x0f05deB9c5931c3F87209674B6d4c6df74F6DCBc", - "staticMessageIdWeightedMultisigIsmFactory": "0x58924b11A3B03D533192Dd5a92bc358F5a970E34" + "staticMessageIdWeightedMultisigIsmFactory": "0x58924b11A3B03D533192Dd5a92bc358F5a970E34", + "technicalStack": "other" }, "cheesechain": { "aggregationHook": "0x8007d1e60991fB9BE1be26f70A7cE284fdE7da97", "blockExplorers": [ { - "apiUrl": "https://fetascan.io/api", + "apiUrl": "https://fetascan.xyz/api", "family": "blockscout", "name": "Fetascan", - "url": "https://fetascan.io" + "url": "https://fetascan.xyz" } ], "blocks": { "confirmations": 1, - "estimateBlockTime": 90, - "reorgPeriod": 0 + "estimateBlockTime": 30, + "reorgPeriod": 1 }, "chainId": 383353, "deployer": { @@ -592,7 +596,7 @@ "interchainAccountIsm": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "interchainAccountRouter": "0xEF9A332Ec1fD233Bf9344A58be56ff9E104B4f60", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0xbf8c84422e09BB9866F9E416d6f447A33e261D05", + "interchainSecurityModule": "0x05f6BAa16F1aCf7b19c4A09E019D856c10ab8355", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "cheesechain", @@ -638,7 +642,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 7560, "deployer": { @@ -655,7 +659,7 @@ "from": 4842212 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x18E1F88A882cE015b135Acdf356dd4F5773763B9", + "interchainSecurityModule": "0x9A746C4BC2bE7E657A3469f0a0DAA1dE517b8514", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "cyber", @@ -689,7 +693,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0x71388C9E25BE7b229B5d17Df7D4DB3F7DA7C962d", "interchainAccountIsm": "0x67F36550b73B731e5b2FC44E4F8f250d89c87bD6", "interchainAccountRouter": "0x7B032cBB00AD7438E802A66D8b64761A06E5df22", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "opstack" }, "degenchain": { "aggregationHook": "0xDC995884ec53b6Bc809ed614f5E92084600002ed", @@ -704,7 +709,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 10, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 666666666, "deployer": { @@ -721,7 +726,7 @@ "from": 23783929 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xcCdAcCCc5defCb56852e2945eaF0Ba2b3751B1aF", + "interchainSecurityModule": "0x168E9C1481F50E66Cc5F5E24b04eBf7071629c4E", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "degenchain", @@ -758,7 +763,7 @@ "eclipsemainnet": { "blockExplorers": [ { - "apiUrl": "https://mainnetbeta-rpc.eclipse.xyz", + "apiUrl": "https://explorer.eclipse.xyz/api", "family": "other", "name": "Eclipse Explorer", "url": "https://explorer.eclipse.xyz/" @@ -779,7 +784,8 @@ "gasCurrencyCoinGeckoId": "ethereum", "index": { "from": 1, - "mode": "sequence" + "mode": "sequence", + "chunk": 100 }, "interchainGasPaymaster": "ABb3i11z7wKoGCfeRQNQbVYWjAm7jG7HzZnDLV4RKRbK", "mailbox": "EitxJuv2iBjsg2d7jVy2LDC1e2zBrx4GB5Y9h2Ko3A9Y", @@ -796,7 +802,9 @@ "http": "https://mainnetbeta-rpc.eclipse.xyz" } ], - "validatorAnnounce": "Hqnn593pqDZWLy6bKZ4NbY767wFhUNBShDrLktuQa3Q2" + "validatorAnnounce": "Hqnn593pqDZWLy6bKZ4NbY767wFhUNBShDrLktuQa3Q2", + "interchainSecurityModule": "BgG35GxoaMgmiam3EJzcwivwQ2DTYGPTLfUCg7bhiH6V", + "technicalStack": "other" }, "endurance": { "aggregationHook": "0x62c39B0500760c46Ae9Ae312A30f63445dc24C3a", @@ -811,7 +819,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 12, - "reorgPeriod": 14 + "reorgPeriod": 15 }, "chainId": 648, "deployer": { @@ -831,7 +839,7 @@ "interchainAccountIsm": "0xCeafc098e5c3c7768b9229Be2FEC275862A81Abd", "interchainAccountRouter": "0xed9a722c543883FB7e07E78F3879762DE09eA7D5", "interchainGasPaymaster": "0xB30EAB08aa87138D57168D0e236850A530f49921", - "interchainSecurityModule": "0x0A5bB58D8C66Dee715C22E6Dc2FFAABBc2E2473d", + "interchainSecurityModule": "0x094120BaC576aD7D88ec6893C9B220a0e64923E9", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xC831271c1fB212012811a91Dd43e5926C1020563", "name": "endurance", @@ -860,7 +868,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x3c7653dD0Ec21A833f99293CDC17495CE249532c", "staticMerkleRootWeightedMultisigIsmFactory": "0xc441521bA37EaCd9af4f319CcdA27E9D48f74281", - "staticMessageIdWeightedMultisigIsmFactory": "0x730f8a4128Fa8c53C777B62Baa1abeF94cAd34a9" + "staticMessageIdWeightedMultisigIsmFactory": "0x730f8a4128Fa8c53C777B62Baa1abeF94cAd34a9", + "technicalStack": "other" }, "ethereum": { "aggregationHook": "0xb87AC8EA4533AE017604E44470F7c1E550AC6F10", @@ -879,9 +888,9 @@ } ], "blocks": { - "confirmations": 3, + "confirmations": 2, "estimateBlockTime": 13, - "reorgPeriod": 14 + "reorgPeriod": 15 }, "chainId": 1, "deployer": { @@ -901,7 +910,7 @@ "interchainAccountIsm": "0x292C614ED53DaaDBf971521bc2C652d1ca51cB47", "interchainAccountRouter": "0x5E532F7B610618eE73C2B462978e94CB1F7995Ce", "interchainGasPaymaster": "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611", - "interchainSecurityModule": "0x33F9760969408cc5138Bc6683F42514a74114a53", + "interchainSecurityModule": "0x23d160e4474Ce011829c71Bf1bCaA40F0b5612D5", "mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239", "merkleTreeHook": "0x48e6c30B97748d1e2e03bf3e9FbE3890ca5f8CCA", "name": "ethereum", @@ -932,13 +941,10 @@ "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "maxFeePerGas": 150000000000, - "maxPriorityFeePerGas": 5000000000 - }, "validatorAnnounce": "0xCe74905e51497b4adD3639366708b821dcBcff96", "staticMerkleRootWeightedMultisigIsmFactory": "0xA2502bF73e5313c1bf48E47C887cdcbf2640FA41", - "staticMessageIdWeightedMultisigIsmFactory": "0x4272124Fba59CbA076D85375895f94B6a3485c3E" + "staticMessageIdWeightedMultisigIsmFactory": "0x4272124Fba59CbA076D85375895f94B6a3485c3E", + "technicalStack": "other" }, "fraxtal": { "aggregationHook": "0xD7ff06cDd83642D648baF0d36f77e79349120dA4", @@ -953,7 +959,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 252, "deployer": { @@ -973,7 +979,7 @@ "interchainAccountIsm": "0x7C012DCA02C42cfA3Fd7Da3B0ED7234B52AE68eF", "interchainAccountRouter": "0xbed53B5C5BCE9433f25A2A702e6df13E22d84Ae9", "interchainGasPaymaster": "0x2Fca7f6eC3d4A0408900f2BB30004d4616eE985E", - "interchainSecurityModule": "0xDEFa968CE11b3F707C785cEF07e2cFb79b6B0D14", + "interchainSecurityModule": "0x8B497dff421844Bb0882E0C495d0851D4461675C", "mailbox": "0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3", "merkleTreeHook": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "name": "fraxtal", @@ -1005,7 +1011,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x1956848601549de5aa0c887892061fA5aB4f6fC4", "staticMerkleRootWeightedMultisigIsmFactory": "0x7947b7Fe737B4bd1D3387153f32148974066E591", - "staticMessageIdWeightedMultisigIsmFactory": "0x1A41a365A693b6A7aED1a46316097d290f569F22" + "staticMessageIdWeightedMultisigIsmFactory": "0x1A41a365A693b6A7aED1a46316097d290f569F22", + "technicalStack": "opstack" }, "fusemainnet": { "aggregationHook": "0xF4135554ED2c60dB9c1166933797164C43ABb6E2", @@ -1020,7 +1027,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 1 + "reorgPeriod": 19 }, "chainId": 122, "deployer": { @@ -1040,7 +1047,7 @@ "interchainAccountIsm": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD", "interchainAccountRouter": "0x2351FBe24C1212F253b7a300ff0cBCFd97952a19", "interchainGasPaymaster": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", - "interchainSecurityModule": "0x94f9670390f63CFA0Bf49944ccD28322922b4B14", + "interchainSecurityModule": "0x69b33D67B9C51D45E23d22E727FF186DD6298ECA", "mailbox": "0x3071D4DA6020C956Fe15Bfd0a9Ca8D4574f16696", "merkleTreeHook": "0xfBc08389224d23b79cb21cDc16c5d42F0ad0F57f", "name": "fusemainnet", @@ -1078,7 +1085,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x60bB6D060393D3C206719A7bD61844cC82891cfB", "staticMerkleRootWeightedMultisigIsmFactory": "0xe522A5DcA58e3ab7fEd2bf25DA3E8d90c14083a8", - "staticMessageIdWeightedMultisigIsmFactory": "0x53642476e24E28c3218E8Da44eDEBB4adB9DE13e" + "staticMessageIdWeightedMultisigIsmFactory": "0x53642476e24E28c3218E8Da44eDEBB4adB9DE13e", + "technicalStack": "other" }, "gnosis": { "aggregationHook": "0xdD1FA1C12496474c1dDC67a658Ba81437F818861", @@ -1093,7 +1101,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 14 + "reorgPeriod": 5 }, "chainId": 100, "deployer": { @@ -1113,7 +1121,7 @@ "interchainAccountIsm": "0x07E2062A1bC66a2C1d05cb5C3870a4AF86e0056E", "interchainAccountRouter": "0xBE70Ab882D1F7E37e04a70CDd9Ec23b37a234064", "interchainGasPaymaster": "0xDd260B99d302f0A3fF885728c086f729c06f227f", - "interchainSecurityModule": "0x46902BD2e22A6672eD2E8D04771cC8CB5eD06481", + "interchainSecurityModule": "0x00533a5F14B3a0632C86f99E4e20a10b73C4AE0D", "mailbox": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "name": "gnosis", @@ -1147,7 +1155,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x87ED6926abc9E38b9C7C19f835B41943b622663c", "staticMerkleRootWeightedMultisigIsmFactory": "0xA37ce588515668632D9025272859D2E5bD3210BB", - "staticMessageIdWeightedMultisigIsmFactory": "0x5B7365640c82F402C43A3961F3fD34Ae31f52931" + "staticMessageIdWeightedMultisigIsmFactory": "0x5B7365640c82F402C43A3961F3fD34Ae31f52931", + "technicalStack": "other" }, "inevm": { "aggregationHook": "0xe0dDb5dE7D52918237cC1Ae131F29dcAbcb0F62B", @@ -1162,7 +1171,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 3 }, "chainId": 2525, "customHook": "0xA376b27212D608324808923Add679A2c9FAFe9Da", @@ -1175,7 +1184,7 @@ "domainId": 2525, "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", - "fallbackRoutingHook": "0xA376b27212D608324808923Add679A2c9FAFe9Da", + "fallbackRoutingHook": "0xf8dba46ff9d8ef650052c89ca2df793fabc375f9", "gasCurrencyCoinGeckoId": "injective-protocol", "index": { "from": 37 @@ -1183,7 +1192,7 @@ "interchainAccountIsm": "0x708E002637792FDC031E6B62f23DD60014AC976a", "interchainAccountRouter": "0xfB8cea1c7F45608Da30655b50bbF355D123A4358", "interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117", - "interchainSecurityModule": "0x2dcFd91eCd43adfd5644De57387575F97539C18a", + "interchainSecurityModule": "0xB8F85B879775adF156Dd4AFa43e97DeB880d99D4", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", "name": "inevm", @@ -1220,7 +1229,14 @@ }, "injective": { "bech32Prefix": "inj", - "blockExplorers": [], + "blockExplorers": [ + { + "apiUrl": "https://www.mintscan.io/injective", + "family": "other", + "name": "Mintscan", + "url": "https://www.mintscan.io/injective" + } + ], "blocks": { "confirmations": 1, "estimateBlockTime": 1, @@ -1246,7 +1262,7 @@ } ], "index": { - "chunk": 5, + "chunk": 25, "from": 58419500 }, "interchainGasPaymaster": "0x27ae52298e5b53b34b7ae0ca63e05845c31e1f59", @@ -1271,7 +1287,8 @@ } ], "slip44": 118, - "validatorAnnounce": "0x1fb225b2fcfbe75e614a1d627de97ff372242eed" + "validatorAnnounce": "0x1fb225b2fcfbe75e614a1d627de97ff372242eed", + "technicalStack": "other" }, "kroma": { "aggregationHook": "0xF6C1769d5390Be0f77080eF7791fBbA7eF4D5659", @@ -1286,7 +1303,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 255, "deployer": { @@ -1303,7 +1320,7 @@ "from": 14616307 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x58D6fb4aADd3ae83ec529d3d0f42Ae904207a336", + "interchainSecurityModule": "0xE2968dAb74541184Ad95651b1e5Cf34Ab1bBEc97", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "kroma", @@ -1340,7 +1357,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0x749848D7b783A328638C3ea74AcFcfb73c977CbE", "interchainAccountIsm": "0xd64d126941EaC2Cf53e0E4E8146cC70449b60D73", "interchainAccountRouter": "0x1A4F09A615aA4a35E5a146DC2fa19975bebF21A5", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "opstack" }, "linea": { "aggregationHook": "0x43fF73dF1E170D076D9Ed30d4C6922A9D34322dE", @@ -1355,7 +1373,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 59144, "deployer": { @@ -1375,7 +1393,7 @@ "interchainAccountIsm": "0xdcA646C56E7768DD11654956adE24bfFf9Ba4893", "interchainAccountRouter": "0xD59dA396F162Ed93a41252Cebb8d5DD4F093238C", "interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28", - "interchainSecurityModule": "0x72CC2baC9e33Eb96395FE8caC142D368fbF8Dc50", + "interchainSecurityModule": "0x0EEF1e64646EE01DeED4850074Cd4B97C0A630a9", "mailbox": "0x02d16BC51af6BfD153d67CA61754cF912E82C4d9", "merkleTreeHook": "0xC077A0Cc408173349b1c9870C667B40FE3C01dd7", "name": "linea", @@ -1413,7 +1431,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05", "staticMerkleRootWeightedMultisigIsmFactory": "0x11df2f96bC60DfE5A2ab193AD0FCC0a0336F22d0", - "staticMessageIdWeightedMultisigIsmFactory": "0xb0772086edF20278501bb2aF8D8efDe4B71C73Ce" + "staticMessageIdWeightedMultisigIsmFactory": "0xb0772086edF20278501bb2aF8D8efDe4B71C73Ce", + "technicalStack": "other" }, "lisk": { "aggregationHook": "0xDC995884ec53b6Bc809ed614f5E92084600002ed", @@ -1428,7 +1447,7 @@ "blocks": { "confirmations": 3, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 1135, "deployer": { @@ -1445,7 +1464,7 @@ "from": 4195553 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x1b9cDC236760f40064863DA3d67f5553208B65DE", + "interchainSecurityModule": "0x63Bb509b9CA644609B15Ea55E56f0Acbbb9dB02E", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "lisk", @@ -1476,7 +1495,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", "interchainAccountIsm": "0xD8aF449f8fEFbA2064863DCE5aC248F8B232635F", "interchainAccountRouter": "0x3881c3e945CBB89ae67c43E82f570baDF1c6EA94", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "opstack" }, "lukso": { "aggregationHook": "0xeCBe91B90ab862aa26E5a241D13d1746D24C74A1", @@ -1491,7 +1511,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 12, - "reorgPeriod": 14 + "reorgPeriod": 15 }, "chainId": 42, "deployer": { @@ -1508,7 +1528,7 @@ "from": 3088760 }, "interchainGasPaymaster": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", - "interchainSecurityModule": "0xc6ec1364d1ce3E963Fa65A0bDF57eC722478e1FB", + "interchainSecurityModule": "0x609ad94304896607A6D81DB00d882245045B79da", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x062200d92dF6bB7bA89Ce4D6800110450f94784e", "name": "lukso", @@ -1545,7 +1565,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", "interchainAccountIsm": "0xd64d126941EaC2Cf53e0E4E8146cC70449b60D73", "interchainAccountRouter": "0x1A4F09A615aA4a35E5a146DC2fa19975bebF21A5", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "other" }, "mantapacific": { "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", @@ -1560,7 +1581,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 169, "deployer": { @@ -1581,7 +1602,7 @@ "interchainAccountIsm": "0x8Ea50255C282F89d1A14ad3F159437EE5EF0507f", "interchainAccountRouter": "0x693A4cE39d99e46B04cb562329e3F0141cA17331", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x855a679627f8A283861d363E8Daaf2e6fac74230", + "interchainSecurityModule": "0xC012e8E3cBeB6295E1E4837FBA5DB8E077EBc549", "isTestnet": false, "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", @@ -1615,7 +1636,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", "staticMerkleRootWeightedMultisigIsmFactory": "0x0A5d831c09204888B8791BF4E9c49445aD54f2C5", - "staticMessageIdWeightedMultisigIsmFactory": "0xc11f8Cf2343d3788405582F65B8af6A4F7a6FfC8" + "staticMessageIdWeightedMultisigIsmFactory": "0xc11f8Cf2343d3788405582F65B8af6A4F7a6FfC8", + "technicalStack": "opstack" }, "mantle": { "aggregationHook": "0x76b396EaCBF3580B80Ee34C94e780a1Dee76EC72", @@ -1630,7 +1652,7 @@ "blocks": { "confirmations": 3, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 2 }, "chainId": 5000, "deployer": { @@ -1650,7 +1672,7 @@ "interchainAccountIsm": "0xe039DA3A0071BEd087A12660D7b03cf669c7776E", "interchainAccountRouter": "0x45285463352c53a481e882cD5E2AF2E25BBdAd0D", "interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28", - "interchainSecurityModule": "0xDA06C0c607CdbcED8B01d7b7caafeeDA550b4120", + "interchainSecurityModule": "0x8722328A5Ed815965F9B5eBAA21d04f0F9BFDd35", "mailbox": "0x398633D19f4371e1DB5a8EFE90468eB70B1176AA", "merkleTreeHook": "0x5332D1AC0A626D265298c14ff681c0A8D28dB86d", "name": "mantle", @@ -1675,7 +1697,7 @@ "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "storageGasOracle": "0xf9DbC8776Bc2812c4DBEc45383A1783Ac758Fb55", - "technicalStack": "other", + "technicalStack": "opstack", "testRecipient": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x1956848601549de5aa0c887892061fA5aB4f6fC4", @@ -1695,7 +1717,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 4200, "deployer": { @@ -1712,7 +1734,7 @@ "from": 13523607 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xD89521A871B43f1116083d79A937445D2675Be37", + "interchainSecurityModule": "0xE8176Fc70f129255aA83d3db242C2246Ad77Af7D", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "merlin", @@ -1746,7 +1768,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xD7EcB0396406682a27E87F7946c25Ac531140959", "interchainAccountIsm": "0xA8A311B69f688c1D9928259D872C31ca0d473642", "interchainAccountRouter": "0x9e8b689e83d929cb8c2d9166E55319a4e6aA83B7", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "polygoncdk" }, "metis": { "aggregationHook": "0xDC995884ec53b6Bc809ed614f5E92084600002ed", @@ -1761,7 +1784,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 1088, "deployer": { @@ -1778,7 +1801,7 @@ "from": 17966274 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x1ffca4c17aEe10a8fCAd338750191ce1C2188b6f", + "interchainSecurityModule": "0xA9309228762699D5c81A4b0BAfd06Da21589746b", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "metis", @@ -1803,7 +1826,7 @@ "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "storageGasOracle": "0x7b2e996742fA42d223652A344252B725D1bC428C", - "technicalStack": "arbitrumnitro", + "technicalStack": "opstack", "testRecipient": "0x2c61Cda929e4e2174cb10cd8e2724A9ceaD62E67", "validatorAnnounce": "0x062200d92dF6bB7bA89Ce4D6800110450f94784e", "staticMerkleRootWeightedMultisigIsmFactory": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", @@ -1825,7 +1848,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 185, "deployer": { @@ -1843,7 +1866,7 @@ "from": 3752032 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x1ffca4c17aEe10a8fCAd338750191ce1C2188b6f", + "interchainSecurityModule": "0xA9309228762699D5c81A4b0BAfd06Da21589746b", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "mint", @@ -1874,7 +1897,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", "interchainAccountIsm": "0x1A4F09A615aA4a35E5a146DC2fa19975bebF21A5", "interchainAccountRouter": "0xb2674E213019972f937CCFc5e23BF963D915809e", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "opstack" }, "mode": { "aggregationHook": "0x80D80cfBa98dD2d456ECd43Dcc1f852D5C4EeD7a", @@ -1889,7 +1913,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 34443, "deployer": { @@ -1909,7 +1933,7 @@ "interchainAccountIsm": "0xa377b8269e0A47cdd2fD5AAeAe860b45623c6d82", "interchainAccountRouter": "0x6e1B9f776bd415d7cC3C7458A5f0d801016918f8", "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "interchainSecurityModule": "0x691ca7575ff8D2415158b95f4F4D9307aD9115d5", + "interchainSecurityModule": "0x0777dFffcEd18EE416e35401E0e5e0413b7D43be", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "name": "mode", @@ -1937,7 +1961,7 @@ "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "storageGasOracle": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "technicalStack": "other", + "technicalStack": "opstack", "testRecipient": "0x12582c7B0f43c6A667CBaA7fA8b112F7fb1E69F0", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x48083C69f5a42c6B69ABbAd48AE195BD36770ee2", @@ -1957,7 +1981,7 @@ "blocks": { "confirmations": 2, "estimateBlockTime": 12, - "reorgPeriod": 2 + "reorgPeriod": "finalized" }, "chainId": 1284, "deployer": { @@ -1977,7 +2001,7 @@ "interchainAccountIsm": "0x79b3730CE3685f65802aF1771319992bA960EB9D", "interchainAccountRouter": "0xc4482f66191754a8629D35289043C4EB0285F10E", "interchainGasPaymaster": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "interchainSecurityModule": "0x3E4c6E0f336657467175cFeC13fdecC78BaE253d", + "interchainSecurityModule": "0xad1Ad827035eDe500aFd0ff122c53f6eA607Eb5C", "mailbox": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", "merkleTreeHook": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", "name": "moonbeam", @@ -2005,13 +2029,14 @@ "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", + "staticMerkleRootWeightedMultisigIsmFactory": "0x13B09a1d80e93E03221e1F393B588C28a5dE9B69", + "staticMessageIdWeightedMultisigIsmFactory": "0x1E14479A04786900F32c916c11eE1EEf81B5a6bA", + "technicalStack": "polkadotsubstrate", "transactionOverrides": { "maxFeePerGas": 350000000000, "maxPriorityFeePerGas": 50000000000 - }, - "validatorAnnounce": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", - "staticMerkleRootWeightedMultisigIsmFactory": "0x13B09a1d80e93E03221e1F393B588C28a5dE9B69", - "staticMessageIdWeightedMultisigIsmFactory": "0x1E14479A04786900F32c916c11eE1EEf81B5a6bA" + } }, "neutron": { "bech32Prefix": "neutron", @@ -2082,7 +2107,8 @@ "type": "cosmosKey" }, "slip44": 118, - "validatorAnnounce": "0xf3aa0d652226e21ae35cd9035c492ae41725edc9036edf0d6a48701b153b90a0" + "validatorAnnounce": "0xf3aa0d652226e21ae35cd9035c492ae41725edc9036edf0d6a48701b153b90a0", + "technicalStack": "other" }, "optimism": { "aggregationHook": "0x4ccC6d8eB79f2a1EC9bcb0f211fef7907631F91f", @@ -2097,7 +2123,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 10 }, "chainId": 10, "deployer": { @@ -2117,7 +2143,7 @@ "interchainAccountIsm": "0x2c46BF14641d00549ECa4779BF5CBf91602C1DEd", "interchainAccountRouter": "0x03D6cC17d45E9EA27ED757A8214d1F07F7D901aD", "interchainGasPaymaster": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "interchainSecurityModule": "0x1bc5500Cc5d338e2daDBa6db936b4046030aF3A3", + "interchainSecurityModule": "0x3878aB31B2426A92E8a1E0AE758d848879F7F5E8", "mailbox": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", "merkleTreeHook": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", "name": "optimism", @@ -2147,7 +2173,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", "staticMerkleRootWeightedMultisigIsmFactory": "0x313b18228236bf89fc67cca152c62f1896eEa362", - "staticMessageIdWeightedMultisigIsmFactory": "0x3A2e96403d076e9f953166A9E4c61bcD9D164CFe" + "staticMessageIdWeightedMultisigIsmFactory": "0x3A2e96403d076e9f953166A9E4c61bcD9D164CFe", + "technicalStack": "opstack" }, "osmosis": { "bech32Prefix": "osmo", @@ -2184,7 +2211,7 @@ } ], "index": { - "chunk": 5, + "chunk": 10, "from": 14389169 }, "interchainGasPaymaster": "0xd20a9dcf61939fc2fe6ad501b9457b1029b3cc7ab12ed72675ea2e10d831ee5d", @@ -2215,7 +2242,8 @@ "type": "cosmosKey" }, "slip44": 118, - "validatorAnnounce": "0xaf867da5b09a20ee49161d57f99477c0c42d100f34eb53da0d2eb7fc6c257235" + "validatorAnnounce": "0xaf867da5b09a20ee49161d57f99477c0c42d100f34eb53da0d2eb7fc6c257235", + "technicalStack": "other" }, "polygon": { "aggregationHook": "0x34dAb05650Cf590088bA18aF9d597f3e081bCc47", @@ -2230,7 +2258,7 @@ "blocks": { "confirmations": 3, "estimateBlockTime": 2, - "reorgPeriod": 256 + "reorgPeriod": "finalized" }, "chainId": 137, "deployer": { @@ -2250,7 +2278,7 @@ "interchainAccountIsm": "0xBAC4529cdfE7CCe9E858BF706e41F8Ed096C1BAd", "interchainAccountRouter": "0xF163949AD9F88977ebF649D0461398Ca752E64B9", "interchainGasPaymaster": "0x0071740Bf129b05C4684abfbBeD248D80971cce2", - "interchainSecurityModule": "0x017B4ee44A205B3c9576512F83c583F59da3f1f4", + "interchainSecurityModule": "0x9fFC02BfB5C7260C985b005C0cF40d7EC601aac2", "mailbox": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", "merkleTreeHook": "0x73FbD25c3e817DC4B4Cd9d00eff6D83dcde2DfF6", "name": "polygon", @@ -2284,13 +2312,14 @@ "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "maxFeePerGas": 550000000000, - "maxPriorityFeePerGas": 50000000000 - }, "validatorAnnounce": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", "staticMerkleRootWeightedMultisigIsmFactory": "0x07CE1B0cFfa436AE2fb7Fbd7318648774FdA53f9", - "staticMessageIdWeightedMultisigIsmFactory": "0x9e22945bE593946618383B108CC5bce09eBA4C26" + "staticMessageIdWeightedMultisigIsmFactory": "0x9e22945bE593946618383B108CC5bce09eBA4C26", + "technicalStack": "other", + "transactionOverrides": { + "maxFeePerGas": 800000000000, + "maxPriorityFeePerGas": 50000000000 + } }, "polygonzkevm": { "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", @@ -2305,7 +2334,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 10, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 1101, "deployer": { @@ -2326,7 +2355,7 @@ "interchainAccountIsm": "0xc1198e241DAe48BF5AEDE5DCE49Fe4A6064cF7a7", "interchainAccountRouter": "0x20a0A32a110362920597F72974E1E0d7e25cA20a", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x6d1c7Cba7fa5cA1895EBC2045A93D53719C47Fed", + "interchainSecurityModule": "0xc6c475184F197FA65f233dFc22FA6bD4cE48B4fE", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "polygonzkevm", @@ -2356,12 +2385,13 @@ "storageGasOracle": "0x19dc38aeae620380430C200a6E990D5Af5480117", "testRecipient": "0xD127D4549cb4A5B2781303a4fE99a10EAd13263A", "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": 1000000000 - }, "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", "staticMerkleRootWeightedMultisigIsmFactory": "0xc24f3ba8619Fe9db9b95fB616D6945779669e591", - "staticMessageIdWeightedMultisigIsmFactory": "0x882Ad0CcB25CDf928e2a9C899F23eC033C4113f7" + "staticMessageIdWeightedMultisigIsmFactory": "0x882Ad0CcB25CDf928e2a9C899F23eC033C4113f7", + "technicalStack": "polygoncdk", + "transactionOverrides": { + "gasPrice": 1000000000 + } }, "proofofplay": { "aggregationHook": "0xF6C1769d5390Be0f77080eF7791fBbA7eF4D5659", @@ -2376,7 +2406,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 1, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 70700, "deployer": { @@ -2393,7 +2423,7 @@ "from": 32018468 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xA00CCe6085E4fae65EfD61cEfd080cb99B6d750f", + "interchainSecurityModule": "0x70e8beCE806914959c1B5D8F75d2217058D31437", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "proofofplay", @@ -2439,7 +2469,7 @@ ], "blocks": { "confirmations": 1, - "estimateBlockTime": 4, + "estimateBlockTime": 30, "reorgPeriod": 0 }, "chainId": 111188, @@ -2457,7 +2487,7 @@ "from": 363159 }, "interchainGasPaymaster": "0x3071D4DA6020C956Fe15Bfd0a9Ca8D4574f16696", - "interchainSecurityModule": "0x20c612974cBAE138e76b991C1FBA829b7E52C070", + "interchainSecurityModule": "0x43346a54445BBdf8241062904E8A13AA62842a02", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x55E4F0bc6b7Bb493D50839A8592e7ad8d5e93cf7", "name": "real", @@ -2504,7 +2534,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 690, "deployer": { @@ -2524,7 +2554,7 @@ "interchainAccountIsm": "0x5DA60220C5dDe35b7aE91c042ff5979047FA0785", "interchainAccountRouter": "0x7a4d31a686A36285d68e14EDD53631417eB19603", "interchainGasPaymaster": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", - "interchainSecurityModule": "0xbB8D11681396d410fa00B92Bc92Fff1e61a31C3F", + "interchainSecurityModule": "0xd8b6B632526834D8192860e6B6CE47165Fd02a42", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", "name": "redstone", @@ -2553,7 +2583,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x12582c7B0f43c6A667CBaA7fA8b112F7fb1E69F0", "staticMerkleRootWeightedMultisigIsmFactory": "0x794Fe7970EE45945b0ad2667f99A5bBc9ddfB5d7", - "staticMessageIdWeightedMultisigIsmFactory": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0" + "staticMessageIdWeightedMultisigIsmFactory": "0x7B8AA8f23Ab6B0757eC6FC71894211376D9335b0", + "technicalStack": "opstack" }, "sanko": { "aggregationHook": "0xF6C1769d5390Be0f77080eF7791fBbA7eF4D5659", @@ -2567,8 +2598,8 @@ ], "blocks": { "confirmations": 1, - "estimateBlockTime": 10, - "reorgPeriod": 0 + "estimateBlockTime": 15, + "reorgPeriod": 1 }, "chainId": 1996, "deployer": { @@ -2585,7 +2616,7 @@ "from": 937117 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x597DB3036b5E7bE9b2216F599e1Ba92734c4cd6f", + "interchainSecurityModule": "0x6A2748201F66647ad6D164CB3340A893881A4bb2", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "sanko", @@ -2632,7 +2663,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 30 + "reorgPeriod": 17 }, "chainId": 534352, "deployer": { @@ -2653,7 +2684,7 @@ "interchainAccountIsm": "0x32af5Df81fEd5E26119F6640FBB13f3d63a94CDe", "interchainAccountRouter": "0x0B48a744698ba8dFa514742dFEB6728f52fD66f7", "interchainGasPaymaster": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "interchainSecurityModule": "0xBf15bABBB938F0F734F77Eda8b583429838490E7", + "interchainSecurityModule": "0xAd1a987BfE0D6fbD92089628daC7C7e4bA9a6AAF", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", "name": "scroll", @@ -2680,12 +2711,13 @@ "storageGasOracle": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", "testRecipient": "0x674f4698d063cE4C0d604c88dD7D542De72f327f", "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": 2000000000 - }, "validatorAnnounce": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "staticMerkleRootWeightedMultisigIsmFactory": "0xcb0D04010584AA5244b5826c990eeA4c16BeAC8C", - "staticMessageIdWeightedMultisigIsmFactory": "0x609707355a53d2aAb6366f48E2b607C599D26B29" + "staticMessageIdWeightedMultisigIsmFactory": "0x609707355a53d2aAb6366f48E2b607C599D26B29", + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 200000000 + } }, "sei": { "aggregationHook": "0x40514BD46C57455933Be8BAedE96C4F0Ba3507D6", @@ -2720,7 +2752,7 @@ "interchainAccountIsm": "0xf35dc7B9eE4Ebf0cd3546Bd6EE3b403dE2b9F5D6", "interchainAccountRouter": "0xBcaedE97a98573A88242B3b0CB0A255F3f90d4d5", "interchainGasPaymaster": "0xFC62DeF1f08793aBf0E67f69257c6be258194F72", - "interchainSecurityModule": "0xf0FcAb13ACec74402fFC5006bBEb81E3C14f071f", + "interchainSecurityModule": "0x494028EA206642e4c60Ec3d12e96B4549E5e1800", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xca1b69fA4c4a7c7fD839bC50867c589592bcfe49", "name": "sei", @@ -2747,20 +2779,21 @@ "storageGasOracle": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4", "testRecipient": "0xdB670e1a1e312BF17425b08cE55Bdf2cD8F8eD54", "timelockController": "0x0000000000000000000000000000000000000000", - "transactionOverrides": { - "gasPrice": 101000000000 - }, "validatorAnnounce": "0x5332D1AC0A626D265298c14ff681c0A8D28dB86d", "staticMerkleRootWeightedMultisigIsmFactory": "0xDf347f7602fFF536337c0B90cEC19CD6998427C4", - "staticMessageIdWeightedMultisigIsmFactory": "0x816CF11aDFF6De498823F739eAfe350E82ee845D" + "staticMessageIdWeightedMultisigIsmFactory": "0x816CF11aDFF6De498823F739eAfe350E82ee845D", + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 101000000000 + } }, "solanamainnet": { "blockExplorers": [ { - "apiUrl": "https://explorer.solana.com?cluster=mainnet-beta", + "apiUrl": "https://solscan.io", "family": "other", "name": "Solana Explorer", - "url": "https://explorer.solana.com?cluster=mainnet-beta" + "url": "https://solscan.io" } ], "blocks": { @@ -2779,7 +2812,8 @@ "gasCurrencyCoinGeckoId": "solana", "index": { "from": 1, - "mode": "sequence" + "mode": "sequence", + "chunk": 100 }, "interchainGasPaymaster": "JAvHW21tYXE9dtdG83DReqU2b4LUexFuCbtJT5tF8X6M", "mailbox": "E588QtVUvresuXq2KoNEwAmoifCzYGpRBdHByN9KQMbi", @@ -2796,7 +2830,9 @@ "http": "https://api.mainnet-beta.solana.com" } ], - "validatorAnnounce": "pRgs5vN4Pj7WvFbxf6QDHizo2njq2uksqEUbaSghVA8" + "validatorAnnounce": "pRgs5vN4Pj7WvFbxf6QDHizo2njq2uksqEUbaSghVA8", + "interchainSecurityModule": "372D5YP7jMYUgYBXTVJ7BZtzKv1mq1J6wvjSFLNTRreC", + "technicalStack": "other" }, "taiko": { "aggregationHook": "0x1175A31f66C5e3d0ce0ca3B7F80Abe72c6FcE272", @@ -2811,7 +2847,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 12, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 167000, "deployer": { @@ -2831,7 +2867,7 @@ "interchainAccountIsm": "0xAE557e108b3336130370aC74836f1356B4b30Cf2", "interchainAccountRouter": "0x1F8CF09F060A2AE962c0Bb1F92e209a1E7b0E10B", "interchainGasPaymaster": "0x273Bc6b01D9E88c064b6E5e409BdF998246AEF42", - "interchainSecurityModule": "0xbCF47BFb1f863e349b588d6b0A817161541a82A6", + "interchainSecurityModule": "0xC93F2796A17Ee4580c039aeB7b0c923b10ce79C2", "mailbox": "0x28EFBCadA00A7ed6772b3666F3898d276e88CAe3", "merkleTreeHook": "0x6A55822cf11f9fcBc4c75BC2638AfE8Eb942cAdd", "name": "taiko", @@ -2876,7 +2912,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 6, - "reorgPeriod": 0 + "reorgPeriod": "finalized" }, "chainId": 5845, "deployer": { @@ -2893,7 +2929,7 @@ "from": 1678063 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0xA00CCe6085E4fae65EfD61cEfd080cb99B6d750f", + "interchainSecurityModule": "0x70e8beCE806914959c1B5D8F75d2217058D31437", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", @@ -2925,7 +2961,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", "interchainAccountIsm": "0x45285463352c53a481e882cD5E2AF2E25BBdAd0D", "interchainAccountRouter": "0x67F36550b73B731e5b2FC44E4F8f250d89c87bD6", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "polkadotsubstrate" }, "viction": { "aggregationHook": "0x5c7890FAf9c99dC55926F00d624D7Bc6D7ac6834", @@ -2940,7 +2977,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 3 }, "chainId": 88, "deployer": { @@ -2960,7 +2997,7 @@ "interchainAccountIsm": "0x551BbEc45FD665a8C95ca8731CbC32b7653Bc59B", "interchainAccountRouter": "0xc11f8Cf2343d3788405582F65B8af6A4F7a6FfC8", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0x37E2EBF4DD9F6636f28Ac2a20284d2d47f93Cc3A", + "interchainSecurityModule": "0x3465AccC39AE5e6C344184013a57cDCe546834d6", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "viction", @@ -2993,7 +3030,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", "staticMerkleRootWeightedMultisigIsmFactory": "0x766fc1d3F6CFAE5A06Fe8D6b65a3012401Bd36Ba", - "staticMessageIdWeightedMultisigIsmFactory": "0x9f4012ba9368FBb95F56c2Fc2D956df803D8779e" + "staticMessageIdWeightedMultisigIsmFactory": "0x9f4012ba9368FBb95F56c2Fc2D956df803D8779e", + "technicalStack": "other" }, "worldchain": { "aggregationHook": "0x8007d1e60991fB9BE1be26f70A7cE284fdE7da97", @@ -3008,7 +3046,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 480, "deployer": { @@ -3027,7 +3065,7 @@ "interchainAccountIsm": "0xCB9f90EE5d83Ea52ABd922BD70898f0155D54798", "interchainAccountRouter": "0x473884010F0C1742DA8Ad01E7E295624B931076b", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0xbf8c84422e09BB9866F9E416d6f447A33e261D05", + "interchainSecurityModule": "0x05f6BAa16F1aCf7b19c4A09E019D856c10ab8355", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "worldchain", @@ -3043,7 +3081,7 @@ "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "rpcUrls": [ { - "http": "https://raas-backend.alchemy.com/rpc/worldchain-mainnet/rollup" + "http": "https://worldchain-mainnet.g.alchemy.com/public" } ], "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", @@ -3056,7 +3094,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x047ba6c9949baB22d13C347B40819b7A20C4C53a", "staticMerkleRootWeightedMultisigIsmFactory": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", - "staticMessageIdWeightedMultisigIsmFactory": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E" + "staticMessageIdWeightedMultisigIsmFactory": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "technicalStack": "opstack" }, "xai": { "aggregationHook": "0xF6C1769d5390Be0f77080eF7791fBbA7eF4D5659", @@ -3071,7 +3110,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 1, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 660279, "deployer": { @@ -3088,7 +3127,7 @@ "from": 24395308 }, "interchainGasPaymaster": "0x9844aFFaBE17c37F791ff99ABa58B0FbB75e22AF", - "interchainSecurityModule": "0x18E1F88A882cE015b135Acdf356dd4F5773763B9", + "interchainSecurityModule": "0x4886ed96bcdba2ad85Bf518C3171C39e256ac840", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "name": "xai", @@ -3135,7 +3174,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 10, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 196, "deployer": { @@ -3156,7 +3195,7 @@ "interchainAccountIsm": "0x29B37088724B745C0ABcE591449Cf042772160C2", "interchainAccountRouter": "0x03cF708E42C89623bd83B281A56935cB562b9258", "interchainGasPaymaster": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", - "interchainSecurityModule": "0x6455CbF559E2227aA23409930D0F860e2f244B70", + "interchainSecurityModule": "0x59B0ec92522F164b72c9BE473382197c564B92dc", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0054D19613f20dD72721A146ED408971a2CCA9BD", "name": "xlayer", @@ -3188,7 +3227,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x047ba6c9949baB22d13C347B40819b7A20C4C53a", "staticMerkleRootWeightedMultisigIsmFactory": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", - "staticMessageIdWeightedMultisigIsmFactory": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2" + "staticMessageIdWeightedMultisigIsmFactory": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2", + "technicalStack": "polygoncdk" }, "zetachain": { "aggregationHook": "0x80D80cfBa98dD2d456ECd43Dcc1f852D5C4EeD7a", @@ -3223,7 +3263,7 @@ "interchainAccountIsm": "0x2b6d3F7d28B5EC8C3C028fBCAdcf774D9709Dd29", "interchainAccountRouter": "0x3AdCBc94ab8C48EC52D06dc65Bb787fD1981E3d5", "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "interchainSecurityModule": "0xCAc4123018406911D43e778410313A594276c96E", + "interchainSecurityModule": "0xd32353Ae5719ac4f8f24AeD81A2A6898d2632D26", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "name": "zetachain", @@ -3255,7 +3295,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x48083C69f5a42c6B69ABbAd48AE195BD36770ee2", "staticMerkleRootWeightedMultisigIsmFactory": "0xF645AeA0b8D26c9DBdfdeF2DEe59F845715BE32F", - "staticMessageIdWeightedMultisigIsmFactory": "0x0ed553e7e5D55535457d1E778Ba96cF839c18442" + "staticMessageIdWeightedMultisigIsmFactory": "0x0ed553e7e5D55535457d1E778Ba96cF839c18442", + "technicalStack": "other" }, "zircuit": { "aggregationHook": "0x198e8c938EC00Da143e772628c7958DD97B7c2A6", @@ -3270,7 +3311,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 48900, "deployer": { @@ -3288,7 +3329,7 @@ "from": 1511458 }, "interchainGasPaymaster": "0x03cF708E42C89623bd83B281A56935cB562b9258", - "interchainSecurityModule": "0xe569C2cC7f2e41ABF1c1CFdE7BBd1694BEF5cc5D", + "interchainSecurityModule": "0xFFec270FE3D0e3B9348B3664BE73A5d4906BA620", "mailbox": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", "merkleTreeHook": "0x4C97D35c668EE5194a13c8DE8Afc18cce40C9F28", "name": "zircuit", @@ -3325,7 +3366,8 @@ "staticMessageIdWeightedMultisigIsmFactory": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", "interchainAccountIsm": "0xd386Bb418B61E296e1689C95AfE94A2E321a6eaD", "interchainAccountRouter": "0x51545389E04c2Ac07d98A40b85d29B480a2AF6ce", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "opstack" }, "zoramainnet": { "aggregationHook": "0x1e7115a7E45804C81C77caFF37f2BA421f32a0b4", @@ -3340,7 +3382,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 7777777, "deployer": { @@ -3360,7 +3402,7 @@ "interchainAccountIsm": "0xb2674E213019972f937CCFc5e23BF963D915809e", "interchainAccountRouter": "0x11b76D93a9D39Eb51F54eBf5566308640cDe882b", "interchainGasPaymaster": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", - "interchainSecurityModule": "0xe2089faE2D98f2CF280E8d218E5a36966ee04d39", + "interchainSecurityModule": "0xED5fD1715A0885a3C7B908BAd5c8C64Ba5166265", "mailbox": "0xF5da68b2577EF5C0A0D98aA2a58483a68C2f232a", "merkleTreeHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", "name": "zoramainnet", @@ -3389,7 +3431,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", "staticMerkleRootWeightedMultisigIsmFactory": "0x33AA12b4e8E79cA551Ca9D1F2eC7d2cE02129dd4", - "staticMessageIdWeightedMultisigIsmFactory": "0xB31553F20D7b06Eb8Eaefe29376146e1d276d091" + "staticMessageIdWeightedMultisigIsmFactory": "0xB31553F20D7b06Eb8Eaefe29376146e1d276d091", + "technicalStack": "opstack" }, "astar": { "blockExplorers": [ @@ -3403,7 +3446,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 13, - "reorgPeriod": 0 + "reorgPeriod": "finalized" }, "chainId": 592, "deployer": { @@ -3430,7 +3473,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0x8Ad4d573D7EafC4Ca58f1dB704B8Db804814D674", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3452,7 +3495,8 @@ }, "interchainAccountIsm": "0xd01A3E167d59FF98c983E83BAa5da0C3e0ADe726", "interchainAccountRouter": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "polkadotsubstrate" }, "astarzkevm": { "blockExplorers": [ @@ -3466,7 +3510,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 3776, "deployer": { @@ -3496,7 +3540,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x71c9bB4297518034f89cDb243fBfd37d549bEbAa", + "interchainSecurityModule": "0xa18979d2e5b8A64f62E0f9e9523d28E934F1104c", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3518,7 +3562,8 @@ }, "interchainAccountIsm": "0xa35cbc2d169284580d82AecED883d0800aa7fbfC", "interchainAccountRouter": "0x7621e04860F0bDe63311db9D5D8b589AD3458A1f", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "polygoncdk" }, "bitlayer": { "blockExplorers": [ @@ -3532,7 +3577,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 20 }, "chainId": 200901, "deployer": { @@ -3565,7 +3610,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0x8Ad4d573D7EafC4Ca58f1dB704B8Db804814D674", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3587,7 +3632,8 @@ }, "interchainAccountIsm": "0xa97ec3E58cBd60199dcFDd6396431BE85c2E363e", "interchainAccountRouter": "0xA0a44cB8Bc0f7EDe788b0Cd29524A5b14fED7b45", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "other" }, "coredao": { "blockExplorers": [ @@ -3601,7 +3647,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 21 }, "chainId": 1116, "deployer": { @@ -3640,7 +3686,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0x8Ad4d573D7EafC4Ca58f1dB704B8Db804814D674", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3662,7 +3708,8 @@ }, "interchainAccountIsm": "0x87bDFaBbCC36D8B1aEdA871Cd54b2e86C7a4d597", "interchainAccountRouter": "0xd01A3E167d59FF98c983E83BAa5da0C3e0ADe726", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "other" }, "dogechain": { "blockExplorers": [ @@ -3676,7 +3723,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 2000, "deployer": { @@ -3703,7 +3750,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0x8Ad4d573D7EafC4Ca58f1dB704B8Db804814D674", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3725,7 +3772,8 @@ }, "interchainAccountIsm": "0x87bDFaBbCC36D8B1aEdA871Cd54b2e86C7a4d597", "interchainAccountRouter": "0xd01A3E167d59FF98c983E83BAa5da0C3e0ADe726", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "polygoncdk" }, "flare": { "blockExplorers": [ @@ -3739,7 +3787,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 3 }, "chainId": 14, "deployer": { @@ -3775,7 +3823,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0x8Ad4d573D7EafC4Ca58f1dB704B8Db804814D674", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3797,7 +3845,8 @@ }, "interchainAccountIsm": "0xc2Da384799488B4e1E773d70a83346529145085B", "interchainAccountRouter": "0x87bDFaBbCC36D8B1aEdA871Cd54b2e86C7a4d597", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "other" }, "molten": { "blockExplorers": [ @@ -3810,7 +3859,7 @@ ], "blocks": { "confirmations": 1, - "estimateBlockTime": 1, + "estimateBlockTime": 30, "reorgPeriod": 0 }, "chainId": 360, @@ -3842,7 +3891,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0x8Ad4d573D7EafC4Ca58f1dB704B8Db804814D674", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3875,7 +3924,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 0 + "reorgPeriod": "finalized" }, "chainId": 109, "deployer": { @@ -3905,7 +3954,7 @@ "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0xc401e251CCa7A364114504A994D6fC7cb1c243AB", "interchainGasPaymaster": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", - "interchainSecurityModule": "0x78A31fF00D79158c22C09DC6D7Fa7188e21925E4", + "interchainSecurityModule": "0xf08b7F859966ed27286Fe7d924A42b40e2DB80Bd", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "merkleTreeHook": "0x441a01Fca2eD731C0Fc4633998332f9FEDB17575", "pausableHook": "0x5Ed813B8b41f25c8002B01A72bbDBe6A0232Fe27", @@ -3927,7 +3976,1656 @@ }, "interchainAccountIsm": "0xA0a44cB8Bc0f7EDe788b0Cd29524A5b14fED7b45", "interchainAccountRouter": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", - "timelockController": "0x0000000000000000000000000000000000000000" + "timelockController": "0x0000000000000000000000000000000000000000", + "technicalStack": "other" + }, + "everclear": { + "blockExplorers": [ + { + "apiUrl": "https://scan.everclear.org/api", + "family": "blockscout", + "name": "Everclear Explorer", + "url": "https://scan.everclear.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 2 + }, + "chainId": 25327, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Everclear", + "domainId": 25327, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 37 + }, + "name": "everclear", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.everclear.raas.gelato.cloud" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0x14F6CA3B04f077AF26922B7957B89b32F8C842a9", + "domainRoutingIsm": "0xDEed16fe4b1c9b2a93483EDFf34C77A9b57D31Ff", + "domainRoutingIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "fallbackRoutingHook": "0x3C2b535a49c6827DF0b8e94467e6922c99E3c092", + "interchainAccountIsm": "0xcd9D3744512F07AE844c40E27912092d7c503565", + "interchainAccountRouter": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", + "interchainGasPaymaster": "0xb58257cc81E47EC72fD38aE16297048de23163b4", + "interchainSecurityModule": "0xcC5C35a6214982d9018B95e9684D5b4dA626237e", + "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", + "merkleTreeHook": "0xCC3D1659D50461d27a2F025dDb2c9B06B584B7e1", + "pausableHook": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", + "pausableIsm": "0xA38D1D7F217A52A27b0e6BF50E0a9ddAD05798C0", + "protocolFee": "0xC49aF4965264FA7BB6424CE37aA06773ad177224", + "proxyAdmin": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "staticAggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticAggregationIsm": "0x47F57D08d01544Ed082152B73807A9D39305Fac6", + "staticAggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticMerkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticMessageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMessageIdWeightedMultisigIsmFactory": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "storageGasOracle": "0xf8344D85a1429708e0BE6724218E938087e596DF", + "testRecipient": "0xF15D70941dE2Bf95A23d6488eBCbedE0a444137f", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xC88bAD76EC7acD9fd3b9Bb264f7f5C18097c5710" + }, + "oortmainnet": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet-scan.oortech.com/api", + "family": "other", + "name": "Oort Olympus Explorer", + "url": "https://mainnet-scan.oortech.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 0 + }, + "chainId": 970, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Oort", + "domainId": 970, + "gasCurrencyCoinGeckoId": "oort", + "name": "oortmainnet", + "nativeToken": { + "decimals": 18, + "name": "Oort", + "symbol": "OORT" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet-rpc.oortech.com" + } + ], + "aggregationHook": "0x90F9ac2201bCC0fA177955175708eCB5963f0eCA", + "domainRoutingIsm": "0x4101B9B755FC58FcEA156e70B42a38CFF8A46F77", + "domainRoutingIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "fallbackRoutingHook": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "interchainAccountIsm": "0xc23BaF5Eb5848D19701BbE7f139645e6bd58a319", + "interchainAccountRouter": "0x7c58Cadcc2b60ACF794eE1843488d6f5703f76BE", + "interchainGasPaymaster": "0xb4fc9B5fD57499Ef6FfF3995728a55F7A618ef86", + "interchainSecurityModule": "0x50B5Edd94A1C7ad18Fa2CA667A30Dc051a695aEe", + "mailbox": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "merkleTreeHook": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "pausableHook": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2", + "pausableIsm": "0x989B7307d266151BE763935C856493D968b2affF", + "protocolFee": "0xfdefdDc8E153d5E0463d7E193F79A3714be16021", + "proxyAdmin": "0x148CF67B8A242c1360bb2C93fCe203EC4d4f9B56", + "staticAggregationHookFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticAggregationIsm": "0xdc2da0D5d7A69fa86366235E426ff4a0E214cC60", + "staticAggregationIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMerkleRootMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootWeightedMultisigIsmFactory": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticMessageIdMultisigIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticMessageIdWeightedMultisigIsmFactory": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "storageGasOracle": "0x2c61Cda929e4e2174cb10cd8e2724A9ceaD62E67", + "testRecipient": "0x58556AaeB2e3829d52EE5E711D44735412efA43B", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x6f77d5Ef273C38CC19d1d02352785F52565A1A6c", + "index": { + "from": 26847587 + }, + "technicalStack": "other" + }, + "alephzeroevm": { + "blockExplorers": [ + { + "apiUrl": "https://evm-explorer.alephzero.org/api", + "family": "blockscout", + "name": "Aleph Zero Explorer", + "url": "https://evm-explorer.alephzero.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 5 + }, + "chainId": 41455, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Aleph Zero EVM", + "displayNameShort": "Aleph Zero EVM", + "domainId": 41455, + "gasCurrencyCoinGeckoId": "aleph-zero", + "index": { + "from": 3421962 + }, + "name": "alephzeroevm", + "nativeToken": { + "decimals": 18, + "name": "AZERO", + "symbol": "AZERO" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.alephzero.raas.gelato.cloud" + }, + { + "http": "https://alephzero.drpc.org" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0x66b3b9f399d415Bd6CcC94407deB2AB86750Cac2", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "interchainAccountIsm": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "interchainAccountRouter": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", + "interchainGasPaymaster": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "interchainSecurityModule": "0x58D6fb4aADd3ae83ec529d3d0f42Ae904207a336", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "pausableHook": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "pausableIsm": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "protocolFee": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x221b3527B08aD096dDCc74B170a23c59B031DC14", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "testRecipient": "0x989B7307d266151BE763935C856493D968b2affF", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76" + }, + "chiliz": { + "blockExplorers": [ + { + "apiUrl": "https://api.routescan.io/v2/network/mainnet/evm/88888/etherscan/api", + "family": "routescan", + "name": "Chiliscan", + "url": "https://chiliscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 9 + }, + "chainId": 88888, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Chiliz", + "domainId": 88888, + "gasCurrencyCoinGeckoId": "chiliz", + "name": "chiliz", + "nativeToken": { + "decimals": 18, + "name": "Chiliz", + "symbol": "CHZ" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.ankr.com/chiliz" + }, + { + "http": "https://chiliz.publicnode.com" + } + ], + "aggregationHook": "0x66b3b9f399d415Bd6CcC94407deB2AB86750Cac2", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "interchainAccountIsm": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", + "interchainAccountRouter": "0xbb0AE51BCa526cF313b6a95BfaB020794af6C394", + "interchainGasPaymaster": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "interchainSecurityModule": "0x58D6fb4aADd3ae83ec529d3d0f42Ae904207a336", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "pausableHook": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "pausableIsm": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "protocolFee": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x221b3527B08aD096dDCc74B170a23c59B031DC14", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "testRecipient": "0x989B7307d266151BE763935C856493D968b2affF", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "index": { + "from": 17051552 + }, + "technicalStack": "other", + "transactionOverrides": { + "maxPriorityFeePerGas": 1000000000 + } + }, + "immutablezkevm": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.immutable.com/api/eth-rpc", + "family": "blockscout", + "name": "Immutable Explorer", + "url": "https://explorer.immutable.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 20 + }, + "chainId": 13371, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Immutable zkEVM", + "domainId": 13371, + "gasCurrencyCoinGeckoId": "immutable-x", + "name": "immutablezkevm", + "nativeToken": { + "decimals": 18, + "name": "Immutable", + "symbol": "IMX" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.immutable.com" + }, + { + "http": "https://immutable.gateway.tenderly.co" + } + ], + "aggregationHook": "0x66b3b9f399d415Bd6CcC94407deB2AB86750Cac2", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "interchainAccountIsm": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainAccountRouter": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", + "interchainGasPaymaster": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "interchainSecurityModule": "0x58D6fb4aADd3ae83ec529d3d0f42Ae904207a336", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "pausableHook": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "pausableIsm": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "protocolFee": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x221b3527B08aD096dDCc74B170a23c59B031DC14", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "testRecipient": "0x989B7307d266151BE763935C856493D968b2affF", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "index": { + "from": 12797545 + }, + "technicalStack": "other", + "transactionOverrides": { + "maxFeePerGas": 100000000000, + "maxPriorityFeePerGas": 100000000000 + } + }, + "lumia": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.lumia.org/api/eth-rpc", + "family": "blockscout", + "name": "Lumia Prism Explorer", + "url": "https://explorer.lumia.org" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 4, + "reorgPeriod": 5 + }, + "chainId": 994873017, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Lumia Prism", + "domainId": 994873017, + "gasCurrencyCoinGeckoId": "orion-protocol", + "name": "lumia", + "nativeToken": { + "decimals": 18, + "name": "Lumia", + "symbol": "LUMIA" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://994873017.rpc.thirdweb.com" + }, + { + "http": "https://mainnet-rpc.lumia.org" + } + ], + "aggregationHook": "0x2F4cf1a8DBDb31689BD62695029362C821401BA9", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "interchainAccountIsm": "0x25EAC2007b0D40E3f0AF112FD346412321038719", + "interchainAccountRouter": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", + "interchainGasPaymaster": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "interchainSecurityModule": "0x9dccF81bB9f419425b0a6584E8800556B92209Cc", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "pausableHook": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "pausableIsm": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", + "protocolFee": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x68ec44d6aB0dE138eaA159D74C5A2b473919f3df", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "testRecipient": "0xf7D882A816D4845BB221Ceb03CE531d1e7645F60", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x989B7307d266151BE763935C856493D968b2affF", + "index": { + "from": 1923136 + }, + "technicalStack": "polygoncdk" + }, + "rari": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet.explorer.rarichain.org/api", + "family": "blockscout", + "name": "Rari Mainnet Explorer", + "url": "https://mainnet.explorer.rarichain.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 30, + "reorgPeriod": 0 + }, + "chainId": 1380012617, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "RARI Chain", + "domainId": 1380012617, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 541753 + }, + "name": "rari", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.rpc.rarichain.org/http" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0x66b3b9f399d415Bd6CcC94407deB2AB86750Cac2", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "interchainAccountIsm": "0x99fEFc1119E86Ee0153eb887cF8E8ab2d92A16e8", + "interchainAccountRouter": "0xbB88a31E4b709b645c06825c0E0b5CAC906d97DE", + "interchainGasPaymaster": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "interchainSecurityModule": "0x58D6fb4aADd3ae83ec529d3d0f42Ae904207a336", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "pausableHook": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "pausableIsm": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "protocolFee": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x221b3527B08aD096dDCc74B170a23c59B031DC14", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "testRecipient": "0x989B7307d266151BE763935C856493D968b2affF", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76" + }, + "rootstock": { + "blockExplorers": [ + { + "apiUrl": "https://rootstock.blockscout.com/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://rootstock.blockscout.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 30, + "reorgPeriod": 4 + }, + "chainId": 30, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Rootstock", + "domainId": 30, + "gasCurrencyCoinGeckoId": "rootstock", + "name": "rootstock", + "nativeToken": { + "decimals": 18, + "name": "Rootstock Smart Bitcoin", + "symbol": "RBTC" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.mainnet.rootstock.io/kXhXHf6TnnfW1POvr4UT0YUvujmuju-M" + }, + { + "http": "https://public-node.rsk.co" + }, + { + "http": "https://mycrypto.rsk.co" + } + ], + "aggregationHook": "0xA530b21B2c1517ceFcAAE890c7f8A167e4C0f51E", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x148CF67B8A242c1360bb2C93fCe203EC4d4f9B56", + "interchainSecurityModule": "0x81EC949b07e033b0346E60C2098464d115F0a997", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "pausableHook": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD", + "pausableIsm": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "protocolFee": "0x989B7307d266151BE763935C856493D968b2affF", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x3D409C61721f9D5558A3AdfA45A215C795533377", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918", + "testRecipient": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "index": { + "from": 6749741 + }, + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 70000000 + } + }, + "superposition": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.superposition.so/api", + "family": "blockscout", + "name": "Superposition Explorer", + "url": "https://explorer.superposition.so/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 60, + "reorgPeriod": 0 + }, + "chainId": 55244, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Superposition", + "domainId": 55244, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 1201 + }, + "name": "superposition", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.superposition.so" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0x2F4cf1a8DBDb31689BD62695029362C821401BA9", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "interchainAccountIsm": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", + "interchainAccountRouter": "0x83475ca5bEB2Eaa59A2FF48a0544ebaa4a32c2de", + "interchainGasPaymaster": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "interchainSecurityModule": "0x9dccF81bB9f419425b0a6584E8800556B92209Cc", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "pausableHook": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "pausableIsm": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", + "protocolFee": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x68ec44d6aB0dE138eaA159D74C5A2b473919f3df", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "testRecipient": "0x71388C9E25BE7b229B5d17Df7D4DB3F7DA7C962d", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x989B7307d266151BE763935C856493D968b2affF" + }, + "metall2": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.metall2.com/api", + "family": "blockscout", + "name": "Metal L2 Explorer", + "url": "https://explorer.metall2.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 1750, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Metal L2", + "domainId": 1750, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": false, + "name": "metall2", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.metall2.com" + } + ], + "aggregationHook": "0x5C6a12730a0dF828289f26D4abFdCC80FC74896C", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "interchainAccountIsm": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "interchainAccountRouter": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "interchainGasPaymaster": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "interchainSecurityModule": "0x9d1481A1fc2515aeE0Bf4eEeDDB75893DfAF0752", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918", + "pausableHook": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "pausableIsm": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "protocolFee": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x21BAaDf623448DE682235998E1332eC481011b89", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0x749848D7b783A328638C3ea74AcFcfb73c977CbE", + "testRecipient": "0x504236Da6344e5E144def5653C2b1d0fFd18cB7d", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "index": { + "from": 8547390 + }, + "technicalStack": "opstack" + }, + "polynomial": { + "blockExplorers": [ + { + "apiUrl": "https://polynomialscan.io/api", + "family": "routescan", + "name": "Polynomial Explorer", + "url": "https://polynomialscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 8008, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Polynomial", + "domainId": 8008, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "polynomial", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.polynomial.fi" + } + ], + "aggregationHook": "0x38ca4cfB82E2799c0b9CE94113679825F5fb2cf7", + "domainRoutingIsm": "0xF1B7ba7382D73D228cdcfB26A28B6a61e14Fa432", + "domainRoutingIsmFactory": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "fallbackRoutingHook": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "interchainSecurityModule": "0x8f93c32C19986B891beA2bF317686Aa2336b3854", + "mailbox": "0x02d16BC51af6BfD153d67CA61754cF912E82C4d9", + "merkleTreeHook": "0x148CF67B8A242c1360bb2C93fCe203EC4d4f9B56", + "pausableHook": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "pausableIsm": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "protocolFee": "0xfdefdDc8E153d5E0463d7E193F79A3714be16021", + "proxyAdmin": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", + "staticAggregationHookFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationIsm": "0xEA00eE6AFDEeC7453b6AFCadEC34d0F29dCdB16b", + "staticAggregationIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMerkleRootMultisigIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticMerkleRootWeightedMultisigIsmFactory": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "staticMessageIdMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdWeightedMultisigIsmFactory": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "storageGasOracle": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "testRecipient": "0xf7D882A816D4845BB221Ceb03CE531d1e7645F60", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x6f77d5Ef273C38CC19d1d02352785F52565A1A6c", + "index": { + "from": 5310074 + }, + "technicalStack": "opstack" + }, + "flow": { + "blockExplorers": [ + { + "apiUrl": "https://evm.flowscan.io/api", + "family": "blockscout", + "name": "EVM on Flow Explorer", + "url": "https://evm.flowscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 25 + }, + "chainId": 747, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "EVM on Flow", + "domainId": 747, + "gasCurrencyCoinGeckoId": "flow", + "isTestnet": false, + "name": "flow", + "nativeToken": { + "decimals": 18, + "name": "Flow", + "symbol": "FLOW" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.evm.nodes.onflow.org" + } + ], + "aggregationHook": "0x24A681D6B1B7Fb6208938be982Efd512E842867a", + "domainRoutingIsm": "0x85804e2ae21273F6539CFa1A688FE6154004D4dF", + "domainRoutingIsmFactory": "0x8C3e1794018a589c9E9226b8543105fCb6cC88C4", + "fallbackRoutingHook": "0x549F241472FccdA169E3202048aE2241231A7772", + "interchainAccountIsm": "0xb5668713E9BA8bC96f97D691663E70b54CE90b0A", + "interchainAccountRouter": "0xc5068BB6803ADbe5600DE5189fe27A4dAcE31170", + "interchainGasPaymaster": "0xA1Df6B70044029a2D1eDDC50EfDE2813e478140a", + "interchainSecurityModule": "0x01180E7B92A4fEA1e250C92E32A02Df1cA058Fe0", + "mailbox": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "merkleTreeHook": "0x25d668D37f20E6f396cB5DF1DFf5A3f2F568e707", + "pausableHook": "0xC9ab9Dc82F05eA118F266611f4c474529d43b599", + "pausableIsm": "0x14C586824E6d04F0761BF9fCa6983F7282002299", + "protocolFee": "0x3AdCBc94ab8C48EC52D06dc65Bb787fD1981E3d5", + "proxyAdmin": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "staticAggregationHookFactory": "0xfBc08389224d23b79cb21cDc16c5d42F0ad0F57f", + "staticAggregationIsm": "0x729E790E70902429873c23BaA73eE39aCEEfc461", + "staticAggregationIsmFactory": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", + "staticMerkleRootMultisigIsmFactory": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", + "staticMerkleRootWeightedMultisigIsmFactory": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "staticMessageIdMultisigIsmFactory": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "staticMessageIdWeightedMultisigIsmFactory": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "storageGasOracle": "0x59973ae98D340cA0577ed92502402E82E987Ba2E", + "testRecipient": "0xC1eC2f9BB21148a58ea571770c5F1FAa01cDFF11", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xE2B36A37bD98ba81658dC5454F2dB2F98438d140", + "index": { + "from": 4240431 + }, + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 100000000 + } + }, + "stride": { + "bech32Prefix": "stride", + "blockExplorers": [ + { + "apiUrl": "https://www.mintscan.io/stride", + "family": "other", + "name": "Mintscan", + "url": "https://www.mintscan.io/stride" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 1 + }, + "chainId": "stride-1", + "deployer": { + "name": "Stride Labs", + "url": "https://www.stride.zone" + }, + "displayName": "Stride", + "domainId": 745, + "gasCurrencyCoinGeckoId": "stride", + "grpcUrls": [ + { + "http": "https://stride-grpc.publicnode.com:443" + } + ], + "isTestnet": false, + "name": "stride", + "nativeToken": { + "decimals": 6, + "denom": "ustrd", + "name": "Stride", + "symbol": "STRD" + }, + "protocol": "cosmos", + "restUrls": [ + { + "http": "https://stride-api.polkachu.com" + } + ], + "rpcUrls": [ + { + "http": "https://stride-rpc.polkachu.com" + } + ], + "slip44": 118, + "interchainGasPaymaster": "0x602D5BF31F85FE90BF812A5EE6E2CC8CF6998A687125CABEDD120983CB88DA54", + "mailbox": "0x89945750e089d84581f194e1947a58480b335f18386ad4f761f05feebf5e2454", + "merkleTreeHook": "0x7ab4a8c3ba5371e34cd8d5dc584e0d924504fc21c3cbf41c3f64d436176bf007", + "validatorAnnounce": "0xf57d954bf3ddb5f1032a0e020a99e931215cf83ceb4de987c781488065aaae0d", + "gasPrice": { + "denom": "ustrd", + "amount": "0.005" + }, + "canonicalAsset": "ustrd", + "contractAddressBytes": 32, + "index": { + "from": 9152000, + "chunk": 5 + } + }, + "apechain": { + "blockExplorers": [ + { + "apiUrl": "https://apechain.calderaexplorer.xyz/api", + "family": "blockscout", + "name": "ApeChain Explorer", + "url": "https://apechain.calderaexplorer.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.2, + "reorgPeriod": 5 + }, + "chainId": 33139, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "ApeChain", + "domainId": 33139, + "gasCurrencyCoinGeckoId": "apecoin", + "index": { + "from": 1759561 + }, + "name": "apechain", + "nativeToken": { + "decimals": 18, + "name": "ApeCoin", + "symbol": "APE" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.apechain.com/http" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0x9C16FBa8b0c8a356E0f4398f524ae73Fd5a677B6", + "domainRoutingIsm": "0xDEed16fe4b1c9b2a93483EDFf34C77A9b57D31Ff", + "domainRoutingIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "fallbackRoutingHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "interchainAccountIsm": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "interchainAccountRouter": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", + "interchainGasPaymaster": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", + "interchainSecurityModule": "0x9FF3f38DED52D74EF4b666A7A09BcB5F38d6D272", + "mailbox": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", + "merkleTreeHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "pausableHook": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "pausableIsm": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", + "protocolFee": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "proxyAdmin": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "staticAggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticAggregationIsm": "0x9FF3f38DED52D74EF4b666A7A09BcB5F38d6D272", + "staticAggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticMerkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticMessageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMessageIdWeightedMultisigIsmFactory": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "storageGasOracle": "0xF1854214392864c628A16930E73B699f7a51b3EE", + "testRecipient": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96" + }, + "arbitrumnova": { + "blockExplorers": [ + { + "apiUrl": "https://api-nova.arbiscan.io/api", + "family": "etherscan", + "name": "Arbiscan Nova", + "url": "https://nova.arbiscan.io/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 42170, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Arbitrum Nova", + "domainId": 42170, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 78794208 + }, + "name": "arbitrumnova", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://nova.arbitrum.io/rpc" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53" + }, + "b3": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.b3.fun/api", + "family": "blockscout", + "name": "B3 Explorer", + "url": "https://explorer.b3.fun" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 8333, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "B3", + "domainId": 8333, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "b3", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet-rpc.b3.fun" + } + ], + "technicalStack": "opstack", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "from": 7504261 + } + }, + "fantom": { + "blockExplorers": [ + { + "apiUrl": "https://api.ftmscan.com/api", + "family": "etherscan", + "name": "FTMScan", + "url": "https://ftmscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 250, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Fantom Opera", + "domainId": 250, + "gasCurrencyCoinGeckoId": "fantom", + "name": "fantom", + "nativeToken": { + "decimals": 18, + "name": "Fantom", + "symbol": "FTM" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpcapi.fantom.network" + }, + { + "http": "https://fantom-rpc.publicnode.com" + }, + { + "http": "https://fantom-pokt.nodies.app" + }, + { + "http": "https://rpc.fantom.network" + }, + { + "http": "https://rpc2.fantom.network" + }, + { + "http": "https://rpc3.fantom.network" + } + ], + "technicalStack": "other", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "from": 95635171 + } + }, + "gravity": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.gravity.xyz/api", + "family": "blockscout", + "name": "Gravity Alpha Explorer", + "url": "https://explorer.gravity.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 1625, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Gravity Alpha Mainnet", + "domainId": 1625, + "gasCurrencyCoinGeckoId": "g-token", + "index": { + "from": 13374779 + }, + "name": "gravity", + "nativeToken": { + "decimals": 18, + "name": "Gravity", + "symbol": "G" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.gravity.xyz" + } + ], + "technicalStack": "arbitrumnitro", + "aggregationHook": "0x836E1b748cac2FAc6264Baf2bF83cd9a79b723C6", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "interchainAccountIsm": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "interchainAccountRouter": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", + "interchainGasPaymaster": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", + "interchainSecurityModule": "0xfa19BfEcB4fed2e0268ee5008a11cD946DcC13c3", + "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "merkleTreeHook": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", + "pausableHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "pausableIsm": "0x7621e04860F0bDe63311db9D5D8b589AD3458A1f", + "protocolFee": "0x8C3e1794018a589c9E9226b8543105fCb6cC88C4", + "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xfa19BfEcB4fed2e0268ee5008a11cD946DcC13c3", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "storageGasOracle": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "testRecipient": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "displayNameShort": "Gravity" + }, + "harmony": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.harmony.one/api", + "family": "blockscout", + "name": "Harmony Explorer", + "url": "https://explorer.harmony.one" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 1666600000, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Harmony One", + "domainId": 1666600000, + "gasCurrencyCoinGeckoId": "harmony", + "name": "harmony", + "nativeToken": { + "decimals": 18, + "name": "ONE", + "symbol": "ONE" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://api.harmony.one" + }, + { + "http": "https://api.s0.t.hmny.io" + }, + { + "http": "https://1rpc.io/one" + }, + { + "http": "https://rpc.ankr.com/harmony" + } + ], + "technicalStack": "other", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "chunk": 999, + "from": 64597391 + } + }, + "kaia": { + "blockExplorers": [ + { + "apiUrl": "https://api-cypress.klaytnscope.com/api", + "family": "etherscan", + "name": "Kaiascope", + "url": "https://kaiascope.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 5 + }, + "chainId": 8217, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Kaia", + "domainId": 8217, + "gasCurrencyCoinGeckoId": "kaia", + "name": "kaia", + "nativeToken": { + "decimals": 18, + "name": "Kaia", + "symbol": "KLAY" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://public-en.node.kaia.io" + } + ], + "technicalStack": "other", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "from": 167871545 + } + }, + "morph": { + "blockExplorers": [ + { + "apiUrl": "https://explorer-api.morphl2.io/api", + "family": "blockscout", + "name": "Morph Explorer", + "url": "https://explorer.morphl2.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 4, + "reorgPeriod": 5 + }, + "chainId": 2818, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Morph", + "domainId": 2818, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "morph", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.morphl2.io" + } + ], + "technicalStack": "other", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "from": 94151 + } + }, + "orderly": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.orderly.network/api", + "family": "blockscout", + "name": "Orderly L2 Explorer", + "url": "https://explorer.orderly.network" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 291, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Orderly L2", + "domainId": 291, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "orderly", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.orderly.network" + }, + { + "http": "https://l2-orderly-mainnet-0.t.conduit.xyz" + } + ], + "technicalStack": "opstack", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "from": 16635646 + } + }, + "snaxchain": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.snaxchain.io/api", + "family": "blockscout", + "name": "Snaxchain Mainnet Explorer", + "url": "https://explorer.snaxchain.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 2192, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "SnaxChain", + "domainId": 2192, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "snaxchain", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.snaxchain.io" + } + ], + "technicalStack": "opstack", + "aggregationHook": "0xcFD1c5b1357539566edC273aDaae19CA5e359c42", + "domainRoutingIsm": "0x494415e823236A05c608D6b777bC80082cED6A2E", + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "interchainAccountIsm": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "interchainAccountRouter": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "interchainGasPaymaster": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "interchainSecurityModule": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "merkleTreeHook": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "pausableHook": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "pausableIsm": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "protocolFee": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x92772a801db50044a9D5078CC35CD63CEcD7B424", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMessageIdWeightedMultisigIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "storageGasOracle": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "testRecipient": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "index": { + "from": 3158644 + } + }, + "zeronetwork": { + "blockExplorers": [ + { + "apiUrl": "https://zero-network-api.calderaexplorer.xyz/api", + "family": "etherscan", + "name": "Zero Network Explorer", + "url": "https://zerion-explorer.vercel.app" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 0 + }, + "chainId": 543210, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Zero Network", + "domainId": 543210, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "zeronetwork", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://zero-network.calderachain.xyz" + } + ], + "technicalStack": "zksync", + "domainRoutingIsm": "0x307A9dBD1df2329c3c597aF6853de60660baFFb5", + "domainRoutingIsmFactory": "0x0000000000000000000000000000000000000000", + "fallbackDomainRoutingHook": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", + "fallbackRoutingHook": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", + "interchainGasPaymaster": "0x318FbdB17d4e743aBF3183658a4730777101B75C", + "interchainSecurityModule": "0x307A9dBD1df2329c3c597aF6853de60660baFFb5", + "mailbox": "0xd7b351D2dE3495eA259DD10ab4b9300A378Afbf3", + "merkleTreeHook": "0x55379421409961Ef129738c24261379ef8A547Df", + "proxyAdmin": "0x72e2A678442Edc65f14476A0E4c94312C0469f4A", + "staticAggregationHookFactory": "0x0000000000000000000000000000000000000000", + "staticAggregationIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMerkleRootMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMessageIdMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMessageIdWeightedMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "storageGasOracle": "0xe85d65f04D1562f8571d57326d6798e4584aa254", + "testRecipient": "0xC18bE7ac43334F501fd9622877160b085215dECC", + "validatorAnnounce": "0xB2F0e411B46AbE3248dAFB5e89aDB5b8404F45DF", + "index": { + "from": 475 + } + }, + "zksync": { + "blockExplorers": [ + { + "apiUrl": "https://block-explorer-api.mainnet.zksync.io/api", + "family": "etherscan", + "name": "zkSync Explorer", + "url": "https://explorer.zksync.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 0 + }, + "chainId": 324, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "zkSync", + "domainId": 324, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "zksync", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.era.zksync.io" + } + ], + "technicalStack": "zksync", + "domainRoutingIsm": "0xec650696FDAE2355A928520AD7d6491c6072cf7f", + "domainRoutingIsmFactory": "0x0000000000000000000000000000000000000000", + "fallbackDomainRoutingHook": "0xe4e98Cc5D0318aBFD2adA8A3C6817b727063F500", + "fallbackRoutingHook": "0xe4e98Cc5D0318aBFD2adA8A3C6817b727063F500", + "interchainGasPaymaster": "0xf44AdA86a1f765A938d404699B8070Dd47bD2431", + "interchainSecurityModule": "0xec650696FDAE2355A928520AD7d6491c6072cf7f", + "mailbox": "0x6bD0A2214797Bc81e0b006F7B74d6221BcD8cb6E", + "merkleTreeHook": "0x823500D69D77A52212DC93f8836E9c08581487eE", + "proxyAdmin": "0xD01274DC164D32F8595bE707F221375E68cE300C", + "staticAggregationHookFactory": "0x0000000000000000000000000000000000000000", + "staticAggregationIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMerkleRootMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMerkleRootWeightedMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMessageIdMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "staticMessageIdWeightedMultisigIsmFactory": "0x0000000000000000000000000000000000000000", + "storageGasOracle": "0x37f4afe769087738f0577A77ffA24abef6fCBF99", + "testRecipient": "0xD55078c54b0cEAa87Ba5c3fAeAC89861c69F636d", + "validatorAnnounce": "0x576aF402c97bFE452Dcc203B6c3f6F4EBC92A0f5", + "index": { + "from": 47325797 + } } }, "defaultRpcConsensusType": "fallback" diff --git a/rust/config/test-sealevel-keys/test_deployer-account.json b/rust/main/config/test-sealevel-keys/test_deployer-account.json similarity index 100% rename from rust/config/test-sealevel-keys/test_deployer-account.json rename to rust/main/config/test-sealevel-keys/test_deployer-account.json diff --git a/rust/config/test-sealevel-keys/test_deployer-keypair.json b/rust/main/config/test-sealevel-keys/test_deployer-keypair.json similarity index 100% rename from rust/config/test-sealevel-keys/test_deployer-keypair.json rename to rust/main/config/test-sealevel-keys/test_deployer-keypair.json diff --git a/rust/config/test_sealevel_config.json b/rust/main/config/test_sealevel_config.json similarity index 100% rename from rust/config/test_sealevel_config.json rename to rust/main/config/test_sealevel_config.json diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json new file mode 100644 index 000000000..9cbbb5e1b --- /dev/null +++ b/rust/main/config/testnet_config.json @@ -0,0 +1,1930 @@ +{ + "chains": { + "alfajores": { + "aggregationHook": "0xdBabD76358897E68E4964647C1fb8Bf524f5EFdB", + "blockExplorers": [ + { + "apiUrl": "https://explorer.celo.org/alfajores/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://explorer.celo.org/alfajores" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, + "chainId": 44787, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Alfajores", + "domainId": 44787, + "domainRoutingIsm": "0xD1DCBe1546bb911f2570E939a231A28F14C29638", + "domainRoutingIsmFactory": "0x30d9A03762431F8A917a0C469E7A62Bf55092Ca6", + "fallbackRoutingHook": "0x3528B1aeF3a3d29E0eae90ad777A2b4A6a48aC3F", + "index": { + "from": 20231908 + }, + "interchainAccountIsm": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", + "interchainAccountRouter": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", + "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", + "interchainSecurityModule": "0xC8513429105955cf01669bfD1ac5396Faf0748a5", + "isTestnet": true, + "mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", + "merkleTreeHook": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", + "name": "alfajores", + "nativeToken": { + "decimals": 18, + "name": "CELO", + "symbol": "CELO" + }, + "pausableHook": "0x4a2902395A40Ecf0B57CaB362b59bAffba9BB4aE", + "pausableIsm": "0xA4caB1565083D33899A6eE69B174cC7729b3EaDF", + "protocol": "ethereum", + "protocolFee": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", + "proxyAdmin": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", + "rpcUrls": [ + { + "http": "https://alfajores-forno.celo-testnet.org" + } + ], + "staticAggregationHookFactory": "0x71bB34Ee833467443628CEdFAA188B2387827Cee", + "staticAggregationIsm": "0x8B29157852340cC5d3d0E289be3B0344E8812173", + "staticAggregationIsmFactory": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", + "staticMerkleRootMultisigIsmFactory": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", + "staticMessageIdMultisigIsmFactory": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", + "storageGasOracle": "0x8356113754C7aCa297Db3089b89F87CC125499fb", + "testRecipient": "0x6489d13AcAd3B8dce4c5B31f375DE4f9451E7b38", + "testTokenRecipient": "0x92dC0a76452a9D9358D2d2dEd8CddA209DF67c45", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", + "staticMerkleRootWeightedMultisigIsmFactory": "0x374961678da5911083599314974B94094513F95c", + "staticMessageIdWeightedMultisigIsmFactory": "0x1Fa22d908f5a5E7F5429D9146E5a3740D8AC10d7", + "gasCurrencyCoinGeckoId": "celo" + }, + "arbitrumsepolia": { + "aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19", + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.arbiscan.io/api", + "family": "etherscan", + "name": "Arbiscan", + "url": "https://sepolia.arbiscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 0 + }, + "chainId": 421614, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Arbitrum Sepolia", + "domainId": 421614, + "domainRoutingIsm": "0xaCA2f65aFDa2cbC8BF28DdE096eCF83aCd121c0b", + "domainRoutingIsmFactory": "0xd785272D240B07719e417622cbd2cfA0E584d1bd", + "fallbackRoutingHook": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", + "index": { + "from": 49690504 + }, + "interchainGasPaymaster": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "interchainSecurityModule": "0xA6D6d30c37434b142618eF97AB15a71871d721C6", + "isTestnet": true, + "mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", + "merkleTreeHook": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", + "name": "arbitrumsepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "pausableIsm": "0x54ba950390670f27892AFFC670Ba6ed598E5B8Df", + "protocol": "ethereum", + "protocolFee": "0x75f3E2a4f424401195A5E176246Ecc9f7e7680ff", + "proxyAdmin": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", + "rpcUrls": [ + { + "http": "https://arbitrum-sepolia.blockpi.network/v1/rpc/public" + }, + { + "http": "https://sepolia-rollup.arbitrum.io/rpc" + } + ], + "staticAggregationHookFactory": "0x0526E47C49742C15F8817ef8cf0d8FFc72139D4F", + "staticAggregationIsm": "0x29cEAFEE1F76B9CE271750f86B2bD12C23F9dDb6", + "staticAggregationIsmFactory": "0xfc8d0D2E15A36f1A3F3aE3Cb127B706c1f23Aadc", + "staticMerkleRootMultisigIsmFactory": "0x1D5EbC3e15e9ECDe0e3530C85899556797eeaea5", + "staticMessageIdMultisigIsmFactory": "0xF7F0DaB0BECE4498dAc7eb616e288809D4499371", + "storageGasOracle": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "technicalStack": "arbitrumnitro", + "testRecipient": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "validatorAnnounce": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "staticMerkleRootWeightedMultisigIsmFactory": "0x1aFD5191738d365C8079e955E4cEdDfe7e01C62d", + "staticMessageIdWeightedMultisigIsmFactory": "0xC81e6D1070aFA48DA4e4f35E744CC1aE43532a10", + "gasCurrencyCoinGeckoId": "ethereum", + "interchainAccountIsm": "0xaec6382e1e16Ee12DBEf0e7EA5ADa51217813Fc3", + "interchainAccountRouter": "0x20cC3a33C49fa13627303669edf2DcA7F1E76a50", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "basesepolia": { + "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.basescan.org/api", + "family": "etherscan", + "name": "BaseScan", + "url": "https://sepolia.basescan.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, + "chainId": 84532, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Base Sepolia", + "domainId": 84532, + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "index": { + "from": 13851043 + }, + "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "interchainSecurityModule": "0x3E857CB33b76f680F3dB557Ce3BBf2591A98d92d", + "isTestnet": true, + "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "name": "basesepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", + "protocol": "ethereum", + "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "rpcUrls": [ + { + "http": "https://sepolia.base.org" + }, + { + "http": "https://base-sepolia-rpc.publicnode.com" + } + ], + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "staticMerkleRootWeightedMultisigIsmFactory": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "staticMessageIdWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "gasCurrencyCoinGeckoId": "ethereum", + "interchainAccountIsm": "0xDa5177080f7fC5d9255eB32cC64B9b4e5136A716", + "interchainAccountRouter": "0xd876C01aB40e8cE42Db417fBC79c726d45504dE4", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "bsctestnet": { + "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", + "blockExplorers": [ + { + "apiUrl": "https://api-testnet.bscscan.com/api", + "family": "etherscan", + "name": "BscScan", + "url": "https://testnet.bscscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 9 + }, + "chainId": 97, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "BSC Testnet", + "domainId": 97, + "domainRoutingIsm": "0x82A4aac9bED3DFDfb9569031E19d18431204681C", + "domainRoutingIsmFactory": "0xD2a0c68ed92D1Eb3C699D2808b06dd7b70367F92", + "fallbackRoutingHook": "0x2670ED2EC08cAd135307556685a96bD4c16b007b", + "index": { + "chunk": 1000, + "from": 34323977 + }, + "interchainAccountIsm": "0xa9D8Ec959F34272B1a56D09AF00eeee58970d3AE", + "interchainAccountRouter": "0x6d2B3e304E58c2a19f1492E7cf15CaF63Ce6e0d2", + "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", + "interchainSecurityModule": "0x2B3bEc44051C3A0c26360Ae513e98A947E9939b7", + "isTestnet": true, + "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", + "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", + "name": "bsctestnet", + "nativeToken": { + "decimals": 18, + "name": "BNB", + "symbol": "BNB" + }, + "pausableHook": "0xA71E50eFd93600933650A324AE43d395a8aE4AC7", + "pausableIsm": "0xb98B1596897d3c122111ea6b6b9014A95920F459", + "protocol": "ethereum", + "protocolFee": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", + "proxyAdmin": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", + "rpcUrls": [ + { + "http": "https://bsc-testnet.publicnode.com" + }, + { + "http": "https://bsc-testnet.blockpi.network/v1/rpc/public" + } + ], + "staticAggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", + "staticAggregationIsm": "0x8fb481f65d04c590b8507F75D05Ed29594590376", + "staticAggregationIsmFactory": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", + "staticMerkleRootMultisigIsmFactory": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", + "staticMessageIdMultisigIsmFactory": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", + "storageGasOracle": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", + "testRecipient": "0xfbcD1c00a3d809f36cC1A15918694B17B32c0b6c", + "testTokenRecipient": "0x260f6024119549a40595d0937471e607411E8ea5", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465", + "staticMerkleRootWeightedMultisigIsmFactory": "0xCa152b249791Adf7A09C6c1bdbAb05e4A594966e", + "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1", + "gasCurrencyCoinGeckoId": "binancecoin", + "transactionOverrides": { + "gasPrice": 8000000000 + } + }, + "connextsepolia": { + "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", + "blockExplorers": [ + { + "apiUrl": "https://scan.testnet.everclear.org/api", + "family": "blockscout", + "name": "Everclear Testnet Explorer", + "url": "https://scan.testnet.everclear.org/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 10, + "reorgPeriod": 0 + }, + "chainId": 6398, + "deployer": { + "name": "Everclear", + "url": "https://everclear.org" + }, + "displayName": "Everclear Sepolia", + "domainId": 6398, + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", + "index": { + "from": 4950 + }, + "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", + "interchainSecurityModule": "0x1e58386A3f012D69568B3E1aB5f8E41169Ba69A9", + "isTestnet": true, + "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", + "name": "connextsepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", + "pausableIsm": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", + "protocol": "ethereum", + "protocolFee": "0x863E8c26621c52ACa1849C53500606e73BA272F0", + "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "rpcUrls": [ + { + "http": "https://rpc.connext-sepolia.gelato.digital" + } + ], + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x5e6Fe18eC7D4b159bDC5Fa0C32bB1996277B3ddF", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "storageGasOracle": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", + "technicalStack": "arbitrumnitro", + "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", + "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", + "staticMerkleRootWeightedMultisigIsmFactory": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", + "staticMessageIdWeightedMultisigIsmFactory": "0xcCB305B1f21e5FbC85D1DD7Be5cd8d5bf5B7f863", + "gasCurrencyCoinGeckoId": "ethereum", + "interchainAccountIsm": "0xA30b2CbC14b97aa55bBC947f4AC6c4254971aFD1", + "interchainAccountRouter": "0xc9ab470A61571ac0c39B7E0923fbEaDdB58d98FE", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "eclipsetestnet": { + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.4, + "reorgPeriod": 0 + }, + "chainId": 239092742, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Eclipse Testnet", + "domainId": 239092742, + "index": { + "from": 1, + "mode": "sequence" + }, + "interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", + "isTestnet": true, + "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "name": "eclipsetestnet", + "nativeToken": { + "decimals": 9, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "sealevel", + "rpcUrls": [ + { + "http": "https://testnet.dev2.eclipsenetwork.xyz" + } + ], + "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "gasCurrencyCoinGeckoId": "ethereum" + }, + "ecotestnet": { + "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", + "blockExplorers": [ + { + "apiUrl": "https://eco-testnet.explorer.caldera.xyz/api", + "family": "blockscout", + "name": "ECO Testnet explorer", + "url": "https://eco-testnet.explorer.caldera.xyz/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 0 + }, + "chainId": 471923, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Eco Testnet", + "domainId": 471923, + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "index": { + "from": 1606754 + }, + "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "interchainSecurityModule": "0x4c8A96b43fD59a4171b7c79d657AD9FedFb2d7B5", + "isTestnet": true, + "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "name": "ecotestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", + "protocol": "ethereum", + "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "rpcUrls": [ + { + "http": "https://eco-testnet.rpc.caldera.xyz/http" + } + ], + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "staticMerkleRootWeightedMultisigIsmFactory": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "staticMessageIdWeightedMultisigIsmFactory": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "gasCurrencyCoinGeckoId": "ethereum", + "interchainAccountIsm": "0x0De2F539569Fb1e2e3C1d233f7A63a18B9A17110", + "interchainAccountRouter": "0x2C6dD6768E669EDB7b53f26067C1C4534862c3de", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "fuji": { + "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", + "blockExplorers": [ + { + "apiUrl": "https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api", + "family": "etherscan", + "name": "SnowTrace", + "url": "https://testnet.snowtrace.io" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 2, + "reorgPeriod": 3 + }, + "chainId": 43113, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Fuji", + "domainId": 43113, + "domainRoutingIsm": "0xe82EbDCC1B546CEDa0cf5B5495728700f6dE41B4", + "domainRoutingIsmFactory": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", + "fallbackRoutingHook": "0xc684f7F50DB4b2563218512e021fBdd0BeD6b57E", + "index": { + "from": 26503317 + }, + "interchainAccountIsm": "0xfaB4815BDC5c60c6bD625459C8577aFdD79D9311", + "interchainAccountRouter": "0xeEF6933122894fF217a7dd07510b3D64b747e29b", + "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", + "interchainSecurityModule": "0xe412A2d273c02d6f837532946d1B05A6EAB72B04", + "isTestnet": true, + "mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", + "name": "fuji", + "nativeToken": { + "decimals": 18, + "name": "Avalanche", + "symbol": "AVAX" + }, + "pausableHook": "0x495e9E119b2aa848b418EF6A4d30b42803de43A9", + "pausableIsm": "0x8e3D9139547be48942Aa4a489bc250FEa51679fa", + "protocol": "ethereum", + "protocolFee": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", + "proxyAdmin": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", + "rpcUrls": [ + { + "http": "https://api.avax-test.network/ext/bc/C/rpc", + "pagination": { + "maxBlockRange": 2048 + } + } + ], + "staticAggregationHookFactory": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", + "staticAggregationIsm": "0xdDB75f480841A8333A31c4198ecf1780936B222D", + "staticAggregationIsmFactory": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", + "staticMerkleRootMultisigIsmFactory": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", + "staticMessageIdMultisigIsmFactory": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", + "storageGasOracle": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", + "testRecipient": "0x44a7e1d76fD8AfA244AdE7278336E3D5C658D398", + "testTokenRecipient": "0x9CC10c844B3Bbae2444E39991aB027C4A05D1F2e", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", + "staticMerkleRootWeightedMultisigIsmFactory": "0xff93F32997Ac5450995121385aCE96b184efe89E", + "staticMessageIdWeightedMultisigIsmFactory": "0x8eAB8cBb9037e818C321f675c0bc2EA4649003CF", + "gasCurrencyCoinGeckoId": "avalanche-2" + }, + "holesky": { + "aggregationHook": "0xb1FfD51f03c69A0a3e5AFEBDE639752DB1d56bc9", + "blockExplorers": [ + { + "apiUrl": "https://api-holesky.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://holesky.etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 13, + "reorgPeriod": 2 + }, + "chainId": 17000, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Holesky", + "domainId": 17000, + "domainRoutingIsm": "0x51890869940b4B7eD7426A612676eC63223eF3Db", + "domainRoutingIsmFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "fallbackRoutingHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", + "index": { + "from": 1543015 + }, + "interchainGasPaymaster": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "interchainSecurityModule": "0xA4bFAA24c14f0398903E59344F4a36334F47AA50", + "isTestnet": true, + "mailbox": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", + "merkleTreeHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", + "name": "holesky", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", + "pausableIsm": "0x80fE4Cb8c70fc60B745d4ffD4403c27a8cBC9e02", + "protocol": "ethereum", + "protocolFee": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", + "proxyAdmin": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "rpcUrls": [ + { + "http": "https://ethereum-holesky-rpc.publicnode.com" + } + ], + "staticAggregationHookFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", + "staticAggregationIsm": "0x296Be783e0b1CdDed847E34e6587C24dDb710cf9", + "staticAggregationIsmFactory": "0x54148470292C24345fb828B003461a9444414517", + "staticMerkleRootMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", + "staticMerkleRootWeightedMultisigIsmFactory": "0xFb55597F07417b08195Ba674f4dd58aeC9B89FBB", + "staticMessageIdWeightedMultisigIsmFactory": "0x0E18b28D98C2efDb59252c021320F203305b1B66", + "gasCurrencyCoinGeckoId": "ethereum", + "interchainAccountIsm": "0xb04961F492f447A8bA10f6694Bd888C7619CD2D5", + "interchainAccountRouter": "0xD31eD5a3D26c9787Ab607B0c364B21218D0f8F7b", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "optimismsepolia": { + "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia-optimistic.etherscan.io/api", + "family": "etherscan", + "name": "OP Sepolia Explorer", + "url": "https://sepolia-optimistic.etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 0 + }, + "chainId": 11155420, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Optimism Sepolia", + "domainId": 11155420, + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 15833917 + }, + "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "interchainSecurityModule": "0x4c8A96b43fD59a4171b7c79d657AD9FedFb2d7B5", + "isTestnet": true, + "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "merkleTreeHook": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "name": "optimismsepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "pausableIsm": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", + "protocol": "ethereum", + "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "rpcUrls": [ + { + "http": "https://sepolia.optimism.io" + } + ], + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x815a9642497Ee1E9F061f8b828C85Eb7193DecfC", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "storageGasOracle": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "interchainAccountIsm": "0xA7FA26ef3Ea88CD696779735AC9591E01146DA38", + "interchainAccountRouter": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "plumetestnet": { + "aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", + "blockExplorers": [ + { + "apiUrl": "https://plume-testnet.explorer.caldera.xyz/api", + "family": "blockscout", + "name": "Plume Testnet Explorer", + "url": "https://plume-testnet.explorer.caldera.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, + "chainId": 161221135, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Plume Testnet", + "domainId": 161221135, + "domainRoutingIsm": "0xF554Be1611572dF824556e8060bf90Fe5bE7Ff08", + "domainRoutingIsmFactory": "0x54148470292C24345fb828B003461a9444414517", + "fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "index": { + "from": 5284139 + }, + "interchainAccountIsm": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", + "interchainAccountRouter": "0xB6F8aA9B1b314A6E6DFB465DD3e0E95936347517", + "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "interchainSecurityModule": "0x5708e5CAd632898E16D6A7bA286EC8b8eFD3056b", + "isTestnet": true, + "mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "name": "plumetestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "pausableIsm": "0xDaAc5C311d6e9134C725171Ef06a8a2BaAF4a10f", + "protocol": "ethereum", + "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "proxyAdmin": "0x589C201a07c26b4725A4A829d772f24423da480B", + "rpcConsensusType": "single", + "rpcUrls": [ + { + "http": "https://plume-testnet.rpc.caldera.xyz/http" + } + ], + "staticAggregationHookFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "staticAggregationIsm": "0xDaE6E59fB970F8df1cCCC7d230a7cdeD8BDfCb95", + "staticAggregationIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMerkleRootMultisigIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticMessageIdMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "technicalStack": "arbitrumnitro", + "testRecipient": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "staticMerkleRootWeightedMultisigIsmFactory": "0x7924A1569fE0b860F1eA3c7b4Ed97b5528946f83", + "staticMessageIdWeightedMultisigIsmFactory": "0xb04961F492f447A8bA10f6694Bd888C7619CD2D5" + }, + "polygonamoy": { + "aggregationHook": "0x06a54A2db82D37410C1383c51F96Bd7b3ABD243E", + "blockExplorers": [ + { + "apiUrl": "https://api-amoy.polygonscan.com/api", + "family": "etherscan", + "name": "Polygon Amoy Explorer", + "url": "https://amoy.polygonscan.com" + } + ], + "blocks": { + "confirmations": 5, + "estimateBlockTime": 2, + "reorgPeriod": 10 + }, + "chainId": 80002, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Polygon Amoy", + "domainId": 80002, + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "index": { + "from": 10634605 + }, + "interchainGasPaymaster": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "interchainSecurityModule": "0xC5117582A9b64B5b3071B7f11943b21A515A84C6", + "isTestnet": true, + "mailbox": "0x54148470292C24345fb828B003461a9444414517", + "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "name": "polygonamoy", + "nativeToken": { + "decimals": 18, + "name": "Polygon Ecosystem Token", + "symbol": "POL" + }, + "pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "pausableIsm": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", + "protocol": "ethereum", + "protocolFee": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", + "proxyAdmin": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "rpcUrls": [ + { + "http": "https://rpc-amoy.polygon.technology" + }, + { + "http": "https://polygon-amoy-bor-rpc.publicnode.com" + }, + { + "http": "https://polygon-amoy.blockpi.network/v1/rpc/public" + }, + { + "http": "https://rpc.ankr.com/polygon_amoy" + } + ], + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x1D770b978d915bD96F2ad41b25824C4193EBAfA2", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "storageGasOracle": "0xD0680F80F4f947968206806C2598Cbc5b6FE5b03", + "testRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "validatorAnnounce": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", + "staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "gasCurrencyCoinGeckoId": "polygon-ecosystem-token", + "interchainAccountIsm": "0xd876C01aB40e8cE42Db417fBC79c726d45504dE4", + "interchainAccountRouter": "0xC60C145f1e1904f9d6483A611BF1416697CCc1FE", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "scrollsepolia": { + "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.scrollscan.com/api", + "family": "etherscan", + "name": "Scroll Explorer", + "url": "https://sepolia.scrollscan.dev/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 1 + }, + "chainId": 534351, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Scroll Sepolia", + "domainId": 534351, + "domainRoutingIsm": "0x606511c593fd78AFbeD8A2e02EDe2C6179722276", + "domainRoutingIsmFactory": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", + "fallbackRoutingHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", + "index": { + "from": 1344054 + }, + "interchainAccountIsm": "0xE023239c8dfc172FF008D8087E7442d3eBEd9350", + "interchainAccountRouter": "0xe17c37212d785760E8331D4A4395B17b34Ba8cDF", + "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "interchainSecurityModule": "0x69873c153380149e901b4aD031025Bc195ee1CB8", + "isTestnet": true, + "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", + "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", + "name": "scrollsepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0x390A48fC8Cb8F29B3ffE1B95aD3773414B8DD704", + "pausableIsm": "0xcD19Ff7306E04EA6b8f4B5Ab1c5A198c186aaB42", + "protocol": "ethereum", + "protocolFee": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "proxyAdmin": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", + "rpcUrls": [ + { + "http": "https://sepolia-rpc.scroll.io" + }, + { + "http": "https://rpc.ankr.com/scroll_sepolia_testnet" + }, + { + "http": "https://scroll-sepolia.blockpi.network/v1/rpc/public" + }, + { + "http": "https://scroll-sepolia.chainstacklabs.com" + }, + { + "http": "https://scroll-public.scroll-testnet.quiknode.pro" + } + ], + "staticAggregationHookFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "staticAggregationIsm": "0xcc5BAaa44A8749c7C566b1cb578a315427632053", + "staticAggregationIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticMerkleRootMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "storageGasOracle": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", + "testRecipient": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "testTokenRecipient": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381", + "staticMerkleRootWeightedMultisigIsmFactory": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb", + "staticMessageIdWeightedMultisigIsmFactory": "0x63dFf524F1c7361f4F1bf07D658Bf7f2d5Dd5B20", + "gasCurrencyCoinGeckoId": "ethereum" + }, + "sepolia": { + "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://sepolia.etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 13, + "reorgPeriod": 2 + }, + "chainId": 11155111, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Sepolia", + "domainId": 11155111, + "domainRoutingIsm": "0xfa9a26cCc5417d1C1D03C949b5013Bb5898dA905", + "domainRoutingIsmFactory": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", + "fallbackRoutingHook": "0x17Dc724B7a2F09141C13b8AC33B396073785c2BC", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-sepolia.safe.global", + "index": { + "from": 4517401 + }, + "interchainAccountIsm": "0x83a3068B719F764d413625dA77468ED74789ae02", + "interchainAccountRouter": "0x8e131c8aE5BF1Ed38D05a00892b6001a7d37739d", + "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", + "interchainSecurityModule": "0x43b6a311BF787241BB71b7aE2a29ef639932b9b8", + "isTestnet": true, + "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", + "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", + "name": "sepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", + "pausableIsm": "0x21bdaB116d9DA77E312910fB53aD35dD82C8a76c", + "protocol": "ethereum", + "protocolFee": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", + "proxyAdmin": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", + "rpcUrls": [ + { + "http": "https://ethereum-sepolia.publicnode.com" + }, + { + "http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" + }, + { + "http": "https://rpc.sepolia.org" + } + ], + "staticAggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", + "staticAggregationIsm": "0x7856078C4881e236660Be82fdb12473B4a33cCFf", + "staticAggregationIsmFactory": "0xC83e12EF2627ACE445C298e6eC418684918a6002", + "staticMerkleRootMultisigIsmFactory": "0x0a71AcC99967829eE305a285750017C4916Ca269", + "staticMessageIdMultisigIsmFactory": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", + "storageGasOracle": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", + "testRecipient": "0xeDc1A3EDf87187085A3ABb7A9a65E1e7aE370C07", + "testTokenRecipient": "0x031AD9c560D37baC7d6Bd2d27A2443bAfd10101A", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", + "staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f", + "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94", + "gasCurrencyCoinGeckoId": "ethereum" + }, + "solanatestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.solana.com?cluster=testnet", + "family": "other", + "name": "Solana Explorer", + "url": "https://explorer.solana.com?cluster=testnet" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.4, + "reorgPeriod": 0 + }, + "chainId": 1399811150, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Solana Testnet", + "displayNameShort": "Sol Testnet", + "domainId": 1399811150, + "index": { + "from": 1, + "mode": "sequence" + }, + "interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", + "isTestnet": true, + "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "name": "solanatestnet", + "nativeToken": { + "decimals": 9, + "name": "Solana", + "symbol": "SOL" + }, + "protocol": "sealevel", + "rpcUrls": [ + { + "http": "https://api.testnet.solana.com" + } + ], + "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "gasCurrencyCoinGeckoId": "solana" + }, + "superpositiontestnet": { + "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", + "blockExplorers": [ + { + "apiUrl": "https://testnet-explorer.superposition.so/api", + "family": "blockscout", + "name": "Superposition Testnet Explorer", + "url": "https://testnet-explorer.superposition.so" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 98985, + "displayName": "Superposition Testnet", + "domainId": 98985, + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", + "index": { + "from": 3111622 + }, + "interchainGasPaymaster": "0xeC7eb4196Bd601DEa7585A744FbFB4CF11278450", + "interchainSecurityModule": "0x1e58386A3f012D69568B3E1aB5f8E41169Ba69A9", + "isTestnet": true, + "mailbox": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "merkleTreeHook": "0x4926a10788306D84202A2aDbd290b7743146Cc17", + "name": "superpositiontestnet", + "nativeToken": { + "decimals": 18, + "name": "Superposition", + "symbol": "SPN" + }, + "pausableHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", + "pausableIsm": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", + "protocol": "ethereum", + "protocolFee": "0x863E8c26621c52ACa1849C53500606e73BA272F0", + "proxyAdmin": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "rpcUrls": [ + { + "http": "https://testnet-rpc.superposition.so" + } + ], + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0x5e6Fe18eC7D4b159bDC5Fa0C32bB1996277B3ddF", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "storageGasOracle": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", + "technicalStack": "arbitrumnitro", + "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", + "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", + "staticMerkleRootWeightedMultisigIsmFactory": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c", + "staticMessageIdWeightedMultisigIsmFactory": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "gasCurrencyCoinGeckoId": "superposition", + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "interchainAccountIsm": "0xd09D08a19C6609a1B51e1ca6a055861E7e7A4400", + "interchainAccountRouter": "0x3572a9d808738922194921b275B2A55414BcDA57", + "timelockController": "0x0000000000000000000000000000000000000000" + }, + "berabartio": { + "blockExplorers": [ + { + "apiUrl": "https://api.routescan.io/v2/network/testnet/evm/80084/etherscan/api/", + "family": "routescan", + "name": "Bartio Testnet Explorer", + "url": "https://bartio.beratrail.io/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 1 + }, + "chainId": 80084, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Berachain bArtio", + "domainId": 80084, + "gasCurrencyCoinGeckoId": "berachain", + "isTestnet": true, + "name": "berabartio", + "nativeToken": { + "decimals": 18, + "name": "BERA", + "symbol": "BERA" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://bartio.rpc.berachain.com/" + }, + { + "http": "https://bartio.drpc.org" + }, + { + "http": "https://bera-testnet.nodeinfra.com" + } + ], + "aggregationHook": "0xf24d9D6be85576a1e831a9b2dbF79fC862a1dD03", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "interchainAccountIsm": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "interchainAccountRouter": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "interchainGasPaymaster": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "interchainSecurityModule": "0xDabB212640f59026a861202ca82CDcD8181aD723", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "pausableHook": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "pausableIsm": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "protocolFee": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x7c05c43D5601204c9d732F036fA539C67c7b1329", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", + "testRecipient": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "index": { + "from": 4772111 + } + }, + "camptestnet": { + "blockExplorers": [ + { + "apiUrl": "https://camp-network-testnet.blockscout.com/api", + "family": "blockscout", + "name": "Camp Network Testnet Explorer", + "url": "https://camp-network-testnet.blockscout.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, + "chainId": 325000, + "displayName": "Camp Network Testnet V2", + "domainId": 325000, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": true, + "name": "camptestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc-campnetwork.xyz" + } + ], + "aggregationHook": "0xb97D172479E9EC2501524E02703B42247559A1bD", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "interchainSecurityModule": "0x8214144F223b550E5BFf6164F2136F0Ef30bB8b3", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "pausableIsm": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "protocolFee": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x77d4B4090B666d84b4451C7425682B8F51Dbd827", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "testRecipient": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "index": { + "from": 4591544 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "citreatestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.testnet.citrea.xyz/api", + "family": "blockscout", + "name": "Citrea Testnet Explorer", + "url": "https://explorer.testnet.citrea.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, + "chainId": 5115, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Citrea Testnet", + "domainId": 5115, + "gasCurrencyCoinGeckoId": "bitcoin", + "isTestnet": true, + "name": "citreatestnet", + "nativeToken": { + "decimals": 18, + "name": "Citrea BTC", + "symbol": "cBTC" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.testnet.citrea.xyz" + } + ], + "aggregationHook": "0xE1b1579C643BF6d27af5E1f1777150E5DBc271E0", + "domainRoutingIsm": "0x47824122ab0fD5186EB7509f8B26bb1045f1F3A7", + "domainRoutingIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "fallbackRoutingHook": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", + "interchainAccountIsm": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "interchainAccountRouter": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", + "interchainGasPaymaster": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "interchainSecurityModule": "0x792F905736703DCb511066f2E0C4b97504CD2728", + "mailbox": "0xB08d78F439e55D02C398519eef61606A5926245F", + "merkleTreeHook": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "pausableHook": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", + "pausableIsm": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "protocolFee": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "proxyAdmin": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "staticAggregationHookFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticAggregationIsm": "0x39738A5d90711Be0C93F5efe8F46cD150D6867c1", + "staticAggregationIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "staticMerkleRootMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootWeightedMultisigIsmFactory": "0x54148470292C24345fb828B003461a9444414517", + "staticMessageIdMultisigIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticMessageIdWeightedMultisigIsmFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", + "storageGasOracle": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841", + "testRecipient": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "index": { + "chunk": 999, + "from": 334706 + } + }, + "formtestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.form.network/api", + "family": "blockscout", + "name": "Form Testnet Explorer", + "url": "https://explorer.form.network" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, + "chainId": 132902, + "displayName": "Form Testnet", + "domainId": 132902, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": true, + "name": "formtestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://testnet-rpc.form.network/http" + } + ], + "aggregationHook": "0xb97D172479E9EC2501524E02703B42247559A1bD", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "interchainSecurityModule": "0x8214144F223b550E5BFf6164F2136F0Ef30bB8b3", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "pausableIsm": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "protocolFee": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x77d4B4090B666d84b4451C7425682B8F51Dbd827", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "testRecipient": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "index": { + "from": 12137144 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "hyperliquidevmtestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.hyperliquid.xyz/api", + "family": "other", + "name": "Hyperliquid EVM Testnet Explorer", + "url": "https://explorer.hyperliquid.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, + "chainId": 998, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Hyperliquid EVM Testnet", + "domainId": 998, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": true, + "name": "hyperliquidevmtestnet", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://api.hyperliquid-testnet.xyz/evm" + } + ], + "aggregationHook": "0x5689Ad17c798d5114dc60Ba3c98e4853dF70403D", + "domainRoutingIsm": "0x4ac19e0bafc2aF6B98094F0a1B817dF196551219", + "domainRoutingIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "interchainAccountIsm": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "interchainAccountRouter": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "interchainGasPaymaster": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", + "interchainSecurityModule": "0x59ea2E87C4775E6B963d71101AB879068898CBA8", + "mailbox": "0x589C201a07c26b4725A4A829d772f24423da480B", + "merkleTreeHook": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "pausableHook": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "pausableIsm": "0x75f3E2a4f424401195A5E176246Ecc9f7e7680ff", + "protocolFee": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "proxyAdmin": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "staticAggregationHookFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticAggregationIsm": "0xa20C93076e832388483356fa65c5C2B5F3A4d50c", + "staticAggregationIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMerkleRootMultisigIsmFactory": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "staticMerkleRootWeightedMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "staticMessageIdMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMessageIdWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "storageGasOracle": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "testRecipient": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "index": { + "from": 7032169 + } + }, + "soneiumtestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer-testnet.soneium.org/api", + "family": "blockscout", + "name": "Soneium Minato Testnet Explorer", + "url": "https://explorer-testnet.soneium.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, + "chainId": 1946, + "displayName": "Soneium Minato Testnet", + "domainId": 1946, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": true, + "name": "soneiumtestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.minato.soneium.org" + } + ], + "aggregationHook": "0xb97D172479E9EC2501524E02703B42247559A1bD", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "interchainSecurityModule": "0x8214144F223b550E5BFf6164F2136F0Ef30bB8b3", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "pausableIsm": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "protocolFee": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x77d4B4090B666d84b4451C7425682B8F51Dbd827", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "testRecipient": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "index": { + "from": 2054457 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "suavetoliman": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.toliman.suave.flashbots.net/api", + "family": "blockscout", + "name": "SUAVE Toliman Testnet Explorer", + "url": "https://explorer.toliman.suave.flashbots.net" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 4, + "reorgPeriod": 1 + }, + "chainId": 33626250, + "displayName": "SUAVE Toliman Testnet", + "domainId": 33626250, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": true, + "name": "suavetoliman", + "nativeToken": { + "decimals": 18, + "name": "TEEth", + "symbol": "TEEth" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.toliman.suave.flashbots.net" + } + ], + "aggregationHook": "0xb97D172479E9EC2501524E02703B42247559A1bD", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "interchainAccountIsm": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "interchainAccountRouter": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "interchainGasPaymaster": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "interchainSecurityModule": "0x8214144F223b550E5BFf6164F2136F0Ef30bB8b3", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "pausableHook": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "pausableIsm": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "protocolFee": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x77d4B4090B666d84b4451C7425682B8F51Dbd827", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "testRecipient": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "index": { + "from": 1921514 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "test1": { + "blockExplorers": [ + { + "apiKey": "fakekey", + "apiUrl": "https://api.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 0 + }, + "chainId": 9913371, + "displayName": "Test 1", + "domainId": 9913371, + "isTestnet": true, + "name": "test1", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "http://127.0.0.1:8545" + } + ], + "aggregationHook": "0x7F54A0734c5B443E5B04cc26B54bb8ecE0455785", + "domainRoutingIsm": "0xb0279Db6a2F1E01fbC8483FCCef0Be2bC6299cC3", + "fallbackRoutingHook": "0x99bbA657f2BbC93c02D617f8bA121cB8Fc104Acf", + "interchainGasPaymaster": "0x5eb3Bc0a489C5A8288765d2336659EbCA68FCd00", + "interchainSecurityModule": "0xb0279Db6a2F1E01fbC8483FCCef0Be2bC6299cC3", + "mailbox": "0xE6E340D132b5f46d1e472DebcD681B2aBc16e57E", + "merkleTreeHook": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528", + "protocolFee": "0x1291Be112d480055DaFd8a610b7d1e203891C274", + "proxyAdmin": "0xc5a5C42992dECbae36851359345FE25997F5C42d", + "storageGasOracle": "0x0E801D84Fa97b50751Dbf25036d067dCf18858bF", + "testRecipient": "0xCD8a1C3ba11CF5ECfa6267617243239504a98d90", + "validatorAnnounce": "0xb7278A61aa25c888815aFC32Ad3cC52fF24fE575", + "index": { + "from": 30 + } + }, + "test2": { + "blockExplorers": [ + { + "apiKey": "fakekey", + "apiUrl": "https://api.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 1 + }, + "chainId": 9913372, + "displayName": "Test 2", + "domainId": 9913372, + "isTestnet": true, + "name": "test2", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "http://127.0.0.1:8545" + } + ], + "aggregationHook": "0x5f07F66a6c12BAE727A0e0C84c2f83Ef3c83b44c", + "domainRoutingIsm": "0x3Ca8f9C04c7e3E1624Ac2008F92f6F366A869444", + "fallbackRoutingHook": "0x4C4a2f8c81640e47606d3fd77B353E87Ba015584", + "interchainGasPaymaster": "0xDC11f7E700A4c898AE5CAddB1082cFfa76512aDD", + "interchainSecurityModule": "0x3Ca8f9C04c7e3E1624Ac2008F92f6F366A869444", + "mailbox": "0x7bc06c482DEAd17c0e297aFbC32f6e63d3846650", + "merkleTreeHook": "0x04C89607413713Ec9775E14b954286519d836FEf", + "protocolFee": "0x0355B7B8cb128fA5692729Ab3AAa199C1753f726", + "proxyAdmin": "0x2bdCC0de6bE1f7D2ee689a0342D76F52E8EFABa3", + "storageGasOracle": "0x21dF544947ba3E8b3c32561399E88B52Dc8b2823", + "testRecipient": "0x172076E0166D1F9Cc711C77Adf8488051744980C", + "validatorAnnounce": "0xf4B146FbA71F41E0592668ffbF264F1D186b2Ca8", + "index": { + "from": 57 + } + }, + "test3": { + "blockExplorers": [ + { + "apiKey": "fakekey", + "apiUrl": "https://api.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 2 + }, + "chainId": 9913373, + "displayName": "Test 3", + "domainId": 9913373, + "isTestnet": true, + "name": "test3", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "http://127.0.0.1:8545" + } + ], + "aggregationHook": "0xd5BA21a5bDE25af311a900191c52ce9Fc8Ab9b8d", + "domainRoutingIsm": "0xa12fFA0B9f159BB4C54bce579611927Addc51610", + "fallbackRoutingHook": "0xf953b3A269d80e3eB0F2947630Da976B896A8C5b", + "interchainGasPaymaster": "0xe8D2A1E88c91DCd5433208d4152Cc4F399a7e91d", + "interchainSecurityModule": "0xa12fFA0B9f159BB4C54bce579611927Addc51610", + "mailbox": "0x2B0d36FACD61B71CC05ab8F3D2355ec3631C0dd5", + "merkleTreeHook": "0xA4899D35897033b927acFCf422bc745916139776", + "protocolFee": "0xCace1b78160AE76398F486c8a18044da0d66d86D", + "proxyAdmin": "0xBEc49fA140aCaA83533fB00A2BB19bDdd0290f25", + "storageGasOracle": "0xAA292E8611aDF267e563f334Ee42320aC96D0463", + "testRecipient": "0xc0F115A19107322cFBf1cDBC7ea011C19EbDB4F8", + "validatorAnnounce": "0xF8e31cb472bc70500f08Cd84917E5A1912Ec8397", + "index": { + "from": 84 + } + }, + "test4": { + "blockExplorers": [ + { + "apiKey": "fakekey", + "apiUrl": "https://api.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 0 + }, + "chainId": 31337, + "displayName": "Test 4", + "domainId": 31337, + "isTestnet": true, + "name": "test4", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "http://127.0.0.1:8545" + } + ], + "aggregationHook": "0xfD4Ab5938aAcE9B094cc3B298d18be83E170B2fc", + "domainRoutingIsm": "0x532B02BD614Fd18aEE45603d02866cFb77575CB3", + "fallbackRoutingHook": "0x1f10F3Ba7ACB61b2F50B9d6DdCf91a6f787C0E82", + "interchainGasPaymaster": "0x5fc748f1FEb28d7b76fa1c6B07D8ba2d5535177c", + "interchainSecurityModule": "0x532B02BD614Fd18aEE45603d02866cFb77575CB3", + "mailbox": "0x07882Ae1ecB7429a84f1D53048d35c4bB2056877", + "merkleTreeHook": "0xE3011A37A904aB90C8881a99BD1F6E21401f1522", + "protocolFee": "0x8A93d247134d91e0de6f96547cB0204e5BE8e5D8", + "proxyAdmin": "0x34B40BA116d5Dec75548a9e9A8f15411461E8c70", + "storageGasOracle": "0x457cCf29090fe5A24c19c1bc95F492168C0EaFdb", + "testRecipient": "0xd6e1afe5cA8D00A2EFC01B89997abE2De47fdfAf", + "validatorAnnounce": "0xF32D39ff9f6Aa7a7A64d7a4F00a54826Ef791a55", + "index": { + "from": 111 + } + }, + "arcadiatestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.khalani.network/api", + "family": "blockscout", + "name": "Arcadia Testnet Explorer", + "url": "https://explorer.khalani.network" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 1098411886, + "displayName": "Arcadia Testnet", + "domainId": 1098411886, + "isTestnet": true, + "name": "arcadiatestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.khalani.network" + } + ], + "aggregationHook": "0x862Ce2De59C13a0406c104d317CfaEf6B672D638", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "interchainAccountIsm": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", + "interchainAccountRouter": "0xBF2C366530C1269d531707154948494D3fF4AcA7", + "interchainGasPaymaster": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "interchainSecurityModule": "0xd89063A7e8Eaee25dA8D3b7eBcbAeF9869702A80", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "pausableHook": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "pausableIsm": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "protocolFee": "0x01812D60958798695391dacF092BAc4a715B1718", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x206789B0d838568eaFDcCa1e551FCF5c00bF99E2", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "testRecipient": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "index": { + "from": 5243565 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "sonictestnet": { + "blockExplorers": [ + { + "apiUrl": "https://testnet.soniclabs.com/api", + "family": "routescan", + "name": "Sonic Testnet Explorer", + "url": "https://testnet.soniclabs.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 64165, + "displayName": "Sonic Testnet", + "domainId": 64165, + "isTestnet": true, + "name": "sonictestnet", + "nativeToken": { + "decimals": 18, + "name": "Sonic", + "symbol": "S" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.testnet.soniclabs.com" + } + ], + "aggregationHook": "0x04B8A7B7BF29b269428c4976D6408BAf6fd42922", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "interchainAccountIsm": "0xc08675806BA844467E559E45E4bB59e66778bDcd", + "interchainAccountRouter": "0x39c85C84876479694A2470c0E8075e9d68049aFc", + "interchainGasPaymaster": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "interchainSecurityModule": "0x8a5D09753Ab5571fa78131EF839C70AFa3c45bFd", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "pausableHook": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "pausableIsm": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "protocolFee": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0xf0F828278DEfB0c703EE78E620D20BA72CD56D82", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "testRecipient": "0x01812D60958798695391dacF092BAc4a715B1718", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "index": { + "from": 78198240 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "unichaintestnet": { + "blockExplorers": [ + { + "apiUrl": "https://unichain-sepolia.blockscout.com/api", + "family": "blockscout", + "name": "Unichain Sepolia Testnet Explorer", + "url": "https://unichain-sepolia.blockscout.com" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 1301, + "displayName": "Unichain Testnet", + "domainId": 1301, + "isTestnet": true, + "name": "unichaintestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://sepolia.unichain.org" + } + ], + "aggregationHook": "0x04B8A7B7BF29b269428c4976D6408BAf6fd42922", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "interchainAccountIsm": "0x3ca332A585FDB9d4FF51f2FA8999eA32184D3606", + "interchainAccountRouter": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", + "interchainGasPaymaster": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "interchainSecurityModule": "0x4B2e8f63E345Db18973E46cE70972cE3D76585Bf", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "pausableHook": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "pausableIsm": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "protocolFee": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0xf0F828278DEfB0c703EE78E620D20BA72CD56D82", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "testRecipient": "0x01812D60958798695391dacF092BAc4a715B1718", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "index": { + "from": 1721192 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + }, + "odysseytestnet": { + "blockExplorers": [ + { + "apiUrl": "https://odyssey-explorer.ithaca.xyz/api", + "family": "blockscout", + "name": "Odyssey Explorer", + "url": "https://odyssey-explorer.ithaca.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 1 + }, + "chainId": 911867, + "displayName": "Odyssey Testnet", + "domainId": 911867, + "isTestnet": true, + "name": "odysseytestnet", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://odyssey.ithaca.xyz" + } + ], + "aggregationHook": "0xf96cF73BB4e57F90479cD8f74bb4C1f6a0c3da50", + "domainRoutingIsm": "0x2a2F4AAaf726abb4B969c2804D38e188555683b5", + "domainRoutingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "fallbackRoutingHook": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "interchainAccountIsm": "0xBF2C366530C1269d531707154948494D3fF4AcA7", + "interchainAccountRouter": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", + "interchainGasPaymaster": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "interchainSecurityModule": "0x9e71cC1A91E48CfFA2F7D2956eB5c3b730bD8605", + "mailbox": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "merkleTreeHook": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "pausableHook": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "pausableIsm": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "protocolFee": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", + "proxyAdmin": "0x54148470292C24345fb828B003461a9444414517", + "staticAggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "staticAggregationIsm": "0x9e71cC1A91E48CfFA2F7D2956eB5c3b730bD8605", + "staticAggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "staticMerkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "staticMerkleRootWeightedMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "staticMessageIdWeightedMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", + "testRecipient": "0x5e65279Fb7293a058776e37587398fcc3E9184b1", + "timelockController": "0x0000000000000000000000000000000000000000", + "validatorAnnounce": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", + "index": { + "from": 67925 + }, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + } + } + }, + "defaultRpcConsensusType": "fallback" +} diff --git a/rust/ethers-prometheus/.gitignore b/rust/main/ethers-prometheus/.gitignore similarity index 100% rename from rust/ethers-prometheus/.gitignore rename to rust/main/ethers-prometheus/.gitignore diff --git a/rust/ethers-prometheus/Cargo.toml b/rust/main/ethers-prometheus/Cargo.toml similarity index 95% rename from rust/ethers-prometheus/Cargo.toml rename to rust/main/ethers-prometheus/Cargo.toml index 2f4344fa5..3d2e74d6d 100644 --- a/rust/ethers-prometheus/Cargo.toml +++ b/rust/main/ethers-prometheus/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "ethers-prometheus" documentation.workspace = true diff --git a/rust/ethers-prometheus/abis/Erc20.abi.json b/rust/main/ethers-prometheus/abis/Erc20.abi.json similarity index 100% rename from rust/ethers-prometheus/abis/Erc20.abi.json rename to rust/main/ethers-prometheus/abis/Erc20.abi.json diff --git a/rust/ethers-prometheus/build.rs b/rust/main/ethers-prometheus/build.rs similarity index 100% rename from rust/ethers-prometheus/build.rs rename to rust/main/ethers-prometheus/build.rs diff --git a/rust/ethers-prometheus/src/json_rpc_client.rs b/rust/main/ethers-prometheus/src/json_rpc_client.rs similarity index 100% rename from rust/ethers-prometheus/src/json_rpc_client.rs rename to rust/main/ethers-prometheus/src/json_rpc_client.rs diff --git a/rust/ethers-prometheus/src/lib.rs b/rust/main/ethers-prometheus/src/lib.rs similarity index 100% rename from rust/ethers-prometheus/src/lib.rs rename to rust/main/ethers-prometheus/src/lib.rs diff --git a/rust/ethers-prometheus/src/middleware/error.rs b/rust/main/ethers-prometheus/src/middleware/error.rs similarity index 100% rename from rust/ethers-prometheus/src/middleware/error.rs rename to rust/main/ethers-prometheus/src/middleware/error.rs diff --git a/rust/ethers-prometheus/src/middleware/mod.rs b/rust/main/ethers-prometheus/src/middleware/mod.rs similarity index 99% rename from rust/ethers-prometheus/src/middleware/mod.rs rename to rust/main/ethers-prometheus/src/middleware/mod.rs index 592e6a200..cb3fd16cf 100644 --- a/rust/ethers-prometheus/src/middleware/mod.rs +++ b/rust/main/ethers-prometheus/src/middleware/mod.rs @@ -335,10 +335,9 @@ impl Middleware for PrometheusMiddleware { .inc_by((Instant::now() - start).as_secs_f64()); } } - Ok(result?) } - + #[allow(clippy::redundant_closure)] // TODO: `rustc` 1.80.1 clippy issue async fn get_logs(&self, filter: &Filter) -> Result, Self::Error> { let start = Instant::now(); let result = self.inner.get_logs(filter).await; diff --git a/rust/helm/agent-common/Chart.yaml b/rust/main/helm/agent-common/Chart.yaml similarity index 100% rename from rust/helm/agent-common/Chart.yaml rename to rust/main/helm/agent-common/Chart.yaml diff --git a/rust/helm/agent-common/templates/_helpers.tpl b/rust/main/helm/agent-common/templates/_helpers.tpl similarity index 100% rename from rust/helm/agent-common/templates/_helpers.tpl rename to rust/main/helm/agent-common/templates/_helpers.tpl diff --git a/rust/helm/hyperlane-agent/.helmignore b/rust/main/helm/hyperlane-agent/.helmignore similarity index 100% rename from rust/helm/hyperlane-agent/.helmignore rename to rust/main/helm/hyperlane-agent/.helmignore diff --git a/rust/helm/hyperlane-agent/Chart.lock b/rust/main/helm/hyperlane-agent/Chart.lock similarity index 100% rename from rust/helm/hyperlane-agent/Chart.lock rename to rust/main/helm/hyperlane-agent/Chart.lock diff --git a/rust/helm/hyperlane-agent/Chart.yaml b/rust/main/helm/hyperlane-agent/Chart.yaml similarity index 100% rename from rust/helm/hyperlane-agent/Chart.yaml rename to rust/main/helm/hyperlane-agent/Chart.yaml diff --git a/rust/helm/hyperlane-agent/templates/NOTES.txt b/rust/main/helm/hyperlane-agent/templates/NOTES.txt similarity index 100% rename from rust/helm/hyperlane-agent/templates/NOTES.txt rename to rust/main/helm/hyperlane-agent/templates/NOTES.txt diff --git a/rust/helm/hyperlane-agent/templates/_helpers.tpl b/rust/main/helm/hyperlane-agent/templates/_helpers.tpl similarity index 100% rename from rust/helm/hyperlane-agent/templates/_helpers.tpl rename to rust/main/helm/hyperlane-agent/templates/_helpers.tpl diff --git a/rust/helm/hyperlane-agent/templates/configmap.yaml b/rust/main/helm/hyperlane-agent/templates/configmap.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/configmap.yaml rename to rust/main/helm/hyperlane-agent/templates/configmap.yaml diff --git a/rust/helm/hyperlane-agent/templates/external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/external-secret.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/external-secret.yaml rename to rust/main/helm/hyperlane-agent/templates/external-secret.yaml diff --git a/rust/helm/hyperlane-agent/templates/relayer-external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-external-secret.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/relayer-external-secret.yaml rename to rust/main/helm/hyperlane-agent/templates/relayer-external-secret.yaml diff --git a/rust/helm/hyperlane-agent/templates/relayer-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/relayer-statefulset.yaml rename to rust/main/helm/hyperlane-agent/templates/relayer-statefulset.yaml diff --git a/rust/helm/hyperlane-agent/templates/scraper-external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/scraper-external-secret.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/scraper-external-secret.yaml rename to rust/main/helm/hyperlane-agent/templates/scraper-external-secret.yaml diff --git a/rust/helm/hyperlane-agent/templates/scraper-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/scraper-statefulset.yaml rename to rust/main/helm/hyperlane-agent/templates/scraper-statefulset.yaml diff --git a/rust/helm/hyperlane-agent/templates/serviceaccount.yaml b/rust/main/helm/hyperlane-agent/templates/serviceaccount.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/serviceaccount.yaml rename to rust/main/helm/hyperlane-agent/templates/serviceaccount.yaml diff --git a/rust/helm/hyperlane-agent/templates/validator-configmap.yaml b/rust/main/helm/hyperlane-agent/templates/validator-configmap.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/validator-configmap.yaml rename to rust/main/helm/hyperlane-agent/templates/validator-configmap.yaml diff --git a/rust/helm/hyperlane-agent/templates/validator-external-secret.yaml b/rust/main/helm/hyperlane-agent/templates/validator-external-secret.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/validator-external-secret.yaml rename to rust/main/helm/hyperlane-agent/templates/validator-external-secret.yaml diff --git a/rust/helm/hyperlane-agent/templates/validator-statefulset.yaml b/rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml similarity index 100% rename from rust/helm/hyperlane-agent/templates/validator-statefulset.yaml rename to rust/main/helm/hyperlane-agent/templates/validator-statefulset.yaml diff --git a/rust/helm/hyperlane-agent/values.yaml b/rust/main/helm/hyperlane-agent/values.yaml similarity index 100% rename from rust/helm/hyperlane-agent/values.yaml rename to rust/main/helm/hyperlane-agent/values.yaml diff --git a/rust/hyperlane-base/Cargo.toml b/rust/main/hyperlane-base/Cargo.toml similarity index 95% rename from rust/hyperlane-base/Cargo.toml rename to rust/main/hyperlane-base/Cargo.toml index 471815f69..5bfe50c6e 100644 --- a/rust/hyperlane-base/Cargo.toml +++ b/rust/main/hyperlane-base/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "hyperlane-base" documentation.workspace = true @@ -54,7 +52,7 @@ hyperlane-core = { path = "../hyperlane-core", features = ["agent", "float"] } hyperlane-ethereum = { path = "../chains/hyperlane-ethereum" } hyperlane-fuel = { path = "../chains/hyperlane-fuel" } hyperlane-sealevel = { path = "../chains/hyperlane-sealevel" } -hyperlane-cosmos = { path = "../chains/hyperlane-cosmos"} +hyperlane-cosmos = { path = "../chains/hyperlane-cosmos" } hyperlane-test = { path = "../hyperlane-test" } # dependency version is determined by etheres diff --git a/rust/hyperlane-base/build.rs b/rust/main/hyperlane-base/build.rs similarity index 100% rename from rust/hyperlane-base/build.rs rename to rust/main/hyperlane-base/build.rs diff --git a/rust/hyperlane-base/src/agent.rs b/rust/main/hyperlane-base/src/agent.rs similarity index 98% rename from rust/hyperlane-base/src/agent.rs rename to rust/main/hyperlane-base/src/agent.rs index 8bd696ccf..0ca9f4554 100644 --- a/rust/hyperlane-base/src/agent.rs +++ b/rust/main/hyperlane-base/src/agent.rs @@ -60,6 +60,7 @@ pub trait BaseAgent: Send + Sync + Debug { /// Call this from `main` to fully initialize and run the agent for its entire /// lifecycle. This assumes only a single agent is being run. This will /// initialize the metrics server and tracing as well. +#[allow(unexpected_cfgs)] // TODO: `rustc` 1.80.1 clippy issue pub async fn agent_main() -> Result<()> { if env::var("ONELINE_BACKTRACES") .map(|v| v.to_lowercase()) diff --git a/rust/hyperlane-base/src/contract_sync/broadcast.rs b/rust/main/hyperlane-base/src/contract_sync/broadcast.rs similarity index 100% rename from rust/hyperlane-base/src/contract_sync/broadcast.rs rename to rust/main/hyperlane-base/src/contract_sync/broadcast.rs diff --git a/rust/hyperlane-base/src/contract_sync/cursors/mod.rs b/rust/main/hyperlane-base/src/contract_sync/cursors/mod.rs similarity index 100% rename from rust/hyperlane-base/src/contract_sync/cursors/mod.rs rename to rust/main/hyperlane-base/src/contract_sync/cursors/mod.rs diff --git a/rust/hyperlane-base/src/contract_sync/cursors/rate_limited.rs b/rust/main/hyperlane-base/src/contract_sync/cursors/rate_limited.rs similarity index 100% rename from rust/hyperlane-base/src/contract_sync/cursors/rate_limited.rs rename to rust/main/hyperlane-base/src/contract_sync/cursors/rate_limited.rs diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs b/rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs similarity index 99% rename from rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs rename to rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs index 6a0f66a78..75c086c24 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs +++ b/rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs @@ -358,6 +358,7 @@ impl ContractSyncCursor /// The logs to ingest. If any logs are duplicated or their sequence is higher than the current indexing snapshot, /// they are filtered out. #[instrument(err, ret, skip(logs), fields(range=?range, logs=?logs.iter().map(|(log, _)| log.sequence).collect::>()))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn update( &mut self, logs: Vec<(Indexed, LogMeta)>, diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs b/rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs similarity index 99% rename from rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs rename to rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs index ae6629e95..4fcbbff8d 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs +++ b/rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs @@ -438,6 +438,7 @@ impl ContractSyncCursor /// This means that while gaps result in a rewind here, already known logs may be "fast forwarded" through, /// and the cursor won't actually end up re-indexing already known logs. #[instrument(err, ret, skip(logs), fields(range=?range, logs=?logs.iter().map(|(log, _)| log.sequence).collect::>()))] + #[allow(clippy::blocks_in_conditions)] // TODO: `rustc` 1.80.1 clippy issue async fn update( &mut self, logs: Vec<(Indexed, LogMeta)>, diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs b/rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs similarity index 100% rename from rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs rename to rust/main/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs diff --git a/rust/hyperlane-base/src/contract_sync/eta_calculator.rs b/rust/main/hyperlane-base/src/contract_sync/eta_calculator.rs similarity index 100% rename from rust/hyperlane-base/src/contract_sync/eta_calculator.rs rename to rust/main/hyperlane-base/src/contract_sync/eta_calculator.rs diff --git a/rust/hyperlane-base/src/contract_sync/metrics.rs b/rust/main/hyperlane-base/src/contract_sync/metrics.rs similarity index 100% rename from rust/hyperlane-base/src/contract_sync/metrics.rs rename to rust/main/hyperlane-base/src/contract_sync/metrics.rs diff --git a/rust/hyperlane-base/src/contract_sync/mod.rs b/rust/main/hyperlane-base/src/contract_sync/mod.rs similarity index 94% rename from rust/hyperlane-base/src/contract_sync/mod.rs rename to rust/main/hyperlane-base/src/contract_sync/mod.rs index 50d0eac0f..8e6f8278f 100644 --- a/rust/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/main/hyperlane-base/src/contract_sync/mod.rs @@ -31,6 +31,14 @@ use cursors::ForwardBackwardSequenceAwareSyncCursor; const SLEEP_DURATION: Duration = Duration::from_secs(5); +#[derive(Debug, derive_new::new)] +#[allow(dead_code)] +/// Utility struct for pretty-printing indexed items. +struct IndexedTxIdAndSequence { + tx_id: H512, + sequence: Option, +} + /// Entity that drives the syncing of an agent's db with on-chain data. /// Extracts chain-specific data (emitted checkpoints, messages, etc) from an /// `indexer` and fills the agent's db with this data. @@ -162,7 +170,7 @@ where Ok(logs) => logs, Err(err) => { warn!(?err, ?range, "Error fetching logs in range"); - break SLEEP_DURATION; + break Some(SLEEP_DURATION); } }; @@ -172,7 +180,7 @@ where ?range, num_logs = logs_found, estimated_time_to_sync = fmt_sync_time(eta), - sequences = ?logs.iter().map(|(log, _)| log.sequence).collect::>(), + sequences = ?logs.iter().map(|(log, meta)| IndexedTxIdAndSequence::new(meta.transaction_id, log.sequence)).collect::>(), cursor = ?cursor, "Found log(s) in index range" ); @@ -188,13 +196,20 @@ where // Update cursor if let Err(err) = cursor.update(logs, range).await { warn!(?err, "Error updating cursor"); - break SLEEP_DURATION; + break Some(SLEEP_DURATION); }; - break Default::default(); + break None; }, - CursorAction::Sleep(duration) => duration, + CursorAction::Sleep(duration) => Some(duration), }; - sleep(sleep_duration).await + if let Some(sleep_duration) = sleep_duration { + debug!( + cursor = ?cursor, + ?sleep_duration, + "Cursor can't make progress, sleeping", + ); + sleep(sleep_duration).await + } } async fn dedupe_and_store_logs( diff --git a/rust/main/hyperlane-base/src/db/error.rs b/rust/main/hyperlane-base/src/db/error.rs new file mode 100644 index 000000000..73b5e35bf --- /dev/null +++ b/rust/main/hyperlane-base/src/db/error.rs @@ -0,0 +1,34 @@ +use std::{io, path::PathBuf}; + +use hyperlane_core::{ChainCommunicationError, HyperlaneProtocolError}; + +/// DB Error type +#[derive(thiserror::Error, Debug)] +pub enum DbError { + /// Rocks DB Error + #[error("{0}")] + RockError(#[from] rocksdb::Error), + #[error("Failed to open {path}, canonicalized as {canonicalized}: {source}")] + /// Error opening the database + OpeningError { + /// Rocksdb error during opening + #[source] + source: rocksdb::Error, + /// Raw database path provided + path: PathBuf, + /// Parsed path used + canonicalized: PathBuf, + }, + /// Could not parse the provided database path string + #[error("Invalid database path supplied {1:?}; {0}")] + InvalidDbPath(#[source] io::Error, String), + /// Hyperlane Error + #[error("{0}")] + HyperlaneError(#[from] HyperlaneProtocolError), +} + +impl From for ChainCommunicationError { + fn from(value: DbError) -> Self { + ChainCommunicationError::from_other(value) + } +} diff --git a/rust/main/hyperlane-base/src/db/mod.rs b/rust/main/hyperlane-base/src/db/mod.rs new file mode 100644 index 000000000..04a3e59cc --- /dev/null +++ b/rust/main/hyperlane-base/src/db/mod.rs @@ -0,0 +1,162 @@ +pub use error::*; +use hyperlane_core::{ + GasPaymentKey, HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, + InterchainGasPaymentMeta, MerkleTreeInsertion, PendingOperationStatus, H256, +}; +pub use rocks::*; + +pub use self::storage_types::{InterchainGasExpenditureData, InterchainGasPaymentData}; + +mod error; +mod rocks; +pub(crate) mod storage_types; + +#[allow(missing_docs)] +/// Hyperlane database interface +pub trait HyperlaneDb: Send + Sync { + /// Retrieve the nonce of the highest processed message we're aware of + fn retrieve_highest_seen_message_nonce(&self) -> DbResult>; + + /// Retrieve a message by its nonce + fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult>; + + /// Retrieve whether a message has been processed + fn retrieve_processed_by_nonce(&self, nonce: &u32) -> DbResult>; + + /// Get the origin domain of the database + fn domain(&self) -> &HyperlaneDomain; + + fn store_message_id_by_nonce(&self, nonce: &u32, id: &H256) -> DbResult<()>; + + fn retrieve_message_id_by_nonce(&self, nonce: &u32) -> DbResult>; + + fn store_message_by_id(&self, id: &H256, message: &HyperlaneMessage) -> DbResult<()>; + + fn retrieve_message_by_id(&self, id: &H256) -> DbResult>; + + fn store_dispatched_block_number_by_nonce( + &self, + nonce: &u32, + block_number: &u64, + ) -> DbResult<()>; + + fn retrieve_dispatched_block_number_by_nonce(&self, nonce: &u32) -> DbResult>; + + /// Store whether a message was processed by its nonce + fn store_processed_by_nonce(&self, nonce: &u32, processed: &bool) -> DbResult<()>; + + fn store_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + processed: &bool, + ) -> DbResult<()>; + + fn retrieve_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + ) -> DbResult>; + + fn store_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + data: &InterchainGasExpenditureData, + ) -> DbResult<()>; + + fn retrieve_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + + /// Store the status of an operation by its message id + fn store_status_by_message_id( + &self, + message_id: &H256, + status: &PendingOperationStatus, + ) -> DbResult<()>; + + /// Retrieve the status of an operation by its message id + fn retrieve_status_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + + fn store_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + data: &InterchainGasPaymentData, + ) -> DbResult<()>; + + fn retrieve_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + ) -> DbResult>; + + fn store_gas_payment_by_sequence( + &self, + sequence: &u32, + payment: &InterchainGasPayment, + ) -> DbResult<()>; + + fn retrieve_gas_payment_by_sequence( + &self, + sequence: &u32, + ) -> DbResult>; + + fn store_gas_payment_block_by_sequence( + &self, + sequence: &u32, + block_number: &u64, + ) -> DbResult<()>; + + fn retrieve_gas_payment_block_by_sequence(&self, sequence: &u32) -> DbResult>; + + /// Store the retry count for a pending message by its message id + fn store_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + count: &u32, + ) -> DbResult<()>; + + /// Retrieve the retry count for a pending message by its message id + fn retrieve_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + ) -> DbResult>; + + fn store_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + insertion: &MerkleTreeInsertion, + ) -> DbResult<()>; + + /// Retrieve the merkle tree insertion event by its leaf index + fn retrieve_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult>; + + fn store_merkle_leaf_index_by_message_id( + &self, + message_id: &H256, + leaf_index: &u32, + ) -> DbResult<()>; + + /// Retrieve the merkle leaf index of a message in the merkle tree + fn retrieve_merkle_leaf_index_by_message_id(&self, message_id: &H256) -> DbResult>; + + fn store_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + block_number: &u64, + ) -> DbResult<()>; + + fn retrieve_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult>; + + fn store_highest_seen_message_nonce_number(&self, nonce: &u32) -> DbResult<()>; + + /// Retrieve the nonce of the highest processed message we're aware of + fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult>; +} diff --git a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs similarity index 67% rename from rust/hyperlane-base/src/db/rocks/hyperlane_db.rs rename to rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs index 109f833b9..76a51bfe3 100644 --- a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/main/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -1,18 +1,18 @@ use async_trait::async_trait; use eyre::{bail, Result}; -use paste::paste; use tracing::{debug, instrument, trace}; use hyperlane_core::{ - GasPaymentKey, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, + Decode, Encode, GasPaymentKey, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, HyperlaneSequenceAwareIndexerStoreReader, HyperlaneWatermarkedLogStore, Indexed, InterchainGasExpenditure, InterchainGasPayment, InterchainGasPaymentMeta, LogMeta, MerkleTreeInsertion, PendingOperationStatus, H256, }; -use super::{ +use super::{DbError, TypedDB, DB}; +use crate::db::{ storage_types::{InterchainGasExpenditureData, InterchainGasPaymentData}, - DbError, TypedDB, DB, + HyperlaneDb, }; // these keys MUST not be given multiple uses in case multiple agents are @@ -23,6 +23,7 @@ const MESSAGE_DISPATCHED_BLOCK_NUMBER: &str = "message_dispatched_block_number_" const MESSAGE: &str = "message_"; const NONCE_PROCESSED: &str = "nonce_processed_"; const GAS_PAYMENT_BY_SEQUENCE: &str = "gas_payment_by_sequence_"; +const GAS_PAYMENT_BLOCK_BY_SEQUENCE: &str = "gas_payment_block_by_sequence_"; const HIGHEST_SEEN_MESSAGE_NONCE: &str = "highest_seen_message_nonce_"; const GAS_PAYMENT_FOR_MESSAGE_ID: &str = "gas_payment_sequence_for_message_id_v2_"; const GAS_PAYMENT_META_PROCESSED: &str = "gas_payment_meta_processed_v3_"; @@ -86,12 +87,12 @@ impl HyperlaneRocksDB { dispatched_block_number: u64, ) -> DbResult { if let Ok(Some(_)) = self.retrieve_message_id_by_nonce(&message.nonce) { - trace!(msg=?message, "Message already stored in db"); + trace!(hyp_message=?message, "Message already stored in db"); return Ok(false); } let id = message.id(); - debug!(msg=?message, "Storing new message in db",); + debug!(hyp_message=?message, "Storing new message in db",); // - `id` --> `message` self.store_message_by_id(&id, message)?; @@ -119,16 +120,11 @@ impl HyperlaneRocksDB { .retrieve_highest_seen_message_nonce()? .unwrap_or_default(); if nonce >= current_max { - self.store_highest_seen_message_nonce_number(&Default::default(), &nonce)?; + self.store_highest_seen_message_nonce_number(&nonce)?; } Ok(()) } - /// Retrieve the nonce of the highest processed message we're aware of - pub fn retrieve_highest_seen_message_nonce(&self) -> DbResult> { - self.retrieve_highest_seen_message_nonce_number(&Default::default()) - } - /// If the provided gas payment, identified by its metadata, has not been /// processed, processes the gas payment and records it as processed. /// Returns whether the gas payment was processed for the first time. @@ -442,109 +438,241 @@ impl HyperlaneWatermarkedLogStore for HyperlaneRocksDB { } } -/// Database interface required for processing messages -pub trait ProcessMessage: Send + Sync { - /// Retrieve the nonce of the highest processed message we're aware of - fn retrieve_highest_seen_message_nonce(&self) -> DbResult>; +impl HyperlaneDb for HyperlaneRocksDB { + fn retrieve_highest_seen_message_nonce(&self) -> DbResult> { + self.retrieve_highest_seen_message_nonce_number() + } - /// Retrieve a message by its nonce - fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult>; + fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult> { + self.retrieve_message_by_nonce(nonce) + } - /// Retrieve whether a message has been processed - fn retrieve_processed_by_nonce(&self, nonce: u32) -> DbResult>; + fn domain(&self) -> &HyperlaneDomain { + self.domain() + } - /// Get the origin domain of the database - fn domain(&self) -> &HyperlaneDomain; -} + fn store_message_id_by_nonce(&self, nonce: &u32, id: &H256) -> DbResult<()> { + self.store_value_by_key(MESSAGE_ID, nonce, id) + } -impl ProcessMessage for HyperlaneRocksDB { - fn retrieve_highest_seen_message_nonce(&self) -> DbResult> { - self.retrieve_highest_seen_message_nonce() + fn retrieve_message_id_by_nonce(&self, nonce: &u32) -> DbResult> { + self.retrieve_value_by_key(MESSAGE_ID, nonce) } - fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult> { - self.retrieve_message_by_nonce(nonce) + fn store_message_by_id(&self, id: &H256, message: &HyperlaneMessage) -> DbResult<()> { + self.store_value_by_key(MESSAGE, id, message) } - fn retrieve_processed_by_nonce(&self, nonce: u32) -> DbResult> { - self.retrieve_processed_by_nonce(&nonce) + fn retrieve_message_by_id(&self, id: &H256) -> DbResult> { + self.retrieve_value_by_key(MESSAGE, id) } - fn domain(&self) -> &HyperlaneDomain { - self.domain() + fn store_dispatched_block_number_by_nonce( + &self, + nonce: &u32, + block_number: &u64, + ) -> DbResult<()> { + self.store_value_by_key(MESSAGE_DISPATCHED_BLOCK_NUMBER, nonce, block_number) } -} -/// Generate a call to ChainSetup for the given builder -macro_rules! make_store_and_retrieve { - ($vis:vis, $name_suffix:ident, $key_prefix: ident, $key_ty:ty, $val_ty:ty$(,)?) => { - impl HyperlaneRocksDB { - paste! { - /// Stores a key value pair in the DB - $vis fn [] ( - &self, - key: &$key_ty, - val: &$val_ty, - ) -> DbResult<()> { - self.store_keyed_encodable($key_prefix, key, val) - } - - /// Retrieves a key value pair from the DB - $vis fn [] ( - &self, - key: &$key_ty, - ) -> DbResult> { - self.retrieve_keyed_decodable($key_prefix, key) - } - } - } - }; + fn retrieve_dispatched_block_number_by_nonce(&self, nonce: &u32) -> DbResult> { + self.retrieve_value_by_key(MESSAGE_DISPATCHED_BLOCK_NUMBER, nonce) + } + + /// Store whether a message was processed by its nonce + fn store_processed_by_nonce(&self, nonce: &u32, processed: &bool) -> DbResult<()> { + self.store_value_by_key(NONCE_PROCESSED, nonce, processed) + } + + fn retrieve_processed_by_nonce(&self, nonce: &u32) -> DbResult> { + self.retrieve_value_by_key(NONCE_PROCESSED, nonce) + } + + fn store_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + processed: &bool, + ) -> DbResult<()> { + self.store_value_by_key(GAS_PAYMENT_META_PROCESSED, meta, processed) + } + + fn retrieve_processed_by_gas_payment_meta( + &self, + meta: &InterchainGasPaymentMeta, + ) -> DbResult> { + self.retrieve_value_by_key(GAS_PAYMENT_META_PROCESSED, meta) + } + + fn store_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + data: &InterchainGasExpenditureData, + ) -> DbResult<()> { + self.store_value_by_key(GAS_EXPENDITURE_FOR_MESSAGE_ID, message_id, data) + } + + fn retrieve_interchain_gas_expenditure_data_by_message_id( + &self, + message_id: &H256, + ) -> DbResult> { + self.retrieve_value_by_key(GAS_EXPENDITURE_FOR_MESSAGE_ID, message_id) + } + + /// Store the status of an operation by its message id + fn store_status_by_message_id( + &self, + message_id: &H256, + status: &PendingOperationStatus, + ) -> DbResult<()> { + self.store_value_by_key(STATUS_BY_MESSAGE_ID, message_id, status) + } + + /// Retrieve the status of an operation by its message id + fn retrieve_status_by_message_id( + &self, + message_id: &H256, + ) -> DbResult> { + self.retrieve_value_by_key(STATUS_BY_MESSAGE_ID, message_id) + } + + fn store_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + data: &InterchainGasPaymentData, + ) -> DbResult<()> { + self.store_value_by_key(GAS_PAYMENT_FOR_MESSAGE_ID, key, data) + } + + fn retrieve_interchain_gas_payment_data_by_gas_payment_key( + &self, + key: &GasPaymentKey, + ) -> DbResult> { + self.retrieve_value_by_key(GAS_PAYMENT_FOR_MESSAGE_ID, key) + } + + fn store_gas_payment_by_sequence( + &self, + sequence: &u32, + payment: &InterchainGasPayment, + ) -> DbResult<()> { + self.store_value_by_key(GAS_PAYMENT_BY_SEQUENCE, sequence, payment) + } + + fn retrieve_gas_payment_by_sequence( + &self, + sequence: &u32, + ) -> DbResult> { + self.retrieve_value_by_key(GAS_PAYMENT_BY_SEQUENCE, sequence) + } + + fn store_gas_payment_block_by_sequence( + &self, + sequence: &u32, + block_number: &u64, + ) -> DbResult<()> { + self.store_value_by_key(GAS_PAYMENT_BLOCK_BY_SEQUENCE, sequence, block_number) + } + + fn retrieve_gas_payment_block_by_sequence(&self, sequence: &u32) -> DbResult> { + self.retrieve_value_by_key(GAS_PAYMENT_BLOCK_BY_SEQUENCE, sequence) + } + + /// Store the retry count for a pending message by its message id + fn store_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + count: &u32, + ) -> DbResult<()> { + self.store_value_by_key( + PENDING_MESSAGE_RETRY_COUNT_FOR_MESSAGE_ID, + message_id, + count, + ) + } + + /// Retrieve the retry count for a pending message by its message id + fn retrieve_pending_message_retry_count_by_message_id( + &self, + message_id: &H256, + ) -> DbResult> { + self.retrieve_value_by_key(PENDING_MESSAGE_RETRY_COUNT_FOR_MESSAGE_ID, message_id) + } + + fn store_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + insertion: &MerkleTreeInsertion, + ) -> DbResult<()> { + self.store_value_by_key(MERKLE_TREE_INSERTION, leaf_index, insertion) + } + + /// Retrieve the merkle tree insertion event by its leaf index + fn retrieve_merkle_tree_insertion_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult> { + self.retrieve_value_by_key(MERKLE_TREE_INSERTION, leaf_index) + } + + fn store_merkle_leaf_index_by_message_id( + &self, + message_id: &H256, + leaf_index: &u32, + ) -> DbResult<()> { + self.store_value_by_key(MERKLE_LEAF_INDEX_BY_MESSAGE_ID, message_id, leaf_index) + } + + /// Retrieve the merkle leaf index of a message in the merkle tree + fn retrieve_merkle_leaf_index_by_message_id(&self, message_id: &H256) -> DbResult> { + self.retrieve_value_by_key(MERKLE_LEAF_INDEX_BY_MESSAGE_ID, message_id) + } + + fn store_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + block_number: &u64, + ) -> DbResult<()> { + self.store_value_by_key( + MERKLE_TREE_INSERTION_BLOCK_NUMBER_BY_LEAF_INDEX, + leaf_index, + block_number, + ) + } + + fn retrieve_merkle_tree_insertion_block_number_by_leaf_index( + &self, + leaf_index: &u32, + ) -> DbResult> { + self.retrieve_value_by_key(MERKLE_TREE_INSERTION_BLOCK_NUMBER_BY_LEAF_INDEX, leaf_index) + } + + fn store_highest_seen_message_nonce_number(&self, nonce: &u32) -> DbResult<()> { + // There's no unit struct Encode/Decode impl, so just use `bool` and always use the `Default::default()` key + self.store_value_by_key(HIGHEST_SEEN_MESSAGE_NONCE, &bool::default(), nonce) + } + + /// Retrieve the nonce of the highest processed message we're aware of + fn retrieve_highest_seen_message_nonce_number(&self) -> DbResult> { + // There's no unit struct Encode/Decode impl, so just use `bool` and always use the `Default::default()` key + self.retrieve_value_by_key(HIGHEST_SEEN_MESSAGE_NONCE, &bool::default()) + } } -make_store_and_retrieve!(pub, message_id_by_nonce, MESSAGE_ID, u32, H256); -make_store_and_retrieve!(pub(self), message_by_id, MESSAGE, H256, HyperlaneMessage); -make_store_and_retrieve!(pub(self), dispatched_block_number_by_nonce, MESSAGE_DISPATCHED_BLOCK_NUMBER, u32, u64); -make_store_and_retrieve!(pub, processed_by_nonce, NONCE_PROCESSED, u32, bool); -make_store_and_retrieve!(pub(self), processed_by_gas_payment_meta, GAS_PAYMENT_META_PROCESSED, InterchainGasPaymentMeta, bool); -make_store_and_retrieve!(pub(self), interchain_gas_expenditure_data_by_message_id, GAS_EXPENDITURE_FOR_MESSAGE_ID, H256, InterchainGasExpenditureData); -make_store_and_retrieve!( - pub, - status_by_message_id, - STATUS_BY_MESSAGE_ID, - H256, - PendingOperationStatus -); -make_store_and_retrieve!(pub(self), interchain_gas_payment_data_by_gas_payment_key, GAS_PAYMENT_FOR_MESSAGE_ID, GasPaymentKey, InterchainGasPaymentData); -make_store_and_retrieve!(pub(self), gas_payment_by_sequence, GAS_PAYMENT_BY_SEQUENCE, u32, InterchainGasPayment); -make_store_and_retrieve!(pub(self), gas_payment_block_by_sequence, GAS_PAYMENT_BY_SEQUENCE, u32, u64); -make_store_and_retrieve!( - pub, - pending_message_retry_count_by_message_id, - PENDING_MESSAGE_RETRY_COUNT_FOR_MESSAGE_ID, - H256, - u32 -); -make_store_and_retrieve!( - pub, - merkle_tree_insertion_by_leaf_index, - MERKLE_TREE_INSERTION, - u32, - MerkleTreeInsertion -); -make_store_and_retrieve!( - pub, - merkle_leaf_index_by_message_id, - MERKLE_LEAF_INDEX_BY_MESSAGE_ID, - H256, - u32 -); -make_store_and_retrieve!( - pub, - merkle_tree_insertion_block_number_by_leaf_index, - MERKLE_TREE_INSERTION_BLOCK_NUMBER_BY_LEAF_INDEX, - u32, - u64 -); -// There's no unit struct Encode/Decode impl, so just use `bool`, have visibility be private (by omitting the first argument), and wrap -// with a function that always uses the `Default::default()` key -make_store_and_retrieve!(, highest_seen_message_nonce_number, HIGHEST_SEEN_MESSAGE_NONCE, bool, u32); +impl HyperlaneRocksDB { + fn store_value_by_key( + &self, + prefix: impl AsRef<[u8]>, + key: &K, + value: &V, + ) -> DbResult<()> { + self.store_encodable(prefix, key.to_vec(), value) + } + + fn retrieve_value_by_key( + &self, + prefix: impl AsRef<[u8]>, + key: &K, + ) -> DbResult> { + self.retrieve_decodable(prefix, key.to_vec()) + } +} diff --git a/rust/hyperlane-base/src/db/rocks/iterator.rs b/rust/main/hyperlane-base/src/db/rocks/iterator.rs similarity index 100% rename from rust/hyperlane-base/src/db/rocks/iterator.rs rename to rust/main/hyperlane-base/src/db/rocks/iterator.rs diff --git a/rust/hyperlane-base/src/db/rocks/mod.rs b/rust/main/hyperlane-base/src/db/rocks/mod.rs similarity index 63% rename from rust/hyperlane-base/src/db/rocks/mod.rs rename to rust/main/hyperlane-base/src/db/rocks/mod.rs index e9a626a15..b92c9bc7e 100644 --- a/rust/hyperlane-base/src/db/rocks/mod.rs +++ b/rust/main/hyperlane-base/src/db/rocks/mod.rs @@ -1,7 +1,6 @@ -use std::path::PathBuf; -use std::{io, path::Path, sync::Arc}; +use std::{path::Path, sync::Arc}; -use hyperlane_core::{ChainCommunicationError, HyperlaneProtocolError}; +use super::error::DbError; use rocksdb::{Options, DB as Rocks}; use tracing::info; @@ -16,9 +15,6 @@ mod hyperlane_db; /// Type-specific db operations mod typed_db; -/// Internal-use storage types. -mod storage_types; - /// Database test utilities. #[cfg(any(test, feature = "test-utils"))] pub mod test_utils; @@ -33,37 +29,6 @@ impl From for DB { } } -/// DB Error type -#[derive(thiserror::Error, Debug)] -pub enum DbError { - /// Rocks DB Error - #[error("{0}")] - RockError(#[from] rocksdb::Error), - #[error("Failed to open {path}, canonicalized as {canonicalized}: {source}")] - /// Error opening the database - OpeningError { - /// Rocksdb error during opening - #[source] - source: rocksdb::Error, - /// Raw database path provided - path: PathBuf, - /// Parsed path used - canonicalized: PathBuf, - }, - /// Could not parse the provided database path string - #[error("Invalid database path supplied {1:?}; {0}")] - InvalidDbPath(#[source] io::Error, String), - /// Hyperlane Error - #[error("{0}")] - HyperlaneError(#[from] HyperlaneProtocolError), -} - -impl From for ChainCommunicationError { - fn from(value: DbError) -> Self { - ChainCommunicationError::from_other(value) - } -} - type Result = std::result::Result; impl DB { diff --git a/rust/hyperlane-base/src/db/rocks/test_utils.rs b/rust/main/hyperlane-base/src/db/rocks/test_utils.rs similarity index 100% rename from rust/hyperlane-base/src/db/rocks/test_utils.rs rename to rust/main/hyperlane-base/src/db/rocks/test_utils.rs diff --git a/rust/hyperlane-base/src/db/rocks/typed_db.rs b/rust/main/hyperlane-base/src/db/rocks/typed_db.rs similarity index 98% rename from rust/hyperlane-base/src/db/rocks/typed_db.rs rename to rust/main/hyperlane-base/src/db/rocks/typed_db.rs index 485731ea3..3008ef838 100644 --- a/rust/hyperlane-base/src/db/rocks/typed_db.rs +++ b/rust/main/hyperlane-base/src/db/rocks/typed_db.rs @@ -1,6 +1,6 @@ use hyperlane_core::{Decode, Encode, HyperlaneDomain}; -use crate::db::{DbError, DB}; +use crate::db::{error::DbError, DB}; type Result = std::result::Result; diff --git a/rust/hyperlane-base/src/db/rocks/storage_types.rs b/rust/main/hyperlane-base/src/db/storage_types.rs similarity index 90% rename from rust/hyperlane-base/src/db/rocks/storage_types.rs rename to rust/main/hyperlane-base/src/db/storage_types.rs index 9c5282c34..bde1eb773 100644 --- a/rust/hyperlane-base/src/db/rocks/storage_types.rs +++ b/rust/main/hyperlane-base/src/db/storage_types.rs @@ -8,15 +8,18 @@ use hyperlane_core::{ /// Subset of `InterchainGasPayment` excluding the message id which is stored in /// the key. #[derive(Debug, Copy, Clone)] -pub(super) struct InterchainGasPaymentData { +pub struct InterchainGasPaymentData { + /// The amount of tokens paid for the gas. pub payment: U256, + /// The amount of gas paid for. pub gas_amount: U256, } /// Subset of `InterchainGasExpenditure` excluding the message id which is /// stored in the key. +#[allow(missing_docs)] #[derive(Debug, Copy, Clone)] -pub(super) struct InterchainGasExpenditureData { +pub struct InterchainGasExpenditureData { pub tokens_used: U256, pub gas_used: U256, } @@ -31,6 +34,7 @@ impl Default for InterchainGasPaymentData { } impl InterchainGasPaymentData { + /// Complete the data with the message id and destination. pub fn complete(self, message_id: H256, destination: u32) -> InterchainGasPayment { InterchainGasPayment { message_id, @@ -82,6 +86,7 @@ impl Default for InterchainGasExpenditureData { } impl InterchainGasExpenditureData { + /// Complete the data with the message id. pub fn complete(self, message_id: H256) -> InterchainGasExpenditure { InterchainGasExpenditure { message_id, diff --git a/rust/hyperlane-base/src/lib.rs b/rust/main/hyperlane-base/src/lib.rs similarity index 100% rename from rust/hyperlane-base/src/lib.rs rename to rust/main/hyperlane-base/src/lib.rs diff --git a/rust/hyperlane-base/src/metadata.rs b/rust/main/hyperlane-base/src/metadata.rs similarity index 100% rename from rust/hyperlane-base/src/metadata.rs rename to rust/main/hyperlane-base/src/metadata.rs diff --git a/rust/hyperlane-base/src/metrics/agent_metrics.rs b/rust/main/hyperlane-base/src/metrics/agent_metrics.rs similarity index 99% rename from rust/hyperlane-base/src/metrics/agent_metrics.rs rename to rust/main/hyperlane-base/src/metrics/agent_metrics.rs index 14e233629..4ece5b0e3 100644 --- a/rust/hyperlane-base/src/metrics/agent_metrics.rs +++ b/rust/main/hyperlane-base/src/metrics/agent_metrics.rs @@ -1,4 +1,5 @@ //! Metrics either related to the agents, or observed by them +#![allow(unexpected_cfgs)] // TODO: `rustc` 1.80.1 clippy issue use std::sync::Arc; use std::time::Duration; diff --git a/rust/hyperlane-base/src/metrics/core.rs b/rust/main/hyperlane-base/src/metrics/core.rs similarity index 92% rename from rust/hyperlane-base/src/metrics/core.rs rename to rust/main/hyperlane-base/src/metrics/core.rs index bc28ecca9..5603ece22 100644 --- a/rust/hyperlane-base/src/metrics/core.rs +++ b/rust/main/hyperlane-base/src/metrics/core.rs @@ -1,6 +1,7 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::{Debug, Formatter}; use std::sync::OnceLock; +use std::time; use eyre::Result; use hyperlane_core::{HyperlaneDomain, H160}; @@ -458,11 +459,19 @@ struct AppContextKey { app_context: String, } +/// If this period has elapsed since a validator was last updated in the metrics, +/// it will be removed from the metrics. +const MIN_VALIDATOR_METRIC_RESET_PERIOD: time::Duration = time::Duration::from_secs(60 * 3); + /// Manages metrics for observing sets of validators. pub struct ValidatorObservabilityMetricManager { observed_validator_latest_index: IntGaugeVec, - app_context_validators: RwLock>>, + // AppContextKey -> Validator -> Last updated at + // Used to track the last time a validator was updated in the metrics, allowing + // for the removal of validators that have not been updated in a while to support + // changing validator sets. + app_context_validators: RwLock>>, } impl ValidatorObservabilityMetricManager { @@ -490,10 +499,23 @@ impl ValidatorObservabilityMetricManager { let mut app_context_validators = self.app_context_validators.write().await; - // First, clear out all previous metrics for the app context. + let mut new_set = HashMap::new(); + + // First, attempt to clear out all previous metrics for the app context. // This is necessary because the set of validators may have changed. if let Some(prev_validators) = app_context_validators.get(&key) { - for validator in prev_validators { + // If the validator was last updated in the metrics more than + // a certain period ago, remove it from the metrics. + // Some leniency is given here to allow this function to be called + // multiple times in a short period without clearing out the metrics, + // e.g. when a message's ISM aggregates multiple different validator sets. + for (validator, last_updated_at) in prev_validators { + if last_updated_at.elapsed() < MIN_VALIDATOR_METRIC_RESET_PERIOD { + // If the last metric refresh was too recent, keep the validator + // and the time of its last metric update. + new_set.insert(*validator, *last_updated_at); + continue; + } // We unwrap because an error here occurs if the # of labels // provided is incorrect, and we'd like to loudly fail in e2e if that // happens. @@ -508,8 +530,6 @@ impl ValidatorObservabilityMetricManager { } } - let mut set = HashSet::new(); - // Then set the new metrics and update the cached set of validators. for (validator, latest_checkpoint) in latest_checkpoints { self.observed_validator_latest_index @@ -522,9 +542,9 @@ impl ValidatorObservabilityMetricManager { // If the latest checkpoint is None, set to -1 to indicate that // the validator did not provide a valid latest checkpoint index. .set(latest_checkpoint.map(|i| i as i64).unwrap_or(-1)); - set.insert(*validator); + new_set.insert(*validator, time::Instant::now()); } - app_context_validators.insert(key, set); + app_context_validators.insert(key, new_set); } /// Gauge for reporting recently observed latest checkpoint indices for validator sets. diff --git a/rust/hyperlane-base/src/metrics/json_rpc_client.rs b/rust/main/hyperlane-base/src/metrics/json_rpc_client.rs similarity index 100% rename from rust/hyperlane-base/src/metrics/json_rpc_client.rs rename to rust/main/hyperlane-base/src/metrics/json_rpc_client.rs diff --git a/rust/hyperlane-base/src/metrics/mod.rs b/rust/main/hyperlane-base/src/metrics/mod.rs similarity index 100% rename from rust/hyperlane-base/src/metrics/mod.rs rename to rust/main/hyperlane-base/src/metrics/mod.rs diff --git a/rust/hyperlane-base/src/metrics/provider.rs b/rust/main/hyperlane-base/src/metrics/provider.rs similarity index 100% rename from rust/hyperlane-base/src/metrics/provider.rs rename to rust/main/hyperlane-base/src/metrics/provider.rs diff --git a/rust/hyperlane-base/src/oneline_eyre/handler.rs b/rust/main/hyperlane-base/src/oneline_eyre/handler.rs similarity index 100% rename from rust/hyperlane-base/src/oneline_eyre/handler.rs rename to rust/main/hyperlane-base/src/oneline_eyre/handler.rs diff --git a/rust/hyperlane-base/src/oneline_eyre/mod.rs b/rust/main/hyperlane-base/src/oneline_eyre/mod.rs similarity index 100% rename from rust/hyperlane-base/src/oneline_eyre/mod.rs rename to rust/main/hyperlane-base/src/oneline_eyre/mod.rs diff --git a/rust/hyperlane-base/src/server/base_server.rs b/rust/main/hyperlane-base/src/server/base_server.rs similarity index 100% rename from rust/hyperlane-base/src/server/base_server.rs rename to rust/main/hyperlane-base/src/server/base_server.rs diff --git a/rust/hyperlane-base/src/server/mod.rs b/rust/main/hyperlane-base/src/server/mod.rs similarity index 100% rename from rust/hyperlane-base/src/server/mod.rs rename to rust/main/hyperlane-base/src/server/mod.rs diff --git a/rust/hyperlane-base/src/settings/aws_credentials.rs b/rust/main/hyperlane-base/src/settings/aws_credentials.rs similarity index 94% rename from rust/hyperlane-base/src/settings/aws_credentials.rs rename to rust/main/hyperlane-base/src/settings/aws_credentials.rs index a86f6a1f2..8a35aa942 100644 --- a/rust/hyperlane-base/src/settings/aws_credentials.rs +++ b/rust/main/hyperlane-base/src/settings/aws_credentials.rs @@ -1,3 +1,6 @@ +#![allow(clippy::doc_markdown)] // TODO: `rustc` 1.80.1 clippy issue +#![allow(clippy::doc_lazy_continuation)] // TODO: `rustc` 1.80.1 clippy issue + use async_trait::async_trait; use rusoto_core::credential::{ AutoRefreshingProvider, AwsCredentials, CredentialsError, EnvironmentProvider, diff --git a/rust/hyperlane-base/src/settings/base.rs b/rust/main/hyperlane-base/src/settings/base.rs similarity index 100% rename from rust/hyperlane-base/src/settings/base.rs rename to rust/main/hyperlane-base/src/settings/base.rs diff --git a/rust/hyperlane-base/src/settings/chains.rs b/rust/main/hyperlane-base/src/settings/chains.rs similarity index 95% rename from rust/hyperlane-base/src/settings/chains.rs rename to rust/main/hyperlane-base/src/settings/chains.rs index b842d7225..0e08b2d15 100644 --- a/rust/hyperlane-base/src/settings/chains.rs +++ b/rust/main/hyperlane-base/src/settings/chains.rs @@ -10,13 +10,13 @@ use hyperlane_core::{ config::OperationBatchConfig, AggregationIsm, CcipReadIsm, ContractLocator, HyperlaneAbi, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, IndexMode, InterchainGasPaymaster, InterchainGasPayment, InterchainSecurityModule, Mailbox, - MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, RoutingIsm, SequenceAwareIndexer, - ValidatorAnnounce, H256, + MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, ReorgPeriod, RoutingIsm, + SequenceAwareIndexer, ValidatorAnnounce, H256, }; use hyperlane_cosmos as h_cosmos; use hyperlane_ethereum::{ self as h_eth, BuildableWithProvider, EthereumInterchainGasPaymasterAbi, EthereumMailboxAbi, - EthereumValidatorAnnounceAbi, + EthereumReorgPeriod, EthereumValidatorAnnounceAbi, }; use hyperlane_fuel as h_fuel; use hyperlane_sealevel as h_sealevel; @@ -45,7 +45,7 @@ pub struct ChainConf { /// Signer configuration for this chain pub signer: Option, /// The reorg period of the chain, i.e. the number of blocks until finality - pub reorg_period: u32, + pub reorg_period: ReorgPeriod, /// Addresses of contracts on the chain pub addresses: CoreContractAddresses, /// The chain connection details @@ -188,7 +188,7 @@ impl ChainConf { let provider = CosmosProvider::new( locator.domain.clone(), conf.clone(), - Some(locator.clone()), + locator.clone(), None, )?; Ok(Box::new(provider) as Box) @@ -210,6 +210,7 @@ impl ChainConf { ChainConnectionConf::Fuel(conf) => { let wallet = self.fuel_signer().await.context(ctx)?; hyperlane_fuel::FuelMailbox::new(conf, locator, wallet) + .await .map(|m| Box::new(m) as Box) .map_err(Into::into) } @@ -271,13 +272,13 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, - h_eth::SequenceIndexerBuilder { - reorg_period: self.reorg_period, - }, + h_eth::SequenceIndexerBuilder { reorg_period }, ) .await } @@ -288,11 +289,12 @@ impl ChainConf { } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; + let reorg_period = self.reorg_period.as_blocks().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosMailboxDispatchIndexer::new( conf.clone(), locator, signer, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } @@ -310,13 +312,13 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, - h_eth::DeliveryIndexerBuilder { - reorg_period: self.reorg_period, - }, + h_eth::DeliveryIndexerBuilder { reorg_period }, ) .await } @@ -327,11 +329,12 @@ impl ChainConf { } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; + let reorg_period = self.reorg_period.as_blocks().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosMailboxDeliveryIndexer::new( conf.clone(), locator, signer, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } @@ -388,13 +391,15 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, h_eth::InterchainGasPaymasterIndexerBuilder { mailbox_address: self.addresses.mailbox.into(), - reorg_period: self.reorg_period, + reorg_period, }, ) .await @@ -407,10 +412,11 @@ impl ChainConf { Ok(indexer as Box>) } ChainConnectionConf::Cosmos(conf) => { + let reorg_period = self.reorg_period.as_blocks().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosInterchainGasPaymasterIndexer::new( conf.clone(), locator, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } @@ -428,13 +434,13 @@ impl ChainConf { match &self.connection { ChainConnectionConf::Ethereum(conf) => { + let reorg_period = + EthereumReorgPeriod::try_from(&self.reorg_period).context(ctx)?; self.build_ethereum( conf, &locator, metrics, - h_eth::MerkleTreeHookIndexerBuilder { - reorg_period: self.reorg_period, - }, + h_eth::MerkleTreeHookIndexerBuilder { reorg_period }, ) .await } @@ -449,12 +455,13 @@ impl ChainConf { } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; + let reorg_period = self.reorg_period.as_blocks().context(ctx)?; let indexer = Box::new(h_cosmos::CosmosMerkleTreeHookIndexer::new( conf.clone(), locator, // TODO: remove signer requirement entirely signer, - self.reorg_period, + reorg_period, )?); Ok(indexer as Box>) } diff --git a/rust/hyperlane-base/src/settings/checkpoint_syncer.rs b/rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs similarity index 50% rename from rust/hyperlane-base/src/settings/checkpoint_syncer.rs rename to rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs index cb4fa1c8d..3434a7168 100644 --- a/rust/hyperlane-base/src/settings/checkpoint_syncer.rs +++ b/rust/main/hyperlane-base/src/settings/checkpoint_syncer.rs @@ -7,6 +7,7 @@ use eyre::{eyre, Context, Report, Result}; use prometheus::IntGauge; use rusoto_core::Region; use std::{env, path::PathBuf}; +use tracing::error; use ya_gcp::{AuthFlow, ServiceAccountAuth}; /// Checkpoint Syncer types @@ -73,21 +74,25 @@ impl FromStr for CheckpointSyncerConf { "gs" => { let service_account_key = env::var(GCS_SERVICE_ACCOUNT_KEY).ok(); let user_secrets = env::var(GCS_USER_SECRET).ok(); - if let Some(ind) = suffix.find('/') { - let (bucket, folder) = suffix.split_at(ind); - Ok(Self::Gcs { + let url_components = suffix.split('/').collect::>(); + let (bucket, folder): (&str, Option) = match url_components.len() { + 2 => Ok((url_components[0], None)), + 3 => Ok((url_components[0], Some(url_components[1].to_owned()))), + _ => Err(eyre!("Error parsing storage location; could not split bucket and folder ({suffix})")) + }?; + match folder { + None => Ok(CheckpointSyncerConf::Gcs { bucket: bucket.into(), - folder: Some(folder.into()), + folder: None, service_account_key, user_secrets, - }) - } else { - Ok(Self::Gcs { - bucket: suffix.into(), - folder: None, + }), + Some(folder) => Ok(CheckpointSyncerConf::Gcs { + bucket: bucket.into(), + folder: Some(folder), service_account_key, user_secrets, - }) + }), } } _ => Err(eyre!("Unknown storage location prefix `{prefix}`")), @@ -97,7 +102,37 @@ impl FromStr for CheckpointSyncerConf { impl CheckpointSyncerConf { /// Turn conf info a Checkpoint Syncer - pub async fn build( + /// + /// # Panics + /// + /// Panics if a reorg event has been posted to the checkpoint store, + /// to prevent any operation processing until the reorg is resolved. + pub async fn build_and_validate( + &self, + latest_index_gauge: Option, + ) -> Result, Report> { + let syncer: Box = self.build(latest_index_gauge).await?; + + match syncer.reorg_status().await { + Ok(Some(reorg_event)) => { + panic!( + "A reorg event has been detected: {:#?}. Please resolve the reorg to continue.", + reorg_event + ); + } + Err(err) => { + error!( + ?err, + "Failed to read reorg status. Assuming no reorg occurred." + ); + } + _ => {} + } + Ok(syncer) + } + + // keep this private to force all initializations to perform the reorg check via `build_and_validate` + async fn build( &self, latest_index_gauge: Option, ) -> Result, Report> { @@ -139,3 +174,79 @@ impl CheckpointSyncerConf { }) } } + +#[cfg(test)] +mod test { + use std::panic::AssertUnwindSafe; + + use futures_util::FutureExt; + use hyperlane_core::{ReorgEvent, ReorgPeriod, H256}; + + #[tokio::test] + async fn test_build_and_validate() { + use super::*; + + // initialize a local checkpoint store + let temp_checkpoint_dir = tempfile::tempdir().unwrap(); + let checkpoint_path = format!("file://{}", temp_checkpoint_dir.path().to_str().unwrap()); + let checkpoint_syncer_conf = CheckpointSyncerConf::from_str(&checkpoint_path).unwrap(); + + // create a checkpoint syncer and write a reorg event + // then `drop` it, to simulate a restart + { + let checkpoint_syncer = checkpoint_syncer_conf + .build_and_validate(None) + .await + .unwrap(); + + let dummy_local_merkle_root = H256::from_str( + "0x8da44bc8198e9874db215ec2000037c58e16918c94743d70c838ecb10e243c64", + ) + .unwrap(); + let dummy_canonical_merkle_root = H256::from_str( + "0xb437b888332ef12f7260c7f679aad3c96b91ab81c2dc7242f8b290f0b6bba92b", + ) + .unwrap(); + let dummy_checkpoint_index = 56; + let unix_timestamp = 1620000000; + let reorg_period = ReorgPeriod::from_blocks(5); + let dummy_reorg_event = ReorgEvent { + local_merkle_root: dummy_local_merkle_root, + canonical_merkle_root: dummy_canonical_merkle_root, + checkpoint_index: dummy_checkpoint_index, + unix_timestamp, + reorg_period, + }; + checkpoint_syncer + .write_reorg_status(&dummy_reorg_event) + .await + .unwrap(); + } + + // Initialize a new checkpoint syncer and expect it to panic due to the reorg event. + // `AssertUnwindSafe` is required for ignoring some type checks so the panic can be caught. + let startup_result = AssertUnwindSafe(checkpoint_syncer_conf.build_and_validate(None)) + .catch_unwind() + .await + .unwrap_err(); + if let Some(err) = startup_result.downcast_ref::() { + assert_eq!( + *err, + r#"A reorg event has been detected: ReorgEvent { + local_merkle_root: 0x8da44bc8198e9874db215ec2000037c58e16918c94743d70c838ecb10e243c64, + canonical_merkle_root: 0xb437b888332ef12f7260c7f679aad3c96b91ab81c2dc7242f8b290f0b6bba92b, + checkpoint_index: 56, + unix_timestamp: 1620000000, + reorg_period: Blocks( + 5, + ), +}. Please resolve the reorg to continue."# + ); + } else { + panic!( + "Caught panic has a different type than the expected one (`String`): {:?}", + startup_result + ); + } + } +} diff --git a/rust/hyperlane-base/src/settings/loader/arguments.rs b/rust/main/hyperlane-base/src/settings/loader/arguments.rs similarity index 100% rename from rust/hyperlane-base/src/settings/loader/arguments.rs rename to rust/main/hyperlane-base/src/settings/loader/arguments.rs diff --git a/rust/hyperlane-base/src/settings/loader/case_adapter.rs b/rust/main/hyperlane-base/src/settings/loader/case_adapter.rs similarity index 100% rename from rust/hyperlane-base/src/settings/loader/case_adapter.rs rename to rust/main/hyperlane-base/src/settings/loader/case_adapter.rs diff --git a/rust/hyperlane-base/src/settings/loader/environment.rs b/rust/main/hyperlane-base/src/settings/loader/environment.rs similarity index 100% rename from rust/hyperlane-base/src/settings/loader/environment.rs rename to rust/main/hyperlane-base/src/settings/loader/environment.rs diff --git a/rust/hyperlane-base/src/settings/loader/mod.rs b/rust/main/hyperlane-base/src/settings/loader/mod.rs similarity index 98% rename from rust/hyperlane-base/src/settings/loader/mod.rs rename to rust/main/hyperlane-base/src/settings/loader/mod.rs index 028649273..f4a81bf5d 100644 --- a/rust/hyperlane-base/src/settings/loader/mod.rs +++ b/rust/main/hyperlane-base/src/settings/loader/mod.rs @@ -27,7 +27,7 @@ where let mut base_config_sources = vec![]; let mut builder = Config::builder(); - // Always load the default config files (`rust/config/*.json`) + // Always load the default config files (`rust/main/config/*.json`) for entry in PathBuf::from("./config") .read_dir() .context("Failed to open config directory") diff --git a/rust/hyperlane-base/src/settings/mod.rs b/rust/main/hyperlane-base/src/settings/mod.rs similarity index 100% rename from rust/hyperlane-base/src/settings/mod.rs rename to rust/main/hyperlane-base/src/settings/mod.rs diff --git a/rust/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs similarity index 90% rename from rust/hyperlane-base/src/settings/parser/connection_parser.rs rename to rust/main/hyperlane-base/src/settings/parser/connection_parser.rs index c7b105c2e..67edcfbc5 100644 --- a/rust/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/connection_parser.rs @@ -1,14 +1,17 @@ use eyre::eyre; +use url::Url; + use h_eth::TransactionOverrides; use hyperlane_core::config::{ConfigErrResultExt, OperationBatchConfig}; use hyperlane_core::{config::ConfigParsingError, HyperlaneDomainProtocol}; -use url::Url; +use hyperlane_cosmos::NativeToken; use crate::settings::envs::*; use crate::settings::ChainConnectionConf; use super::{parse_base_and_override_urls, parse_cosmos_gas_price, ValueParser}; +#[allow(clippy::question_mark)] // TODO: `rustc` 1.80.1 clippy issue pub fn build_ethereum_connection_conf( rpcs: &[Url], chain: &ValueParser, @@ -134,19 +137,39 @@ pub fn build_cosmos_connection_conf( .parse_u64() .end(); + let native_token_decimals = chain + .chain(err) + .get_key("nativeToken") + .get_key("decimals") + .parse_u32() + .unwrap_or(18); + + let native_token_denom = chain + .chain(err) + .get_key("nativeToken") + .get_key("denom") + .parse_string() + .unwrap_or(""); + + let native_token = NativeToken { + decimals: native_token_decimals, + denom: native_token_denom.to_owned(), + }; + if !local_err.is_ok() { err.merge(local_err); None } else { Some(ChainConnectionConf::Cosmos(h_cosmos::ConnectionConf::new( grpcs, - rpcs.first().unwrap().to_string(), + rpcs.to_owned(), chain_id.unwrap().to_string(), prefix.unwrap().to_string(), canonical_asset.unwrap(), gas_price.unwrap(), contract_address_bytes.unwrap().try_into().unwrap(), operation_batch, + native_token, ))) } } diff --git a/rust/hyperlane-base/src/settings/parser/json_value_parser.rs b/rust/main/hyperlane-base/src/settings/parser/json_value_parser.rs similarity index 99% rename from rust/hyperlane-base/src/settings/parser/json_value_parser.rs rename to rust/main/hyperlane-base/src/settings/parser/json_value_parser.rs index 61a881926..2f15e25d3 100644 --- a/rust/hyperlane-base/src/settings/parser/json_value_parser.rs +++ b/rust/main/hyperlane-base/src/settings/parser/json_value_parser.rs @@ -8,6 +8,7 @@ use itertools::Itertools; use serde::de::{DeserializeOwned, StdError}; use serde_json::Value; +#[allow(unused_imports)] // TODO: `rustc` 1.80.1 clippy issue pub use super::super::envs::*; /// A serde-json value config parsing utility. diff --git a/rust/hyperlane-base/src/settings/parser/mod.rs b/rust/main/hyperlane-base/src/settings/parser/mod.rs similarity index 96% rename from rust/hyperlane-base/src/settings/parser/mod.rs rename to rust/main/hyperlane-base/src/settings/parser/mod.rs index 3c3a3aa65..d176ad857 100644 --- a/rust/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/main/hyperlane-base/src/settings/parser/mod.rs @@ -11,26 +11,31 @@ use std::{ use convert_case::{Case, Casing}; use eyre::{eyre, Context}; -use h_cosmos::RawCosmosAmount; -use hyperlane_core::{ - cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, - HyperlaneDomainTechnicalStack, IndexMode, -}; use itertools::Itertools; use serde::Deserialize; use serde_json::Value; use url::Url; -pub use self::json_value_parser::ValueParser; -pub use super::envs::*; +use h_cosmos::RawCosmosAmount; +use hyperlane_core::{ + cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, + HyperlaneDomainTechnicalStack, IndexMode, ReorgPeriod, +}; + use crate::settings::{ chains::IndexSettings, parser::connection_parser::build_connection_conf, trace::TracingConfig, ChainConf, CoreContractAddresses, Settings, SignerConf, }; +pub use super::envs::*; + +pub use self::json_value_parser::ValueParser; + mod connection_parser; mod json_value_parser; +const DEFAULT_CHUNK_SIZE: u32 = 1999; + /// The base agent config #[derive(Debug, Deserialize)] #[serde(transparent)] @@ -133,8 +138,8 @@ fn parse_chain( .chain(&mut err) .get_opt_key("blocks") .get_key("reorgPeriod") - .parse_u32() - .unwrap_or(1); + .parse_value("Invalid reorgPeriod") + .unwrap_or(ReorgPeriod::from_blocks(1)); let rpcs = parse_base_and_override_urls(&chain, "rpcUrls", "customRpcUrls", "http", &mut err); @@ -149,7 +154,7 @@ fn parse_chain( .get_opt_key("index") .get_opt_key("chunk") .parse_u32() - .unwrap_or(1999); + .unwrap_or(DEFAULT_CHUNK_SIZE); let mode = chain .chain(&mut err) .get_opt_key("index") @@ -329,9 +334,16 @@ fn parse_signer(signer: ValueParser) -> ConfigResult { .get_key("prefix") .parse_string() .unwrap_or_default(); + let account_address_type = signer + .chain(&mut err) + .get_opt_key("accountAddressType") + .parse_from_str("Expected Account Address Type") + .end() + .unwrap_or_default(); err.into_result(SignerConf::CosmosKey { key, prefix: prefix.to_string(), + account_address_type, }) }}; } diff --git a/rust/hyperlane-base/src/settings/signers.rs b/rust/main/hyperlane-base/src/settings/signers.rs similarity index 92% rename from rust/hyperlane-base/src/settings/signers.rs rename to rust/main/hyperlane-base/src/settings/signers.rs index d0642434f..459a6337b 100644 --- a/rust/hyperlane-base/src/settings/signers.rs +++ b/rust/main/hyperlane-base/src/settings/signers.rs @@ -3,7 +3,7 @@ use ed25519_dalek::SecretKey; use ethers::prelude::{AwsSigner, LocalWallet}; use ethers::utils::hex::ToHex; use eyre::{bail, Context, Report}; -use hyperlane_core::H256; +use hyperlane_core::{AccountAddressType, H256}; use hyperlane_sealevel::Keypair; use rusoto_core::Region; use rusoto_kms::KmsClient; @@ -34,6 +34,8 @@ pub enum SignerConf { key: H256, /// Prefix for cosmos address prefix: String, + /// Account address type for cosmos address + account_address_type: AccountAddressType, }, /// Assume node will sign on RPC calls #[default] @@ -101,7 +103,7 @@ impl ChainSigner for hyperlane_ethereum::Signers { impl BuildableWithSignerConf for fuels::prelude::WalletUnlocked { async fn build(conf: &SignerConf) -> Result { if let SignerConf::HexKey { key } = conf { - let key = fuels::signers::fuel_crypto::SecretKey::try_from(key.as_bytes()) + let key = fuels::crypto::SecretKey::try_from(key.as_bytes()) .context("Invalid fuel signer key")?; Ok(fuels::prelude::WalletUnlocked::new_from_private_key( key, None, @@ -143,10 +145,16 @@ impl ChainSigner for Keypair { #[async_trait] impl BuildableWithSignerConf for hyperlane_cosmos::Signer { async fn build(conf: &SignerConf) -> Result { - if let SignerConf::CosmosKey { key, prefix } = conf { + if let SignerConf::CosmosKey { + key, + prefix, + account_address_type, + } = conf + { Ok(hyperlane_cosmos::Signer::new( key.as_bytes().to_vec(), prefix.clone(), + account_address_type, )?) } else { bail!(format!("{conf:?} key is not supported by cosmos")); diff --git a/rust/hyperlane-base/src/settings/trace/fmt.rs b/rust/main/hyperlane-base/src/settings/trace/fmt.rs similarity index 100% rename from rust/hyperlane-base/src/settings/trace/fmt.rs rename to rust/main/hyperlane-base/src/settings/trace/fmt.rs diff --git a/rust/hyperlane-base/src/settings/trace/mod.rs b/rust/main/hyperlane-base/src/settings/trace/mod.rs similarity index 100% rename from rust/hyperlane-base/src/settings/trace/mod.rs rename to rust/main/hyperlane-base/src/settings/trace/mod.rs diff --git a/rust/hyperlane-base/src/settings/trace/span_metrics.rs b/rust/main/hyperlane-base/src/settings/trace/span_metrics.rs similarity index 100% rename from rust/hyperlane-base/src/settings/trace/span_metrics.rs rename to rust/main/hyperlane-base/src/settings/trace/span_metrics.rs diff --git a/rust/hyperlane-base/src/traits/checkpoint_syncer.rs b/rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs similarity index 73% rename from rust/hyperlane-base/src/traits/checkpoint_syncer.rs rename to rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs index 44828bcbe..0dc6a1e6b 100644 --- a/rust/hyperlane-base/src/traits/checkpoint_syncer.rs +++ b/rust/main/hyperlane-base/src/traits/checkpoint_syncer.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use eyre::Result; use crate::AgentMetadata; -use hyperlane_core::{SignedAnnouncement, SignedCheckpointWithMessageId}; +use hyperlane_core::{ReorgEvent, SignedAnnouncement, SignedCheckpointWithMessageId}; /// A generic trait to read/write Checkpoints offchain #[async_trait] @@ -34,4 +34,10 @@ pub trait CheckpointSyncer: Debug + Send + Sync { async fn write_announcement(&self, signed_announcement: &SignedAnnouncement) -> Result<()>; /// Return the announcement storage location for this syncer fn announcement_location(&self) -> String; + /// If a bigger than expected reorg was detected on the validated chain, this flag can be set to inform + /// the validator agent to stop publishing checkpoints. Once any remediation is done, this flag can be reset + /// to resume operation. + async fn write_reorg_status(&self, reorg_event: &ReorgEvent) -> Result<()>; + /// Read the reorg status of the chain being validated + async fn reorg_status(&self) -> Result>; } diff --git a/rust/hyperlane-base/src/traits/mod.rs b/rust/main/hyperlane-base/src/traits/mod.rs similarity index 100% rename from rust/hyperlane-base/src/traits/mod.rs rename to rust/main/hyperlane-base/src/traits/mod.rs diff --git a/rust/hyperlane-base/src/types/gcs_storage.rs b/rust/main/hyperlane-base/src/types/gcs_storage.rs similarity index 85% rename from rust/hyperlane-base/src/types/gcs_storage.rs rename to rust/main/hyperlane-base/src/types/gcs_storage.rs index 6094ae8c3..ba40ec2ca 100644 --- a/rust/hyperlane-base/src/types/gcs_storage.rs +++ b/rust/main/hyperlane-base/src/types/gcs_storage.rs @@ -2,13 +2,20 @@ use crate::{AgentMetadata, CheckpointSyncer}; use async_trait::async_trait; use derive_new::new; use eyre::{bail, Result}; -use hyperlane_core::{SignedAnnouncement, SignedCheckpointWithMessageId}; +use hyperlane_core::{ReorgEvent, SignedAnnouncement, SignedCheckpointWithMessageId}; use std::fmt; -use ya_gcp::{storage::StorageClient, AuthFlow, ClientBuilder, ClientBuilderConfig}; +use ya_gcp::{ + storage::{ + api::{error::HttpStatusError, http::StatusCode, Error}, + ObjectError, StorageClient, + }, + AuthFlow, ClientBuilder, ClientBuilderConfig, +}; const LATEST_INDEX_KEY: &str = "gcsLatestIndexKey"; const METADATA_KEY: &str = "gcsMetadataKey"; const ANNOUNCEMENT_KEY: &str = "gcsAnnouncementKey"; +const REORG_FLAG_KEY: &str = "gcsReorgFlagKey"; /// Path to GCS users_secret file pub const GCS_USER_SECRET: &str = "GCS_USER_SECRET"; /// Path to GCS Service account key @@ -127,7 +134,10 @@ impl CheckpointSyncer for GcsStorageClient { Ok(data) => Ok(Some(serde_json::from_slice(data.as_ref())?)), Err(e) => match e { // never written before to this bucket - ya_gcp::storage::ObjectError::InvalidName(_) => Ok(None), + ObjectError::InvalidName(_) => Ok(None), + ObjectError::Failure(Error::HttpStatus(HttpStatusError(StatusCode::NOT_FOUND))) => { + Ok(None) + } _ => bail!(e), }, } @@ -153,11 +163,19 @@ impl CheckpointSyncer for GcsStorageClient { /// Attempt to fetch the signed (checkpoint, messageId) tuple at this index async fn fetch_checkpoint(&self, index: u32) -> Result> { - let res = self + match self .inner .get_object(&self.bucket, GcsStorageClient::get_checkpoint_key(index)) - .await?; - Ok(Some(serde_json::from_slice(res.as_ref())?)) + .await + { + Ok(data) => Ok(Some(serde_json::from_slice(data.as_ref())?)), + Err(e) => match e { + ObjectError::Failure(Error::HttpStatus(HttpStatusError(StatusCode::NOT_FOUND))) => { + Ok(None) + } + _ => bail!(e), + }, + } } /// Write the signed (checkpoint, messageId) tuple to this syncer @@ -200,6 +218,18 @@ impl CheckpointSyncer for GcsStorageClient { fn announcement_location(&self) -> String { format!("gs://{}/{}", &self.bucket, ANNOUNCEMENT_KEY) } + + async fn write_reorg_status(&self, reorged_event: &ReorgEvent) -> Result<()> { + let serialized_metadata = serde_json::to_string_pretty(reorged_event)?; + self.inner + .insert_object(&self.bucket, REORG_FLAG_KEY, serialized_metadata) + .await?; + Ok(()) + } + + async fn reorg_status(&self) -> Result> { + Ok(None) + } } #[tokio::test] diff --git a/rust/hyperlane-base/src/types/local_storage.rs b/rust/main/hyperlane-base/src/types/local_storage.rs similarity index 82% rename from rust/hyperlane-base/src/types/local_storage.rs rename to rust/main/hyperlane-base/src/types/local_storage.rs index 71047f246..bb1ebf123 100644 --- a/rust/hyperlane-base/src/types/local_storage.rs +++ b/rust/main/hyperlane-base/src/types/local_storage.rs @@ -4,7 +4,7 @@ use crate::traits::CheckpointSyncer; use crate::AgentMetadata; use async_trait::async_trait; use eyre::{Context, Result}; -use hyperlane_core::{SignedAnnouncement, SignedCheckpointWithMessageId}; +use hyperlane_core::{ReorgEvent, SignedAnnouncement, SignedCheckpointWithMessageId}; use prometheus::IntGauge; #[derive(Debug, Clone)] @@ -41,6 +41,10 @@ impl LocalStorage { self.path.join("announcement.json") } + fn reorg_flag_path(&self) -> PathBuf { + self.path.join("reorg_flag.json") + } + fn metadata_file_path(&self) -> PathBuf { self.path.join("metadata_latest.json") } @@ -116,4 +120,21 @@ impl CheckpointSyncer for LocalStorage { fn announcement_location(&self) -> String { format!("file://{}", self.path.to_str().unwrap()) } + + async fn write_reorg_status(&self, reorged_event: &ReorgEvent) -> Result<()> { + let serialized_reorg = serde_json::to_string_pretty(reorged_event)?; + let path = self.reorg_flag_path(); + tokio::fs::write(&path, &serialized_reorg) + .await + .with_context(|| format!("Writing reorg status to {path:?}"))?; + Ok(()) + } + + async fn reorg_status(&self) -> Result> { + let Ok(data) = tokio::fs::read(self.reorg_flag_path()).await else { + return Ok(None); + }; + let reorg = serde_json::from_slice(&data)?; + Ok(Some(reorg)) + } } diff --git a/rust/hyperlane-base/src/types/mod.rs b/rust/main/hyperlane-base/src/types/mod.rs similarity index 100% rename from rust/hyperlane-base/src/types/mod.rs rename to rust/main/hyperlane-base/src/types/mod.rs diff --git a/rust/hyperlane-base/src/types/multisig.rs b/rust/main/hyperlane-base/src/types/multisig.rs similarity index 100% rename from rust/hyperlane-base/src/types/multisig.rs rename to rust/main/hyperlane-base/src/types/multisig.rs diff --git a/rust/hyperlane-base/src/types/s3_storage.rs b/rust/main/hyperlane-base/src/types/s3_storage.rs similarity index 91% rename from rust/hyperlane-base/src/types/s3_storage.rs rename to rust/main/hyperlane-base/src/types/s3_storage.rs index 4d03ff040..ea04a1c4f 100644 --- a/rust/hyperlane-base/src/types/s3_storage.rs +++ b/rust/main/hyperlane-base/src/types/s3_storage.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use derive_new::new; use eyre::{bail, Result}; use futures_util::TryStreamExt; -use hyperlane_core::{SignedAnnouncement, SignedCheckpointWithMessageId}; +use hyperlane_core::{ReorgEvent, SignedAnnouncement, SignedCheckpointWithMessageId}; use prometheus::IntGauge; use rusoto_core::{ credential::{Anonymous, AwsCredentials, StaticProvider}, @@ -145,6 +145,10 @@ impl S3Storage { fn announcement_key() -> String { "announcement.json".to_owned() } + + fn reorg_flag_key() -> String { + "reorg_flag.json".to_owned() + } } #[async_trait] @@ -216,4 +220,19 @@ impl CheckpointSyncer for S3Storage { } } } + + async fn write_reorg_status(&self, reorged_event: &ReorgEvent) -> Result<()> { + let serialized_reorg = serde_json::to_string(reorged_event)?; + self.write_to_bucket(S3Storage::reorg_flag_key(), &serialized_reorg) + .await?; + Ok(()) + } + + async fn reorg_status(&self) -> Result> { + self.anonymously_read_from_bucket(S3Storage::reorg_flag_key()) + .await? + .map(|data| serde_json::from_slice(&data)) + .transpose() + .map_err(Into::into) + } } diff --git a/rust/hyperlane-base/src/types/utils.rs b/rust/main/hyperlane-base/src/types/utils.rs similarity index 100% rename from rust/hyperlane-base/src/types/utils.rs rename to rust/main/hyperlane-base/src/types/utils.rs diff --git a/rust/hyperlane-base/tests/chain_config.rs b/rust/main/hyperlane-base/tests/chain_config.rs similarity index 96% rename from rust/hyperlane-base/tests/chain_config.rs rename to rust/main/hyperlane-base/tests/chain_config.rs index 7e59b2360..9559280c1 100644 --- a/rust/hyperlane-base/tests/chain_config.rs +++ b/rust/main/hyperlane-base/tests/chain_config.rs @@ -6,7 +6,7 @@ use hyperlane_base::settings::{parser::RawAgentConf, Settings}; use hyperlane_core::{config::*, KnownHyperlaneDomain}; use walkdir::WalkDir; -/// Relative path to the `hyperlane-monorepo/rust/config/` +/// Relative path to the `hyperlane-monorepo/rust/main/config/` /// directory, which is where the agent's config files /// currently live. const AGENT_CONFIG_PATH_ROOT: &str = "../config"; @@ -92,7 +92,7 @@ fn chain_name_domain_records() -> BTreeSet { .flat_map(|x: &Settings| { x.chains.values().map(|v| ChainCoordinate { name: v.domain.name().into(), - domain: (&v.domain).try_into().expect("Invalid domain id"), + domain: (&v.domain).into(), }) }) .collect() diff --git a/rust/hyperlane-core/Cargo.toml b/rust/main/hyperlane-core/Cargo.toml similarity index 89% rename from rust/hyperlane-core/Cargo.toml rename to rust/main/hyperlane-core/Cargo.toml index 2bb5f46d8..4e835f914 100644 --- a/rust/hyperlane-core/Cargo.toml +++ b/rust/main/hyperlane-core/Cargo.toml @@ -33,6 +33,7 @@ itertools.workspace = true num = { workspace = true, features = ["serde"] } num-derive.workspace = true num-traits.workspace = true +prometheus.workspace = true serde = { workspace = true } serde_json = { workspace = true } sha3 = { workspace = true } @@ -44,7 +45,7 @@ tracing.workspace = true typetag.workspace = true primitive-types = { workspace = true, optional = true } solana-sdk = { workspace = true, optional = true } -tiny-keccak = { workspace = true, features = ["keccak"]} +tiny-keccak = { workspace = true, features = ["keccak"] } uint.workspace = true [dev-dependencies] @@ -56,6 +57,11 @@ float = [] test-utils = ["dep:config"] agent = ["ethers", "strum"] strum = ["dep:strum"] -ethers = ["dep:ethers-core", "dep:ethers-contract", "dep:ethers-providers", "dep:primitive-types"] +ethers = [ + "dep:ethers-core", + "dep:ethers-contract", + "dep:ethers-providers", + "dep:primitive-types", +] solana = ["dep:solana-sdk"] async = ["tokio", "futures"] diff --git a/rust/hyperlane-core/src/accumulator/incremental.rs b/rust/main/hyperlane-core/src/accumulator/incremental.rs similarity index 100% rename from rust/hyperlane-core/src/accumulator/incremental.rs rename to rust/main/hyperlane-core/src/accumulator/incremental.rs diff --git a/rust/hyperlane-core/src/accumulator/merkle.rs b/rust/main/hyperlane-core/src/accumulator/merkle.rs similarity index 100% rename from rust/hyperlane-core/src/accumulator/merkle.rs rename to rust/main/hyperlane-core/src/accumulator/merkle.rs diff --git a/rust/hyperlane-core/src/accumulator/mod.rs b/rust/main/hyperlane-core/src/accumulator/mod.rs similarity index 100% rename from rust/hyperlane-core/src/accumulator/mod.rs rename to rust/main/hyperlane-core/src/accumulator/mod.rs diff --git a/rust/hyperlane-core/src/accumulator/sparse.rs b/rust/main/hyperlane-core/src/accumulator/sparse.rs similarity index 100% rename from rust/hyperlane-core/src/accumulator/sparse.rs rename to rust/main/hyperlane-core/src/accumulator/sparse.rs diff --git a/rust/hyperlane-core/src/accumulator/zero_hashes.rs b/rust/main/hyperlane-core/src/accumulator/zero_hashes.rs similarity index 100% rename from rust/hyperlane-core/src/accumulator/zero_hashes.rs rename to rust/main/hyperlane-core/src/accumulator/zero_hashes.rs diff --git a/rust/hyperlane-core/src/chain.rs b/rust/main/hyperlane-core/src/chain.rs similarity index 79% rename from rust/hyperlane-core/src/chain.rs rename to rust/main/hyperlane-core/src/chain.rs index d1b7c5e34..134881bf7 100644 --- a/rust/hyperlane-core/src/chain.rs +++ b/rust/main/hyperlane-core/src/chain.rs @@ -3,16 +3,20 @@ use std::{ fmt::{Debug, Formatter}, hash::{Hash, Hasher}, + num::NonZeroU32, }; use derive_new::new; use num_derive::FromPrimitive; use num_traits::FromPrimitive; -use serde::Serialize; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + #[cfg(feature = "strum")] use strum::{EnumIter, EnumString, IntoStaticStr}; -use crate::{utils::many_to_one, HyperlaneProtocolError, IndexMode, H160, H256}; +use crate::{ + utils::many_to_one, ChainCommunicationError, HyperlaneProtocolError, IndexMode, H160, H256, +}; #[derive(Debug, Clone)] pub struct Address(pub bytes::Bytes); @@ -39,6 +43,80 @@ impl<'a> std::fmt::Display for ContractLocator<'a> { } } +#[derive(Default, Debug, Clone, PartialEq)] +pub enum ReorgPeriod { + #[default] + None, + Blocks(NonZeroU32), + Tag(String), +} + +impl ReorgPeriod { + pub fn from_blocks(blocks: u32) -> Self { + NonZeroU32::try_from(blocks) + .map(ReorgPeriod::Blocks) + .unwrap_or(ReorgPeriod::None) + } + + pub fn as_blocks(&self) -> Result { + match self { + ReorgPeriod::None => Ok(0), + ReorgPeriod::Blocks(blocks) => Ok(blocks.get()), + ReorgPeriod::Tag(_) => Err(ChainCommunicationError::InvalidReorgPeriod(self.clone())), + } + } + + pub fn is_none(&self) -> bool { + matches!(self, ReorgPeriod::None) + } +} + +impl Serialize for ReorgPeriod { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + ReorgPeriod::None => serializer.serialize_u32(0), + ReorgPeriod::Blocks(blocks) => serializer.serialize_u32(blocks.get()), + ReorgPeriod::Tag(tag) => serializer.serialize_str(tag), + } + } +} + +impl<'de> Deserialize<'de> for ReorgPeriod { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use serde::de; + + struct ReorgPeriodVisitor; + + impl<'de> de::Visitor<'de> for ReorgPeriodVisitor { + type Value = ReorgPeriod; + + fn expecting(&self, f: &mut Formatter) -> std::fmt::Result { + f.write_str("reorgPeriod as a number or string") + } + + fn visit_u64(self, v: u64) -> Result { + let v = v.try_into().map_err(de::Error::custom)?; + Ok(ReorgPeriod::from_blocks(v)) + } + + fn visit_str(self, v: &str) -> Result { + match v.parse::() { + Ok(v) => self.visit_u32(v), + Err(_) => Ok(ReorgPeriod::Tag(v.to_string())), + } + } + } + + deserializer.deserialize_any(ReorgPeriodVisitor) + } +} + /// All domains supported by Hyperlane. #[derive(FromPrimitive, PartialEq, Eq, Debug, Clone, Copy, Hash, Serialize)] #[cfg_attr( @@ -218,6 +296,10 @@ impl HyperlaneDomainProtocol { )] pub enum HyperlaneDomainTechnicalStack { ArbitrumNitro, + OpStack, + PolygonCDK, + PolkadotSubstrate, + ZkSync, #[default] Other, } @@ -291,19 +373,28 @@ impl KnownHyperlaneDomain { // Test chains ConnextSepolia, PlumeTestnet, SuperpositionTestnet ], + HyperlaneDomainTechnicalStack::OpStack: [ + Ancient8, Blast, Bob, Cyber, Fraxtal, Kroma, Lisk, MantaPacific, Mantle, Metis, + Mint, Mode, Optimism, Redstone, Worldchain, Zircuit, ZoraMainnet + ], + HyperlaneDomainTechnicalStack::PolygonCDK: [ + Merlin, Xlayer + ], + HyperlaneDomainTechnicalStack::PolkadotSubstrate: [ + Moonbeam, Tangle + ], + HyperlaneDomainTechnicalStack::ZkSync: [], HyperlaneDomainTechnicalStack::Other: [ - Ancient8, Avalanche, BinanceSmartChain, Blast, Bob, Celo, Cyber, EclipseMainnet, Endurance, Ethereum, - Fraxtal, Fuji, FuseMainnet, Gnosis, Injective, Kroma, Linea, Lisk, Lukso, - MantaPacific, Mantle, Merlin, Metis, Mint, Mode, Moonbeam, Neutron, Optimism, Osmosis, - Polygon, Redstone, Sei, SolanaMainnet, Taiko, Tangle, Viction, Worldchain, Xlayer, Zetachain, - Zircuit, ZoraMainnet, + Avalanche, BinanceSmartChain, Celo, EclipseMainnet, Endurance, Ethereum, + FuseMainnet, Gnosis, Injective, Linea, Lukso, Neutron, Osmosis, Polygon, + Sei, SolanaMainnet, Taiko, Viction, Zetachain, // Local chains CosmosTest99990, CosmosTest99991, FuelTest1, SealevelTest1, SealevelTest2, Test1, Test2, Test3, // Test chains - Alfajores, BinanceSmartChainTestnet, Chiado, Holesky, MoonbaseAlpha, ScrollSepolia, + Alfajores, BinanceSmartChainTestnet, Chiado, Fuji, Holesky, MoonbaseAlpha, ScrollSepolia, Sepolia ], }) @@ -494,9 +585,9 @@ impl HyperlaneDomain { #[cfg(test)] #[cfg(feature = "strum")] mod tests { - use std::str::FromStr; + use std::{num::NonZeroU32, str::FromStr}; - use crate::KnownHyperlaneDomain; + use crate::{KnownHyperlaneDomain, ReorgPeriod}; #[test] fn domain_strings() { @@ -549,4 +640,32 @@ mod tests { ); assert!("foo".parse::().is_err()); } + + #[test] + fn parse_reorg_period() { + assert_eq!( + serde_json::from_value::(0.into()).unwrap(), + ReorgPeriod::None + ); + + assert_eq!( + serde_json::from_value::("0".into()).unwrap(), + ReorgPeriod::None + ); + + assert_eq!( + serde_json::from_value::(12.into()).unwrap(), + ReorgPeriod::Blocks(NonZeroU32::new(12).unwrap()) + ); + + assert_eq!( + serde_json::from_value::("12".into()).unwrap(), + ReorgPeriod::Blocks(NonZeroU32::new(12).unwrap()) + ); + + assert_eq!( + serde_json::from_value::("finalized".into()).unwrap(), + ReorgPeriod::Tag("finalized".into()) + ); + } } diff --git a/rust/hyperlane-core/src/config/config_path.rs b/rust/main/hyperlane-core/src/config/config_path.rs similarity index 100% rename from rust/hyperlane-core/src/config/config_path.rs rename to rust/main/hyperlane-core/src/config/config_path.rs diff --git a/rust/hyperlane-core/src/config/mod.rs b/rust/main/hyperlane-core/src/config/mod.rs similarity index 100% rename from rust/hyperlane-core/src/config/mod.rs rename to rust/main/hyperlane-core/src/config/mod.rs diff --git a/rust/hyperlane-core/src/config/str_or_int.rs b/rust/main/hyperlane-core/src/config/str_or_int.rs similarity index 96% rename from rust/hyperlane-core/src/config/str_or_int.rs rename to rust/main/hyperlane-core/src/config/str_or_int.rs index 242d98336..b2c1acbec 100644 --- a/rust/hyperlane-core/src/config/str_or_int.rs +++ b/rust/main/hyperlane-core/src/config/str_or_int.rs @@ -91,6 +91,7 @@ impl TryFrom<&StrOrInt> for U256 { StrOrInt::Str(s) => s.parse().map_err(|_| { StrOrIntParseError::Other(format!("Unable to parse U256 string ({s})")) })?, + #[allow(clippy::unnecessary_fallible_conversions)] // TODO: `rustc` 1.80.1 clippy issue StrOrInt::Int(i) => (*i).try_into().map_err(|_| { StrOrIntParseError::Other(format!("Unable to parse integer as U256 ({i})")) })?, diff --git a/rust/hyperlane-core/src/config/trait_ext.rs b/rust/main/hyperlane-core/src/config/trait_ext.rs similarity index 100% rename from rust/hyperlane-core/src/config/trait_ext.rs rename to rust/main/hyperlane-core/src/config/trait_ext.rs diff --git a/rust/hyperlane-core/src/error.rs b/rust/main/hyperlane-core/src/error.rs similarity index 97% rename from rust/hyperlane-core/src/error.rs rename to rust/main/hyperlane-core/src/error.rs index 56bbf4a82..63c0f3655 100644 --- a/rust/hyperlane-core/src/error.rs +++ b/rust/main/hyperlane-core/src/error.rs @@ -10,8 +10,10 @@ use crate::config::StrOrIntParseError; use crate::rpc_clients::RpcClientError; use std::string::FromUtf8Error; -use crate::HyperlaneProviderError; -use crate::{Error as PrimitiveTypeError, HyperlaneSignerError, H256, U256}; +use crate::{ + Error as PrimitiveTypeError, HyperlaneProviderError, HyperlaneSignerError, ReorgPeriod, H256, + U256, +}; /// The result of interacting with a chain. pub type ChainResult = Result; @@ -91,9 +93,6 @@ pub enum ChainCommunicationError { /// Failed to parse strings or integers #[error("Data parsing error {0:?}")] StrOrIntParseError(#[from] StrOrIntParseError), - /// BlockNotFoundError - #[error("Block not found: {0:?}")] - BlockNotFound(H256), /// utf8 error #[error("{0}")] Utf8(#[from] FromUtf8Error), @@ -157,6 +156,9 @@ pub enum ChainCommunicationError { /// Hyperlane signer error #[error("{0}")] HyperlaneSignerError(#[from] HyperlaneSignerError), + /// Invalid reorg period + #[error("Invalid reorg period: {0:?}")] + InvalidReorgPeriod(ReorgPeriod), } impl ChainCommunicationError { diff --git a/rust/hyperlane-core/src/lib.rs b/rust/main/hyperlane-core/src/lib.rs similarity index 95% rename from rust/hyperlane-core/src/lib.rs rename to rust/main/hyperlane-core/src/lib.rs index 610fa3d81..4c5200b0c 100644 --- a/rust/hyperlane-core/src/lib.rs +++ b/rust/main/hyperlane-core/src/lib.rs @@ -3,8 +3,8 @@ #![warn(missing_docs)] #![deny(unsafe_code)] +#![allow(unknown_lints)] // TODO: `rustc` 1.80.1 clippy issue #![forbid(where_clauses_object_safety)] - extern crate core; pub use chain::*; diff --git a/rust/hyperlane-core/src/metrics/agent.rs b/rust/main/hyperlane-core/src/metrics/agent.rs similarity index 100% rename from rust/hyperlane-core/src/metrics/agent.rs rename to rust/main/hyperlane-core/src/metrics/agent.rs diff --git a/rust/hyperlane-core/src/metrics/mod.rs b/rust/main/hyperlane-core/src/metrics/mod.rs similarity index 100% rename from rust/hyperlane-core/src/metrics/mod.rs rename to rust/main/hyperlane-core/src/metrics/mod.rs diff --git a/rust/hyperlane-core/src/rpc_clients/error.rs b/rust/main/hyperlane-core/src/rpc_clients/error.rs similarity index 100% rename from rust/hyperlane-core/src/rpc_clients/error.rs rename to rust/main/hyperlane-core/src/rpc_clients/error.rs diff --git a/rust/hyperlane-core/src/rpc_clients/fallback.rs b/rust/main/hyperlane-core/src/rpc_clients/fallback.rs similarity index 100% rename from rust/hyperlane-core/src/rpc_clients/fallback.rs rename to rust/main/hyperlane-core/src/rpc_clients/fallback.rs diff --git a/rust/hyperlane-core/src/rpc_clients/mod.rs b/rust/main/hyperlane-core/src/rpc_clients/mod.rs similarity index 100% rename from rust/hyperlane-core/src/rpc_clients/mod.rs rename to rust/main/hyperlane-core/src/rpc_clients/mod.rs diff --git a/rust/hyperlane-core/src/rpc_clients/retry.rs b/rust/main/hyperlane-core/src/rpc_clients/retry.rs similarity index 100% rename from rust/hyperlane-core/src/rpc_clients/retry.rs rename to rust/main/hyperlane-core/src/rpc_clients/retry.rs diff --git a/rust/hyperlane-core/src/test_output.rs b/rust/main/hyperlane-core/src/test_output.rs similarity index 100% rename from rust/hyperlane-core/src/test_output.rs rename to rust/main/hyperlane-core/src/test_output.rs diff --git a/rust/hyperlane-core/src/test_utils.rs b/rust/main/hyperlane-core/src/test_utils.rs similarity index 72% rename from rust/hyperlane-core/src/test_utils.rs rename to rust/main/hyperlane-core/src/test_utils.rs index c24d70419..242570680 100644 --- a/rust/hyperlane-core/src/test_utils.rs +++ b/rust/main/hyperlane-core/src/test_utils.rs @@ -3,7 +3,7 @@ use std::io::Read; use std::path::PathBuf; use crate::accumulator::merkle::Proof; -use crate::H256; +use crate::{HyperlaneDomain, H256}; /// Struct representing a single merkle test case #[derive(serde::Deserialize, serde::Serialize)] @@ -38,3 +38,15 @@ pub fn find_vector(final_component: &str) -> PathBuf { git_dir.join("vectors").join(final_component) } + +/// Create a dummy domain for testing purposes +pub fn dummy_domain(domain_id: u32, name: &str) -> HyperlaneDomain { + let test_domain = HyperlaneDomain::new_test_domain(name); + HyperlaneDomain::Unknown { + domain_id, + domain_name: name.to_owned(), + domain_type: test_domain.domain_type(), + domain_protocol: test_domain.domain_protocol(), + domain_technical_stack: test_domain.domain_technical_stack(), + } +} diff --git a/rust/hyperlane-core/src/traits/aggregation_ism.rs b/rust/main/hyperlane-core/src/traits/aggregation_ism.rs similarity index 100% rename from rust/hyperlane-core/src/traits/aggregation_ism.rs rename to rust/main/hyperlane-core/src/traits/aggregation_ism.rs diff --git a/rust/hyperlane-core/src/traits/ccip_read_ism.rs b/rust/main/hyperlane-core/src/traits/ccip_read_ism.rs similarity index 100% rename from rust/hyperlane-core/src/traits/ccip_read_ism.rs rename to rust/main/hyperlane-core/src/traits/ccip_read_ism.rs diff --git a/rust/hyperlane-core/src/traits/cursor.rs b/rust/main/hyperlane-core/src/traits/cursor.rs similarity index 100% rename from rust/hyperlane-core/src/traits/cursor.rs rename to rust/main/hyperlane-core/src/traits/cursor.rs diff --git a/rust/hyperlane-core/src/traits/db.rs b/rust/main/hyperlane-core/src/traits/db.rs similarity index 100% rename from rust/hyperlane-core/src/traits/db.rs rename to rust/main/hyperlane-core/src/traits/db.rs diff --git a/rust/hyperlane-core/src/traits/deployed.rs b/rust/main/hyperlane-core/src/traits/deployed.rs similarity index 100% rename from rust/hyperlane-core/src/traits/deployed.rs rename to rust/main/hyperlane-core/src/traits/deployed.rs diff --git a/rust/hyperlane-core/src/traits/encode.rs b/rust/main/hyperlane-core/src/traits/encode.rs similarity index 100% rename from rust/hyperlane-core/src/traits/encode.rs rename to rust/main/hyperlane-core/src/traits/encode.rs diff --git a/rust/hyperlane-core/src/traits/indexer.rs b/rust/main/hyperlane-core/src/traits/indexer.rs similarity index 100% rename from rust/hyperlane-core/src/traits/indexer.rs rename to rust/main/hyperlane-core/src/traits/indexer.rs diff --git a/rust/hyperlane-core/src/traits/interchain_gas.rs b/rust/main/hyperlane-core/src/traits/interchain_gas.rs similarity index 100% rename from rust/hyperlane-core/src/traits/interchain_gas.rs rename to rust/main/hyperlane-core/src/traits/interchain_gas.rs diff --git a/rust/hyperlane-core/src/traits/interchain_security_module.rs b/rust/main/hyperlane-core/src/traits/interchain_security_module.rs similarity index 100% rename from rust/hyperlane-core/src/traits/interchain_security_module.rs rename to rust/main/hyperlane-core/src/traits/interchain_security_module.rs diff --git a/rust/hyperlane-core/src/traits/mailbox.rs b/rust/main/hyperlane-core/src/traits/mailbox.rs similarity index 91% rename from rust/hyperlane-core/src/traits/mailbox.rs rename to rust/main/hyperlane-core/src/traits/mailbox.rs index d5e9081b6..83646e659 100644 --- a/rust/hyperlane-core/src/traits/mailbox.rs +++ b/rust/main/hyperlane-core/src/traits/mailbox.rs @@ -1,12 +1,11 @@ use std::fmt::Debug; -use std::num::NonZeroU64; use async_trait::async_trait; use derive_new::new; use crate::{ traits::TxOutcome, utils::domain_hash, BatchItem, ChainCommunicationError, ChainResult, - HyperlaneContract, HyperlaneMessage, QueueOperation, TxCostEstimate, H256, U256, + HyperlaneContract, HyperlaneMessage, QueueOperation, ReorgPeriod, TxCostEstimate, H256, U256, }; /// Interface for the Mailbox chain contract. Allows abstraction over different @@ -20,9 +19,9 @@ pub trait Mailbox: HyperlaneContract + Send + Sync + Debug { /// Gets the current leaf count of the merkle tree /// - /// - `lag` is how far behind the current block to query, if not specified + /// - `reorg_period` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn count(&self, lag: Option) -> ChainResult; + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult; /// Fetch the status of a message async fn delivered(&self, id: H256) -> ChainResult; diff --git a/rust/hyperlane-core/src/traits/merkle_tree_hook.rs b/rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs similarity index 56% rename from rust/hyperlane-core/src/traits/merkle_tree_hook.rs rename to rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs index 35f3fa4ef..183ca7383 100644 --- a/rust/hyperlane-core/src/traits/merkle_tree_hook.rs +++ b/rust/main/hyperlane-core/src/traits/merkle_tree_hook.rs @@ -1,11 +1,11 @@ use std::fmt::Debug; -use std::num::NonZeroU64; use async_trait::async_trait; use auto_impl::auto_impl; use crate::{ accumulator::incremental::IncrementalMerkle, ChainResult, Checkpoint, HyperlaneContract, + ReorgPeriod, }; /// Interface for the MerkleTreeHook chain contract. Allows abstraction over different @@ -15,19 +15,19 @@ use crate::{ pub trait MerkleTreeHook: HyperlaneContract + Send + Sync + Debug { /// Return the incremental merkle tree in storage /// - /// - `lag` is how far behind the current block to query, if not specified + /// - `reorg_period` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn tree(&self, lag: Option) -> ChainResult; + async fn tree(&self, reorg_period: &ReorgPeriod) -> ChainResult; /// Gets the current leaf count of the merkle tree /// - /// - `lag` is how far behind the current block to query, if not specified + /// - `reorg_period` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn count(&self, lag: Option) -> ChainResult; + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult; /// Get the latest checkpoint. /// - /// - `lag` is how far behind the current block to query, if not specified + /// - `reorg_period` is how far behind the current block to query, if not specified /// it will query at the latest block. - async fn latest_checkpoint(&self, lag: Option) -> ChainResult; + async fn latest_checkpoint(&self, reorg_period: &ReorgPeriod) -> ChainResult; } diff --git a/rust/hyperlane-core/src/traits/mod.rs b/rust/main/hyperlane-core/src/traits/mod.rs similarity index 100% rename from rust/hyperlane-core/src/traits/mod.rs rename to rust/main/hyperlane-core/src/traits/mod.rs diff --git a/rust/hyperlane-core/src/traits/multisig_ism.rs b/rust/main/hyperlane-core/src/traits/multisig_ism.rs similarity index 100% rename from rust/hyperlane-core/src/traits/multisig_ism.rs rename to rust/main/hyperlane-core/src/traits/multisig_ism.rs diff --git a/rust/hyperlane-core/src/traits/pending_operation.rs b/rust/main/hyperlane-core/src/traits/pending_operation.rs similarity index 91% rename from rust/hyperlane-core/src/traits/pending_operation.rs rename to rust/main/hyperlane-core/src/traits/pending_operation.rs index 599a0e400..e0a6622c3 100644 --- a/rust/hyperlane-core/src/traits/pending_operation.rs +++ b/rust/main/hyperlane-core/src/traits/pending_operation.rs @@ -13,6 +13,7 @@ use crate::{ }; use async_trait::async_trait; use num::CheckedDiv; +use prometheus::IntGauge; use strum::Display; use tracing::warn; @@ -60,9 +61,28 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { /// The domain this operation will take place on. fn destination_domain(&self) -> &HyperlaneDomain; + /// The sender address of this operation. + fn sender_address(&self) -> &H256; + + /// The recipient address of this operation. + fn recipient_address(&self) -> &H256; + /// Label to use for metrics granularity. fn app_context(&self) -> Option; + /// Get the metric associated with this operation. + fn get_metric(&self) -> Option>; + + /// Decrement the metric associated with this operation if it exists. + fn decrement_metric_if_exists(&self) { + if let Some(metric) = self.get_metric() { + metric.dec(); + } + } + + /// Set the metric associated with this operation. + fn set_metric(&mut self, metric: Arc); + /// The status of the operation, which should explain why it is in the /// queue. fn status(&self) -> PendingOperationStatus; @@ -70,6 +90,22 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { /// Set the status of the operation. fn set_status(&mut self, status: PendingOperationStatus); + /// Set the status of the operation and update the metrics. + fn set_status_and_update_metrics( + &mut self, + status: Option, + new_metric: Arc, + ) { + if let Some(status) = status { + self.set_status(status); + } + if let Some(old_metric) = self.get_metric() { + old_metric.dec(); + } + new_metric.inc(); + self.set_metric(new_metric); + } + /// Get tuple of labels for metrics. fn get_operation_labels(&self) -> (String, String) { let app_context = self.app_context().unwrap_or("Unknown".to_string()); @@ -83,7 +119,7 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { async fn prepare(&mut self) -> PendingOperationResult; /// Submit this operation to the blockchain - async fn submit(&mut self); + async fn submit(&mut self) -> PendingOperationResult; /// Set the outcome of the `submit` call fn set_submission_outcome(&mut self, outcome: TxOutcome); @@ -188,6 +224,9 @@ pub enum ReprepareReason { #[strum(to_string = "Error getting message metadata builder")] /// Error getting message metadata builder ErrorGettingMetadataBuilder, + #[strum(to_string = "Error submitting")] + /// Error submitting + ErrorSubmitting, #[strum(to_string = "Error building metadata")] /// Error building metadata ErrorBuildingMetadata, diff --git a/rust/hyperlane-core/src/traits/provider.rs b/rust/main/hyperlane-core/src/traits/provider.rs similarity index 62% rename from rust/hyperlane-core/src/traits/provider.rs rename to rust/main/hyperlane-core/src/traits/provider.rs index 654e80218..63b4d01dd 100644 --- a/rust/hyperlane-core/src/traits/provider.rs +++ b/rust/main/hyperlane-core/src/traits/provider.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use auto_impl::auto_impl; use thiserror::Error; -use crate::{BlockInfo, ChainInfo, ChainResult, HyperlaneChain, TxnInfo, H256, U256}; +use crate::{BlockInfo, ChainInfo, ChainResult, HyperlaneChain, TxnInfo, H256, H512, U256}; /// Interface for a provider. Allows abstraction over different provider types /// for different chains. @@ -16,11 +16,11 @@ use crate::{BlockInfo, ChainInfo, ChainResult, HyperlaneChain, TxnInfo, H256, U2 #[async_trait] #[auto_impl(&, Box, Arc)] pub trait HyperlaneProvider: HyperlaneChain + Send + Sync + Debug { - /// Get block info for a given block hash - async fn get_block_by_hash(&self, hash: &H256) -> ChainResult; + /// Get block info for a given block height + async fn get_block_by_height(&self, height: u64) -> ChainResult; /// Get txn info for a given txn hash - async fn get_txn_by_hash(&self, hash: &H256) -> ChainResult; + async fn get_txn_by_hash(&self, hash: &H512) -> ChainResult; /// Returns whether a contract exists at the provided address async fn is_contract(&self, address: &H256) -> ChainResult; @@ -35,13 +35,19 @@ pub trait HyperlaneProvider: HyperlaneChain + Send + Sync + Debug { /// Errors when querying for provider information. #[derive(Error, Debug)] pub enum HyperlaneProviderError { - /// The requested block hash is not yet known by the provider - #[error("Block is not part of chain yet {0:?}")] - BlockIsNotPartOfChainYet(H256), /// The provider did not return the gas which was used #[error("Provider did not return gas used")] NoGasUsed, - /// Could not find a transaction, block, or other object - #[error("Could not find object from provider with hash {0:?}")] - CouldNotFindObjectByHash(H256), + /// Could not find a transaction by hash + #[error("Could not find transaction from provider with hash {0:?}")] + CouldNotFindTransactionByHash(H512), + /// Could not find a block by height + #[error("Could not find block from provider with height {0:?}")] + CouldNotFindBlockByHeight(u64), + /// The requested block does not have its hash + #[error("Block with height {0:?} does not contain its hash")] + BlockWithoutHash(u64), + /// Incorrect block is received + #[error("Requested block with height {0:?}, received block with height {1:?}")] + IncorrectBlockByHeight(u64, u64), } diff --git a/rust/hyperlane-core/src/traits/routing_ism.rs b/rust/main/hyperlane-core/src/traits/routing_ism.rs similarity index 100% rename from rust/hyperlane-core/src/traits/routing_ism.rs rename to rust/main/hyperlane-core/src/traits/routing_ism.rs diff --git a/rust/hyperlane-core/src/traits/signing.rs b/rust/main/hyperlane-core/src/traits/signing.rs similarity index 99% rename from rust/hyperlane-core/src/traits/signing.rs rename to rust/main/hyperlane-core/src/traits/signing.rs index 34e5676c4..5859d3c25 100644 --- a/rust/hyperlane-core/src/traits/signing.rs +++ b/rust/main/hyperlane-core/src/traits/signing.rs @@ -1,10 +1,11 @@ +use std::fmt::{Debug, Formatter}; + use async_trait::async_trait; use auto_impl::auto_impl; use serde::{ ser::{SerializeStruct, Serializer}, Deserialize, Serialize, }; -use std::fmt::{Debug, Formatter}; use crate::utils::bytes_to_hex; use crate::{Signature, H160, H256}; diff --git a/rust/hyperlane-core/src/traits/validator_announce.rs b/rust/main/hyperlane-core/src/traits/validator_announce.rs similarity index 100% rename from rust/hyperlane-core/src/traits/validator_announce.rs rename to rust/main/hyperlane-core/src/traits/validator_announce.rs diff --git a/rust/main/hyperlane-core/src/types/account_address_type.rs b/rust/main/hyperlane-core/src/types/account_address_type.rs new file mode 100644 index 000000000..2e8079d70 --- /dev/null +++ b/rust/main/hyperlane-core/src/types/account_address_type.rs @@ -0,0 +1,11 @@ +/// Specifies the account address type +#[derive( + Clone, Debug, Default, strum::Display, strum::EnumString, strum::IntoStaticStr, strum::EnumIter, +)] +pub enum AccountAddressType { + /// Bitcoin style address: RIPEMD160(SHA256(pubkey)) + #[default] + Bitcoin, + /// Ethereum style address: KECCAK256(pubkey)[20] + Ethereum, +} diff --git a/rust/hyperlane-core/src/types/announcement.rs b/rust/main/hyperlane-core/src/types/announcement.rs similarity index 100% rename from rust/hyperlane-core/src/types/announcement.rs rename to rust/main/hyperlane-core/src/types/announcement.rs diff --git a/rust/main/hyperlane-core/src/types/block_id.rs b/rust/main/hyperlane-core/src/types/block_id.rs new file mode 100644 index 000000000..57f69045e --- /dev/null +++ b/rust/main/hyperlane-core/src/types/block_id.rs @@ -0,0 +1,17 @@ +use crate::H256; + +/// Struct `BlockId` contains two types of identifiers for the same block: hash and height. +#[derive(Debug, Default, Copy, Clone)] +pub struct BlockId { + /// Block hash + pub hash: H256, + /// Block height + pub height: u64, +} + +impl BlockId { + /// Creates instance of `BlockId` struct + pub fn new(hash: H256, height: u64) -> Self { + Self { hash, height } + } +} diff --git a/rust/hyperlane-core/src/types/chain_data.rs b/rust/main/hyperlane-core/src/types/chain_data.rs similarity index 97% rename from rust/hyperlane-core/src/types/chain_data.rs rename to rust/main/hyperlane-core/src/types/chain_data.rs index 5f5ecb2e3..219f6989d 100644 --- a/rust/hyperlane-core/src/types/chain_data.rs +++ b/rust/main/hyperlane-core/src/types/chain_data.rs @@ -1,6 +1,6 @@ use derive_new::new; -use crate::{H256, U256}; +use crate::{H256, H512, U256}; /// Info about a given block in the chain. #[derive(Debug, Clone, Default)] @@ -27,7 +27,7 @@ pub struct ChainInfo { #[derive(Debug, Clone)] pub struct TxnInfo { /// Hash of this transaction - pub hash: H256, + pub hash: H512, /// Amount of gas which was allocated for running the transaction pub gas_limit: U256, /// Represents the maximum tx fee that will go to the miner as part of the diff --git a/rust/hyperlane-core/src/types/checkpoint.rs b/rust/main/hyperlane-core/src/types/checkpoint.rs similarity index 100% rename from rust/hyperlane-core/src/types/checkpoint.rs rename to rust/main/hyperlane-core/src/types/checkpoint.rs diff --git a/rust/main/hyperlane-core/src/types/conversions.rs b/rust/main/hyperlane-core/src/types/conversions.rs new file mode 100644 index 000000000..5336e0e2b --- /dev/null +++ b/rust/main/hyperlane-core/src/types/conversions.rs @@ -0,0 +1,138 @@ +use uint::unroll; + +use crate::{H256, H512}; + +/// Creates a big-endian hex representation of the address +pub fn address_to_bytes(data: &H256) -> Vec { + if is_h160(data.as_fixed_bytes()) { + // take the last 20 bytes + data.as_fixed_bytes()[12..32].into() + } else { + h256_to_bytes(data) + } +} + +/// Creates a big-endian hex representation of the address +pub fn bytes_to_address(data: Vec) -> eyre::Result { + if (data.len() != 20) && (data.len() != 32) { + return Err(eyre::eyre!("Invalid address length")); + } + if data.len() == 20 { + let mut prefix = vec![0; 12]; + prefix.extend(data); + Ok(H256::from_slice(&prefix[..])) + } else { + Ok(H256::from_slice(&data[..])) + } +} + +/// Creates a big-endian hex representation of the address hash +pub fn h256_to_bytes(data: &H256) -> Vec { + data.as_fixed_bytes().as_slice().into() +} + +/// Creates a big-endian hex representation of the address hash +pub fn h512_to_bytes(data: &H512) -> Vec { + if is_h256(data.as_fixed_bytes()) { + // take the last 32 bytes + data.as_fixed_bytes()[32..64].into() + } else { + data.as_fixed_bytes().as_slice().into() + } +} + +/// Convert bytes into H512 with padding +pub fn bytes_to_h512(data: &[u8]) -> H512 { + assert!(data.len() <= 64); + + if data.len() == 64 { + return H512::from_slice(data); + } + + let mut buf = [0; 64]; + buf[64 - data.len()..64].copy_from_slice(data); + + H512::from_slice(&buf) +} + +/// Checks if a byte slice fits within 160 bits. Assumes a big-endian encoding; +/// ignores leading zeros. Current implementation only supports up to a 32 byte +/// array but this could easily be extended if needed. +pub const fn is_h160(data: &[u8; S]) -> bool { + assert!(S <= 32); + if S <= 20 { + true + } else { + let mut z = data[0]; + unroll! { + for i in 0..11 { + if S >= i + 22 { + z |= data[i + 1] + } + } + } + + z == 0 + } +} + +/// Checks if a byte slice fits within 32 bytes. Assumes a big-endian encoding; +/// ignores leading zeros. Current implementation only supports up to a 64 byte long +/// array but this could easily be extended if needed. +pub const fn is_h256(data: &[u8; S]) -> bool { + assert!(S <= 64); + if S <= 32 { + true + } else { + unroll! { + for i in 0..32 { + if data[i] != 0 { + return false; + } + } + } + + true + } +} + +#[cfg(test)] +mod test { + #[test] + fn is_h160() { + let v: [u8; 32] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, + ]; + assert!(super::is_h160(&v)); + + let v: [u8; 32] = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, + ]; + assert!(!super::is_h160(&v)); + } + + #[test] + fn is_h256() { + let v: [u8; 64] = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, 0x04, 0x1d, 0x05, 0x1e, + ]; + assert!(super::is_h256(&v)); + + let v: [u8; 64] = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0xfa, 0xd1, + 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, + 0x1c, 0xa8, 0x03, 0x1c, 0x04, 0x1d, 0x05, 0x1e, + ]; + assert!(!super::is_h256(&v)); + } +} diff --git a/rust/hyperlane-core/src/types/identifiers.rs b/rust/main/hyperlane-core/src/types/identifiers.rs similarity index 100% rename from rust/hyperlane-core/src/types/identifiers.rs rename to rust/main/hyperlane-core/src/types/identifiers.rs diff --git a/rust/hyperlane-core/src/types/indexing.rs b/rust/main/hyperlane-core/src/types/indexing.rs similarity index 100% rename from rust/hyperlane-core/src/types/indexing.rs rename to rust/main/hyperlane-core/src/types/indexing.rs diff --git a/rust/hyperlane-core/src/types/log_metadata.rs b/rust/main/hyperlane-core/src/types/log_metadata.rs similarity index 96% rename from rust/hyperlane-core/src/types/log_metadata.rs rename to rust/main/hyperlane-core/src/types/log_metadata.rs index e85824dab..3953ec1b2 100644 --- a/rust/hyperlane-core/src/types/log_metadata.rs +++ b/rust/main/hyperlane-core/src/types/log_metadata.rs @@ -53,6 +53,7 @@ impl From<&EthersLogMeta> for LogMeta { } // note: this ordering assumes both logs are part of the same blockchain. +#[allow(clippy::non_canonical_partial_ord_impl)] // TODO: `rustc` 1.80.1 clippy issue impl PartialOrd for LogMeta { fn partial_cmp(&self, other: &Self) -> Option { Some(match self.block_number.cmp(&other.block_number) { diff --git a/rust/hyperlane-core/src/types/merkle_tree.rs b/rust/main/hyperlane-core/src/types/merkle_tree.rs similarity index 100% rename from rust/hyperlane-core/src/types/merkle_tree.rs rename to rust/main/hyperlane-core/src/types/merkle_tree.rs diff --git a/rust/hyperlane-core/src/types/message.rs b/rust/main/hyperlane-core/src/types/message.rs similarity index 99% rename from rust/hyperlane-core/src/types/message.rs rename to rust/main/hyperlane-core/src/types/message.rs index 727fa6eb9..576e1eb48 100644 --- a/rust/hyperlane-core/src/types/message.rs +++ b/rust/main/hyperlane-core/src/types/message.rs @@ -97,7 +97,7 @@ impl From<&RawHyperlaneMessage> for HyperlaneMessage { let sender: [u8; 32] = m[9..41].try_into().unwrap(); let destination: [u8; 4] = m[41..45].try_into().unwrap(); let recipient: [u8; 32] = m[45..77].try_into().unwrap(); - let body = m[77..].try_into().unwrap(); + let body = m[77..].into(); Self { version, nonce: u32::from_be_bytes(nonce), diff --git a/rust/hyperlane-core/src/types/mod.rs b/rust/main/hyperlane-core/src/types/mod.rs similarity index 96% rename from rust/hyperlane-core/src/types/mod.rs rename to rust/main/hyperlane-core/src/types/mod.rs index 8987d9a86..84df52d18 100644 --- a/rust/hyperlane-core/src/types/mod.rs +++ b/rust/main/hyperlane-core/src/types/mod.rs @@ -1,29 +1,39 @@ -use serde::{Deserialize, Serialize}; use std::fmt; use std::io::{Read, Write}; use std::ops::Add; +use serde::{Deserialize, Serialize}; + pub use self::primitive_types::*; #[cfg(feature = "ethers")] pub use ::primitive_types as ethers_core_types; +pub use account_address_type::AccountAddressType; pub use announcement::*; +pub use block_id::BlockId; pub use chain_data::*; pub use checkpoint::*; +pub use conversions::*; pub use indexing::*; pub use log_metadata::*; pub use merkle_tree::*; pub use message::*; +pub use reorg::*; pub use transaction::*; use crate::{Decode, Encode, HyperlaneProtocolError}; +/// This module contains enum for account address type +mod account_address_type; mod announcement; +mod block_id; mod chain_data; mod checkpoint; +mod conversions; mod indexing; mod log_metadata; mod merkle_tree; mod message; +mod reorg; mod serialize; mod transaction; diff --git a/rust/hyperlane-core/src/types/primitive_types.rs b/rust/main/hyperlane-core/src/types/primitive_types.rs similarity index 99% rename from rust/hyperlane-core/src/types/primitive_types.rs rename to rust/main/hyperlane-core/src/types/primitive_types.rs index c5636b3b9..648119cdd 100644 --- a/rust/hyperlane-core/src/types/primitive_types.rs +++ b/rust/main/hyperlane-core/src/types/primitive_types.rs @@ -44,7 +44,7 @@ construct_uint! { mod fixed_hashes { // we can't change how they made the macro, so ignore the lint - #![allow(clippy::incorrect_clone_impl_on_copy_type)] + #![allow(clippy::non_canonical_clone_impl)] use borsh::{BorshDeserialize, BorshSerialize}; use fixed_hash::construct_fixed_hash; diff --git a/rust/main/hyperlane-core/src/types/reorg.rs b/rust/main/hyperlane-core/src/types/reorg.rs new file mode 100644 index 000000000..eb41844c0 --- /dev/null +++ b/rust/main/hyperlane-core/src/types/reorg.rs @@ -0,0 +1,20 @@ +use derive_new::new; +use serde::{Deserialize, Serialize}; + +use crate::{ReorgPeriod, H256}; + +/// Details about a detected chain reorg, from an agent's perspective +#[derive(Debug, Clone, Serialize, Deserialize, new)] +pub struct ReorgEvent { + /// the merkle root built from this agent's indexed events + pub local_merkle_root: H256, + /// the onchain merkle root + pub canonical_merkle_root: H256, + /// the index of the checkpoint when the reorg was detected + /// (due to a mismatch between local and canonical merkle roots) + pub checkpoint_index: u32, + /// the timestamp when the reorg was detected, in seconds since the Unix epoch + pub unix_timestamp: u64, + /// the reorg period configured for the agent + pub reorg_period: ReorgPeriod, +} diff --git a/rust/hyperlane-core/src/types/serialize.rs b/rust/main/hyperlane-core/src/types/serialize.rs similarity index 99% rename from rust/hyperlane-core/src/types/serialize.rs rename to rust/main/hyperlane-core/src/types/serialize.rs index 9ffb69994..73558e10c 100644 --- a/rust/hyperlane-core/src/types/serialize.rs +++ b/rust/main/hyperlane-core/src/types/serialize.rs @@ -1,4 +1,6 @@ #![allow(unused)] +#![allow(unexpected_cfgs)] // TODO: `rustc` 1.80.1 clippy issue + // Based on https://github.com/paritytech/parity-common/blob/7194def73feb7d97644303f1a6ddbab29bbb799f/primitive-types/impls/serde/src/serialize.rs // Copyright 2020 Parity Technologies diff --git a/rust/hyperlane-core/src/types/transaction.rs b/rust/main/hyperlane-core/src/types/transaction.rs similarity index 100% rename from rust/hyperlane-core/src/types/transaction.rs rename to rust/main/hyperlane-core/src/types/transaction.rs diff --git a/rust/hyperlane-core/src/utils.rs b/rust/main/hyperlane-core/src/utils.rs similarity index 100% rename from rust/hyperlane-core/src/utils.rs rename to rust/main/hyperlane-core/src/utils.rs diff --git a/rust/hyperlane-test/Cargo.toml b/rust/main/hyperlane-test/Cargo.toml similarity index 87% rename from rust/hyperlane-test/Cargo.toml rename to rust/main/hyperlane-test/Cargo.toml index cbf4b2cdc..300fa91d5 100644 --- a/rust/hyperlane-test/Cargo.toml +++ b/rust/main/hyperlane-test/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "hyperlane-test" documentation.workspace = true diff --git a/rust/hyperlane-test/src/lib.rs b/rust/main/hyperlane-test/src/lib.rs similarity index 77% rename from rust/hyperlane-test/src/lib.rs rename to rust/main/hyperlane-test/src/lib.rs index d86c53cc9..aa76ffae5 100644 --- a/rust/hyperlane-test/src/lib.rs +++ b/rust/main/hyperlane-test/src/lib.rs @@ -2,6 +2,7 @@ #![forbid(unsafe_code)] #![cfg_attr(test, warn(missing_docs))] +#![allow(unknown_lints)] // TODO: `rustc` 1.80.1 clippy issue #![forbid(where_clauses_object_safety)] /// Mock contracts diff --git a/rust/hyperlane-test/src/mocks/mailbox.rs b/rust/main/hyperlane-test/src/mocks/mailbox.rs similarity index 87% rename from rust/hyperlane-test/src/mocks/mailbox.rs rename to rust/main/hyperlane-test/src/mocks/mailbox.rs index dc09e1026..7333e2c8a 100644 --- a/rust/hyperlane-test/src/mocks/mailbox.rs +++ b/rust/main/hyperlane-test/src/mocks/mailbox.rs @@ -1,7 +1,5 @@ #![allow(non_snake_case)] -use std::num::NonZeroU64; - use async_trait::async_trait; use mockall::*; @@ -28,11 +26,11 @@ mock! { nonce: usize, ) -> ChainResult> {} - pub fn _tree(&self, maybe_lag: Option) -> ChainResult {} + pub fn _tree(&self, reorg_period: &ReorgPeriod) -> ChainResult {} - pub fn _count(&self, maybe_lag: Option) -> ChainResult {} + pub fn _count(&self, reorg_period: &ReorgPeriod) -> ChainResult {} - pub fn _latest_checkpoint(&self, maybe_lag: Option) -> ChainResult {} + pub fn _latest_checkpoint(&self, reorg_period: &ReorgPeriod) -> ChainResult {} pub fn _default_ism(&self) -> ChainResult {} pub fn _recipient_ism(&self, recipient: H256) -> ChainResult {} @@ -68,8 +66,8 @@ impl std::fmt::Debug for MockMailboxContract { #[async_trait] impl Mailbox for MockMailboxContract { - async fn count(&self, maybe_lag: Option) -> ChainResult { - self._count(maybe_lag) + async fn count(&self, reorg_period: &ReorgPeriod) -> ChainResult { + self._count(reorg_period) } async fn default_ism(&self) -> ChainResult { diff --git a/rust/hyperlane-test/src/mocks/mod.rs b/rust/main/hyperlane-test/src/mocks/mod.rs similarity index 100% rename from rust/hyperlane-test/src/mocks/mod.rs rename to rust/main/hyperlane-test/src/mocks/mod.rs diff --git a/rust/hyperlane-test/src/mocks/validator_announce.rs b/rust/main/hyperlane-test/src/mocks/validator_announce.rs similarity index 100% rename from rust/hyperlane-test/src/mocks/validator_announce.rs rename to rust/main/hyperlane-test/src/mocks/validator_announce.rs diff --git a/rust/main/rust-toolchain b/rust/main/rust-toolchain new file mode 100644 index 000000000..7f466bd2d --- /dev/null +++ b/rust/main/rust-toolchain @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.80.1" +profile = "default" diff --git a/rust/terraform/.gitignore b/rust/main/terraform/.gitignore similarity index 100% rename from rust/terraform/.gitignore rename to rust/main/terraform/.gitignore diff --git a/rust/terraform/README.md b/rust/main/terraform/README.md similarity index 100% rename from rust/terraform/README.md rename to rust/main/terraform/README.md diff --git a/rust/terraform/globals.tf b/rust/main/terraform/globals.tf similarity index 100% rename from rust/terraform/globals.tf rename to rust/main/terraform/globals.tf diff --git a/rust/terraform/main.tf b/rust/main/terraform/main.tf similarity index 100% rename from rust/terraform/main.tf rename to rust/main/terraform/main.tf diff --git a/rust/terraform/modules/efs/main.tf b/rust/main/terraform/modules/efs/main.tf similarity index 100% rename from rust/terraform/modules/efs/main.tf rename to rust/main/terraform/modules/efs/main.tf diff --git a/rust/terraform/modules/efs/outputs.tf b/rust/main/terraform/modules/efs/outputs.tf similarity index 100% rename from rust/terraform/modules/efs/outputs.tf rename to rust/main/terraform/modules/efs/outputs.tf diff --git a/rust/terraform/modules/efs/variables.tf b/rust/main/terraform/modules/efs/variables.tf similarity index 100% rename from rust/terraform/modules/efs/variables.tf rename to rust/main/terraform/modules/efs/variables.tf diff --git a/rust/terraform/modules/iam_kms/main.tf b/rust/main/terraform/modules/iam_kms/main.tf similarity index 100% rename from rust/terraform/modules/iam_kms/main.tf rename to rust/main/terraform/modules/iam_kms/main.tf diff --git a/rust/terraform/modules/iam_kms/outputs.tf b/rust/main/terraform/modules/iam_kms/outputs.tf similarity index 100% rename from rust/terraform/modules/iam_kms/outputs.tf rename to rust/main/terraform/modules/iam_kms/outputs.tf diff --git a/rust/terraform/modules/iam_kms/variables.tf b/rust/main/terraform/modules/iam_kms/variables.tf similarity index 100% rename from rust/terraform/modules/iam_kms/variables.tf rename to rust/main/terraform/modules/iam_kms/variables.tf diff --git a/rust/terraform/modules/s3/main.tf b/rust/main/terraform/modules/s3/main.tf similarity index 100% rename from rust/terraform/modules/s3/main.tf rename to rust/main/terraform/modules/s3/main.tf diff --git a/rust/terraform/modules/s3/outputs.tf b/rust/main/terraform/modules/s3/outputs.tf similarity index 100% rename from rust/terraform/modules/s3/outputs.tf rename to rust/main/terraform/modules/s3/outputs.tf diff --git a/rust/terraform/modules/s3/variables.tf b/rust/main/terraform/modules/s3/variables.tf similarity index 100% rename from rust/terraform/modules/s3/variables.tf rename to rust/main/terraform/modules/s3/variables.tf diff --git a/rust/terraform/modules/validator/main.tf b/rust/main/terraform/modules/validator/main.tf similarity index 100% rename from rust/terraform/modules/validator/main.tf rename to rust/main/terraform/modules/validator/main.tf diff --git a/rust/terraform/modules/validator/outputs.tf b/rust/main/terraform/modules/validator/outputs.tf similarity index 100% rename from rust/terraform/modules/validator/outputs.tf rename to rust/main/terraform/modules/validator/outputs.tf diff --git a/rust/terraform/modules/validator/variables.tf b/rust/main/terraform/modules/validator/variables.tf similarity index 100% rename from rust/terraform/modules/validator/variables.tf rename to rust/main/terraform/modules/validator/variables.tf diff --git a/rust/terraform/outputs.tf b/rust/main/terraform/outputs.tf similarity index 100% rename from rust/terraform/outputs.tf rename to rust/main/terraform/outputs.tf diff --git a/rust/terraform/variables.tf b/rust/main/terraform/variables.tf similarity index 100% rename from rust/terraform/variables.tf rename to rust/main/terraform/variables.tf diff --git a/rust/utils/abigen/Cargo.toml b/rust/main/utils/abigen/Cargo.toml similarity index 92% rename from rust/utils/abigen/Cargo.toml rename to rust/main/utils/abigen/Cargo.toml index beb601469..1f28d161e 100644 --- a/rust/utils/abigen/Cargo.toml +++ b/rust/main/utils/abigen/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "abigen" documentation.workspace = true diff --git a/rust/utils/abigen/src/lib.rs b/rust/main/utils/abigen/src/lib.rs similarity index 94% rename from rust/utils/abigen/src/lib.rs rename to rust/main/utils/abigen/src/lib.rs index b4b7970fd..3fe21a55b 100644 --- a/rust/utils/abigen/src/lib.rs +++ b/rust/main/utils/abigen/src/lib.rs @@ -104,12 +104,14 @@ pub fn generate_bindings( } #[cfg(feature = "fuels")] if build_type == BuildType::Fuels { + let abi = + fuels_code_gen::Abi::load_from(contract_path.as_ref()).expect("could not load abi"); let tokens = fuels_code_gen::Abigen::generate( - vec![fuels_code_gen::AbigenTarget { - name: contract_name.into(), - abi: abi_source.into(), - program_type: ProgramType::Contract, - }], + vec![fuels_code_gen::AbigenTarget::new( + contract_name.into(), + abi, + ProgramType::Contract, + )], false, ) .expect("could not generate bindings") diff --git a/rust/utils/backtrace-oneline/Cargo.toml b/rust/main/utils/backtrace-oneline/Cargo.toml similarity index 71% rename from rust/utils/backtrace-oneline/Cargo.toml rename to rust/main/utils/backtrace-oneline/Cargo.toml index 7da1749bb..18e96f2b8 100644 --- a/rust/utils/backtrace-oneline/Cargo.toml +++ b/rust/main/utils/backtrace-oneline/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "backtrace-oneline" version = "0.1.0" @@ -9,4 +7,4 @@ publish.workspace = true [dependencies] backtrace.workspace = true -derive-new.workspace = true \ No newline at end of file +derive-new.workspace = true diff --git a/rust/utils/backtrace-oneline/src/lib.rs b/rust/main/utils/backtrace-oneline/src/lib.rs similarity index 100% rename from rust/utils/backtrace-oneline/src/lib.rs rename to rust/main/utils/backtrace-oneline/src/lib.rs diff --git a/rust/main/utils/crypto/Cargo.toml b/rust/main/utils/crypto/Cargo.toml new file mode 100644 index 000000000..de5b64535 --- /dev/null +++ b/rust/main/utils/crypto/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "crypto" +documentation.workspace = true +edition.workspace = true +homepage.workspace = true +license-file.workspace = true +publish.workspace = true +version.workspace = true + +[dependencies] +elliptic-curve = { workspace = true, features = ["sec1"] } +hex = { workspace = true } +k256 = { workspace = true } +thiserror = { workspace = true } diff --git a/rust/main/utils/crypto/src/key.rs b/rust/main/utils/crypto/src/key.rs new file mode 100644 index 000000000..601431f28 --- /dev/null +++ b/rust/main/utils/crypto/src/key.rs @@ -0,0 +1,51 @@ +use std::fmt::{Display, Formatter}; + +use elliptic_curve::sec1::ToEncodedPoint; + +#[derive(Debug, thiserror::Error)] +pub enum PublicKeyError { + Decode(String), +} + +impl Display for PublicKeyError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Decompresses public key of secp256k1 if it was compressed +/// +/// Public key can be expressed in compressed or decompressed forms. +/// Compressed form contains one byte as prefix and x component of the public key. +/// Decompressed form contains one byte as prefix, x and y components of the public key. +pub fn decompress_public_key(public_key: &[u8]) -> Result, PublicKeyError> { + let elliptic: elliptic_curve::PublicKey = + elliptic_curve::PublicKey::from_sec1_bytes(public_key) + .map_err(|e| PublicKeyError::Decode(e.to_string()))?; + + // if public key was compressed, encoding into the point will decompress it. + let point = elliptic.to_encoded_point(false); + let decompressed = point.to_bytes().to_vec(); + Ok(decompressed) +} + +#[cfg(test)] +mod tests { + use crate::key::decompress_public_key; + + #[test] + fn test_decompress_public_key() { + // given + let compressed = "02962d010010b6eec66846322704181570d89e28236796579c535d2e44d20931f4"; + let hex = hex::decode(compressed).unwrap(); + + // when + let decompressed = hex::encode(decompress_public_key(&hex).unwrap()); + + // then + assert_eq!( + "04962d010010b6eec66846322704181570d89e28236796579c535d2e44d20931f40cb1152fb9e61ec7493a0d9a35d2e8a57198e109613854abdd3be5603d504008", + decompressed + ); + } +} diff --git a/rust/main/utils/crypto/src/lib.rs b/rust/main/utils/crypto/src/lib.rs new file mode 100644 index 000000000..edd7a4f4d --- /dev/null +++ b/rust/main/utils/crypto/src/lib.rs @@ -0,0 +1,4 @@ +pub use key::decompress_public_key; +pub use key::PublicKeyError; + +mod key; diff --git a/rust/utils/hex/Cargo.toml b/rust/main/utils/hex/Cargo.toml similarity index 78% rename from rust/utils/hex/Cargo.toml rename to rust/main/utils/hex/Cargo.toml index 0d1584a9f..9159b9e5d 100644 --- a/rust/utils/hex/Cargo.toml +++ b/rust/main/utils/hex/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "hex" version = "0.1.0" diff --git a/rust/utils/hex/src/lib.rs b/rust/main/utils/hex/src/lib.rs similarity index 87% rename from rust/utils/hex/src/lib.rs rename to rust/main/utils/hex/src/lib.rs index 46b4d2894..719a0b786 100644 --- a/rust/utils/hex/src/lib.rs +++ b/rust/main/utils/hex/src/lib.rs @@ -29,27 +29,6 @@ const FROM_HEX_CHARS: [u8; 256] = [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]; -/// Checks if a byte slice fits within 160 bits. Assumes a big-endian encoding; -/// ignores leading zeros. Current implementation only supports up to a 32 byte -/// array but this could easily be extended if needed. -pub const fn is_h160(data: &[u8; S]) -> bool { - assert!(S <= 32); - if S <= 20 { - true - } else { - let mut z = data[0]; - unroll! { - for i in 0..11 { - if S >= i + 22 { - z |= data[i + 1] - } - } - } - - z == 0 - } -} - /// This formats a 160bit byte slice as a lowercase hex string without any /// prefixing (will include leading zeros). pub fn format_h160_raw(data: &[u8; 20]) -> String { @@ -150,23 +129,6 @@ impl Error for InvalidHexCharacter {} mod test { use crate::FROM_HEX_CHARS; - #[test] - fn is_h160() { - let v: [u8; 32] = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, - 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, - 0x1c, 0xa8, 0x03, 0x1c, - ]; - assert!(super::is_h160(&v)); - - let v: [u8; 32] = [ - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xd1, - 0xc9, 0x44, 0x69, 0x70, 0x08, 0x33, 0x71, 0x7f, 0xa8, 0xa3, 0x01, 0x72, 0x78, 0xbc, - 0x1c, 0xa8, 0x03, 0x1c, - ]; - assert!(!super::is_h160(&v)); - } - #[test] fn format_h160() { let v: [u8; 20] = [ diff --git a/rust/utils/run-locally/Cargo.toml b/rust/main/utils/run-locally/Cargo.toml similarity index 87% rename from rust/utils/run-locally/Cargo.toml rename to rust/main/utils/run-locally/Cargo.toml index 06a2b5cdd..9dedae9ce 100644 --- a/rust/utils/run-locally/Cargo.toml +++ b/rust/main/utils/run-locally/Cargo.toml @@ -1,5 +1,3 @@ -cargo-features = ["workspace-inheritance"] - [package] name = "run-locally" documentation.workspace = true @@ -11,8 +9,8 @@ version.workspace = true [dependencies] hyperlane-base = { path = "../../hyperlane-base" } -hyperlane-core = { path = "../../hyperlane-core", features = ["float"]} -hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos"} +hyperlane-core = { path = "../../hyperlane-core", features = ["float"] } +hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } toml_edit.workspace = true k256.workspace = true jobserver.workspace = true @@ -35,7 +33,7 @@ ureq = { workspace = true, default-features = false } which.workspace = true macro_rules_attribute.workspace = true regex.workspace = true -relayer = { path = "../../agents/relayer"} +relayer = { path = "../../agents/relayer" } hyperlane-cosmwasm-interface.workspace = true cosmwasm-schema.workspace = true @@ -44,4 +42,4 @@ anyhow = { workspace = true } vergen = { version = "8.3.2", features = ["build", "git", "gitcl"] } [features] -cosmos = [] \ No newline at end of file +cosmos = [] diff --git a/rust/utils/run-locally/build.rs b/rust/main/utils/run-locally/build.rs similarity index 100% rename from rust/utils/run-locally/build.rs rename to rust/main/utils/run-locally/build.rs diff --git a/rust/utils/run-locally/src/config.rs b/rust/main/utils/run-locally/src/config.rs similarity index 100% rename from rust/utils/run-locally/src/config.rs rename to rust/main/utils/run-locally/src/config.rs diff --git a/rust/utils/run-locally/src/cosmos/cli.rs b/rust/main/utils/run-locally/src/cosmos/cli.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/cli.rs rename to rust/main/utils/run-locally/src/cosmos/cli.rs diff --git a/rust/utils/run-locally/src/cosmos/crypto.rs b/rust/main/utils/run-locally/src/cosmos/crypto.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/crypto.rs rename to rust/main/utils/run-locally/src/cosmos/crypto.rs diff --git a/rust/utils/run-locally/src/cosmos/deploy.rs b/rust/main/utils/run-locally/src/cosmos/deploy.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/deploy.rs rename to rust/main/utils/run-locally/src/cosmos/deploy.rs diff --git a/rust/utils/run-locally/src/cosmos/link.rs b/rust/main/utils/run-locally/src/cosmos/link.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/link.rs rename to rust/main/utils/run-locally/src/cosmos/link.rs diff --git a/rust/utils/run-locally/src/cosmos/mod.rs b/rust/main/utils/run-locally/src/cosmos/mod.rs similarity index 90% rename from rust/utils/run-locally/src/cosmos/mod.rs rename to rust/main/utils/run-locally/src/cosmos/mod.rs index 7ce697cb6..d78685333 100644 --- a/rust/utils/run-locally/src/cosmos/mod.rs +++ b/rust/main/utils/run-locally/src/cosmos/mod.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] // TODO: `rustc` 1.80.1 clippy issue + use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::thread::sleep; @@ -309,6 +311,7 @@ fn launch_cosmos_relayer( } #[apply(as_task)] +#[allow(clippy::let_and_return)] // TODO: `rustc` 1.80.1 clippy issue fn launch_cosmos_scraper( agent_config_path: String, chains: Vec, @@ -460,6 +463,12 @@ fn run_locally() { .unwrap() ); + // count all the dispatched messages + let mut dispatched_messages = 0; + + // dispatch the first batch of messages (before agents start) + dispatched_messages += dispatch(&osmosisd, linker, &nodes); + let config_dir = tempdir().unwrap(); // export agent config @@ -522,9 +531,51 @@ fn run_locally() { let starting_relayer_balance: f64 = agent_balance_sum(hpl_rly_metrics_port).unwrap(); - // dispatch messages - let mut dispatched_messages = 0; + // dispatch the second batch of messages (after agents start) + dispatched_messages += dispatch(&osmosisd, linker, &nodes); + + let _stack = CosmosHyperlaneStack { + validators: hpl_val.into_iter().map(|v| v.join()).collect(), + relayer: hpl_rly.join(), + scraper: hpl_scr.join(), + postgres, + }; + // Mostly copy-pasta from `rust/main/utils/run-locally/src/main.rs` + // TODO: refactor to share code + let loop_start = Instant::now(); + let mut failure_occurred = false; + loop { + // look for the end condition. + if termination_invariants_met( + hpl_rly_metrics_port, + hpl_scr_metrics_port, + dispatched_messages, + starting_relayer_balance, + ) + .unwrap_or(false) + { + // end condition reached successfully + break; + } else if (Instant::now() - loop_start).as_secs() > TIMEOUT_SECS { + // we ran out of time + log!("timeout reached before message submission was confirmed"); + failure_occurred = true; + break; + } + + sleep(Duration::from_secs(5)); + } + + if failure_occurred { + panic!("E2E tests failed"); + } else { + log!("E2E tests passed"); + } +} + +fn dispatch(osmosisd: &Path, linker: &str, nodes: &[CosmosNetwork]) -> u32 { + let mut dispatched_messages = 0; for node in nodes.iter() { let targets = nodes .iter() @@ -542,7 +593,7 @@ fn run_locally() { for target in targets { dispatched_messages += 1; let cli = OsmosisCLI::new( - osmosisd.clone(), + osmosisd.to_path_buf(), node.launch_resp.home_path.to_str().unwrap(), ); @@ -571,78 +622,43 @@ fn run_locally() { } } - let _stack = CosmosHyperlaneStack { - validators: hpl_val.into_iter().map(|v| v.join()).collect(), - relayer: hpl_rly.join(), - scraper: hpl_scr.join(), - postgres, - }; - - // Mostly copy-pasta from `rust/utils/run-locally/src/main.rs` - // TODO: refactor to share code - let loop_start = Instant::now(); - let mut failure_occurred = false; - loop { - // look for the end condition. - if termination_invariants_met( - hpl_rly_metrics_port, - dispatched_messages, - starting_relayer_balance, - ) - .unwrap_or(false) - { - // end condition reached successfully - break; - } else if (Instant::now() - loop_start).as_secs() > TIMEOUT_SECS { - // we ran out of time - log!("timeout reached before message submission was confirmed"); - failure_occurred = true; - break; - } - - sleep(Duration::from_secs(5)); - } - - if failure_occurred { - panic!("E2E tests failed"); - } else { - log!("E2E tests passed"); - } + dispatched_messages } fn termination_invariants_met( relayer_metrics_port: u32, + scraper_metrics_port: u32, messages_expected: u32, starting_relayer_balance: f64, ) -> eyre::Result { - let gas_payments_scraped = fetch_metric( + let expected_gas_payments = messages_expected; + let gas_payments_event_count = fetch_metric( &relayer_metrics_port.to_string(), "hyperlane_contract_sync_stored_events", &hashmap! {"data_type" => "gas_payment"}, )? .iter() .sum::(); - let expected_gas_payments = messages_expected; - if gas_payments_scraped != expected_gas_payments { + if gas_payments_event_count != expected_gas_payments { log!( "Relayer has indexed {} gas payments, expected {}", - gas_payments_scraped, + gas_payments_event_count, expected_gas_payments ); return Ok(false); } - let delivered_messages_scraped = fetch_metric( + let msg_processed_count = fetch_metric( &relayer_metrics_port.to_string(), "hyperlane_operations_processed_count", &hashmap! {"phase" => "confirmed"}, )? .iter() .sum::(); - if delivered_messages_scraped != messages_expected { + if msg_processed_count != messages_expected { log!( "Relayer confirmed {} submitted messages, expected {}", - delivered_messages_scraped, + msg_processed_count, messages_expected ); return Ok(false); @@ -664,6 +680,54 @@ fn termination_invariants_met( return Ok(false); } + let dispatched_messages_scraped = fetch_metric( + &scraper_metrics_port.to_string(), + "hyperlane_contract_sync_stored_events", + &hashmap! {"data_type" => "message_dispatch"}, + )? + .iter() + .sum::(); + if dispatched_messages_scraped != messages_expected { + log!( + "Scraper has scraped {} dispatched messages, expected {}", + dispatched_messages_scraped, + messages_expected + ); + return Ok(false); + } + + let gas_payments_scraped = fetch_metric( + &scraper_metrics_port.to_string(), + "hyperlane_contract_sync_stored_events", + &hashmap! {"data_type" => "gas_payment"}, + )? + .iter() + .sum::(); + if gas_payments_scraped != expected_gas_payments { + log!( + "Scraper has scraped {} gas payments, expected {}", + gas_payments_scraped, + expected_gas_payments + ); + return Ok(false); + } + + let delivered_messages_scraped = fetch_metric( + &scraper_metrics_port.to_string(), + "hyperlane_contract_sync_stored_events", + &hashmap! {"data_type" => "message_delivery"}, + )? + .iter() + .sum::(); + if delivered_messages_scraped != messages_expected { + log!( + "Scraper has scraped {} delivered messages, expected {}", + delivered_messages_scraped, + messages_expected + ); + return Ok(false); + } + log!("Termination invariants have been meet"); Ok(true) } diff --git a/rust/utils/run-locally/src/cosmos/rpc.rs b/rust/main/utils/run-locally/src/cosmos/rpc.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/rpc.rs rename to rust/main/utils/run-locally/src/cosmos/rpc.rs diff --git a/rust/utils/run-locally/src/cosmos/source.rs b/rust/main/utils/run-locally/src/cosmos/source.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/source.rs rename to rust/main/utils/run-locally/src/cosmos/source.rs diff --git a/rust/utils/run-locally/src/cosmos/types.rs b/rust/main/utils/run-locally/src/cosmos/types.rs similarity index 96% rename from rust/utils/run-locally/src/cosmos/types.rs rename to rust/main/utils/run-locally/src/cosmos/types.rs index 78a45e6c2..4ce8f42a5 100644 --- a/rust/utils/run-locally/src/cosmos/types.rs +++ b/rust/main/utils/run-locally/src/cosmos/types.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, path::PathBuf}; -use hyperlane_cosmos::RawCosmosAmount; +use hyperlane_cosmos::{NativeToken, RawCosmosAmount}; use hyperlane_cosmwasm_interface::types::bech32_decode; use super::{cli::OsmosisCLI, CosmosNetwork}; @@ -125,6 +125,7 @@ pub struct AgentConfig { pub index: AgentConfigIndex, pub gas_price: RawCosmosAmount, pub contract_address_bytes: usize, + pub native_token: NativeToken, } #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] @@ -178,6 +179,10 @@ impl AgentConfig { }, contract_address_bytes: 32, index: AgentConfigIndex { from: 1, chunk: 5 }, + native_token: NativeToken { + decimals: 6, + denom: "uosmo".to_string(), + }, } } } diff --git a/rust/utils/run-locally/src/cosmos/utils.rs b/rust/main/utils/run-locally/src/cosmos/utils.rs similarity index 100% rename from rust/utils/run-locally/src/cosmos/utils.rs rename to rust/main/utils/run-locally/src/cosmos/utils.rs diff --git a/rust/utils/run-locally/src/ethereum/mod.rs b/rust/main/utils/run-locally/src/ethereum/mod.rs similarity index 100% rename from rust/utils/run-locally/src/ethereum/mod.rs rename to rust/main/utils/run-locally/src/ethereum/mod.rs diff --git a/rust/utils/run-locally/src/ethereum/multicall.rs b/rust/main/utils/run-locally/src/ethereum/multicall.rs similarity index 100% rename from rust/utils/run-locally/src/ethereum/multicall.rs rename to rust/main/utils/run-locally/src/ethereum/multicall.rs diff --git a/rust/utils/run-locally/src/invariants.rs b/rust/main/utils/run-locally/src/invariants.rs similarity index 100% rename from rust/utils/run-locally/src/invariants.rs rename to rust/main/utils/run-locally/src/invariants.rs diff --git a/rust/utils/run-locally/src/invariants/common.rs b/rust/main/utils/run-locally/src/invariants/common.rs similarity index 100% rename from rust/utils/run-locally/src/invariants/common.rs rename to rust/main/utils/run-locally/src/invariants/common.rs diff --git a/rust/utils/run-locally/src/invariants/post_startup_invariants.rs b/rust/main/utils/run-locally/src/invariants/post_startup_invariants.rs similarity index 100% rename from rust/utils/run-locally/src/invariants/post_startup_invariants.rs rename to rust/main/utils/run-locally/src/invariants/post_startup_invariants.rs diff --git a/rust/utils/run-locally/src/invariants/termination_invariants.rs b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs similarity index 91% rename from rust/utils/run-locally/src/invariants/termination_invariants.rs rename to rust/main/utils/run-locally/src/invariants/termination_invariants.rs index a1c3e6316..248daec84 100644 --- a/rust/utils/run-locally/src/invariants/termination_invariants.rs +++ b/rust/main/utils/run-locally/src/invariants/termination_invariants.rs @@ -10,10 +10,14 @@ use relayer::GAS_EXPENDITURE_LOG_MESSAGE; use crate::invariants::SOL_MESSAGES_EXPECTED; use crate::logging::log; use crate::solana::solana_termination_invariants_met; -use crate::{fetch_metric, AGENT_LOGGING_DIR, ZERO_MERKLE_INSERTION_KATHY_MESSAGES}; +use crate::{ + fetch_metric, AGENT_LOGGING_DIR, RELAYER_METRICS_PORT, SCRAPER_METRICS_PORT, + ZERO_MERKLE_INSERTION_KATHY_MESSAGES, +}; /// Use the metrics to check if the relayer queues are empty and the expected /// number of messages have been sent. +#[allow(clippy::unnecessary_get_then_check)] // TODO: `rustc` 1.80.1 clippy issue pub fn termination_invariants_met( config: &Config, starting_relayer_balance: f64, @@ -28,7 +32,11 @@ pub fn termination_invariants_met( }; let total_messages_expected = eth_messages_expected + sol_messages_expected; - let lengths = fetch_metric("9092", "hyperlane_submitter_queue_length", &hashmap! {})?; + let lengths = fetch_metric( + RELAYER_METRICS_PORT, + "hyperlane_submitter_queue_length", + &hashmap! {}, + )?; assert!(!lengths.is_empty(), "Could not find queue length metric"); if lengths.iter().sum::() != ZERO_MERKLE_INSERTION_KATHY_MESSAGES { log!("Relayer queues not empty. Lengths: {:?}", lengths); @@ -37,10 +45,13 @@ pub fn termination_invariants_met( // Also ensure the counter is as expected (total number of messages), summed // across all mailboxes. - let msg_processed_count = - fetch_metric("9092", "hyperlane_messages_processed_count", &hashmap! {})? - .iter() - .sum::(); + let msg_processed_count = fetch_metric( + RELAYER_METRICS_PORT, + "hyperlane_messages_processed_count", + &hashmap! {}, + )? + .iter() + .sum::(); if msg_processed_count != total_messages_expected { log!( "Relayer has {} processed messages, expected {}", @@ -51,7 +62,7 @@ pub fn termination_invariants_met( } let gas_payment_events_count = fetch_metric( - "9092", + RELAYER_METRICS_PORT, "hyperlane_contract_sync_stored_events", &hashmap! {"data_type" => "gas_payments"}, )? @@ -113,7 +124,7 @@ pub fn termination_invariants_met( ); let gas_payment_sealevel_events_count = fetch_metric( - "9092", + RELAYER_METRICS_PORT, "hyperlane_contract_sync_stored_events", &hashmap! { "data_type" => "gas_payments", @@ -143,7 +154,7 @@ pub fn termination_invariants_met( } let dispatched_messages_scraped = fetch_metric( - "9093", + SCRAPER_METRICS_PORT, "hyperlane_contract_sync_stored_events", &hashmap! {"data_type" => "message_dispatch"}, )? @@ -159,7 +170,7 @@ pub fn termination_invariants_met( } let gas_payments_scraped = fetch_metric( - "9093", + SCRAPER_METRICS_PORT, "hyperlane_contract_sync_stored_events", &hashmap! {"data_type" => "gas_payment"}, )? @@ -179,7 +190,7 @@ pub fn termination_invariants_met( } let delivered_messages_scraped = fetch_metric( - "9093", + SCRAPER_METRICS_PORT, "hyperlane_contract_sync_stored_events", &hashmap! {"data_type" => "message_delivery"}, )? diff --git a/rust/utils/run-locally/src/logging.rs b/rust/main/utils/run-locally/src/logging.rs similarity index 100% rename from rust/utils/run-locally/src/logging.rs rename to rust/main/utils/run-locally/src/logging.rs diff --git a/rust/utils/run-locally/src/main.rs b/rust/main/utils/run-locally/src/main.rs similarity index 92% rename from rust/utils/run-locally/src/main.rs rename to rust/main/utils/run-locally/src/main.rs index 7cfbce96c..ac9166063 100644 --- a/rust/utils/run-locally/src/main.rs +++ b/rust/main/utils/run-locally/src/main.rs @@ -1,3 +1,5 @@ +#![allow(clippy::doc_lazy_continuation)] // TODO: `rustc` 1.80.1 clippy issue + //! Run this from the hyperlane-monorepo/rust directory using `cargo run -r -p //! run-locally`. //! @@ -86,11 +88,15 @@ const SEALEVEL_VALIDATOR_KEYS: &[&str] = &[ ]; const AGENT_BIN_PATH: &str = "target/debug"; -const INFRA_PATH: &str = "../typescript/infra"; -const MONOREPO_ROOT_PATH: &str = "../"; +const SOLANA_AGNET_BIN_PATH: &str = "../sealevel/target/debug/"; +const INFRA_PATH: &str = "../../typescript/infra"; +const MONOREPO_ROOT_PATH: &str = "../../"; const ZERO_MERKLE_INSERTION_KATHY_MESSAGES: u32 = 10; +const RELAYER_METRICS_PORT: &str = "9092"; +const SCRAPER_METRICS_PORT: &str = "9093"; + type DynPath = Box>; static RUN_LOG_WATCHERS: AtomicBool = AtomicBool::new(true); @@ -207,7 +213,7 @@ fn main() -> ExitCode { multicall_address_string, ) .hyp_env("CHAINS_TEST3_MAXBATCHSIZE", "5") - .hyp_env("METRICSPORT", "9092") + .hyp_env("METRICSPORT", RELAYER_METRICS_PORT) .hyp_env("DB", relayer_db.to_str().unwrap()) .hyp_env("CHAINS_TEST1_SIGNER_KEY", RELAYER_KEYS[0]) .hyp_env("CHAINS_TEST2_SIGNER_KEY", RELAYER_KEYS[1]) @@ -281,7 +287,7 @@ fn main() -> ExitCode { .hyp_env("CHAINS_TEST3_RPCCONSENSUSTYPE", "quorum") .hyp_env("CHAINS_TEST3_CUSTOMRPCURLS", "http://127.0.0.1:8545") .hyp_env("CHAINSTOSCRAPE", "test1,test2,test3") - .hyp_env("METRICSPORT", "9093") + .hyp_env("METRICSPORT", SCRAPER_METRICS_PORT) .hyp_env( "DB", "postgresql://postgres:47221c18c610@localhost:5432/postgres", @@ -307,7 +313,11 @@ fn main() -> ExitCode { // let solana_paths = if config.sealevel_enabled { - let (solana_path, solana_path_tempdir) = install_solana_cli_tools().join(); + let (solana_path, solana_path_tempdir) = install_solana_cli_tools( + SOLANA_CONTRACTS_CLI_RELEASE_URL.to_owned(), + SOLANA_CONTRACTS_CLI_VERSION.to_owned(), + ) + .join(); state.data.push(Box::new(solana_path_tempdir)); let solana_program_builder = build_solana_programs(solana_path.clone()); Some((solana_program_builder.join(), solana_path)) @@ -317,19 +327,13 @@ fn main() -> ExitCode { // this task takes a long time in the CI so run it in parallel log!("Building rust..."); - let build_rust = Program::new("cargo") + let build_main = Program::new("cargo") .cmd("build") .arg("features", "test-utils memory-profiling") .arg("bin", "relayer") .arg("bin", "validator") .arg("bin", "scraper") - .arg("bin", "init-db"); - let build_rust = if config.sealevel_enabled { - build_rust.arg("bin", "hyperlane-sealevel-client") - } else { - build_rust - }; - let build_rust = build_rust + .arg("bin", "init-db") .filter_logs(|l| !l.contains("workspace-inheritance")) .run(); @@ -346,11 +350,26 @@ fn main() -> ExitCode { .spawn("SQL", None); state.push_agent(postgres); - build_rust.join(); + build_main.join(); + if config.sealevel_enabled { + Program::new("cargo") + .working_dir("../sealevel") + .cmd("build") + .arg("bin", "hyperlane-sealevel-client") + .filter_logs(|l| !l.contains("workspace-inheritance")) + .run() + .join(); + } let solana_ledger_dir = tempdir().unwrap(); - let solana_config_path = if let Some((solana_program_path, solana_path)) = solana_paths.clone() - { + let solana_config_path = if let Some((solana_program_path, _)) = solana_paths.clone() { + // use the agave 2.x validator version to ensure mainnet compatibility + let (solana_path, solana_path_tempdir) = install_solana_cli_tools( + SOLANA_NETWORK_CLI_RELEASE_URL.to_owned(), + SOLANA_NETWORK_CLI_VERSION.to_owned(), + ) + .join(); + state.data.push(Box::new(solana_path_tempdir)); let start_solana_validator = start_solana_test_validator( solana_path.clone(), solana_program_path, diff --git a/rust/utils/run-locally/src/metrics.rs b/rust/main/utils/run-locally/src/metrics.rs similarity index 100% rename from rust/utils/run-locally/src/metrics.rs rename to rust/main/utils/run-locally/src/metrics.rs diff --git a/rust/utils/run-locally/src/program.rs b/rust/main/utils/run-locally/src/program.rs similarity index 100% rename from rust/utils/run-locally/src/program.rs rename to rust/main/utils/run-locally/src/program.rs diff --git a/rust/utils/run-locally/src/solana.rs b/rust/main/utils/run-locally/src/solana.rs similarity index 86% rename from rust/utils/run-locally/src/solana.rs rename to rust/main/utils/run-locally/src/solana.rs index 0ccca4f64..022d086a6 100644 --- a/rust/utils/run-locally/src/solana.rs +++ b/rust/main/utils/run-locally/src/solana.rs @@ -11,10 +11,16 @@ use tempfile::{tempdir, NamedTempFile}; use crate::logging::log; use crate::program::Program; use crate::utils::{as_task, concat_path, AgentHandles, ArbitraryData, TaskHandle}; -use crate::AGENT_BIN_PATH; +use crate::SOLANA_AGNET_BIN_PATH; + +/// Solana CLI version for compiling programs +pub const SOLANA_CONTRACTS_CLI_VERSION: &str = "1.14.20"; +pub const SOLANA_CONTRACTS_CLI_RELEASE_URL: &str = "github.com/solana-labs/solana"; + +/// Solana version used by mainnet validators +pub const SOLANA_NETWORK_CLI_VERSION: &str = "2.0.13"; +pub const SOLANA_NETWORK_CLI_RELEASE_URL: &str = "github.com/anza-xyz/agave"; -/// The Solana CLI tool version to download and use. -const SOLANA_CLI_VERSION: &str = "1.14.20"; const SOLANA_PROGRAM_LIBRARY_ARCHIVE: &str = "https://github.com/hyperlane-xyz/solana-program-library/releases/download/2024-08-23/spl.tar.gz"; @@ -48,12 +54,13 @@ const SOLANA_HYPERLANE_PROGRAMS: &[&str] = &[ "hyperlane-sealevel-igp", ]; -const SOLANA_KEYPAIR: &str = "config/test-sealevel-keys/test_deployer-keypair.json"; -const SOLANA_DEPLOYER_ACCOUNT: &str = "config/test-sealevel-keys/test_deployer-account.json"; +const SOLANA_KEYPAIR: &str = "../main/config/test-sealevel-keys/test_deployer-keypair.json"; +const SOLANA_DEPLOYER_ACCOUNT: &str = + "../main/config/test-sealevel-keys/test_deployer-account.json"; const SOLANA_WARPROUTE_TOKEN_CONFIG_FILE: &str = - "sealevel/environments/local-e2e/warp-routes/testwarproute/token-config.json"; -const SOLANA_CHAIN_CONFIG_FILE: &str = "sealevel/environments/local-e2e/chain-config.json"; -const SOLANA_ENVS_DIR: &str = "sealevel/environments"; + "../sealevel/environments/local-e2e/warp-routes/testwarproute/token-config.json"; +const SOLANA_CHAIN_CONFIG_FILE: &str = "../sealevel/environments/local-e2e/chain-config.json"; +const SOLANA_ENVS_DIR: &str = "../sealevel/environments"; const SOLANA_ENV_NAME: &str = "local-e2e"; @@ -66,14 +73,21 @@ const SOLANA_REMOTE_CHAIN_ID: &str = "13376"; pub const SOLANA_CHECKPOINT_LOCATION: &str = "/tmp/test_sealevel_checkpoints_0x70997970c51812dc3a010c7d01b50e0d17dc79c8"; -const SOLANA_OVERHEAD_CONFIG_FILE: &str = "sealevel/environments/local-e2e/overheads.json"; +const SOLANA_OVERHEAD_CONFIG_FILE: &str = "../sealevel/environments/local-e2e/overheads.json"; // Install the CLI tools and return the path to the bin dir. #[apply(as_task)] -pub fn install_solana_cli_tools() -> (PathBuf, impl ArbitraryData) { +pub fn install_solana_cli_tools( + release_url: String, + release_version: String, +) -> (PathBuf, impl ArbitraryData) { let solana_download_dir = tempdir().unwrap(); let solana_tools_dir = tempdir().unwrap(); - log!("Downloading solana cli release v{}", SOLANA_CLI_VERSION); + log!( + "Downloading solana cli release v{} from {}", + release_version, + release_url + ); let solana_release_name = { // best effort to pick one of the supported targets let target = if cfg!(target_os = "linux") { @@ -96,7 +110,9 @@ pub fn install_solana_cli_tools() -> (PathBuf, impl ArbitraryData) { Program::new("curl") .arg("output", &solana_archive_name) .flag("location") - .cmd(format!("https://github.com/solana-labs/solana/releases/download/v{SOLANA_CLI_VERSION}/{solana_archive_name}")) + .cmd(format!( + "https://{release_url}/releases/download/v{release_version}/{solana_archive_name}" + )) .flag("silent") .working_dir(solana_download_dir.as_ref().to_str().unwrap()) .run() @@ -159,7 +175,7 @@ pub fn build_solana_programs(solana_cli_tools_path: PathBuf) -> PathBuf { for &path in SOLANA_HYPERLANE_PROGRAMS { build_sbf .clone() - .working_dir(concat_path("sealevel/programs", path)) + .working_dir(concat_path("../sealevel/programs", path)) .run() .join(); } @@ -282,6 +298,7 @@ pub fn start_solana_test_validator( } #[apply(as_task)] +#[allow(clippy::get_first)] // TODO: `rustc` 1.80.1 clippy issue pub fn initiate_solana_hyperlane_transfer( solana_cli_tools_path: PathBuf, solana_config_path: PathBuf, @@ -359,13 +376,18 @@ pub fn solana_termination_invariants_met( .join("\n") .contains("Message delivered") } - fn sealevel_client(solana_cli_tools_path: &Path, solana_config_path: &Path) -> Program { - Program::new(concat_path(AGENT_BIN_PATH, "hyperlane-sealevel-client")) - .env("PATH", updated_path(solana_cli_tools_path)) - .env("RUST_BACKTRACE", "1") - .arg("config", solana_config_path.to_str().unwrap()) - .arg("keypair", SOLANA_KEYPAIR) + Program::new(concat_path( + SOLANA_AGNET_BIN_PATH, + "hyperlane-sealevel-client", + )) + .env("PATH", updated_path(solana_cli_tools_path)) + .env("RUST_BACKTRACE", "1") + .arg("config", solana_config_path.to_str().unwrap()) + .arg( + "keypair", + "config/test-sealevel-keys/test_deployer-keypair.json", + ) } fn updated_path(solana_cli_tools_path: &Path) -> String { diff --git a/rust/utils/run-locally/src/utils.rs b/rust/main/utils/run-locally/src/utils.rs similarity index 100% rename from rust/utils/run-locally/src/utils.rs rename to rust/main/utils/run-locally/src/utils.rs diff --git a/rust-toolchain b/rust/rust-toolchain similarity index 100% rename from rust-toolchain rename to rust/rust-toolchain diff --git a/rust/sealevel/.cargo/config.toml b/rust/sealevel/.cargo/config.toml new file mode 100644 index 000000000..bff29e6e1 --- /dev/null +++ b/rust/sealevel/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] diff --git a/rust/sealevel/.vscode/extensions.json b/rust/sealevel/.vscode/extensions.json new file mode 100644 index 000000000..c8e7623ea --- /dev/null +++ b/rust/sealevel/.vscode/extensions.json @@ -0,0 +1,13 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "rust-lang.rust-analyzer", + "tamasfe.even-better-toml", + "rust-lang.rust-analyzer", + ], + // List of extensions recommended by VS Code that should not be recommended for users of this workspace. + "unwantedRecommendations": [] +} diff --git a/rust/sealevel/.vscode/settings.json b/rust/sealevel/.vscode/settings.json new file mode 100644 index 000000000..785253018 --- /dev/null +++ b/rust/sealevel/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.exclude": { + "target": true, + }, +} diff --git a/rust/sealevel/Cargo.lock b/rust/sealevel/Cargo.lock new file mode 100644 index 000000000..81beeb35b --- /dev/null +++ b/rust/sealevel/Cargo.lock @@ -0,0 +1,7106 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "access-control" +version = "0.1.0" +dependencies = [ + "solana-program", +] + +[[package]] +name = "account-utils" +version = "0.1.0" +dependencies = [ + "borsh", + "solana-program", + "spl-type-length-value", +] + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array 0.14.7", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm-siv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "polyval", + "subtle", + "zeroize", +] + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.15", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + +[[package]] +name = "arrayref" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ascii" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" + +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "assert_matches" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" + +[[package]] +name = "async-compression" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-rwlock" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261803dcc39ba9e72760ba6e16d0199b1eef9fc44e81bffabbebb9f5aea3906c" +dependencies = [ + "async-mutex", + "event-listener", +] + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "async_io_stream" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" +dependencies = [ + "futures", + "pharos", + "rustc_version", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "auto_impl" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +dependencies = [ + "proc-macro-error", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base16ct" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" + +[[package]] +name = "base58" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" + +[[package]] +name = "base58check" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee2fe4c9a0c84515f136aaae2466744a721af6d63339c18689d9e995d74d99b" +dependencies = [ + "base58", + "sha2 0.8.2", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bech32" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" + +[[package]] +name = "bigdecimal" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d712318a27c7150326677b321a5fa91b55f6d9034ffd67f20319e147d40cee" +dependencies = [ + "autocfg", + "libm", + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitmaps" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2" +dependencies = [ + "typenum", +] + +[[package]] +name = "bitvec" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41262f11d771fd4a61aa3ce019fca363b4b6c282fca9da2a31186d3965a47a5c" +dependencies = [ + "either", + "radium 0.3.0", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium 0.7.0", + "tap", + "wyz", +] + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake3" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729b71f35bd3fa1a4c86b85d32c8b9069ea7fe14f7a53cfabb65f62d4265b888" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "digest 0.10.7", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding 0.1.5", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding 0.2.1", + "generic-array 0.14.7", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "borsh" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15bf3650200d8bffa99015595e10f1fbd17de07abbc25bb067da79e769939bfa" +dependencies = [ + "borsh-derive", + "hashbrown 0.11.2", +] + +[[package]] +name = "borsh-derive" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6441c552f230375d18e3cc377677914d2ca2b0d36e52129fe15450a2dce46775" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2 1.0.86", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "brotli" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "git+https://github.com/fitzgen/bumpalo?tag=3.14.0#c610d5adc54b9428465d72c4666c305df04c792a" + +[[package]] +name = "bv" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" +dependencies = [ + "feature-probe", + "serde", +] + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +dependencies = [ + "serde", +] + +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "caps" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +dependencies = [ + "libc", + "thiserror", +] + +[[package]] +name = "cargo-platform" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "chrono-humanize" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799627e6b4d27827a814e837b9d8a504832086081806d45b1afa34dc982b023b" +dependencies = [ + "chrono", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap 0.11.0", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex 0.2.4", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap 0.16.1", +] + +[[package]] +name = "clap" +version = "4.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa" +dependencies = [ + "anstream", + "anstyle", + "clap_lex 0.6.0", + "strsim 0.10.0", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "coins-bip32" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634c509653de24b439672164bbf56f5f582a2ab0e313d3b0f6af0b7345cf2560" +dependencies = [ + "bincode", + "bs58 0.4.0", + "coins-core", + "digest 0.10.7", + "getrandom 0.2.15", + "hmac 0.12.1", + "k256", + "lazy_static", + "serde", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "coins-bip39" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" +dependencies = [ + "bitvec 0.17.4", + "coins-bip32", + "getrandom 0.2.15", + "hex", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "rand 0.8.5", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "coins-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94090a6663f224feae66ab01e41a2555a8296ee07b5f20dab8888bdefc9f617" +dependencies = [ + "base58check", + "base64 0.12.3", + "bech32", + "blake2", + "digest 0.10.7", + "generic-array 0.14.7", + "hex", + "ripemd", + "serde", + "serde_derive", + "sha2 0.10.8", + "sha3 0.10.8", + "thiserror", +] + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "combine" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" +dependencies = [ + "ascii", + "byteorder", + "either", + "memchr", + "unreachable", +] + +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "console_log" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89f72f65e8501878b8a004d5a1afb780987e2ce2b4532c562e367a72c57499f" +dependencies = [ + "log", + "web-sys", +] + +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "constant_time_eq" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array 0.14.7", + "rand_core 0.6.4", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.7", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.2" +source = "git+https://github.com/Eclipse-Laboratories-Inc/curve25519-dalek?branch=v3.2.2-relax-zeroize#5154e5d02be0d9a7486dde86d67ff0327511c717" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "serde", + "subtle", + "zeroize", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", + "rayon", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid 0.7.1", +] + +[[package]] +name = "der" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" +dependencies = [ + "const-oid 0.9.6", + "zeroize", +] + +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint 0.4.6", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivation-path" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" + +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "dialoguer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +dependencies = [ + "console", + "shell-words", + "tempfile", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dir-diff" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ad16bf5f84253b50d6557681c58c3ab67c47c77d39fed9aeb56e947290bd10" +dependencies = [ + "walkdir", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "dlopen" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +dependencies = [ + "dlopen_derive", + "lazy_static", + "libc", + "winapi", +] + +[[package]] +name = "dlopen_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" +dependencies = [ + "libc", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "eager" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" + +[[package]] +name = "ecdsa" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" +dependencies = [ + "der 0.6.1", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ecdsa-signature" +version = "0.1.0" +dependencies = [ + "getrandom 0.2.15", + "hyperlane-core", + "solana-program", + "thiserror", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "git+https://github.com/Eclipse-Laboratories-Inc/ed25519-dalek?branch=main#7529d65506147b6cb24ca6d8f4fc062cac33b395" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "ed25519-dalek-bip32" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2be62a4061b872c8c0873ee4fc6f101ce7b889d039f019c5fa2af471a59908" +dependencies = [ + "derivation-path", + "ed25519-dalek", + "hmac 0.12.1", + "sha2 0.10.8", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" +dependencies = [ + "base16ct", + "crypto-bigint", + "der 0.6.1", + "digest 0.10.7", + "ff", + "generic-array 0.14.7", + "group", + "pkcs8 0.9.0", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enum-iterator" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2953d1df47ac0eb70086ccabf0275aa8da8591a28bd358ee2b52bd9f9e3ff9e9" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "eth-keystore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" +dependencies = [ + "aes", + "ctr", + "digest 0.10.7", + "hex", + "hmac 0.12.1", + "pbkdf2 0.11.0", + "rand 0.8.5", + "scrypt", + "serde", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", + "thiserror", + "uuid", +] + +[[package]] +name = "ethabi" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" +dependencies = [ + "ethereum-types", + "hex", + "once_cell", + "regex", + "serde", + "serde_json", + "sha3 0.10.8", + "thiserror", + "uint", +] + +[[package]] +name = "ethbloom" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" +dependencies = [ + "crunchy", + "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scale-info", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" +dependencies = [ + "ethbloom", + "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-serde 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types", + "scale-info", + "uint", +] + +[[package]] +name = "ethers" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "ethers-addressbook", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-middleware", + "ethers-providers", + "ethers-signers", +] + +[[package]] +name = "ethers-addressbook" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "ethers-core", + "once_cell", + "serde", + "serde_json", +] + +[[package]] +name = "ethers-contract" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "ethers-contract-abigen", + "ethers-contract-derive", + "ethers-core", + "ethers-providers", + "futures-util", + "hex", + "once_cell", + "pin-project", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "ethers-contract-abigen" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "Inflector", + "cfg-if", + "dunce", + "ethers-core", + "eyre", + "getrandom 0.2.15", + "hex", + "proc-macro2 1.0.86", + "quote 1.0.37", + "regex", + "reqwest", + "serde", + "serde_json", + "syn 1.0.109", + "toml", + "url", + "walkdir", +] + +[[package]] +name = "ethers-contract-derive" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "ethers-contract-abigen", + "ethers-core", + "hex", + "proc-macro2 1.0.86", + "quote 1.0.37", + "serde_json", + "syn 1.0.109", +] + +[[package]] +name = "ethers-core" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "arrayvec", + "bytes", + "cargo_metadata", + "chrono", + "convert_case 0.6.0", + "elliptic-curve", + "ethabi", + "generic-array 0.14.7", + "hex", + "k256", + "once_cell", + "open-fastrlp", + "proc-macro2 1.0.86", + "rand 0.8.5", + "rlp", + "rlp-derive", + "serde", + "serde_json", + "strum 0.24.1", + "syn 1.0.109", + "thiserror", + "tiny-keccak", + "unicode-xid 0.2.5", +] + +[[package]] +name = "ethers-etherscan" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "ethers-core", + "getrandom 0.2.15", + "reqwest", + "semver", + "serde", + "serde-aux", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "ethers-middleware" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "async-trait", + "auto_impl 0.5.0", + "ethers-contract", + "ethers-core", + "ethers-etherscan", + "ethers-providers", + "ethers-signers", + "futures-locks", + "futures-util", + "instant", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", +] + +[[package]] +name = "ethers-providers" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "async-trait", + "auto_impl 1.2.0", + "base64 0.13.1", + "ethers-core", + "futures-core", + "futures-timer", + "futures-util", + "getrandom 0.2.15", + "hashers", + "hex", + "http", + "once_cell", + "parking_lot 0.11.2", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "tracing-futures", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-timer", + "web-sys", + "ws_stream_wasm", +] + +[[package]] +name = "ethers-signers" +version = "1.0.2" +source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2024-04-25#361b69b9561e11eb3cf8000a51de1985e2571785" +dependencies = [ + "async-trait", + "coins-bip32", + "coins-bip39", + "elliptic-curve", + "eth-keystore", + "ethers-core", + "hex", + "rand 0.8.5", + "sha2 0.10.8", + "thiserror", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "eyre" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "feature-probe" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" + +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "flate2" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-locks" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ec6fe3675af967e67c5536c0b9d44e34e6c52f86bedc4ea49c5317b8e94d06" +dependencies = [ + "futures-channel", + "futures-task", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "serde", + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "goblin" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" +dependencies = [ + "log", + "plain", + "scroll", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.5.0", + "slab", + "tokio", + "tokio-util 0.7.12", + "tracing", +] + +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashers" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" +dependencies = [ + "fxhash", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "histogram" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cb882ccb290b8646e554b157ab0b71e64e8d5bef775cd66b6531e52d302669" + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array 0.14.7", + "hmac 0.8.1", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyperlane-core" +version = "0.1.0" +dependencies = [ + "async-rwlock", + "async-trait", + "auto_impl 1.2.0", + "bigdecimal", + "borsh", + "bs58 0.5.1", + "bytes", + "convert_case 0.6.0", + "derive-new", + "derive_more", + "eyre", + "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.2.15", + "hex", + "itertools 0.13.0", + "num 0.4.3", + "num-derive 0.4.2", + "num-traits", + "prometheus", + "serde", + "serde_json", + "sha3 0.10.8", + "strum 0.26.3", + "thiserror", + "tiny-keccak", + "tracing", + "typetag", + "uint", +] + +[[package]] +name = "hyperlane-sealevel-client" +version = "0.1.0" +dependencies = [ + "account-utils", + "bincode", + "borsh", + "bs58 0.5.1", + "clap 4.4.17", + "ethers", + "hex", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-hello-world", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-multisig-ism-message-id", + "hyperlane-sealevel-token", + "hyperlane-sealevel-token-collateral", + "hyperlane-sealevel-token-lib", + "hyperlane-sealevel-token-native", + "hyperlane-sealevel-validator-announce", + "pretty_env_logger", + "serde", + "serde_json", + "solana-clap-utils", + "solana-cli-config", + "solana-client", + "solana-program", + "solana-sdk", + "solana-transaction-status", +] + +[[package]] +name = "hyperlane-sealevel-connection-client" +version = "0.1.0" +dependencies = [ + "access-control", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "solana-program", +] + +[[package]] +name = "hyperlane-sealevel-hello-world" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-test-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-noop", +] + +[[package]] +name = "hyperlane-sealevel-igp" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "borsh", + "getrandom 0.2.15", + "hyperlane-core", + "num-derive 0.4.2", + "num-traits", + "serde", + "serializable-account-meta", + "solana-program", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-igp-test" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-igp", + "hyperlane-test-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", +] + +[[package]] +name = "hyperlane-sealevel-interchain-security-module-interface" +version = "0.1.0" +dependencies = [ + "borsh", + "solana-program", + "spl-type-length-value", +] + +[[package]] +name = "hyperlane-sealevel-mailbox" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "base64 0.21.7", + "blake3", + "borsh", + "getrandom 0.2.15", + "hyperlane-core", + "hyperlane-sealevel-interchain-security-module-interface", + "hyperlane-sealevel-message-recipient-interface", + "itertools 0.13.0", + "log", + "num-derive 0.4.2", + "num-traits", + "proc-macro-crate 1.2.1", + "serde", + "serializable-account-meta", + "solana-program", + "spl-noop", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-mailbox-test" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "base64 0.21.7", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-interchain-security-module-interface", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-sealevel-test-ism", + "hyperlane-sealevel-test-send-receiver", + "hyperlane-test-utils", + "itertools 0.13.0", + "log", + "num-derive 0.4.2", + "num-traits", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-noop", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-message-recipient-interface" +version = "0.1.0" +dependencies = [ + "borsh", + "getrandom 0.2.15", + "hyperlane-core", + "solana-program", + "spl-type-length-value", +] + +[[package]] +name = "hyperlane-sealevel-multisig-ism-message-id" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "borsh", + "ecdsa-signature", + "hex", + "hyperlane-core", + "hyperlane-sealevel-interchain-security-module-interface", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-multisig-ism-message-id", + "hyperlane-test-utils", + "multisig-ism", + "num-derive 0.4.2", + "num-traits", + "rand 0.8.5", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-test-ism" +version = "0.1.0" +dependencies = [ + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-interchain-security-module-interface", + "hyperlane-sealevel-mailbox", + "hyperlane-test-transaction-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", +] + +[[package]] +name = "hyperlane-sealevel-test-send-receiver" +version = "0.1.0" +dependencies = [ + "account-utils", + "borsh", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-test-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-noop", +] + +[[package]] +name = "hyperlane-sealevel-token" +version = "0.1.0" +dependencies = [ + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-sealevel-test-ism", + "hyperlane-sealevel-token-lib", + "hyperlane-test-utils", + "num-derive 0.4.2", + "num-traits", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-associated-token-account", + "spl-noop", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-token-collateral" +version = "0.1.0" +dependencies = [ + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-sealevel-test-ism", + "hyperlane-sealevel-token-lib", + "hyperlane-test-utils", + "num-derive 0.4.2", + "num-traits", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-associated-token-account", + "spl-noop", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-token-lib" +version = "0.1.0" +dependencies = [ + "access-control", + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "num-derive 0.4.2", + "num-traits", + "serializable-account-meta", + "solana-program", + "spl-associated-token-account", + "spl-noop", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-token-native" +version = "0.1.0" +dependencies = [ + "account-utils", + "borsh", + "hyperlane-core", + "hyperlane-sealevel-connection-client", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-sealevel-test-ism", + "hyperlane-sealevel-token-lib", + "hyperlane-test-utils", + "num-derive 0.4.2", + "num-traits", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-noop", + "tarpc", + "thiserror", +] + +[[package]] +name = "hyperlane-sealevel-validator-announce" +version = "0.1.0" +dependencies = [ + "account-utils", + "borsh", + "ecdsa-signature", + "hex", + "hyperlane-core", + "hyperlane-sealevel-mailbox", + "hyperlane-test-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "hyperlane-test-transaction-utils" +version = "0.1.0" +dependencies = [ + "solana-program", + "solana-program-test", + "solana-sdk", +] + +[[package]] +name = "hyperlane-test-utils" +version = "0.1.0" +dependencies = [ + "borsh", + "hyperlane-core", + "hyperlane-sealevel-igp", + "hyperlane-sealevel-interchain-security-module-interface", + "hyperlane-sealevel-mailbox", + "hyperlane-sealevel-message-recipient-interface", + "hyperlane-sealevel-test-ism", + "hyperlane-test-transaction-utils", + "serializable-account-meta", + "solana-program", + "solana-program-test", + "solana-sdk", + "spl-noop", + "spl-token-2022", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "im" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" +dependencies = [ + "bitmaps", + "rand_core 0.6.4", + "rand_xoshiro", + "rayon", + "serde", + "sized-chunks", + "typenum", + "version_check", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-rlp" +version = "0.3.0" +source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" +dependencies = [ + "rlp", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-serde" +version = "0.4.0" +source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" +dependencies = [ + "serde", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "index_list" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6ba961c14e98151cd6416dd3685efe786a94c38bc1a535c06ceff0a1600813" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[package]] +name = "indicatif" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" +dependencies = [ + "console", + "lazy_static", + "number_prefix", + "regex", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array 0.14.7", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "inventory" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" + +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "k256" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.8", + "sha3 0.10.8", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall 0.5.3", +] + +[[package]] +name = "libsecp256k1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" +dependencies = [ + "arrayref", + "base64 0.12.3", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + +[[package]] +name = "lz4" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958b4caa893816eea05507c20cfe47574a43d9a697138a7872990bba8a0ece68" +dependencies = [ + "libc", + "lz4-sys", +] + +[[package]] +name = "lz4-sys" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109de74d5d2353660401699a4174a4ff23fcc649caf553df71933c7fb45ad868" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "merlin" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" +dependencies = [ + "byteorder", + "keccak", + "rand_core 0.6.4", + "zeroize", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "multisig-ism" +version = "0.1.0" +dependencies = [ + "borsh", + "ecdsa-signature", + "hex", + "hyperlane-core", + "solana-program", + "spl-type-length-value", + "thiserror", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint 0.2.6", + "num-complex 0.2.4", + "num-integer", + "num-iter", + "num-rational 0.2.4", + "num-traits", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.6", + "num-complex 0.4.6", + "num-integer", + "num-iter", + "num-rational 0.4.2", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint 0.2.6", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.2.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate 1.2.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "open-fastrlp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" +dependencies = [ + "arrayvec", + "auto_impl 1.2.0", + "bytes", + "ethereum-types", + "open-fastrlp-derive", +] + +[[package]] +name = "open-fastrlp-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" +dependencies = [ + "bytes", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "opentelemetry" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" +dependencies = [ + "async-trait", + "crossbeam-channel", + "futures-channel", + "futures-executor", + "futures-util", + "js-sys", + "lazy_static", + "percent-encoding", + "pin-project", + "rand 0.8.5", + "thiserror", +] + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec 1.0.1", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.3", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" +dependencies = [ + "crypto-mac", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.7", + "hmac 0.12.1", + "password-hash", + "sha2 0.10.8", +] + +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "percentage" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd23b938276f14057220b707937bcb42fa76dda7560e57a2da30cb52d557937" +dependencies = [ + "num 0.2.1", +] + +[[package]] +name = "pharos" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" +dependencies = [ + "futures", + "rustc_version", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der 0.5.1", + "spki 0.5.4", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" +dependencies = [ + "der 0.6.1", + "spki 0.6.0", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug 0.3.1", + "universal-hash", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty_env_logger" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +dependencies = [ + "env_logger 0.10.2", + "log", +] + +[[package]] +name = "primitive-types" +version = "0.12.1" +source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" +dependencies = [ + "fixed-hash 0.8.0 (git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane)", + "impl-codec 0.6.0 (git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane)", + "impl-rlp 0.3.0 (git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane)", + "impl-serde 0.4.0 (git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane)", + "scale-info", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot 0.12.3", + "protobuf", + "thiserror", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "quinn" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "fxhash", + "quinn-proto", + "quinn-udp", + "rustls 0.20.9", + "thiserror", + "tokio", + "tracing", + "webpki", +] + +[[package]] +name = "quinn-proto" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fce546b9688f767a57530652488420d419a8b1f44a478b451c3d1ab6d992a55" +dependencies = [ + "bytes", + "fxhash", + "rand 0.8.5", + "ring 0.16.20", + "rustls 0.20.9", + "rustls-native-certs", + "rustls-pemfile 0.2.1", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki", +] + +[[package]] +name = "quinn-udp" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07946277141531aea269befd949ed16b2c85a780ba1043244eda0969e538e54" +dependencies = [ + "futures-util", + "libc", + "quinn-proto", + "socket2 0.4.10", + "tokio", + "tracing", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2 1.0.86", +] + +[[package]] +name = "radium" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "def50a86306165861203e7f84ecffbbdfdea79f0e51039b33de1e952358c47ac" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rcgen" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" +dependencies = [ + "pem", + "ring 0.16.20", + "time", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.6.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "async-compression", + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-rustls 0.24.1", + "tokio-util 0.7.12", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg", +] + +[[package]] +name = "rfc6979" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" +dependencies = [ + "crypto-bigint", + "hmac 0.12.1", + "zeroize", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "spin 0.9.8", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rlp-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "rpassword" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" +dependencies = [ + "libc", + "serde", + "serde_json", + "winapi", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring 0.17.8", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile 1.0.4", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "salsa20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" +dependencies = [ + "cipher", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scale-info" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +dependencies = [ + "cfg-if", + "derive_more", + "parity-scale-codec", + "scale-info-derive", +] + +[[package]] +name = "scale-info-derive" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +dependencies = [ + "proc-macro-crate 3.2.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "scrypt" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" +dependencies = [ + "hmac 0.12.1", + "pbkdf2 0.11.0", + "salsa20", + "sha2 0.10.8", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "sec1" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" +dependencies = [ + "base16ct", + "der 0.6.1", + "generic-array 0.14.7", + "pkcs8 0.9.0", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "send_wrapper" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-aux" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "serde_bytes" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap 1.9.3", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "serializable-account-meta" +version = "0.1.0" +dependencies = [ + "borsh", + "solana-program", +] + +[[package]] +name = "sha-1" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.1", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug 0.3.1", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "sized-chunks" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e" +dependencies = [ + "bitmaps", + "typenum", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "solana-account-decoder" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "Inflector", + "base64 0.13.1", + "bincode", + "bs58 0.4.0", + "bv", + "lazy_static", + "serde", + "serde_derive", + "serde_json", + "solana-address-lookup-table-program", + "solana-config-program", + "solana-sdk", + "solana-vote-program", + "spl-token", + "spl-token-2022", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-address-lookup-table-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "bytemuck", + "log", + "num-derive 0.3.3", + "num-traits", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-program", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-banks-client" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "borsh", + "futures", + "solana-banks-interface", + "solana-program", + "solana-sdk", + "tarpc", + "thiserror", + "tokio", + "tokio-serde", +] + +[[package]] +name = "solana-banks-interface" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "serde", + "solana-sdk", + "tarpc", +] + +[[package]] +name = "solana-banks-server" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "crossbeam-channel", + "futures", + "solana-banks-interface", + "solana-client", + "solana-runtime", + "solana-sdk", + "solana-send-transaction-service", + "tarpc", + "tokio", + "tokio-serde", + "tokio-stream", +] + +[[package]] +name = "solana-bpf-loader-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "byteorder", + "libsecp256k1", + "log", + "solana-measure", + "solana-metrics", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", + "solana_rbpf", + "thiserror", +] + +[[package]] +name = "solana-bucket-map" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "log", + "memmap2", + "modular-bitfield", + "rand 0.7.3", + "solana-measure", + "solana-sdk", + "tempfile", +] + +[[package]] +name = "solana-clap-utils" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "chrono", + "clap 2.34.0", + "rpassword", + "solana-perf", + "solana-remote-wallet", + "solana-sdk", + "thiserror", + "tiny-bip39", + "uriparse", + "url", +] + +[[package]] +name = "solana-cli-config" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "dirs-next", + "lazy_static", + "serde", + "serde_derive", + "serde_yaml", + "solana-clap-utils", + "solana-sdk", + "url", +] + +[[package]] +name = "solana-client" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "async-mutex", + "async-trait", + "base64 0.13.1", + "bincode", + "bs58 0.4.0", + "bytes", + "clap 2.34.0", + "crossbeam-channel", + "enum_dispatch", + "futures", + "futures-util", + "indexmap 1.9.3", + "indicatif", + "itertools 0.10.5", + "jsonrpc-core", + "lazy_static", + "log", + "quinn", + "quinn-proto", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rayon", + "reqwest", + "rustls 0.20.9", + "semver", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-clap-utils", + "solana-faucet", + "solana-measure", + "solana-metrics", + "solana-net-utils", + "solana-sdk", + "solana-streamer", + "solana-transaction-status", + "solana-version", + "solana-vote-program", + "spl-token-2022", + "thiserror", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tungstenite", + "url", +] + +[[package]] +name = "solana-compute-budget-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-config-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "chrono", + "serde", + "serde_derive", + "solana-program-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-faucet" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "byteorder", + "clap 2.34.0", + "crossbeam-channel", + "log", + "serde", + "serde_derive", + "solana-clap-utils", + "solana-cli-config", + "solana-logger", + "solana-metrics", + "solana-sdk", + "solana-version", + "spl-memo 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-frozen-abi" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "ahash", + "blake3", + "block-buffer 0.9.0", + "bs58 0.4.0", + "bv", + "byteorder", + "cc", + "either", + "generic-array 0.14.7", + "getrandom 0.1.16", + "hashbrown 0.12.3", + "im", + "lazy_static", + "log", + "memmap2", + "once_cell", + "rand_core 0.6.4", + "rustc_version", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "solana-frozen-abi-macro", + "subtle", + "thiserror", +] + +[[package]] +name = "solana-frozen-abi-macro" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "solana-logger" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "env_logger 0.9.3", + "lazy_static", + "log", +] + +[[package]] +name = "solana-measure" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "log", + "solana-sdk", +] + +[[package]] +name = "solana-metrics" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "crossbeam-channel", + "gethostname", + "lazy_static", + "log", + "reqwest", + "solana-sdk", +] + +[[package]] +name = "solana-net-utils" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "clap 3.2.25", + "crossbeam-channel", + "log", + "nix", + "rand 0.7.3", + "serde", + "serde_derive", + "socket2 0.4.10", + "solana-logger", + "solana-sdk", + "solana-version", + "tokio", + "url", +] + +[[package]] +name = "solana-perf" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "ahash", + "bincode", + "bv", + "caps", + "curve25519-dalek", + "dlopen", + "dlopen_derive", + "fnv", + "lazy_static", + "libc", + "log", + "nix", + "rand 0.7.3", + "rayon", + "serde", + "solana-metrics", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-vote-program", +] + +[[package]] +name = "solana-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "base64 0.13.1", + "bincode", + "bitflags 1.3.2", + "blake3", + "borsh", + "borsh-derive", + "bs58 0.4.0", + "bv", + "bytemuck", + "cc", + "console_error_panic_hook", + "console_log", + "curve25519-dalek", + "getrandom 0.2.15", + "itertools 0.10.5", + "js-sys", + "lazy_static", + "libc", + "libsecp256k1", + "log", + "memoffset", + "num-derive 0.3.3", + "num-traits", + "parking_lot 0.12.3", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk-macro", + "thiserror", + "tiny-bip39", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "solana-program-runtime" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "base64 0.13.1", + "bincode", + "eager", + "enum-iterator", + "itertools 0.10.5", + "libc", + "libloading", + "log", + "num-derive 0.3.3", + "num-traits", + "rand 0.7.3", + "rustc_version", + "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-program-test" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "assert_matches", + "async-trait", + "base64 0.13.1", + "bincode", + "chrono-humanize", + "log", + "serde", + "solana-banks-client", + "solana-banks-server", + "solana-bpf-loader-program", + "solana-logger", + "solana-program-runtime", + "solana-runtime", + "solana-sdk", + "solana-vote-program", + "thiserror", + "tokio", +] + +[[package]] +name = "solana-rayon-threadlimit" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "lazy_static", + "num_cpus", +] + +[[package]] +name = "solana-remote-wallet" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "console", + "dialoguer", + "log", + "num-derive 0.3.3", + "num-traits", + "parking_lot 0.12.3", + "qstring", + "semver", + "solana-sdk", + "thiserror", + "uriparse", +] + +[[package]] +name = "solana-runtime" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "arrayref", + "bincode", + "blake3", + "bv", + "bytemuck", + "byteorder", + "bzip2", + "crossbeam-channel", + "dashmap", + "dir-diff", + "flate2", + "fnv", + "im", + "index_list", + "itertools 0.10.5", + "lazy_static", + "log", + "lru", + "lz4", + "memmap2", + "num-derive 0.3.3", + "num-traits", + "num_cpus", + "once_cell", + "ouroboros", + "rand 0.7.3", + "rayon", + "regex", + "rustc_version", + "serde", + "serde_derive", + "solana-address-lookup-table-program", + "solana-bucket-map", + "solana-compute-budget-program", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-measure", + "solana-metrics", + "solana-program-runtime", + "solana-rayon-threadlimit", + "solana-sdk", + "solana-stake-program", + "solana-vote-program", + "solana-zk-token-proof-program", + "solana-zk-token-sdk", + "strum 0.24.1", + "strum_macros 0.24.3", + "symlink", + "tar", + "tempfile", + "thiserror", + "zstd", +] + +[[package]] +name = "solana-sdk" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "assert_matches", + "base64 0.13.1", + "bincode", + "bitflags 1.3.2", + "borsh", + "bs58 0.4.0", + "bytemuck", + "byteorder", + "chrono", + "derivation-path", + "digest 0.10.7", + "ed25519-dalek", + "ed25519-dalek-bip32", + "generic-array 0.14.7", + "hmac 0.12.1", + "itertools 0.10.5", + "js-sys", + "lazy_static", + "libsecp256k1", + "log", + "memmap2", + "num-derive 0.3.3", + "num-traits", + "pbkdf2 0.11.0", + "qstring", + "rand 0.7.3", + "rand_chacha 0.2.2", + "rustc_version", + "rustversion", + "serde", + "serde_bytes", + "serde_derive", + "serde_json", + "sha2 0.10.8", + "sha3 0.10.8", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-logger", + "solana-program", + "solana-sdk-macro", + "thiserror", + "uriparse", + "wasm-bindgen", +] + +[[package]] +name = "solana-sdk-macro" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bs58 0.4.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "solana-send-transaction-service" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "crossbeam-channel", + "log", + "solana-client", + "solana-measure", + "solana-metrics", + "solana-runtime", + "solana-sdk", +] + +[[package]] +name = "solana-stake-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "log", + "num-derive 0.3.3", + "num-traits", + "rustc_version", + "serde", + "serde_derive", + "solana-config-program", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-program-runtime", + "solana-sdk", + "solana-vote-program", + "thiserror", +] + +[[package]] +name = "solana-streamer" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "crossbeam-channel", + "futures-util", + "histogram", + "indexmap 1.9.3", + "itertools 0.10.5", + "libc", + "log", + "nix", + "pem", + "percentage", + "pkcs8 0.8.0", + "quinn", + "rand 0.7.3", + "rcgen", + "rustls 0.20.9", + "solana-metrics", + "solana-perf", + "solana-sdk", + "thiserror", + "tokio", + "x509-parser", +] + +[[package]] +name = "solana-transaction-status" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "Inflector", + "base64 0.13.1", + "bincode", + "borsh", + "bs58 0.4.0", + "lazy_static", + "log", + "serde", + "serde_derive", + "serde_json", + "solana-account-decoder", + "solana-address-lookup-table-program", + "solana-measure", + "solana-metrics", + "solana-sdk", + "solana-vote-program", + "spl-associated-token-account", + "spl-memo 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "solana-version" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "log", + "rustc_version", + "semver", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-sdk", +] + +[[package]] +name = "solana-vote-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bincode", + "log", + "num-derive 0.3.3", + "num-traits", + "rustc_version", + "serde", + "serde_derive", + "solana-frozen-abi", + "solana-frozen-abi-macro", + "solana-metrics", + "solana-program-runtime", + "solana-sdk", + "thiserror", +] + +[[package]] +name = "solana-zk-token-proof-program" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "bytemuck", + "getrandom 0.1.16", + "num-derive 0.3.3", + "num-traits", + "solana-program-runtime", + "solana-sdk", + "solana-zk-token-sdk", +] + +[[package]] +name = "solana-zk-token-sdk" +version = "1.14.13" +source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" +dependencies = [ + "aes-gcm-siv", + "arrayref", + "base64 0.13.1", + "bincode", + "bytemuck", + "byteorder", + "cipher", + "curve25519-dalek", + "getrandom 0.1.16", + "itertools 0.10.5", + "lazy_static", + "merlin", + "num-derive 0.3.3", + "num-traits", + "rand 0.7.3", + "serde", + "serde_json", + "sha3 0.9.1", + "solana-program", + "solana-sdk", + "subtle", + "thiserror", + "zeroize", +] + +[[package]] +name = "solana_rbpf" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80a28c5dfe7e8af38daa39d6561c8e8b9ed7a2f900951ebe7362ad6348d36c73" +dependencies = [ + "byteorder", + "combine", + "goblin", + "hash32", + "libc", + "log", + "rand 0.8.5", + "rustc-demangle", + "scroll", + "thiserror", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der 0.5.1", +] + +[[package]] +name = "spki" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" +dependencies = [ + "base64ct", + "der 0.6.1", +] + +[[package]] +name = "spl-associated-token-account" +version = "1.1.2" +source = "git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane#5de3c060c276afb4e767111b3b42fbbf4a81d83f" +dependencies = [ + "assert_matches", + "borsh", + "num-derive 0.3.3", + "num-traits", + "solana-program", + "spl-token", + "spl-token-2022", + "thiserror", +] + +[[package]] +name = "spl-memo" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-memo" +version = "3.0.1" +source = "git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane#5de3c060c276afb4e767111b3b42fbbf4a81d83f" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-noop" +version = "0.1.3" +source = "git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane#5de3c060c276afb4e767111b3b42fbbf4a81d83f" +dependencies = [ + "solana-program", +] + +[[package]] +name = "spl-token" +version = "3.5.0" +source = "git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane#5de3c060c276afb4e767111b3b42fbbf4a81d83f" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.5.11", + "solana-program", + "thiserror", +] + +[[package]] +name = "spl-token-2022" +version = "0.5.0" +source = "git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane#5de3c060c276afb4e767111b3b42fbbf4a81d83f" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.5.11", + "solana-program", + "solana-zk-token-sdk", + "spl-memo 3.0.1 (git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane)", + "spl-token", + "thiserror", +] + +[[package]] +name = "spl-type-length-value" +version = "0.1.0" +source = "git+https://github.com/hyperlane-xyz/solana-program-library.git?branch=hyperlane#5de3c060c276afb4e767111b3b42fbbf4a81d83f" +dependencies = [ + "arrayref", + "bytemuck", + "num-derive 0.3.3", + "num-traits", + "num_enum 0.6.1", + "solana-program", + "thiserror", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros 0.26.4", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck 0.5.0", + "proc-macro2 1.0.86", + "quote 1.0.37", + "rustversion", + "syn 2.0.77", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", + "unicode-xid 0.2.5", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tarpc" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38a012bed6fb9681d3bf71ffaa4f88f3b4b9ed3198cda6e4c8462d24d4bb80" +dependencies = [ + "anyhow", + "fnv", + "futures", + "humantime", + "opentelemetry", + "pin-project", + "rand 0.8.5", + "serde", + "static_assertions", + "tarpc-plugins", + "thiserror", + "tokio", + "tokio-serde", + "tokio-util 0.6.10", + "tracing", + "tracing-opentelemetry", +] + +[[package]] +name = "tarpc-plugins" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 1.0.109", +] + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-bip39" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" +dependencies = [ + "anyhow", + "hmac 0.8.1", + "once_cell", + "pbkdf2 0.4.0", + "rand 0.7.3", + "rustc-hash", + "sha2 0.9.9", + "thiserror", + "unicode-normalization", + "wasm-bindgen", + "zeroize", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot 0.12.3", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.7", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.9", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-serde" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" +dependencies = [ + "bincode", + "bytes", + "educe", + "futures-core", + "futures-sink", + "pin-project", + "serde", + "serde_json", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "rustls 0.20.9", + "tokio", + "tokio-rustls 0.23.4", + "tungstenite", + "webpki", + "webpki-roots 0.22.6", +] + +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "slab", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap 2.5.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" +dependencies = [ + "once_cell", + "opentelemetry", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +dependencies = [ + "base64 0.13.1", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand 0.8.5", + "rustls 0.20.9", + "sha-1", + "thiserror", + "url", + "utf-8", + "webpki", + "webpki-roots 0.22.6", +] + +[[package]] +name = "typeid" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "typetag" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba3b6e86ffe0054b2c44f2d86407388b933b16cb0a70eea3929420db1d9bbe" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b20a22c42c8f1cd23ce5e34f165d4d37038f5b663ad20fb6adbdf029172483" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "unicode-xid" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "unreachable" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" +dependencies = [ + "void", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "uriparse" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200d0fc04d809396c2ad43f3c95da3582a2556eba8d453c1087f4120ee352ff" +dependencies = [ + "fnv", + "lazy_static", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.15", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote 1.0.37", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "ws_stream_wasm" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" +dependencies = [ + "async_io_stream", + "futures", + "js-sys", + "log", + "pharos", + "rustc_version", + "send_wrapper", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "xattr" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" +dependencies = [ + "libc", + "linux-raw-sys", + "rustix", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2 1.0.86", + "quote 1.0.37", + "syn 2.0.77", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/rust/Cargo.toml b/rust/sealevel/Cargo.toml similarity index 79% rename from rust/Cargo.toml rename to rust/sealevel/Cargo.toml index fe5aa030e..5887dc8d1 100644 --- a/rust/Cargo.toml +++ b/rust/sealevel/Cargo.toml @@ -1,50 +1,35 @@ [workspace] members = [ - "agents/relayer", - "agents/scraper", - "agents/validator", - "chains/hyperlane-cosmos", - "chains/hyperlane-ethereum", - "chains/hyperlane-fuel", - "chains/hyperlane-sealevel", - "ethers-prometheus", - "hyperlane-base", - "hyperlane-core", - "hyperlane-test", - "sealevel/client", - "sealevel/libraries/access-control", - "sealevel/libraries/account-utils", - "sealevel/libraries/ecdsa-signature", - "sealevel/libraries/hyperlane-sealevel-connection-client", - "sealevel/libraries/hyperlane-sealevel-token", - "sealevel/libraries/interchain-security-module-interface", - "sealevel/libraries/message-recipient-interface", - "sealevel/libraries/multisig-ism", - "sealevel/libraries/serializable-account-meta", - "sealevel/libraries/test-transaction-utils", - "sealevel/libraries/test-utils", - "sealevel/programs/hyperlane-sealevel-igp", - "sealevel/programs/hyperlane-sealevel-igp-test", - "sealevel/programs/hyperlane-sealevel-token", - "sealevel/programs/hyperlane-sealevel-token-collateral", - "sealevel/programs/hyperlane-sealevel-token-native", - "sealevel/programs/ism/multisig-ism-message-id", - "sealevel/programs/ism/test-ism", - "sealevel/programs/mailbox", - "sealevel/programs/mailbox-test", - "sealevel/programs/test-send-receiver", - "sealevel/programs/validator-announce", - "utils/abigen", - "utils/backtrace-oneline", - "utils/hex", - "utils/run-locally", + "client", + "libraries/access-control", + "libraries/account-utils", + "libraries/ecdsa-signature", + "libraries/hyperlane-sealevel-connection-client", + "libraries/hyperlane-sealevel-token", + "libraries/interchain-security-module-interface", + "libraries/message-recipient-interface", + "libraries/multisig-ism", + "libraries/serializable-account-meta", + "libraries/test-transaction-utils", + "libraries/test-utils", + "programs/hyperlane-sealevel-igp", + "programs/hyperlane-sealevel-igp-test", + "programs/hyperlane-sealevel-token", + "programs/hyperlane-sealevel-token-collateral", + "programs/hyperlane-sealevel-token-native", + "programs/ism/multisig-ism-message-id", + "programs/ism/test-ism", + "programs/mailbox", + "programs/mailbox-test", + "programs/test-send-receiver", + "programs/validator-announce", ] [workspace.package] documentation = "https://docs.hyperlane.xyz" edition = "2021" homepage = "https://hyperlane.xyz" -license-file = "../LICENSE.md" +license-file = "../../LICENSE.md" publish = false version = "0.1.0" @@ -67,25 +52,12 @@ color-eyre = "0.6" config = "0.13.3" console-subscriber = "0.2.0" convert_case = "0.6" -cosmrs = { version = "0.14", default-features = false, features = [ - "cosmwasm", - "rpc", - "tokio", - "grpc", -] } -cosmwasm-std = "*" crunchy = "0.2" ctrlc = "3.2" curve25519-dalek = { version = "~3.2", features = ["serde"] } derive-new = "0.5" -derive_builder = "0.12" -derive_more = "0.99" -dhat = "0.3.3" -ed25519-dalek = "~1.0" eyre = "=0.6.8" fixed-hash = "0.8.0" -fuels = "0.38" -fuels-code-gen = "0.38" futures = "0.3" futures-util = "0.3" generic-array = { version = "0.14", features = ["serde", "more_lengths"] } @@ -94,10 +66,6 @@ bech32 = "0.9.1" elliptic-curve = "0.12.3" getrandom = { version = "0.2", features = ["js"] } hex = "0.4.3" -http = "*" -hyper = "0.14" -hyper-tls = "0.5.0" -hyperlane-cosmwasm-interface = "=0.0.6-rc6" injective-protobuf = "0.2.2" injective-std = "0.1.5" itertools = "*" @@ -168,8 +136,6 @@ static_assertions = "1.1" strum = "0.26.2" strum_macros = "0.26.2" tempfile = "3.3" -tendermint = "0.32.2" -tendermint-rpc = { version = "0.32.0", features = ["http-client", "tokio"] } thiserror = "1.0" time = "0.3" tiny-keccak = "2.0.2" @@ -192,8 +158,6 @@ warp = "0.3" which = "4.3" ya-gcp = { version = "0.11.1", features = ["storage"] } -## TODO: remove this -cosmwasm-schema = "1.2.7" [profile.release.package.access-control] overflow-checks = true @@ -272,11 +236,6 @@ features = ["legacy"] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2024-04-25" -[workspace.dependencies.ethers-core] -features = [] -git = "https://github.com/hyperlane-xyz/ethers-rs" -tag = "2024-04-25" - [workspace.dependencies.ethers-providers] features = [] git = "https://github.com/hyperlane-xyz/ethers-rs" @@ -287,6 +246,11 @@ features = ["aws"] git = "https://github.com/hyperlane-xyz/ethers-rs" tag = "2024-04-25" +[patch.crates-io.bumpalo] +git = "https://github.com/fitzgen/bumpalo" +tag = "3.14.0" +version = "=3.14.0" + [patch.crates-io.curve25519-dalek] branch = "v3.2.2-relax-zeroize" git = "https://github.com/Eclipse-Laboratories-Inc/curve25519-dalek" @@ -391,13 +355,3 @@ version = "=0.5.0" version = "=0.1.0" git = "https://github.com/hyperlane-xyz/solana-program-library.git" branch = "hyperlane" - -[patch.crates-io.tendermint] -branch = "trevor/0.32.2-fork" -git = "https://github.com/hyperlane-xyz/tendermint-rs.git" -version = "=0.32.2" - -[patch.crates-io.tendermint-rpc] -branch = "trevor/0.32.2-fork" -git = "https://github.com/hyperlane-xyz/tendermint-rs.git" -version = "=0.32.2" diff --git a/rust/sealevel/client/Cargo.toml b/rust/sealevel/client/Cargo.toml index 95ba0650a..b799093ae 100644 --- a/rust/sealevel/client/Cargo.toml +++ b/rust/sealevel/client/Cargo.toml @@ -23,14 +23,30 @@ solana-sdk.workspace = true solana-transaction-status.workspace = true account-utils = { path = "../libraries/account-utils" } -hyperlane-core = { path = "../../hyperlane-core" } +hyperlane-core = { path = "../../main/hyperlane-core" } hyperlane-sealevel-connection-client = { path = "../libraries/hyperlane-sealevel-connection-client" } -hyperlane-sealevel-mailbox = { path = "../programs/mailbox", features = ["no-entrypoint", "serde"] } -hyperlane-sealevel-multisig-ism-message-id = { path = "../programs/ism/multisig-ism-message-id", features = ["no-entrypoint"] } -hyperlane-sealevel-token = { path = "../programs/hyperlane-sealevel-token", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../programs/hyperlane-sealevel-igp", features = ["no-entrypoint", "serde"] } -hyperlane-sealevel-token-collateral = { path = "../programs/hyperlane-sealevel-token-collateral", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../programs/mailbox", features = [ + "no-entrypoint", + "serde", +] } +hyperlane-sealevel-multisig-ism-message-id = { path = "../programs/ism/multisig-ism-message-id", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-token = { path = "../programs/hyperlane-sealevel-token", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../programs/hyperlane-sealevel-igp", features = [ + "no-entrypoint", + "serde", +] } +hyperlane-sealevel-token-collateral = { path = "../programs/hyperlane-sealevel-token-collateral", features = [ + "no-entrypoint", +] } hyperlane-sealevel-token-lib = { path = "../libraries/hyperlane-sealevel-token" } -hyperlane-sealevel-token-native = { path = "../programs/hyperlane-sealevel-token-native", features = ["no-entrypoint"] } -hyperlane-sealevel-validator-announce = { path = "../programs/validator-announce", features = ["no-entrypoint"] } -hyperlane-sealevel-hello-world = { path = "../programs/helloworld" } \ No newline at end of file +hyperlane-sealevel-token-native = { path = "../programs/hyperlane-sealevel-token-native", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-validator-announce = { path = "../programs/validator-announce", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-hello-world = { path = "../programs/helloworld" } diff --git a/rust/sealevel/client/src/cmd_utils.rs b/rust/sealevel/client/src/cmd_utils.rs index 872725112..e0cd3ff68 100644 --- a/rust/sealevel/client/src/cmd_utils.rs +++ b/rust/sealevel/client/src/cmd_utils.rs @@ -4,6 +4,8 @@ use std::{ io::Write, path::{Path, PathBuf}, process::{Command, Stdio}, + thread::sleep, + time::Duration, }; use solana_client::{client_error::ClientError, rpc_client::RpcClient}; @@ -77,6 +79,10 @@ pub(crate) fn deploy_program( } build_cmd(command.as_slice(), None, None); + + // TODO: use commitment level instead of just sleeping here? + println!("Sleeping for 2 seconds to allow program to be deployed"); + sleep(Duration::from_secs(2)); } pub(crate) fn create_new_directory(parent_dir: &Path, name: &str) -> PathBuf { diff --git a/rust/sealevel/client/src/context.rs b/rust/sealevel/client/src/context.rs index e704510a7..c3912ee4b 100644 --- a/rust/sealevel/client/src/context.rs +++ b/rust/sealevel/client/src/context.rs @@ -155,8 +155,21 @@ impl<'ctx, 'rpc> TxnBuilder<'ctx, 'rpc> { ); } - let message = Message::new(&self.instructions(), None); - let txn = Transaction::new_unsigned(message); + let message = Message::new(&self.instructions(), Some(&self.ctx.payer_pubkey)); + // Useful for plugging into ledger-friendly tools + if std::env::var("TX_BINARY").is_ok() { + println!( + "\t==== Message as binary: ====\n\t{:?}", + bincode::serialize(&message) + .unwrap() + .iter() + .map(|n| n.to_string()) + .collect::>() + .join(" ") + ); + } + + let txn = Transaction::new_unsigned(message.clone()); println!( "\t==== Transaction in base58: ====\n\t{}", bs58::encode(bincode::serialize(&txn).unwrap()).into_string() diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index 8e9f3e586..bf014785c 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -895,7 +895,7 @@ fn process_mailbox_cmd(ctx: Context, cmd: MailboxCmd) { }; } -fn process_token_cmd(ctx: Context, cmd: TokenCmd) { +fn process_token_cmd(mut ctx: Context, cmd: TokenCmd) { match cmd.cmd { TokenSubCmd::Query(query) => { let (token_account, token_bump) = @@ -1024,6 +1024,7 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { } TokenSubCmd::TransferRemote(xfer) => { is_keypair(&xfer.sender).unwrap(); + ctx.commitment = CommitmentConfig::finalized(); let sender = read_keypair_file(xfer.sender).unwrap(); let recipient = if xfer.recipient.starts_with("0x") { diff --git a/rust/sealevel/client/src/multisig_ism.rs b/rust/sealevel/client/src/multisig_ism.rs index 8911bedfc..9f1f2c5c8 100644 --- a/rust/sealevel/client/src/multisig_ism.rs +++ b/rust/sealevel/client/src/multisig_ism.rs @@ -27,8 +27,10 @@ pub(crate) struct MultisigIsmConfig { /// Note this type is ignored in this tooling. It'll always assume this /// relates to a multisig-ism-message-id variant, which is the only type /// implemented in Sealevel. - #[serde(rename = "type")] - pub module_type: u8, + /// Commenting out for now until this is needed, and due to `infra` + /// generating non-numeric types at the moment. + // #[serde(rename = "type")] + // pub module_type: u8, pub validators: Vec, pub threshold: u8, } diff --git a/rust/sealevel/client/src/router.rs b/rust/sealevel/client/src/router.rs index c2826f240..ad7ee3695 100644 --- a/rust/sealevel/client/src/router.rs +++ b/rust/sealevel/client/src/router.rs @@ -111,7 +111,8 @@ pub struct RpcUrlConfig { #[derive(Debug, Deserialize, Serialize, Clone)] #[serde(rename_all = "camelCase")] pub struct ChainMetadata { - chain_id: u32, + // Can be a string or a number + chain_id: serde_json::Value, /// Hyperlane domain, only required if differs from id above domain_id: Option, name: String, @@ -125,7 +126,19 @@ impl ChainMetadata { } pub fn domain_id(&self) -> u32 { - self.domain_id.unwrap_or(self.chain_id) + self.domain_id.unwrap_or_else(|| { + // Try to parse as a number, otherwise panic, as the domain ID must + // be specified if the chain id is not a number. + self.chain_id + .as_u64() + .and_then(|v| v.try_into().ok()) + .unwrap_or_else(|| { + panic!( + "Unable to get domain ID for chain {:?}: domain_id is undefined and could not fall back to chain_id {:?}", + self.name, self.chain_id + ) + }) + }) } } diff --git a/rust/sealevel/client/src/warp_route.rs b/rust/sealevel/client/src/warp_route.rs index e40adfa57..3cb3328c1 100644 --- a/rust/sealevel/client/src/warp_route.rs +++ b/rust/sealevel/client/src/warp_route.rs @@ -294,6 +294,7 @@ impl RouterDeployer for WarpRouteDeployer { "--fee-payer", ctx.payer_keypair_path(), ]); + println!("running command: {:?}", cmd); let status = cmd .stdout(Stdio::inherit()) @@ -364,8 +365,9 @@ impl RouterDeployer for WarpRouteDeployer { .expect("Failed to run command"); println!("initialized metadata. Status: {status}"); - // Burn the metadata pointer, metadata, and mint authorities by moving them to the mint - let authorities_to_transfer = &["metadata-pointer", "metadata", "mint"]; + // Move the mint authority to the mint account. + // The deployer key will still hold the metadata pointer and metadata authorities. + let authorities_to_transfer = &["mint"]; for authority in authorities_to_transfer { println!("Transferring authority: {authority} to the mint account {mint_account}"); diff --git a/rust/sealevel/environments/mainnet2/.gitignore b/rust/sealevel/environments/mainnet2/.gitignore deleted file mode 100644 index 1b33b4a79..000000000 --- a/rust/sealevel/environments/mainnet2/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**/**/keys diff --git a/rust/sealevel/environments/mainnet2/chain-config.json b/rust/sealevel/environments/mainnet2/chain-config.json deleted file mode 100644 index 23723ef82..000000000 --- a/rust/sealevel/environments/mainnet2/chain-config.json +++ /dev/null @@ -1,350 +0,0 @@ -{ - "bsc": { - "chainId": 56, - "domainId": 56, - "name": "bsc", - "protocol": "ethereum", - "displayName": "Binance Smart Chain", - "displayNameShort": "Binance", - "nativeToken": { - "decimals": 18, - "name": "BNB", - "symbol": "BNB" - }, - "rpcUrls": [ - { - "http": "https://bsc-dataseed.binance.org" - }, - { - "http": "https://rpc.ankr.com/bsc" - } - ], - "blockExplorers": [ - { - "name": "BscScan", - "url": "https://bscscan.com", - "apiUrl": "https://api.bscscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 15, - "estimateBlockTime": 3 - }, - "gasCurrencyCoinGeckoId": "binancecoin", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-bsc.safe.global/", - "transactionOverrides": { - "gasPrice": 7000000000 - } - }, - "avalanche": { - "chainId": 43114, - "domainId": 43114, - "name": "avalanche", - "protocol": "ethereum", - "displayName": "Avalanche", - "nativeToken": { - "decimals": 18, - "name": "Avalanche", - "symbol": "AVAX" - }, - "rpcUrls": [ - { - "http": "https://api.avax.network/ext/bc/C/rpc", - "pagination": { - "maxBlockRange": 100000, - "minBlockNumber": 6765067 - } - } - ], - "blockExplorers": [ - { - "name": "SnowTrace", - "url": "https://snowtrace.io", - "apiUrl": "https://api.snowtrace.io/api", - "family": "other" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 3, - "estimateBlockTime": 2 - }, - "gasCurrencyCoinGeckoId": "avalanche-2", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-avalanche.safe.global/" - }, - "polygon": { - "chainId": 137, - "domainId": 137, - "name": "polygon", - "protocol": "ethereum", - "displayName": "Polygon", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://rpc-mainnet.matic.quiknode.pro", - "pagination": { - "maxBlockRange": 10000, - "minBlockNumber": 19657100 - } - }, - { - "http": "https://polygon-rpc.com" - } - ], - "blockExplorers": [ - { - "name": "PolygonScan", - "url": "https://polygonscan.com", - "apiUrl": "https://api.polygonscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 256, - "estimateBlockTime": 2 - }, - "gasCurrencyCoinGeckoId": "matic-network", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-polygon.safe.global/", - "transactionOverrides": { - "maxFeePerGas": 500000000000, - "maxPriorityFeePerGas": 100000000000 - } - }, - "celo": { - "chainId": 42220, - "domainId": 42220, - "name": "celo", - "protocol": "ethereum", - "displayName": "Celo", - "nativeToken": { - "decimals": 18, - "name": "CELO", - "symbol": "CELO" - }, - "rpcUrls": [ - { - "http": "https://forno.celo.org" - } - ], - "blockExplorers": [ - { - "name": "CeloScan", - "url": "https://celoscan.io", - "apiUrl": "https://api.celoscan.io/api", - "family": "etherscan" - }, - { - "name": "Blockscout", - "url": "https://explorer.celo.org", - "apiUrl": "https://explorer.celo.org/mainnet/api", - "family": "blockscout" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 5 - }, - "gnosisSafeTransactionServiceUrl": "https://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/" - }, - "arbitrum": { - "chainId": 42161, - "domainId": 42161, - "name": "arbitrum", - "protocol": "ethereum", - "displayName": "Arbitrum", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://arb1.arbitrum.io/rpc" - } - ], - "blockExplorers": [ - { - "name": "Arbiscan", - "url": "https://arbiscan.io", - "apiUrl": "https://api.arbiscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 3 - }, - "gasCurrencyCoinGeckoId": "ethereum", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-arbitrum.safe.global/" - }, - "optimism": { - "chainId": 10, - "domainId": 10, - "name": "optimism", - "protocol": "ethereum", - "displayName": "Optimism", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://mainnet.optimism.io" - } - ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://optimistic.etherscan.io", - "apiUrl": "https://api-optimistic.etherscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 3 - }, - "gasCurrencyCoinGeckoId": "ethereum", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-optimism.safe.global/" - }, - "ethereum": { - "chainId": 1, - "domainId": 1, - "name": "ethereum", - "protocol": "ethereum", - "displayName": "Ethereum", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" - }, - { - "http": "https://cloudflare-eth.com" - } - ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://etherscan.io", - "apiUrl": "https://api.etherscan.io/api", - "family": "etherscan" - }, - { - "name": "Blockscout", - "url": "https://blockscout.com/eth/mainnet", - "apiUrl": "https://blockscout.com/eth/mainnet/api", - "family": "blockscout" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 14, - "estimateBlockTime": 13 - }, - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-mainnet.safe.global/", - "transactionOverrides": { - "maxFeePerGas": 150000000000, - "maxPriorityFeePerGas": 5000000000 - } - }, - "moonbeam": { - "chainId": 1284, - "domainId": 1284, - "name": "moonbeam", - "protocol": "ethereum", - "displayName": "Moonbeam", - "nativeToken": { - "decimals": 18, - "name": "GLMR", - "symbol": "GLMR" - }, - "rpcUrls": [ - { - "http": "https://rpc.api.moonbeam.network" - } - ], - "blockExplorers": [ - { - "name": "MoonScan", - "url": "https://moonscan.io", - "apiUrl": "https://api-moonbeam.moonscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 2, - "reorgPeriod": 2, - "estimateBlockTime": 12 - }, - "gnosisSafeTransactionServiceUrl": "https://transaction.multisig.moonbeam.network" - }, - "gnosis": { - "chainId": 100, - "domainId": 100, - "name": "gnosis", - "protocol": "ethereum", - "displayName": "Gnosis", - "nativeToken": { - "name": "xDai", - "symbol": "xDai", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://rpc.gnosischain.com", - "pagination": { - "maxBlockRange": 10000, - "minBlockNumber": 25997478 - } - } - ], - "blockExplorers": [ - { - "name": "GnosisScan", - "url": "https://gnosisscan.io", - "apiUrl": "https://api.gnosisscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 14, - "estimateBlockTime": 5 - }, - "gasCurrencyCoinGeckoId": "xdai", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-gnosis-chain.safe.global/" - }, - "solanamainnet": { - "chainId": 1399811149, - "name": "solanamainnet", - "rpcUrls": [ - { - "http": "https://api.mainnet-beta.solana.com" - } - ] - }, - "nautilus": { - "chainId": 22222, - "name": "nautilus", - "rpcUrls": [ - { - "http": "https://api.nautilus.nautchain.xyz" - } - ] - } -} diff --git a/rust/sealevel/environments/mainnet2/gas-oracle-configs.json b/rust/sealevel/environments/mainnet2/gas-oracle-configs.json deleted file mode 100644 index 75bc47071..000000000 --- a/rust/sealevel/environments/mainnet2/gas-oracle-configs.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "domain": 56, - "gasOracle": { - "type": "remoteGasData", - "tokenExchangeRate": "100000000000000000000", - "gasPrice": "3000000000", - "tokenDecimals": 18 - } - }, - { - "domain": 22222, - "gasOracle": { - "type": "remoteGasData", - "tokenExchangeRate": "4700000000000000", - "gasPrice": "1000000000", - "tokenDecimals": 18 - } - } -] \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/helloworld/hyperlane/helloworld-config.json b/rust/sealevel/environments/mainnet2/helloworld/hyperlane/helloworld-config.json deleted file mode 100644 index 34aaf6284..000000000 --- a/rust/sealevel/environments/mainnet2/helloworld/hyperlane/helloworld-config.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "solanamainnet": {}, - "bsc": { - "foreignDeployment": "0xB97d3bF2fC296c2cAC4056bBC8A783ff39408e20" - }, - "avalanche": { - "foreignDeployment": "0x2A925CD8a5d919c5c6599633090c37fe38A561b6" - }, - "polygon": { - "foreignDeployment": "0x6c0aC8cEA75232aa7BeD8cbe9C4f820E7a77a9C3" - }, - "celo": { - "foreignDeployment": "0x4151773Db70C0b2D4c43Ea44A5FB5803ff1d3e0B" - }, - "arbitrum": { - "foreignDeployment": "0x96271cA0ab9eeFB3Ca481749c0Ca4c705fD4F523" - }, - "optimism": { - "foreignDeployment": "0xA6f0A37DFDe9C2c8F46F010989C47d9edB3a9FA8" - }, - "ethereum": { - "foreignDeployment": "0x9311cEE522A7C122B843b66cC31C6a63e2F92641" - }, - "moonbeam": { - "foreignDeployment": "0xAe067C08703508230357025B38c35Cd12793628c" - }, - "gnosis": { - "foreignDeployment": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4" - } -} diff --git a/rust/sealevel/environments/mainnet2/helloworld/hyperlane/program-ids.json b/rust/sealevel/environments/mainnet2/helloworld/hyperlane/program-ids.json deleted file mode 100644 index 466662638..000000000 --- a/rust/sealevel/environments/mainnet2/helloworld/hyperlane/program-ids.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "moonbeam": { - "hex": "0x000000000000000000000000ae067c08703508230357025b38c35cd12793628c", - "base58": "1111111111113RcuHPfDctyAnFWHvj8tS1q8UHPh" - }, - "bsc": { - "hex": "0x000000000000000000000000b97d3bf2fc296c2cac4056bbc8a783ff39408e20", - "base58": "1111111111113atAoP8gQ2GYeue77ETUPAf8w9zw" - }, - "optimism": { - "hex": "0x000000000000000000000000a6f0a37dfde9c2c8f46f010989c47d9edb3a9fa8", - "base58": "1111111111113KtqevvpYv7NCiadmp6tRRfivB8K" - }, - "avalanche": { - "hex": "0x0000000000000000000000002a925cd8a5d919c5c6599633090c37fe38a561b6", - "base58": "111111111111bQB6b7XVDHSyvi7XmLrQMT8C3xH" - }, - "ethereum": { - "hex": "0x0000000000000000000000009311cee522a7c122b843b66cc31c6a63e2f92641", - "base58": "11111111111133qb6DzNiJ7whNaYGud2WqqtjxFS" - }, - "solanamainnet": { - "hex": "0x3797d0096b18b5b645c346a66d7f18c6c5738782c6bce24da57a3462bdef82b1", - "base58": "4k1gruSdH1r57V9QQK4aunzfMYzLFfF83jdYkkEwyem6" - }, - "celo": { - "hex": "0x0000000000000000000000004151773db70c0b2d4c43ea44a5fb5803ff1d3e0b", - "base58": "111111111111unDVQcjdeHntE83qvf1vsKCZ4av" - }, - "polygon": { - "hex": "0x0000000000000000000000006c0ac8cea75232aa7bed8cbe9c4f820e7a77a9c3", - "base58": "1111111111112WJXE3PCAsCXYZxU9Kh51sSZEa5G" - }, - "arbitrum": { - "hex": "0x00000000000000000000000096271ca0ab9eefb3ca481749c0ca4c705fd4f523", - "base58": "11111111111136L61X7cdT9tPZ4GKBtzJtrjFAd8" - }, - "gnosis": { - "hex": "0x00000000000000000000000026f32245fcf5ad53159e875d5cae62aecf19c2d4", - "base58": "111111111111YURfyMRiiTWy8X6pYHAqmYPmBpf" - } -} diff --git a/rust/sealevel/environments/mainnet2/helloworld/rc/helloworld-config.json b/rust/sealevel/environments/mainnet2/helloworld/rc/helloworld-config.json deleted file mode 100644 index 5e6ae772b..000000000 --- a/rust/sealevel/environments/mainnet2/helloworld/rc/helloworld-config.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "solanamainnet": { - "interchainSecurityModule": "BYTsxBuKVbwgsZFswzB91nrxveQySghwXzaKqn8exNnC" - }, - "gnosis": { - "foreignDeployment": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a" - }, - "bsc": { - "foreignDeployment": "0xe5554478F167936dB253f79f57c41770bfa00Bae" - }, - "avalanche": { - "foreignDeployment": "0xe1De9910fe71cC216490AC7FCF019e13a34481D7" - }, - "polygon": { - "foreignDeployment": "0xAb65C41a1BC580a52f0b166879122EFdce0cB868" - }, - "celo": { - "foreignDeployment": "0xfE29f6a4468536029Fc9c97d3a9669b9fe38E114" - }, - "arbitrum": { - "foreignDeployment": "0x414B67F62b143d6db6E9b633168Dd6fd4DA20642" - }, - "optimism": { - "foreignDeployment": "0xB4caf2CA864B413DAA502fA18A8D48cD0740fC52" - }, - "ethereum": { - "foreignDeployment": "0xed31c20c5517EaC05decD5F6dCd01Fe6d16fD09D" - }, - "moonbeam": { - "foreignDeployment": "0x3eB9eE2CFC8DCB6F58B5869D33336CFcBf1dC354" - } -} diff --git a/rust/sealevel/environments/mainnet2/helloworld/rc/program-ids.json b/rust/sealevel/environments/mainnet2/helloworld/rc/program-ids.json deleted file mode 100644 index e3c0c46c4..000000000 --- a/rust/sealevel/environments/mainnet2/helloworld/rc/program-ids.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "solanamainnet": { - "hex": "0x29dacc0e7124ea39b1fd43ab0fd30e038cf405c0229890229d0086d0b6516f9c", - "base58": "3pPDp16iVTJFge2sm85Q61hW61UN5xNqeG24gqFhzLFV" - }, - "avalanche": { - "hex": "0x000000000000000000000000e1de9910fe71cc216490ac7fcf019e13a34481d7", - "base58": "11111111111149We9K5tM8ijcyNy9zDMG9RyDBCJ" - }, - "arbitrum": { - "hex": "0x000000000000000000000000414b67f62b143d6db6e9b633168dd6fd4da20642", - "base58": "111111111111um79Yc6Evs5e1m2fdD2x7T1cpXb" - }, - "moonbeam": { - "hex": "0x0000000000000000000000003eb9ee2cfc8dcb6f58b5869d33336cfcbf1dc354", - "base58": "111111111111sgjzaeuHfqhExkdPQ1gJdhcSr4j" - }, - "optimism": { - "hex": "0x000000000000000000000000b4caf2ca864b413daa502fa18a8d48cd0740fc52", - "base58": "1111111111113X64nhkfMi9X5MbxKsiDTeeTmjsw" - }, - "ethereum": { - "hex": "0x000000000000000000000000ed31c20c5517eac05decd5f6dcd01fe6d16fd09d", - "base58": "1111111111114JfPmRiKEsR445qonVzCpsAvXCR2" - }, - "gnosis": { - "hex": "0x00000000000000000000000099ca8c74ce7cfa9d72a51fbb05f9821f5f826b3a", - "base58": "11111111111139Gc7eyQjpZrmWkkYQRyA2Grcvmf" - }, - "bsc": { - "hex": "0x000000000000000000000000e5554478f167936db253f79f57c41770bfa00bae", - "base58": "1111111111114CJxuV4VoAh5NsJy9qCGHqryoTCy" - }, - "polygon": { - "hex": "0x000000000000000000000000ab65c41a1bc580a52f0b166879122efdce0cb868", - "base58": "1111111111113PVkHAU9H7moDSoQvhC3Y2wgmovX" - }, - "celo": { - "hex": "0x000000000000000000000000fe29f6a4468536029fc9c97d3a9669b9fe38e114", - "base58": "1111111111114YNh3uhCWh2NjyPttobeNRyuDHYo" - } -} diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/hyperlane/multisig-config.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/hyperlane/multisig-config.json deleted file mode 100644 index 49cb69474..000000000 --- a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/hyperlane/multisig-config.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "celo": { - "type": 3, - "threshold": 4, - "validators": [ - "0x1f20274b1210046769d48174c2f0e7c25ca7d5c5", - "0x3bc014bafa43f93d534aed34f750997cdffcf007", - "0xd79d506d741fa735938f7b7847a926e34a6fe6b0", - "0xe4a258bc61e65914c2a477b2a8a433ab4ebdf44b", - "0x6aea63b0be4679c1385c26a92a3ff8aa6a8379f2", - "0xc0085e1a49bcc69e534272adb82c74c0e007e1ca" - ] - }, - "ethereum": { - "type": 3, - "threshold": 4, - "validators": [ - "0x4c327ccb881a7542be77500b2833dc84c839e7b7", - "0x84cb373148ef9112b277e68acf676fefa9a9a9a0", - "0x0d860c2b28bec3af4fd3a5997283e460ff6f2789", - "0xd4c1211f0eefb97a846c4e6d6589832e52fc03db", - "0x600c90404d5c9df885404d2cc5350c9b314ea3a2", - "0x892DC66F5B2f8C438E03f6323394e34A9C24F2D6" - ] - }, - "avalanche": { - "type": 3, - "threshold": 4, - "validators": [ - "0xa7aa52623fe3d78c343008c95894be669e218b8d", - "0xb6004433fb04f643e2d48ae765c0e7f890f0bc0c", - "0xa07e213e0985b21a6128e6c22ab5fb73948b0cc2", - "0x73853ed9a5f6f2e4c521970a94d43469e3cdaea6", - "0xbd2e136cda02ba627ca882e49b184cbe976081c8", - "0x1418126f944a44dad9edbab32294a8c890e7a9e3" - ] - }, - "polygon": { - "type": 3, - "threshold": 4, - "validators": [ - "0x59a001c3451e7f9f3b4759ea215382c1e9aa5fc1", - "0x009fb042d28944017177920c1d40da02bfebf474", - "0xba4b13e23705a5919c1901150d9697e8ffb3ea71", - "0x2faa4071b718972f9b4beec1d8cbaa4eb6cca6c6", - "0x5ae9b0f833dfe09ef455562a1f603f1634504dd6", - "0x6a163d312f7352a95c9b81dca15078d5bf77a442" - ] - }, - "bsc": { - "type": 3, - "threshold": 4, - "validators": [ - "0xcc84b1eb711e5076b2755cf4ad1d2b42c458a45e", - "0xefe34eae2bca1846b895d2d0762ec21796aa196a", - "0x662674e80e189b0861d6835c287693f50ee0c2ff", - "0x8a0f59075af466841808c529624807656309c9da", - "0xdd2ff046ccd748a456b4757a73d47f165469669f", - "0x034c4924c30ec4aa1b7f3ad58548988f0971e1bf" - ] - }, - "arbitrum": { - "type": 3, - "threshold": 4, - "validators": [ - "0xbcb815f38d481a5eba4d7ac4c9e74d9d0fc2a7e7", - "0xd839424e2e5ace0a81152298dc2b1e3bb3c7fb20", - "0xb8085c954b75b7088bcce69e61d12fcef797cd8d", - "0x9856dcb10fd6e5407fa74b5ab1d3b96cc193e9b7", - "0x505dff4e0827aa5065f5e001db888e0569d46490", - "0x25c6779d4610f940bf2488732e10bcffb9d36f81" - ] - }, - "optimism": { - "type": 3, - "threshold": 4, - "validators": [ - "0x9f2296d5cfc6b5176adc7716c7596898ded13d35", - "0x9c10bbe8efa03a8f49dfdb5c549258e3a8dca097", - "0x62144d4a52a0a0335ea5bb84392ef9912461d9dd", - "0xaff4718d5d637466ad07441ee3b7c4af8e328dbd", - "0xc64d1efeab8ae222bc889fe669f75d21b23005d9", - "0xfa174eb2b4921bb652bc1ada3e8b00e7e280bf3c" - ] - }, - "moonbeam": { - "type": 3, - "threshold": 3, - "validators": [ - "0x237243d32d10e3bdbbf8dbcccc98ad44c1c172ea", - "0x9509c8cf0a06955f27342262af501b74874e98fb", - "0xb7113c999e4d587b162dd1a28c73f3f51c6bdcdc", - "0x26725501597d47352a23cd26f122709f69ad53bc" - ] - }, - "gnosis": { - "type": 3, - "threshold": 3, - "validators": [ - "0xd0529ec8df08d0d63c0f023786bfa81e4bb51fd6", - "0x8a72ff8571c53c62c7ca02e8c97a443cd5674383", - "0x4075c2f6bd6d9562067cfe551d49c2bcafa7d692", - "0xa18580444eaeb1c5957e7b66a6bf84b6519f904d" - ] - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/multisig-config.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/multisig-config.json deleted file mode 100644 index 5e08f27c0..000000000 --- a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/multisig-config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "bsc": { - "type": 3, - "threshold": 1, - "validators": [ - "0x0000000000000000000000000000000000000001" - ] - }, - "nautilus": { - "type": 3, - "threshold": 2, - "validators": [ - "0x9c920af9467595a23cb3433adefc3854d498a437", - "0x87611503e37ce041527c11c24263e8760fccf81f", - "0x573443248cf9929af0001b88f62131f2de29fe9f" - ] - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/program-ids.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/program-ids.json deleted file mode 100644 index 23f0cdd55..000000000 --- a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/nautilus/program-ids.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "program_id": "9k74DkJvS2x9QhG4XfnKsLkqaCDyVfaj8s6FyJyhAeEP" -} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/multisig-config.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/multisig-config.json deleted file mode 100644 index fc090e25d..000000000 --- a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/multisig-config.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "celo": { - "type": 3, - "threshold": 1, - "validators": [ - "0xe7a82e210f512f8e9900d6bc2acbf7981c63e66e" - ] - }, - "ethereum": { - "type": 3, - "threshold": 1, - "validators": [ - "0xaea1adb1c687b061e5b60b9da84cb69e7b5fab44" - ] - }, - "avalanche": { - "type": 3, - "threshold": 1, - "validators": [ - "0x706976391e23dea28152e0207936bd942aba01ce" - ] - }, - "polygon": { - "type": 3, - "threshold": 1, - "validators": [ - "0xef372f6ff7775989b3ac884506ee31c79638c989" - ] - }, - "bsc": { - "type": 3, - "threshold": 1, - "validators": [ - "0x0823081031a4a6f97c6083775c191d17ca96d0ab" - ] - }, - "arbitrum": { - "type": 3, - "threshold": 1, - "validators": [ - "0x1a95b35fb809d57faf1117c1cc29a6c5df289df1" - ] - }, - "optimism": { - "type": 3, - "threshold": 1, - "validators": [ - "0x60e938bf280bbc21bacfd8bf435459d9003a8f98" - ] - }, - "moonbeam": { - "type": 3, - "threshold": 1, - "validators": [ - "0x0df7140811e309dc69638352545151ebb9d5e0fd" - ] - }, - "gnosis": { - "type": 3, - "threshold": 1, - "validators": [ - "0x15f48e78092a4f79febface509cfd76467c6cdbb" - ] - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/program-ids.json b/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/program-ids.json deleted file mode 100644 index 0bf925699..000000000 --- a/rust/sealevel/environments/mainnet2/multisig-ism-message-id/solana/rc/program-ids.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "program_id": "BYTsxBuKVbwgsZFswzB91nrxveQySghwXzaKqn8exNnC" -} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/solana/core/program-ids.json b/rust/sealevel/environments/mainnet2/solana/core/program-ids.json deleted file mode 100644 index 53801da0c..000000000 --- a/rust/sealevel/environments/mainnet2/solana/core/program-ids.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mailbox": "Ge9atjAc3Ltu91VTbNpJDCjZ9CFxFyck4h3YBcTF9XPq", - "validator_announce": "C88Lk5GR6cPxYoJxPbNDDEwsx5Kxn1wZEomvQ2So333g", - "multisig_ism_message_id": "6pHP4EeX2Xek24Be7PPTWCqcpmNEPENW1m9RnZSFSmA1", - "igp_program_id": "HksFWQM1EXJJ5mxo2uZoMfmksXHaNhCunh71NqcQQHZ8", - "overhead_igp_account": "GTj6WzNxLNFydq5zJrV9p13fyqotRoo1MQykNCWuVpbS", - "igp_account": "FCNfmLSZLo5x7oNYmkYU8WdPUu7pj636P9CaMxkmaCp7" -} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json b/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json deleted file mode 100644 index 7d99aea92..000000000 --- a/rust/sealevel/environments/mainnet2/warp-routes/zbc/program-ids.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "solanamainnet": { - "hex": "0xc5ba229fa2822fe65ac2bd0a93d8371d75292c3415dd381923c1088a3308528b", - "base58": "EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa" - }, - "nautilus": { - "hex": "0x0000000000000000000000004501bbe6e731a4bc5c60c03a77435b2f6d5e9fe7", - "base58": "111111111111xm5qkrK7gZ8Cmjr4ggPLRxy2T8a" - } -} diff --git a/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json b/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json deleted file mode 100644 index 90b0e20ce..000000000 --- a/rust/sealevel/environments/mainnet2/warp-routes/zbc/token-config.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "solanamainnet": { - "type": "collateral", - "decimals": 9, - "remoteDecimals": 9, - "token": "wzbcJyhGhQDLTV1S99apZiiBdE4jmYfbw99saMMdP59", - "splTokenProgram": "token", - "interchainSecurityModule": "9k74DkJvS2x9QhG4XfnKsLkqaCDyVfaj8s6FyJyhAeEP", - "owner": "EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3" - }, - "nautilus": { - "type": "native", - "decimals": 18, - "foreignDeployment": "0x4501bBE6e731A4bC5c60C03A77435b2f6d5e9Fe7" - } -} diff --git a/rust/sealevel/environments/mainnet3/chain-config.json b/rust/sealevel/environments/mainnet3/chain-config.json index 363b99621..b2b896b9a 100644 --- a/rust/sealevel/environments/mainnet3/chain-config.json +++ b/rust/sealevel/environments/mainnet3/chain-config.json @@ -11,7 +11,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 888888888, "deployer": { @@ -35,7 +35,50 @@ "http": "https://rpc.ancient8.gg" } ], - "technicalStack": "other" + "technicalStack": "opstack" + }, + "alephzeroevm": { + "blockExplorers": [ + { + "apiUrl": "https://evm-explorer.alephzero.org/api", + "family": "blockscout", + "name": "Aleph Zero Explorer", + "url": "https://evm-explorer.alephzero.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 5 + }, + "chainId": 41455, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Aleph Zero EVM", + "displayNameShort": "Aleph Zero EVM", + "domainId": 41455, + "gasCurrencyCoinGeckoId": "aleph-zero", + "index": { + "from": 3421962 + }, + "name": "alephzeroevm", + "nativeToken": { + "decimals": 18, + "name": "AZERO", + "symbol": "AZERO" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.alephzero.raas.gelato.cloud" + }, + { + "http": "https://alephzero.drpc.org" + } + ], + "technicalStack": "arbitrumnitro" }, "arbitrum": { "blockExplorers": [ @@ -49,7 +92,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 42161, "deployer": { @@ -95,7 +138,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 13, - "reorgPeriod": 0 + "reorgPeriod": 32 }, "chainId": 592, "deployer": { @@ -116,7 +159,8 @@ { "http": "https://evm.astar.network" } - ] + ], + "technicalStack": "polkadotsubstrate" }, "astarzkevm": { "blockExplorers": [ @@ -130,7 +174,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 3776, "deployer": { @@ -154,7 +198,8 @@ { "http": "https://astar-zkevm-rpc.dwellir.com" } - ] + ], + "technicalStack": "polygoncdk" }, "avalanche": { "blockExplorers": [ @@ -197,7 +242,8 @@ "minBlockNumber": 6765067 } } - ] + ], + "technicalStack": "other" }, "base": { "blockExplorers": [ @@ -211,7 +257,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 10 }, "chainId": 8453, "deployer": { @@ -239,7 +285,8 @@ { "http": "https://base.blockpi.network/v1/rpc/public" } - ] + ], + "technicalStack": "opstack" }, "bitlayer": { "blockExplorers": [ @@ -253,7 +300,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 20 }, "chainId": 200901, "deployer": { @@ -280,7 +327,8 @@ { "http": "https://rpc.ankr.com/bitlayer" } - ] + ], + "technicalStack": "other" }, "blast": { "blockExplorers": [ @@ -294,7 +342,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 81457, "deployer": { @@ -320,7 +368,7 @@ "http": "https://rpc.ankr.com/blast" } ], - "technicalStack": "other" + "technicalStack": "opstack" }, "bob": { "blockExplorers": [ @@ -334,7 +382,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 60808, "deployer": { @@ -357,7 +405,7 @@ "http": "https://rpc.gobob.xyz" } ], - "technicalStack": "other" + "technicalStack": "opstack" }, "bsc": { "blockExplorers": [ @@ -401,6 +449,7 @@ "http": "https://bscrpc.com" } ], + "technicalStack": "other", "transactionOverrides": { "gasPrice": 3000000000 } @@ -445,21 +494,22 @@ { "http": "https://forno.celo.org" } - ] + ], + "technicalStack": "other" }, "cheesechain": { "blockExplorers": [ { - "apiUrl": "https://fetascan.io/api", + "apiUrl": "https://fetascan.xyz/api", "family": "blockscout", "name": "Fetascan", - "url": "https://fetascan.io" + "url": "https://fetascan.xyz" } ], "blocks": { "confirmations": 1, - "estimateBlockTime": 90, - "reorgPeriod": 0 + "estimateBlockTime": 30, + "reorgPeriod": 1 }, "chainId": 383353, "deployer": { @@ -487,6 +537,48 @@ ], "technicalStack": "arbitrumnitro" }, + "chiliz": { + "blockExplorers": [ + { + "apiUrl": "https://api.routescan.io/v2/network/mainnet/evm/88888/etherscan/api", + "family": "routescan", + "name": "Chiliscan", + "url": "https://chiliscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 9 + }, + "chainId": 88888, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Chiliz", + "domainId": 88888, + "gasCurrencyCoinGeckoId": "chiliz", + "name": "chiliz", + "nativeToken": { + "decimals": 18, + "name": "Chiliz", + "symbol": "CHZ" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.ankr.com/chiliz" + }, + { + "http": "https://chiliz.publicnode.com" + } + ], + "technicalStack": "other", + "transactionOverrides": { + "maxPriorityFeePerGas": 1000000000 + } + }, "coredao": { "blockExplorers": [ { @@ -499,7 +591,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 21 }, "chainId": 1116, "deployer": { @@ -532,7 +624,8 @@ { "http": "https://rpc-core.icecreamswap.com" } - ] + ], + "technicalStack": "other" }, "cyber": { "blockExplorers": [ @@ -546,7 +639,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 7560, "deployer": { @@ -570,7 +663,8 @@ { "http": "https://cyber.alt.technology" } - ] + ], + "technicalStack": "opstack" }, "degenchain": { "blockExplorers": [ @@ -584,7 +678,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 10, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 666666666, "deployer": { @@ -623,7 +717,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 2000, "deployer": { @@ -644,9 +738,18 @@ { "http": "https://rpc.dogechain.dog" } - ] + ], + "technicalStack": "polygoncdk" }, "eclipsemainnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.eclipse.xyz/api", + "family": "other", + "name": "Eclipse Explorer", + "url": "https://explorer.eclipse.xyz/" + } + ], "blocks": { "confirmations": 1, "estimateBlockTime": 0.4, @@ -671,7 +774,8 @@ { "http": "https://mainnetbeta-rpc.eclipse.xyz" } - ] + ], + "technicalStack": "other" }, "endurance": { "blockExplorers": [ @@ -685,7 +789,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 12, - "reorgPeriod": 14 + "reorgPeriod": 15 }, "chainId": 648, "deployer": { @@ -707,7 +811,8 @@ { "http": "https://rpc-endurance.fusionist.io" } - ] + ], + "technicalStack": "other" }, "ethereum": { "blockExplorers": [ @@ -725,9 +830,9 @@ } ], "blocks": { - "confirmations": 3, + "confirmations": 2, "estimateBlockTime": 13, - "reorgPeriod": 14 + "reorgPeriod": 15 }, "chainId": 1, "deployer": { @@ -753,10 +858,46 @@ "http": "https://cloudflare-eth.com" } ], - "transactionOverrides": { - "maxFeePerGas": 150000000000, - "maxPriorityFeePerGas": 5000000000 - } + "technicalStack": "other" + }, + "everclear": { + "blockExplorers": [ + { + "apiUrl": "https://scan.everclear.org/api", + "family": "blockscout", + "name": "Everclear Explorer", + "url": "https://scan.everclear.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 2 + }, + "chainId": 25327, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Everclear", + "domainId": 25327, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 37 + }, + "name": "everclear", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.everclear.raas.gelato.cloud" + } + ], + "technicalStack": "arbitrumnitro" }, "flare": { "blockExplorers": [ @@ -770,7 +911,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 3 }, "chainId": 14, "deployer": { @@ -800,7 +941,48 @@ { "http": "https://rpc.ankr.com/flare" } - ] + ], + "technicalStack": "other" + }, + "flow": { + "blockExplorers": [ + { + "apiUrl": "https://evm.flowscan.io/api", + "family": "blockscout", + "name": "EVM on Flow Explorer", + "url": "https://evm.flowscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, + "reorgPeriod": 25 + }, + "chainId": 747, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "EVM on Flow", + "domainId": 747, + "gasCurrencyCoinGeckoId": "flow", + "isTestnet": false, + "name": "flow", + "nativeToken": { + "decimals": 18, + "name": "Flow", + "symbol": "FLOW" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.evm.nodes.onflow.org" + } + ], + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 100000000 + } }, "fraxtal": { "blockExplorers": [ @@ -814,7 +996,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 252, "deployer": { @@ -839,7 +1021,8 @@ { "http": "https://fraxtal.drpc.org" } - ] + ], + "technicalStack": "opstack" }, "fusemainnet": { "blockExplorers": [ @@ -853,7 +1036,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 1 + "reorgPeriod": 19 }, "chainId": 122, "deployer": { @@ -884,7 +1067,8 @@ { "http": "https://fuse-pokt.nodies.app" } - ] + ], + "technicalStack": "other" }, "gnosis": { "blockExplorers": [ @@ -898,7 +1082,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 14 + "reorgPeriod": 5 }, "chainId": 100, "deployer": { @@ -924,7 +1108,51 @@ "minBlockNumber": 25997478 } } - ] + ], + "technicalStack": "other" + }, + "immutablezkevm": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.immutable.com/api/eth-rpc", + "family": "blockscout", + "name": "Immutable Explorer", + "url": "https://explorer.immutable.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 20 + }, + "chainId": 13371, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Immutable zkEVM", + "domainId": 13371, + "gasCurrencyCoinGeckoId": "immutable-x", + "name": "immutablezkevm", + "nativeToken": { + "decimals": 18, + "name": "Immutable", + "symbol": "IMX" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.immutable.com" + }, + { + "http": "https://immutable.gateway.tenderly.co" + } + ], + "technicalStack": "other", + "transactionOverrides": { + "maxFeePerGas": 100000000000, + "maxPriorityFeePerGas": 100000000000 + } }, "inevm": { "blockExplorers": [ @@ -938,7 +1166,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 3 }, "chainId": 2525, "deployer": { @@ -977,7 +1205,7 @@ "estimateBlockTime": 1, "reorgPeriod": 10 }, - "chainId": 6909546, + "chainId": "injective-1", "deployer": { "name": "Abacus Works", "url": "https://www.hyperlane.xyz" @@ -1008,7 +1236,8 @@ "http": "https://sentry.tm.injective.network:443" } ], - "slip44": 118 + "slip44": 118, + "technicalStack": "other" }, "kroma": { "blockExplorers": [ @@ -1022,7 +1251,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 255, "deployer": { @@ -1049,7 +1278,8 @@ { "http": "https://1rpc.io/kroma" } - ] + ], + "technicalStack": "opstack" }, "linea": { "blockExplorers": [ @@ -1063,7 +1293,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 59144, "deployer": { @@ -1094,7 +1324,8 @@ { "http": "https://linea.drpc.org" } - ] + ], + "technicalStack": "other" }, "lisk": { "blockExplorers": [ @@ -1108,7 +1339,7 @@ "blocks": { "confirmations": 3, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 1135, "deployer": { @@ -1129,7 +1360,8 @@ { "http": "https://rpc.api.lisk.com" } - ] + ], + "technicalStack": "opstack" }, "lukso": { "blockExplorers": [ @@ -1143,7 +1375,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 12, - "reorgPeriod": 14 + "reorgPeriod": 15 }, "chainId": 42, "deployer": { @@ -1170,7 +1402,47 @@ { "http": "https://42.rpc.thirdweb.com" } - ] + ], + "technicalStack": "other" + }, + "lumia": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.lumia.org/api/eth-rpc", + "family": "blockscout", + "name": "Lumia Prism Explorer", + "url": "https://explorer.lumia.org" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 4, + "reorgPeriod": 5 + }, + "chainId": 994873017, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Lumia Prism", + "domainId": 994873017, + "gasCurrencyCoinGeckoId": "orion-protocol", + "name": "lumia", + "nativeToken": { + "decimals": 18, + "name": "Lumia", + "symbol": "LUMIA" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://994873017.rpc.thirdweb.com" + }, + { + "http": "https://mainnet-rpc.lumia.org" + } + ], + "technicalStack": "polygoncdk" }, "mantapacific": { "blockExplorers": [ @@ -1184,7 +1456,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 169, "deployer": { @@ -1211,7 +1483,8 @@ { "http": "https://manta.nirvanalabs.xyz/mantapublic" } - ] + ], + "technicalStack": "opstack" }, "mantle": { "blockExplorers": [ @@ -1225,7 +1498,7 @@ "blocks": { "confirmations": 3, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 2 }, "chainId": 5000, "deployer": { @@ -1248,7 +1521,7 @@ "http": "https://rpc.mantle.xyz" } ], - "technicalStack": "other" + "technicalStack": "opstack" }, "merlin": { "blockExplorers": [ @@ -1262,7 +1535,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 4200, "deployer": { @@ -1286,7 +1559,45 @@ { "http": "https://merlin.blockpi.network/v1/rpc/public" } - ] + ], + "technicalStack": "polygoncdk" + }, + "metall2": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.metall2.com/api", + "family": "blockscout", + "name": "Metal L2 Explorer", + "url": "https://explorer.metall2.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 1750, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Metal L2", + "domainId": 1750, + "gasCurrencyCoinGeckoId": "ethereum", + "isTestnet": false, + "name": "metall2", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.metall2.com" + } + ], + "technicalStack": "opstack" }, "metis": { "blockExplorers": [ @@ -1300,7 +1611,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 1088, "deployer": { @@ -1325,7 +1636,7 @@ "http": "https://andromeda.metis.io/?owner=1088" } ], - "technicalStack": "arbitrumnitro" + "technicalStack": "opstack" }, "mint": { "blockExplorers": [ @@ -1339,7 +1650,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 185, "deployer": { @@ -1361,7 +1672,8 @@ { "http": "https://rpc.mintchain.io" } - ] + ], + "technicalStack": "opstack" }, "mode": { "blockExplorers": [ @@ -1375,7 +1687,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 34443, "deployer": { @@ -1401,7 +1713,7 @@ "http": "https://mode.drpc.org" } ], - "technicalStack": "other" + "technicalStack": "opstack" }, "molten": { "blockExplorers": [ @@ -1414,7 +1726,7 @@ ], "blocks": { "confirmations": 1, - "estimateBlockTime": 1, + "estimateBlockTime": 30, "reorgPeriod": 0 }, "chainId": 360, @@ -1454,7 +1766,7 @@ "blocks": { "confirmations": 2, "estimateBlockTime": 12, - "reorgPeriod": 2 + "reorgPeriod": 10 }, "chainId": 1284, "deployer": { @@ -1477,6 +1789,7 @@ "http": "https://rpc.api.moonbeam.network" } ], + "technicalStack": "polkadotsubstrate", "transactionOverrides": { "maxFeePerGas": 350000000000, "maxPriorityFeePerGas": 50000000000 @@ -1497,7 +1810,7 @@ "estimateBlockTime": 3, "reorgPeriod": 1 }, - "chainId": 1853125230, + "chainId": "neutron-1", "deployer": { "name": "Abacus Works", "url": "https://www.hyperlane.xyz" @@ -1533,10 +1846,47 @@ } ], "slip44": 118, + "technicalStack": "other", "transactionOverrides": { "gasPrice": "0.0075" } }, + "oortmainnet": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet-scan.oortech.com/api", + "family": "other", + "name": "Oort Olympus Explorer", + "url": "https://mainnet-scan.oortech.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 0 + }, + "chainId": 970, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Oort", + "domainId": 970, + "gasCurrencyCoinGeckoId": "oort", + "name": "oortmainnet", + "nativeToken": { + "decimals": 18, + "name": "Oort", + "symbol": "OORT" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet-rpc.oortech.com" + } + ], + "technicalStack": "other" + }, "optimism": { "blockExplorers": [ { @@ -1549,7 +1899,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 0 + "reorgPeriod": 10 }, "chainId": 10, "deployer": { @@ -1571,7 +1921,8 @@ { "http": "https://mainnet.optimism.io" } - ] + ], + "technicalStack": "opstack" }, "osmosis": { "bech32Prefix": "osmo", @@ -1588,7 +1939,7 @@ "estimateBlockTime": 3, "reorgPeriod": 1 }, - "chainId": 875, + "chainId": "osmosis-1", "deployer": { "name": "Mitosis", "url": "https://mitosis.org" @@ -1621,6 +1972,7 @@ } ], "slip44": 118, + "technicalStack": "other", "transactionOverrides": { "gasPrice": "0.025" } @@ -1646,13 +1998,13 @@ }, "displayName": "Polygon", "domainId": 137, - "gasCurrencyCoinGeckoId": "matic-network", + "gasCurrencyCoinGeckoId": "polygon-ecosystem-token", "gnosisSafeTransactionServiceUrl": "https://safe-transaction-polygon.safe.global/", "name": "polygon", "nativeToken": { "decimals": 18, - "name": "Matic", - "symbol": "MATIC" + "name": "Polygon Ecosystem Token", + "symbol": "POL" }, "protocol": "ethereum", "rpcUrls": [ @@ -1666,8 +2018,9 @@ "http": "https://rpc.ankr.com/polygon" } ], + "technicalStack": "other", "transactionOverrides": { - "maxFeePerGas": 550000000000, + "maxFeePerGas": 800000000000, "maxPriorityFeePerGas": 50000000000 } }, @@ -1683,7 +2036,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 10, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 1101, "deployer": { @@ -1710,10 +2063,47 @@ "http": "https://rpc.ankr.com/polygon_zkevm" } ], + "technicalStack": "polygoncdk", "transactionOverrides": { "gasPrice": 1000000000 } }, + "polynomial": { + "blockExplorers": [ + { + "apiUrl": "https://polynomialscan.io/api", + "family": "routescan", + "name": "Polynomial Explorer", + "url": "https://polynomialscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 5 + }, + "chainId": 8008, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Polynomial", + "domainId": 8008, + "gasCurrencyCoinGeckoId": "ethereum", + "name": "polynomial", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.polynomial.fi" + } + ], + "technicalStack": "opstack" + }, "proofofplay": { "blockExplorers": [ { @@ -1726,7 +2116,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 1, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 70700, "deployer": { @@ -1753,6 +2143,45 @@ ], "technicalStack": "arbitrumnitro" }, + "rari": { + "blockExplorers": [ + { + "apiUrl": "https://mainnet.explorer.rarichain.org/api", + "family": "blockscout", + "name": "Rari Mainnet Explorer", + "url": "https://mainnet.explorer.rarichain.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 30, + "reorgPeriod": 0 + }, + "chainId": 1380012617, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "RARI Chain", + "domainId": 1380012617, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 541753 + }, + "name": "rari", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.rpc.rarichain.org/http" + } + ], + "technicalStack": "arbitrumnitro" + }, "real": { "blockExplorers": [ { @@ -1764,7 +2193,7 @@ ], "blocks": { "confirmations": 1, - "estimateBlockTime": 4, + "estimateBlockTime": 30, "reorgPeriod": 0 }, "chainId": 111188, @@ -1804,7 +2233,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 690, "deployer": { @@ -1826,7 +2255,53 @@ { "http": "https://rpc.redstonechain.com" } - ] + ], + "technicalStack": "opstack" + }, + "rootstock": { + "blockExplorers": [ + { + "apiUrl": "https://rootstock.blockscout.com/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://rootstock.blockscout.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 30, + "reorgPeriod": 4 + }, + "chainId": 30, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Rootstock", + "domainId": 30, + "gasCurrencyCoinGeckoId": "rootstock", + "name": "rootstock", + "nativeToken": { + "decimals": 18, + "name": "Rootstock Smart Bitcoin", + "symbol": "RBTC" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.mainnet.rootstock.io/kXhXHf6TnnfW1POvr4UT0YUvujmuju-M" + }, + { + "http": "https://public-node.rsk.co" + }, + { + "http": "https://mycrypto.rsk.co" + } + ], + "technicalStack": "other", + "transactionOverrides": { + "gasPrice": 70000000 + } }, "sanko": { "blockExplorers": [ @@ -1839,8 +2314,8 @@ ], "blocks": { "confirmations": 1, - "estimateBlockTime": 10, - "reorgPeriod": 0 + "estimateBlockTime": 15, + "reorgPeriod": 1 }, "chainId": 1996, "deployer": { @@ -1879,7 +2354,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 3, - "reorgPeriod": 1 + "reorgPeriod": 17 }, "chainId": 534352, "deployer": { @@ -1902,8 +2377,9 @@ "http": "https://scroll.blockpi.network/v1/rpc/public" } ], + "technicalStack": "other", "transactionOverrides": { - "gasPrice": 2000000000 + "gasPrice": 200000000 } }, "sei": { @@ -1941,6 +2417,7 @@ "http": "https://evm-rpc.sei-apis.com" } ], + "technicalStack": "other", "transactionOverrides": { "gasPrice": 101000000000 } @@ -1957,7 +2434,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 5, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 109, "deployer": { @@ -1981,15 +2458,16 @@ { "http": "https://rpc.shibrpc.com" } - ] + ], + "technicalStack": "other" }, "solanamainnet": { "blockExplorers": [ { - "apiUrl": "https://explorer.solana.com?cluster=mainnet-beta", + "apiUrl": "https://solscan.io", "family": "other", "name": "Solana Explorer", - "url": "https://explorer.solana.com?cluster=mainnet-beta" + "url": "https://solscan.io" } ], "blocks": { @@ -2016,7 +2494,99 @@ { "http": "https://api.mainnet-beta.solana.com" } - ] + ], + "technicalStack": "other" + }, + "stride": { + "bech32Prefix": "stride", + "blockExplorers": [ + { + "apiUrl": "https://www.mintscan.io/stride", + "family": "other", + "name": "Mintscan", + "url": "https://www.mintscan.io/stride" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 1 + }, + "chainId": "stride-1", + "deployer": { + "name": "Stride Labs", + "url": "https://www.stride.zone" + }, + "displayName": "Stride", + "domainId": 745, + "gasCurrencyCoinGeckoId": "stride", + "grpcUrls": [ + { + "http": "stride-grpc.polkachu.com:12290" + } + ], + "isTestnet": false, + "name": "stride", + "nativeToken": { + "decimals": 6, + "denom": "ustrd", + "name": "Stride", + "symbol": "STRD" + }, + "protocol": "cosmos", + "restUrls": [ + { + "http": "https://stride-api.polkachu.com" + } + ], + "rpcUrls": [ + { + "http": "https://stride-rpc.polkachu.com" + } + ], + "slip44": 118, + "transactionOverrides": { + "gasPrice": "0.0025" + } + }, + "superposition": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.superposition.so/api", + "family": "blockscout", + "name": "Superposition Explorer", + "url": "https://explorer.superposition.so/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 60, + "reorgPeriod": 0 + }, + "chainId": 55244, + "deployer": { + "name": "Abacus Works", + "url": "https://www.hyperlane.xyz" + }, + "displayName": "Superposition", + "domainId": 55244, + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 1201 + }, + "name": "superposition", + "nativeToken": { + "decimals": 18, + "name": "Ethereum", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.superposition.so" + } + ], + "technicalStack": "arbitrumnitro" }, "taiko": { "blockExplorers": [ @@ -2030,7 +2600,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 12, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 167000, "deployer": { @@ -2067,7 +2637,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 6, - "reorgPeriod": 0 + "reorgPeriod": 10 }, "chainId": 5845, "deployer": { @@ -2089,7 +2659,8 @@ { "http": "https://rpc.tangle.tools" } - ] + ], + "technicalStack": "polkadotsubstrate" }, "viction": { "blockExplorers": [ @@ -2103,7 +2674,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 3 }, "chainId": 88, "deployer": { @@ -2127,7 +2698,8 @@ { "http": "https://viction.blockpi.network/v1/rpc/public" } - ] + ], + "technicalStack": "other" }, "worldchain": { "blockExplorers": [ @@ -2141,7 +2713,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 480, "deployer": { @@ -2162,7 +2734,8 @@ { "http": "https://raas-backend.alchemy.com/rpc/worldchain-mainnet/rollup" } - ] + ], + "technicalStack": "opstack" }, "xai": { "blockExplorers": [ @@ -2176,7 +2749,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 1, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 660279, "deployer": { @@ -2215,7 +2788,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 10, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 196, "deployer": { @@ -2241,7 +2814,8 @@ { "http": "https://rpc.xlayer.tech" } - ] + ], + "technicalStack": "polygoncdk" }, "zetachain": { "blockExplorers": [ @@ -2280,7 +2854,8 @@ { "http": "https://zetachain-mainnet.g.allthatnode.com/archive/evm" } - ] + ], + "technicalStack": "other" }, "zircuit": { "blockExplorers": [ @@ -2294,7 +2869,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 0 + "reorgPeriod": 5 }, "chainId": 48900, "deployer": { @@ -2322,7 +2897,8 @@ { "http": "https://zircuit-mainnet.drpc.org" } - ] + ], + "technicalStack": "opstack" }, "zoramainnet": { "blockExplorers": [ @@ -2336,7 +2912,7 @@ "blocks": { "confirmations": 1, "estimateBlockTime": 2, - "reorgPeriod": 1 + "reorgPeriod": 5 }, "chainId": 7777777, "deployer": { @@ -2358,6 +2934,7 @@ { "http": "https://rpc.zora.energy" } - ] + ], + "technicalStack": "opstack" } } diff --git a/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json b/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json new file mode 100644 index 000000000..b459e88d4 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/eclipsemainnet/gas-oracle-configs-eclipsemainnet.json @@ -0,0 +1,29 @@ +[ + { + "domain": 1, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "15000000000000000000", + "gasPrice": "10000000000", + "tokenDecimals": 18 + } + }, + { + "domain": 1399811149, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "887000000000000000", + "gasPrice": "15", + "tokenDecimals": 9 + } + }, + { + "domain": 745, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "435246388284187", + "gasPrice": "7", + "tokenDecimals": 6 + } + } +] diff --git a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json b/rust/sealevel/environments/mainnet3/gas-oracle-configs.json deleted file mode 100644 index 0637a088a..000000000 --- a/rust/sealevel/environments/mainnet3/gas-oracle-configs.json +++ /dev/null @@ -1 +0,0 @@ -[] \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/multisig-ism-message-id/eclipsemainnet/hyperlane/multisig-config.json b/rust/sealevel/environments/mainnet3/multisig-ism-message-id/eclipsemainnet/hyperlane/multisig-config.json index d6e5aae1d..2966c15b5 100644 --- a/rust/sealevel/environments/mainnet3/multisig-ism-message-id/eclipsemainnet/hyperlane/multisig-config.json +++ b/rust/sealevel/environments/mainnet3/multisig-ism-message-id/eclipsemainnet/hyperlane/multisig-config.json @@ -1,4 +1,13 @@ { + "alephzeroevm": { + "threshold": 2, + "validators": [ + "0xcae8fab142adc4e434bb7409e40dd932cc3851aa", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" + }, "ancient8": { "threshold": 2, "validators": [ @@ -6,7 +15,7 @@ "0xa5a56e97fb46f0ac3a3d261e404acb998d9a6969", "0x95c7bf235837cb5a609fe6c95870410b9f68bcff" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "arbitrum": { "threshold": 3, @@ -17,17 +26,25 @@ "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "astar": { - "threshold": 1, - "validators": ["0x4d1b2cade01ee3493f44304653d8e352c66ec3e7"], - "type": 3 + "threshold": 2, + "validators": [ + "0x4d1b2cade01ee3493f44304653d8e352c66ec3e7", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "astarzkevm": { - "threshold": 1, - "validators": ["0x89ecdd6caf138934bf3a2fb7b323984d72fd66de"], - "type": 3 + "threshold": 2, + "validators": [ + "0x89ecdd6caf138934bf3a2fb7b323984d72fd66de", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "avalanche": { "threshold": 2, @@ -36,7 +53,7 @@ "0x402e0f8c6e4210d408b6ac00d197d4a099fcd25a", "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "base": { "threshold": 3, @@ -47,12 +64,16 @@ "0xcff391b4e516452d424db66beb9052b041a9ed79", "0x5450447aee7b544c462c9352bef7cad049b0c2dc" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "bitlayer": { - "threshold": 1, - "validators": ["0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f"], - "type": 3 + "threshold": 2, + "validators": [ + "0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "blast": { "threshold": 2, @@ -61,7 +82,7 @@ "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", "0xae53467a5c2a9d9420c188d10fef5e1d9b9a5b80" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "bob": { "threshold": 2, @@ -70,7 +91,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "bsc": { "threshold": 3, @@ -80,7 +101,7 @@ "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", "0x5450447aee7b544c462c9352bef7cad049b0c2dc" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "celo": { "threshold": 3, @@ -91,7 +112,7 @@ "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b", "0x5450447aee7b544c462c9352bef7cad049b0c2dc" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "cheesechain": { "threshold": 2, @@ -99,12 +120,25 @@ "0x478fb53c6860ae8fc35235ba0d38d49b13128226", "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "chiliz": { + "threshold": 2, + "validators": [ + "0x82d024f453b1a3f3f6606226f06b038da27596f3", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "coredao": { - "threshold": 1, - "validators": ["0xbd6e158a3f5830d99d7d2bce192695bc4a148de2"], - "type": 3 + "threshold": 2, + "validators": [ + "0xbd6e158a3f5830d99d7d2bce192695bc4a148de2", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "cyber": { "threshold": 2, @@ -113,7 +147,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "degenchain": { "threshold": 2, @@ -122,12 +156,16 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "dogechain": { - "threshold": 1, - "validators": ["0xe43f742c37858746e6d7e458bc591180d0cba440"], - "type": 3 + "threshold": 2, + "validators": [ + "0xe43f742c37858746e6d7e458bc591180d0cba440", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "endurance": { "threshold": 2, @@ -136,7 +174,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x7419021c0de2772b763e554480158a82a291c1f2" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "ethereum": { "threshold": 4, @@ -149,20 +187,43 @@ "0xb683b742b378632a5f73a2a5a45801b3489bba44", "0xbf1023eff3dba21263bf2db2add67a0d6bcda2de" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "everclear": { + "threshold": 2, + "validators": [ + "0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0xD79DFbF56ee2268f061cc613027a44A880f61Ba2" + ], + "type": "messageIdMultisigIsm" }, "flare": { - "threshold": 1, - "validators": ["0xb65e52be342dba3ab2c088ceeb4290c744809134"], - "type": 3 + "threshold": 2, + "validators": [ + "0xb65e52be342dba3ab2c088ceeb4290c744809134", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" + }, + "flow": { + "threshold": 2, + "validators": [ + "0x3aee1090318e9c54d1d23194dcd0f2bee00ddc97", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "fraxtal": { "threshold": 2, "validators": [ "0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x25b3a88f7cfd3c9f7d7e32b295673a16a6ddbd91" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "fusemainnet": { "threshold": 2, @@ -171,7 +232,7 @@ "0x6760226b34213d262D41D5291Ed57E81a68b4E0b", "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "gnosis": { "threshold": 3, @@ -181,7 +242,16 @@ "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", "0x5450447aee7b544c462c9352bef7cad049b0c2dc" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "immutablezkevm": { + "threshold": 2, + "validators": [ + "0xa787c2952a4d22f776ee6e87e828e6f75de24330", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "inevm": { "threshold": 2, @@ -190,7 +260,7 @@ "0x6B1d09A97b813D53e9D4b7523DA36604C0B52242", "0x9ab11f38a609940153850df611c9a2175dcffe0f" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "injective": { "threshold": 2, @@ -199,7 +269,7 @@ "0x6B1d09A97b813D53e9D4b7523DA36604C0B52242", "0x9e551b6694bbd295d7d6e6a2540c7d41ce70a3b9" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "kroma": { "threshold": 2, @@ -208,7 +278,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "linea": { "threshold": 2, @@ -217,7 +287,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "lisk": { "threshold": 2, @@ -226,18 +296,28 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "lukso": { "threshold": 2, "validators": [ "0xa5e953701dcddc5b958b5defb677a829d908df6d", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47" + ], + "type": "messageIdMultisigIsm" + }, + "lumia": { + "threshold": 2, + "validators": [ + "0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "mantapacific": { - "threshold": 5, + "threshold": 4, "validators": [ "0x8e668c97ad76d0e28375275c41ece4972ab8a5bc", "0x521a3e6bf8d24809fde1c1fd3494a859a16f132c", @@ -247,7 +327,7 @@ "0xcc9a0b6de7fe314bd99223687d784730a75bb957", "0x42b6de2edbaa62c2ea2309ad85d20b3e37d38acf" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "mantle": { "threshold": 2, @@ -256,7 +336,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "merlin": { "threshold": 2, @@ -265,7 +345,16 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "metall2": { + "threshold": 2, + "validators": [ + "0x1b000e1e1f0a032ed382c6d69a2d58f6fe773c09", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "metis": { "threshold": 2, @@ -274,7 +363,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "mint": { "threshold": 2, @@ -283,20 +372,26 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x0230505530b80186f8cdccfaf9993eb97aebe98a" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "mode": { - "threshold": 2, + "threshold": 3, "validators": [ "0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7", - "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x7e29608c6e5792bbf9128599ca309be0728af7b4", + "0x101cE77261245140A0871f9407d6233C8230Ec47" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "molten": { - "threshold": 1, - "validators": ["0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f"], - "type": 3 + "threshold": 2, + "validators": [ + "0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "moonbeam": { "threshold": 3, @@ -306,7 +401,7 @@ "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "neutron": { "threshold": 4, @@ -319,7 +414,16 @@ "0x54b2cca5091b098a1a993dec03c4d1ee9af65999", "0x42b6de2edbaa62c2ea2309ad85d20b3e37d38acf" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "oortmainnet": { + "threshold": 2, + "validators": [ + "0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x032dE4f94676bF9314331e7D83E8Db4aC74c9E21" + ], + "type": "messageIdMultisigIsm" }, "optimism": { "threshold": 3, @@ -330,12 +434,14 @@ "0xb3ac35d3988bca8c2ffd195b1c6bee18536b317b", "0x5450447aee7b544c462c9352bef7cad049b0c2dc" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "osmosis": { "threshold": 1, - "validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"], - "type": 3 + "validators": [ + "0xea483af11c19fa41b16c31d1534c2a486a92bcac" + ], + "type": "messageIdMultisigIsm" }, "polygon": { "threshold": 3, @@ -345,7 +451,7 @@ "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", "0x5450447aee7b544c462c9352bef7cad049b0c2dc" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "polygonzkevm": { "threshold": 2, @@ -354,7 +460,16 @@ "0x865818fe1db986036d5fd0466dcd462562436d1a", "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "polynomial": { + "threshold": 2, + "validators": [ + "0xa63ad0891e921ad5947d57e05831fabb9816eca7", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "proofofplay": { "threshold": 2, @@ -363,7 +478,16 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "rari": { + "threshold": 2, + "validators": [ + "0x989d6862e09de21337078efbd86843a3eb1133e3", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "real": { "threshold": 2, @@ -372,15 +496,25 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "redstone": { "threshold": 2, "validators": [ "0x1400b9737007f7978d8b4bbafb4a69c83f0641a7", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" + }, + "rootstock": { + "threshold": 2, + "validators": [ + "0xcb8e3a72cf427feff27416d0e2ec375a052eaaee", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "sanko": { "threshold": 2, @@ -389,7 +523,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "scroll": { "threshold": 3, @@ -399,25 +533,62 @@ "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", "0xbac4ac39f1d8b5ef15f26fdb1294a7c9aba3f948" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "sei": { - "threshold": 2, + "threshold": 3, "validators": [ "0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "shibarium": { - "threshold": 1, - "validators": ["0xfa33391ee38597cbeef72ccde8c9e13e01e78521"], - "type": 3 + "threshold": 2, + "validators": [ + "0xfa33391ee38597cbeef72ccde8c9e13e01e78521", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "solanamainnet": { - "threshold": 1, - "validators": ["0x28464752829b3ea59a497fca0bdff575c534c3ff"], - "type": 3 + "threshold": 3, + "validators": [ + "0x28464752829b3ea59a497fca0bdff575c534c3ff", + "0x2b7514a2f77bd86bbf093fe6bb67d8611f51c659", + "0xd90ea26ff731d967c5ea660851f7d63cb04ab820", + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0xcb6bcbd0de155072a7ff486d9d7286b0f71dcc2d" + ], + "type": "messageIdMultisigIsm" + }, + "stride": { + "threshold": 4, + "validators": [ + "0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8", + "0x88f0E5528131b10e3463C4c68108217Dd33462ac", + "0xa3eaa1216827ad63dd9db43f6168258a89177990", + "0x3f869C36110F00D10dC74cca3ac1FB133cf019ad", + "0x502dC6135d16E74056f609FBAF76846814C197D3", + "0xc36979780c1aD43275182600a61Ce41f1C390FbE", + "0x87460dcEd16a75AECdBffD4189111d30B099f5b0", + "0xf54982134e52Eb7253236943FBffE0886C5bde0C", + "0x5937b7cE1029C3Ec4bD8e1AaCc0C0f9422654D7d", + "0x3a446ed2923c08445af06e53f0acb558c0e0413c" + ], + "type": "messageIdMultisigIsm" + }, + "superposition": { + "threshold": 2, + "validators": [ + "0x5978d0e6afa9270ddb87cff43a8fa7a763a5dfc4", + "0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], + "type": "messageIdMultisigIsm" }, "taiko": { "threshold": 3, @@ -427,7 +598,7 @@ "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", "0x2F007c82672F2Bb97227D4e3F80Ac481bfB40A2a" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "tangle": { "threshold": 2, @@ -436,7 +607,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0xe271ef9a6e312540f099a378865432fa73f26689" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "viction": { "threshold": 2, @@ -445,7 +616,7 @@ "0xa3f93fe365bf99f431d8fde740b140615e24f99b", "0x1f87c368f8e05a85ef9126d984a980a20930cb9c" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "worldchain": { "threshold": 2, @@ -454,7 +625,7 @@ "0x11e2a683e83617f186614071e422b857256a9aae", "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "xai": { "threshold": 2, @@ -463,7 +634,7 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "xlayer": { "threshold": 2, @@ -472,15 +643,17 @@ "0xfed056cC0967F5BC9C6350F6C42eE97d3983394d", "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "zetachain": { - "threshold": 2, + "threshold": 3, "validators": [ "0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "zircuit": { "threshold": 3, @@ -490,7 +663,7 @@ "0x0180444c9342BD672867Df1432eb3dA354413a6E", "0x1da9176C2CE5cC7115340496fa7D1800a98911CE" ], - "type": 3 + "type": "messageIdMultisigIsm" }, "zoramainnet": { "threshold": 3, @@ -500,6 +673,6 @@ "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], - "type": 3 + "type": "messageIdMultisigIsm" } } diff --git a/rust/sealevel/environments/mainnet3/multisig-ism-message-id/solanamainnet/hyperlane/multisig-config.json b/rust/sealevel/environments/mainnet3/multisig-ism-message-id/solanamainnet/hyperlane/multisig-config.json index c82bf6eb8..7ae8ee840 100644 --- a/rust/sealevel/environments/mainnet3/multisig-ism-message-id/solanamainnet/hyperlane/multisig-config.json +++ b/rust/sealevel/environments/mainnet3/multisig-ism-message-id/solanamainnet/hyperlane/multisig-config.json @@ -20,13 +20,21 @@ "type": 3 }, "astar": { - "threshold": 1, - "validators": ["0x4d1b2cade01ee3493f44304653d8e352c66ec3e7"], + "threshold": 2, + "validators": [ + "0x4d1b2cade01ee3493f44304653d8e352c66ec3e7", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "astarzkevm": { - "threshold": 1, - "validators": ["0x89ecdd6caf138934bf3a2fb7b323984d72fd66de"], + "threshold": 2, + "validators": [ + "0x89ecdd6caf138934bf3a2fb7b323984d72fd66de", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "avalanche": { @@ -50,8 +58,12 @@ "type": 3 }, "bitlayer": { - "threshold": 1, - "validators": ["0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f"], + "threshold": 2, + "validators": [ + "0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "blast": { @@ -102,8 +114,12 @@ "type": 3 }, "coredao": { - "threshold": 1, - "validators": ["0xbd6e158a3f5830d99d7d2bce192695bc4a148de2"], + "threshold": 2, + "validators": [ + "0xbd6e158a3f5830d99d7d2bce192695bc4a148de2", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "cyber": { @@ -125,13 +141,22 @@ "type": 3 }, "dogechain": { - "threshold": 1, - "validators": ["0xe43f742c37858746e6d7e458bc591180d0cba440"], + "threshold": 2, + "validators": [ + "0xe43f742c37858746e6d7e458bc591180d0cba440", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "eclipsemainnet": { - "threshold": 1, - "validators": ["0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0"], + "threshold": 3, + "validators": [ + "0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0", + "0x3571223e745dc0fcbdefa164c9b826b90c0d2dac", + "0xea83086a62617a7228ce4206fae2ea8b0ab23513", + "0x4d4629f5bfeabe66edc7a78da26ef5273c266f97" + ], "type": 3 }, "endurance": { @@ -156,16 +181,30 @@ ], "type": 3 }, + "everclear": { + "threshold": 2, + "validators": [ + "0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0xD79DFbF56ee2268f061cc613027a44A880f61Ba2" + ], + "type": 3 + }, "flare": { - "threshold": 1, - "validators": ["0xb65e52be342dba3ab2c088ceeb4290c744809134"], + "threshold": 2, + "validators": [ + "0xb65e52be342dba3ab2c088ceeb4290c744809134", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "fraxtal": { "threshold": 2, "validators": [ "0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x25b3a88f7cfd3c9f7d7e32b295673a16a6ddbd91" ], "type": 3 }, @@ -237,7 +276,8 @@ "threshold": 2, "validators": [ "0xa5e953701dcddc5b958b5defb677a829d908df6d", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47" ], "type": 3 }, @@ -291,16 +331,22 @@ "type": 3 }, "mode": { - "threshold": 2, + "threshold": 3, "validators": [ "0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7", - "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x7e29608c6e5792bbf9128599ca309be0728af7b4", + "0x101cE77261245140A0871f9407d6233C8230Ec47" ], "type": 3 }, "molten": { - "threshold": 1, - "validators": ["0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f"], + "threshold": 2, + "validators": [ + "0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "moonbeam": { @@ -326,6 +372,15 @@ ], "type": 3 }, + "oortmainnet": { + "threshold": 2, + "validators": [ + "0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36", + "0x032dE4f94676bF9314331e7D83E8Db4aC74c9E21" + ], + "type": 3 + }, "optimism": { "threshold": 3, "validators": [ @@ -339,7 +394,9 @@ }, "osmosis": { "threshold": 1, - "validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"], + "validators": [ + "0xea483af11c19fa41b16c31d1534c2a486a92bcac" + ], "type": 3 }, "polygon": { @@ -383,7 +440,8 @@ "threshold": 2, "validators": [ "0x1400b9737007f7978d8b4bbafb4a69c83f0641a7", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], "type": 3 }, @@ -407,16 +465,22 @@ "type": 3 }, "sei": { - "threshold": 2, + "threshold": 3, "validators": [ "0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], "type": 3 }, "shibarium": { - "threshold": 1, - "validators": ["0xfa33391ee38597cbeef72ccde8c9e13e01e78521"], + "threshold": 2, + "validators": [ + "0xfa33391ee38597cbeef72ccde8c9e13e01e78521", + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" + ], "type": 3 }, "taiko": { @@ -475,10 +539,12 @@ "type": 3 }, "zetachain": { - "threshold": 2, + "threshold": 3, "validators": [ "0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef", - "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f" + "0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f", + "0x101cE77261245140A0871f9407d6233C8230Ec47", + "0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36" ], "type": 3 }, diff --git a/rust/sealevel/environments/mainnet3/solanamainnet/gas-oracle-configs-solanamainnet.json b/rust/sealevel/environments/mainnet3/solanamainnet/gas-oracle-configs-solanamainnet.json new file mode 100644 index 000000000..44f00e80a --- /dev/null +++ b/rust/sealevel/environments/mainnet3/solanamainnet/gas-oracle-configs-solanamainnet.json @@ -0,0 +1,11 @@ +[ + { + "domain": 1408864445, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "25036363636360000000", + "gasPrice": "2", + "tokenDecimals": 9 + } + } +] diff --git a/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/program-ids.json new file mode 100644 index 000000000..6f10e1014 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/program-ids.json @@ -0,0 +1,10 @@ +{ + "stride": { + "hex": "0x0b1798722ccf813f617ffa8d52d36302544807497434740d98e693e9abb245d1", + "base58": "kJMTJuh4tzhzfTV261wh2cisqb7NNev6ZE6czwB3ss6" + }, + "eclipsemainnet": { + "hex": "0xa0c167513f4d025217a48891973c3dbe41e10e76230033ef5d676299a18ca7f5", + "base58": "BpXHAiktwjx7fN6M9ST9wr6qKAsH27wZFhdHEhReJsR6" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/token-config.json new file mode 100644 index 000000000..f501f4ee8 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/TIA-stride-eclipse/token-config.json @@ -0,0 +1,17 @@ +{ + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "remoteDecimals": 6, + "name": "Celestia", + "symbol": "TIA", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/b21b78514a42e8c050b36472c8325fd4b5177366/deployments/warp_routes/TIA/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "stride": { + "type": "collateral", + "decimals": 6, + "token": "ibc/BF3B4F53F3694B66E13C23107C84B6485BD2B96296BB7EC680EA77BBA75B4801", + "foreignDeployment": "0x0b1798722ccf813f617ffa8d52d36302544807497434740d98e693e9abb245d1" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/program-ids.json index bfb247531..f123070fd 100644 --- a/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/program-ids.json +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/program-ids.json @@ -1,10 +1,10 @@ { - "eclipsemainnet": { - "hex": "0xe3f33280ca1f35cb45cc637993b90ce5c7ee8959da401b1763a9de81a7a8eddf", - "base58": "GLpdg3jt6w4eVYiCMhokVZ4mX6hmRvPhcL5RoCjzGr5k" - }, "solanamainnet": { - "hex": "0x3394c8f8213bfcd718f284ddfb65b13777287416b80c9a355bbfb2b1278fc7bd", - "base58": "4UMNyNWW75zo69hxoJaRX5iXNUa5FdRPZZa9vDVCiESg" + "hex": "0x6b4e9fed101f020c0b989737843efb49ac1aa918aa992c4a3808d29bc12f7efd", + "base58": "8DtAGQpcMuD5sG3KdxDy49ydqXUggR1LQtebh2TECbAc" + }, + "eclipsemainnet": { + "hex": "0xd49959a478c4c4170ef86528b8e14646d0a94b4ecbc0582f28d7e4de2e4f5b44", + "base58": "FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y" } } \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/token-config.json index 7cff0b401..af41b5393 100644 --- a/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/token-config.json +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipsesol/token-config.json @@ -9,7 +9,7 @@ "decimals": 9, "name": "Solana", "symbol": "SOL", - "uri": "https://github.com/hyperlane-xyz/hyperlane-registry/blob/b661127dd3dce5ea98b78ae0051fbd10c384b173/deployments/warp_routes/SOL/eclipse/metadata.json", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/77a04c0b7e214ae17853215467f8ddea5e0ac710/deployments/warp_routes/SOL/metadata.json", "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" } } diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipseteth/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipseteth/program-ids.json new file mode 100644 index 000000000..7f4be7582 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipseteth/program-ids.json @@ -0,0 +1,10 @@ +{ + "eclipsemainnet": { + "hex": "0x4e38e43e87ba8942a6c5cfa81445cde1a08400a8d6cb5850f40135aed1ba1c11", + "base58": "6GM7hy6M6LjhadG1xuKXPQ3jPC1eieEszR8DforoRzUp" + }, + "ethereum": { + "hex": "0x000000000000000000000000c2495f3183f043627caecd56daaa726e3b2d9c09", + "base58": "1111111111113hzUGDMkDVcewnZG9Zd1bY8j2op4" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipseteth/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipseteth/token-config.json new file mode 100644 index 000000000..d6c4b02fd --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipseteth/token-config.json @@ -0,0 +1,17 @@ +{ + "eclipsemainnet": { + "type": "synthetic", + "decimals": 9, + "remoteDecimals": 18, + "name": "Turbo ETH", + "symbol": "tETH", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/f5c3d59c5f0155618ae156c971ace5894a88cec6/deployments/warp_routes/tETH/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "ethereum": { + "type": "collateral", + "decimals": 18, + "token": "0x19e099B7aEd41FA52718D780dDA74678113C0b32", + "foreignDeployment": "0xc2495f3183F043627CAECD56dAaa726e3B2D9c09" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipseusdc/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipseusdc/program-ids.json new file mode 100644 index 000000000..453a2bc2b --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipseusdc/program-ids.json @@ -0,0 +1,14 @@ +{ + "eclipsemainnet": { + "hex": "0xcd8f7348bdee9233f64432ab826c3526692e6cb17d6a5a5ddacfe3cbd0d77a9e", + "base58": "EqRSt9aUDMKYKhzd1DGMderr3KNp29VZH3x5P7LFTC8m" + }, + "ethereum": { + "hex": "0x000000000000000000000000e1de9910fe71cc216490ac7fcf019e13a34481d7", + "base58": "11111111111149We9K5tM8ijcyNy9zDMG9RyDBCJ" + }, + "solanamainnet": { + "hex": "0x21419dfbe06b0ab41171d012ac511e058f2d036333e003f0c2e6003ac8dea12c", + "base58": "3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipseusdc/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipseusdc/token-config.json new file mode 100644 index 000000000..8c31fbd0a --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipseusdc/token-config.json @@ -0,0 +1,23 @@ +{ + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", + "splTokenProgram": "token" + }, + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "name": "USD Coin", + "symbol": "USDC", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/77a04c0b7e214ae17853215467f8ddea5e0ac710/deployments/warp_routes/USDC/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "ethereum": { + "type": "collateral", + "decimals": 6, + "token": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "foreignDeployment": "0xe1de9910fe71cc216490ac7fcf019e13a34481d7" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipsewif/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipsewif/program-ids.json new file mode 100644 index 000000000..b9bd29d58 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipsewif/program-ids.json @@ -0,0 +1,10 @@ +{ + "eclipsemainnet": { + "hex": "0x5766ab957c42d24f5071e32c56d07c4b5fcce7ccba0cc9a67b0025b863b65d7b", + "base58": "6tBGmKNxirxBYnm9khkaTtcr2fhJ9YviCgRm1RWM8NJv" + }, + "solanamainnet": { + "hex": "0xb0dda3a11775c3eebae6c89416039b3183e46964b72d82404d59f6f5e9e9c19f", + "base58": "CuQmsT4eSF4dYiiGUGYYQxJ7c58pUAD5ADE3BbFGzQKx" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/eclipsewif/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/eclipsewif/token-config.json new file mode 100644 index 000000000..5d5ab1a54 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/eclipsewif/token-config.json @@ -0,0 +1,17 @@ +{ + "solanamainnet": { + "type": "collateral", + "decimals": 6, + "interchainGasPaymaster": "AkeHBbE5JkwVppujCQQ6WuxsVsJtruBAjUo6fDCFp6fF", + "token": "EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm", + "splTokenProgram": "token" + }, + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "name": "dogwifhat", + "symbol": "WIF", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/9b9b07c9df8cd326e097d87aec67128832221407/deployments/warp_routes/WIF/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + } +} diff --git a/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/program-ids.json b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/program-ids.json new file mode 100644 index 000000000..1af852d36 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/program-ids.json @@ -0,0 +1,10 @@ +{ + "stride": { + "hex": "0x8d7a6737fdd9545dc77d1c5cc2d26cb1321f6c67f0facb030149da9cc58f0cbe", + "base58": "AXGjtKVpzYdXYX155z6qYQC4Up7fi5LPKNXAK32gi3x9" + }, + "eclipsemainnet": { + "hex": "0x0d258188d0761163da174da890d0c1becdee51a01dbc9e2a6bfcb342140eb509", + "base58": "tKUHyJ5NxhnwU94JUmzh1ekukDcHHX8mZF6fqxbMwX6" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/token-config.json b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/token-config.json new file mode 100644 index 000000000..c59c12bb6 --- /dev/null +++ b/rust/sealevel/environments/mainnet3/warp-routes/stTIA-stride-eclipse/token-config.json @@ -0,0 +1,17 @@ +{ + "eclipsemainnet": { + "type": "synthetic", + "decimals": 6, + "remoteDecimals": 6, + "name": "Stride Staked TIA", + "symbol": "stTIA", + "uri": "https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/dee58183e51f4eb43e84dbac0e595a4b389dbe80/deployments/warp_routes/stTIA/metadata.json", + "interchainGasPaymaster": "3Wp4qKkgf4tjXz1soGyTSndCgBPLZFSrZkiDZ8Qp9EEj" + }, + "stride": { + "type": "collateral", + "decimals": 6, + "token": "stutia", + "foreignDeployment": "0x8d7a6737fdd9545dc77d1c5cc2d26cb1321f6c67f0facb030149da9cc58f0cbe" + } +} diff --git a/rust/sealevel/environments/testnet3/chain-config.json b/rust/sealevel/environments/testnet3/chain-config.json deleted file mode 100644 index 5b278c0a0..000000000 --- a/rust/sealevel/environments/testnet3/chain-config.json +++ /dev/null @@ -1,196 +0,0 @@ -{ - "alfajores": { - "chainId": 44787, - "domainId": 44787, - "name": "alfajores", - "protocol": "ethereum", - "displayName": "Alfajores", - "nativeToken": { - "decimals": 18, - "name": "CELO", - "symbol": "CELO" - }, - "rpcUrls": [ - { - "http": "https://alfajores-forno.celo-testnet.org" - } - ], - "blockExplorers": [ - { - "name": "CeloScan", - "url": "https://alfajores.celoscan.io", - "apiUrl": "https://api-alfajores.celoscan.io/api", - "family": "etherscan" - }, - { - "name": "Blockscout", - "url": "https://explorer.celo.org/alfajores", - "apiUrl": "https://explorer.celo.org/alfajores/api", - "family": "blockscout" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 5 - }, - "isTestnet": true - }, - "fuji": { - "chainId": 43113, - "domainId": 43113, - "name": "fuji", - "protocol": "ethereum", - "displayName": "Fuji", - "nativeToken": { - "decimals": 18, - "name": "Avalanche", - "symbol": "AVAX" - }, - "rpcUrls": [ - { - "http": "https://api.avax-test.network/ext/bc/C/rpc", - "pagination": { - "maxBlockRange": 2048 - } - } - ], - "blockExplorers": [ - { - "name": "SnowTrace", - "url": "https://testnet.snowtrace.io", - "apiUrl": "https://api-testnet.snowtrace.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 3, - "estimateBlockTime": 2 - }, - "isTestnet": true - }, - "bsctestnet": { - "chainId": 97, - "domainId": 97, - "name": "bsctestnet", - "protocol": "ethereum", - "displayName": "BSC Testnet", - "nativeToken": { - "decimals": 18, - "name": "BNB", - "symbol": "BNB" - }, - "rpcUrls": [ - { - "http": "https://data-seed-prebsc-1-s3.binance.org:8545" - } - ], - "blockExplorers": [ - { - "name": "BscScan", - "url": "https://testnet.bscscan.com", - "apiUrl": "https://api-testnet.bscscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 9, - "estimateBlockTime": 3 - }, - "isTestnet": true - }, - "moonbasealpha": { - "chainId": 1287, - "domainId": 1287, - "name": "moonbasealpha", - "protocol": "ethereum", - "displayName": "Moonbase Alpha", - "displayNameShort": "Moonbase", - "nativeToken": { - "decimals": 18, - "name": "DEV", - "symbol": "DEV" - }, - "rpcUrls": [ - { - "http": "https://rpc.api.moonbase.moonbeam.network" - } - ], - "blockExplorers": [ - { - "name": "MoonScan", - "url": "https://moonbase.moonscan.io", - "apiUrl": "https://api-moonbase.moonscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 12 - }, - "isTestnet": true - }, - "sepolia": { - "chainId": 11155111, - "domainId": 11155111, - "name": "sepolia", - "protocol": "ethereum", - "displayName": "Sepolia", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://endpoints.omniatech.io/v1/eth/sepolia/public" - }, - { - "http": "https://rpc.sepolia.org" - } - ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://sepolia.etherscan.io", - "apiUrl": "https://api-sepolia.etherscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 2, - "estimateBlockTime": 13 - }, - "isTestnet": true - }, - "solanadevnet": { - "chainId": 1399811151, - "name": "solanadevnet", - "rpcUrls": [ - { - "http": "https://api.devnet.solana.com" - } - ] - }, - "proteustestnet": { - "chainId": 88002, - "domainId": 88002, - "name": "proteustestnet", - "protocol": "ethereum", - "displayName": "Proteus Testnet", - "nativeToken": { - "name": "Zebec", - "symbol": "ZBC", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://api.proteus.nautchain.xyz/solana" - } - ] - } -} diff --git a/rust/sealevel/environments/testnet3/gas-oracle-configs.json b/rust/sealevel/environments/testnet3/gas-oracle-configs.json deleted file mode 100644 index 093327911..000000000 --- a/rust/sealevel/environments/testnet3/gas-oracle-configs.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "domain": 97, - "gasOracle": { - "type": "remoteGasData", - "tokenExchangeRate": "10000000000000000000", - "gasPrice": "15000000000", - "tokenDecimals": 18 - } - }, - { - "domain": 88002, - "gasOracle": { - "type": "remoteGasData", - "tokenExchangeRate": "10000000000000000000", - "gasPrice": "1000000000", - "tokenDecimals": 18 - } - } -] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json deleted file mode 100644 index 425a8209e..000000000 --- a/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "solanadevnet": { - "interchainSecurityModule": "64xkGhsZbxgP5rBJfpPcpmzkzTGkpSVHiDLcMKS5gmQw" - }, - "alfajores": { - "foreignDeployment": "0x477D860f8F41bC69dDD32821F2Bf2C2Af0243F16" - }, - "fuji": { - "foreignDeployment": "0x5da3b8d6F73dF6003A490072106730218c475AAd" - }, - "bsctestnet": { - "foreignDeployment": "0xE09BF59dCA6e622efC33f6fbd8EF85dE45233388" - }, - "moonbasealpha": { - "foreignDeployment": "0x89e02C3C7b97bCBa63279E10E2a44e6cEF69E6B2" - }, - "sepolia": { - "foreignDeployment": "0x5d56B8a669F50193b54319442c6EEE5edD662381" - } -} diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/keys/hyperlane_sealevel_hello_world-solanadevnet.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/keys/hyperlane_sealevel_hello_world-solanadevnet.json deleted file mode 100644 index db75227a7..000000000 --- a/rust/sealevel/environments/testnet3/helloworld/hyperlane/keys/hyperlane_sealevel_hello_world-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[42,226,42,33,87,42,251,0,57,248,173,166,139,84,91,50,218,150,183,254,74,195,88,116,92,195,145,231,63,39,9,98,171,58,146,166,209,139,158,82,151,114,58,235,5,25,129,244,219,192,239,35,53,229,191,115,243,59,174,210,94,26,161,101] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json deleted file mode 100644 index fc338a6fb..000000000 --- a/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "bsctestnet": { - "hex": "0x000000000000000000000000e09bf59dca6e622efc33f6fbd8ef85de45233388", - "base58": "11111111111148VaL9DFuVc9DbDjRR7c3qyCEjyy" - }, - "fuji": { - "hex": "0x0000000000000000000000005da3b8d6f73df6003a490072106730218c475aad", - "base58": "1111111111112JfXZf7EYaEMM1st6wFZbcLN2uwA" - }, - "solanadevnet": { - "hex": "0xab3a92a6d18b9e5297723aeb051981f4dbc0ef2335e5bf73f33baed25e1aa165", - "base58": "CXQX54kdkU5GqdRJjCmHpwHfEMgFb5SeBmMWntP2Ds7J" - }, - "alfajores": { - "hex": "0x000000000000000000000000477d860f8f41bc69ddd32821f2bf2c2af0243f16", - "base58": "111111111111zmUjMVNXAe5bcqPR8cvaPz5SrQu" - }, - "sepolia": { - "hex": "0x0000000000000000000000005d56b8a669f50193b54319442c6eee5edd662381", - "base58": "1111111111112JRRxgtLh6eyMDsTHUehn6bJcPJ8" - }, - "moonbasealpha": { - "hex": "0x00000000000000000000000089e02c3c7b97bcba63279e10e2a44e6cef69e6b2", - "base58": "1111111111112vQhuwgKwhQ7SM1HZEm6yXQkzCau" - } -} diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json b/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json deleted file mode 100644 index 0af4a8161..000000000 --- a/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "solanadevnet": { - "interchainSecurityModule": "2NE6Y1rXp1Kpp6vBNqDHYL7HNk7iqh8BKmvCoZtUcZLn" - }, - "alfajores": { - "foreignDeployment": "0x40Adcb03F3C58170b4751c4140636FC6085Ff475" - }, - "fuji": { - "foreignDeployment": "0xAc003FcDD0EE223664F2A000B5A59D082745700b" - }, - "bsctestnet": { - "foreignDeployment": "0xd259b0e793535325786675542aB296c451535c27" - }, - "moonbasealpha": { - "foreignDeployment": "0xE9D6317a10860340f035f3d09052D9d376855bE8" - }, - "sepolia": { - "foreignDeployment": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217" - } -} diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/keys/hyperlane_sealevel_hello_world-solanadevnet.json b/rust/sealevel/environments/testnet3/helloworld/rc/keys/hyperlane_sealevel_hello_world-solanadevnet.json deleted file mode 100644 index 341933a08..000000000 --- a/rust/sealevel/environments/testnet3/helloworld/rc/keys/hyperlane_sealevel_hello_world-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[158,232,241,234,223,84,236,122,65,31,146,220,11,236,43,97,184,113,181,80,237,157,204,188,166,199,112,171,77,38,68,13,187,162,244,131,230,66,68,157,10,57,239,229,249,96,63,124,85,148,35,172,235,211,200,84,208,117,96,204,208,67,146,40] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json b/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json deleted file mode 100644 index 305c614b9..000000000 --- a/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "sepolia": { - "hex": "0x0000000000000000000000006ad4deba8a147d000c09de6465267a9047d1c217", - "base58": "1111111111112VKnX2KMsqSTDw9YoXRsZJTwTcUW" - }, - "solanadevnet": { - "hex": "0xbba2f483e642449d0a39efe5f9603f7c559423acebd3c854d07560ccd0439228", - "base58": "DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7" - }, - "fuji": { - "hex": "0x000000000000000000000000ac003fcdd0ee223664f2a000b5a59d082745700b", - "base58": "1111111111113Pz2bmxxVNgkKkZPpxgouHiZAjTx" - }, - "moonbasealpha": { - "hex": "0x000000000000000000000000e9d6317a10860340f035f3d09052d9d376855be8", - "base58": "1111111111114Fx2onL6wvVgGmyjgzGhy48HzCZM" - }, - "bsctestnet": { - "hex": "0x000000000000000000000000d259b0e793535325786675542ab296c451535c27", - "base58": "1111111111113vyKMMTb6aSQDhDLqEvqcPBcTtRC" - }, - "alfajores": { - "hex": "0x00000000000000000000000040adcb03f3c58170b4751c4140636fc6085ff475", - "base58": "111111111111uGFbQYrmpk8K5cfeu9x438LAGiQ" - }, -} diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json deleted file mode 100644 index 1f21eb8ce..000000000 --- a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "alfajores": { - "type": 3, - "threshold": 2, - "validators": [ - "0xe6072396568e73ce6803b12b7e04164e839f1e54", - "0x9f177f51289b22515f41f95872e1511391b8e105", - "0x15f77400845eb1c971ad08de050861d5508cad6c" - ] - }, - "fuji": { - "type": 3, - "threshold": 2, - "validators": [ - "0x9fa19ead5ec76e437948b35e227511b106293c40", - "0x227e7d6507762ece0c94678f8c103eff9d682476", - "0x2379e43740e4aa4fde48cf4f00a3106df1d8420d" - ] - }, - "bsctestnet": { - "type": 3, - "threshold": 2, - "validators": [ - "0x23338c8714976dd4a57eaeff17cbd26d7e275c08", - "0x85a618d7450ebc37e0d682371f08dac94eec7a76", - "0x95b76562e4ba1791a27ba4236801271c9115b141" - ] - }, - "sepolia": { - "type": 3, - "threshold": 2, - "validators": [ - "0xbc748ee311f5f2d1975d61cdf531755ce8ce3066", - "0xc4233b2bfe5aec08964a94b403052abb3eafcf07", - "0x6b36286c19f5c10bdc139ea9ee7f82287303f61d" - ] - }, - "moonbasealpha": { - "type": 3, - "threshold": 2, - "validators": [ - "0x890c2aeac157c3f067f3e42b8afc797939c59a32", - "0x1b06d6fe69b972ed7420c83599d5a5c0fc185904", - "0xe70b85206a968a99a597581f0fa09c99e7681093" - ] - }, - "proteustestnet": { - "type": 3, - "threshold": 2, - "validators": [ - "0x79fc73656abb9eeaa5ee853c4569124f5bdaf9d8", - "0x72840388d5ab57323bc4f6e6d3ddedfd5cc911f0", - "0xd4b2a50c53fc6614bb3cd3198e0fdc03f5da973f" - ] - } -} diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json deleted file mode 100644 index 8542855f6..000000000 --- a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[187,239,78,162,24,178,190,184,243,9,66,169,19,139,40,129,55,222,218,2,184,14,122,68,163,6,144,157,76,14,169,237,20,75,176,226,241,81,96,106,31,68,222,130,94,67,105,175,112,84,241,60,117,11,107,135,95,48,20,213,115,123,100,3] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json deleted file mode 100644 index 59851ab71..000000000 --- a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "alfajores": { - "type": 3, - "threshold": 1, - "validators": ["0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de"] - }, - "fuji": { - "type": 3, - "threshold": 1, - "validators": ["0xd81ba169170a9b582812cf0e152d2c168572e21f"] - }, - "bsctestnet": { - "type": 3, - "threshold": 1, - "validators": ["0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb"] - }, - "sepolia": { - "type": 3, - "threshold": 1, - "validators": ["0x183f15924f3a464c54c9393e8d268eb44d2b208c"] - }, - "moonbasealpha": { - "type": 3, - "threshold": 1, - "validators": ["0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d"] - } -} diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/program-ids.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/program-ids.json deleted file mode 100644 index e615b252e..000000000 --- a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/program-ids.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "program_id": "2NE6Y1rXp1Kpp6vBNqDHYL7HNk7iqh8BKmvCoZtUcZLn" -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_igp-keypair.json b/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_igp-keypair.json deleted file mode 100644 index dc81bbd76..000000000 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_igp-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[69,85,36,249,122,60,137,0,166,230,31,176,52,206,13,94,109,64,6,255,11,230,27,197,206,56,53,158,119,12,119,62,252,44,216,245,197,34,23,174,214,41,164,131,127,248,232,97,32,106,180,79,142,12,207,167,241,75,125,139,200,78,190,177] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_mailbox-keypair.json b/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_mailbox-keypair.json deleted file mode 100644 index 1d037e6a7..000000000 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_mailbox-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[41,66,119,0,251,5,86,239,146,22,239,71,92,242,131,74,187,171,216,223,119,184,174,19,60,191,221,113,245,239,17,122,58,40,14,132,102,210,107,196,225,165,211,209,126,115,247,179,7,192,130,21,109,208,255,191,140,95,154,231,85,6,214,241] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json b/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json deleted file mode 100644 index d86f1638c..000000000 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_multisig_ism_message_id-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[247,149,169,2,196,128,74,124,111,206,244,112,63,16,180,19,219,212,45,229,21,114,33,11,202,148,12,47,22,26,192,78,75,78,53,149,190,51,57,253,29,141,136,215,159,45,181,164,239,148,140,163,30,108,158,76,94,113,11,4,142,0,192,20] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_validator_announce-keypair.json b/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_validator_announce-keypair.json deleted file mode 100644 index 5ddd8b30e..000000000 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/keys/hyperlane_sealevel_validator_announce-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[68,92,27,29,32,175,172,214,16,253,88,245,2,84,255,5,186,178,191,163,136,96,18,168,23,83,232,216,205,114,154,143,168,162,161,239,196,33,75,35,20,61,227,247,44,133,46,222,78,227,191,3,46,2,248,246,206,141,64,183,75,184,121,191] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json b/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json deleted file mode 100644 index ab3f1f593..000000000 --- a/rust/sealevel/environments/testnet3/solanadevnet/core/program-ids.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mailbox": "4v25Dz9RccqUrTzmfHzJMsjd1iVoNrWzeJ4o6GYuJrVn", - "validator_announce": "CMHKvdq4CopDf7qXnDCaTybS15QekQeRt4oUB219yxsp", - "multisig_ism_message_id": "64xkGhsZbxgP5rBJfpPcpmzkzTGkpSVHiDLcMKS5gmQw", - "igp_program_id": "HyPQPLfGXDTAQTxzGp7r1uy18KxS89GKgreSHpjeuYDn", - "overhead_igp_account": "AR4hjWPqXEobLvzmv8MTh5k4Se49iTDzbvNX4DpdQGJZ", - "igp_account": "7hMPEGdgBQFsjEz3aaNwZp8WMFHs615zAM3erXBDJuJR" -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/collateraltest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json b/rust/sealevel/environments/testnet3/warp-routes/collateraltest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json deleted file mode 100644 index dbfcba9d8..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/collateraltest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[98,129,191,177,102,138,233,60,156,241,236,62,216,72,254,103,183,93,70,101,75,216,137,204,55,192,74,81,77,235,129,248,250,176,128,150,198,17,155,33,251,185,183,201,212,28,44,194,220,95,98,92,146,8,192,17,20,32,3,58,184,37,56,85] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/collateraltest/program-ids.json b/rust/sealevel/environments/testnet3/warp-routes/collateraltest/program-ids.json deleted file mode 100644 index c2baa7da5..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/collateraltest/program-ids.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fuji": { - "hex": "0x000000000000000000000000a97f4eacbc363f82d25a540440afc6f78920299b", - "base58": "1111111111113Mxh1B6fskiQrE2FY7RuvZDF7PfQ" - }, - "solanadevnet": { - "hex": "0xfab08096c6119b21fbb9b7c9d41c2cc2dc5f625c9208c0111420033ab8253855", - "base58": "Hsb2PdnUvd7VvZJ1svS8TrVLfsRDdDTWoHK5r2RwGZBS" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/collateraltest/token-config.json b/rust/sealevel/environments/testnet3/warp-routes/collateraltest/token-config.json deleted file mode 100644 index 7f3109652..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/collateraltest/token-config.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "solanadevnet": { - "type": "collateral", - "token": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", - "splTokenProgram": "token", - "decimals": 6, - "name": "USD Coin Dev", - "symbol": "USDC" - }, - "fuji": { - "type": "synthetic", - "decimals": 6, - "name": "USD Coin Dev", - "symbol": "USDC", - "foreignDeployment": "0xa97F4eACbc363f82D25a540440AFC6F78920299b" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/nativetest/keys/hyperlane_sealevel_token_native-solanadevnet.json b/rust/sealevel/environments/testnet3/warp-routes/nativetest/keys/hyperlane_sealevel_token_native-solanadevnet.json deleted file mode 100644 index 0d2df675e..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/nativetest/keys/hyperlane_sealevel_token_native-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[211,130,122,136,147,203,98,25,35,161,148,152,75,119,132,211,228,27,148,224,200,130,204,186,108,89,180,204,61,134,31,201,42,140,185,95,186,218,14,12,41,76,133,231,57,34,111,23,173,172,165,221,240,26,185,158,14,250,248,167,59,227,33,3] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/nativetest/program-ids.json b/rust/sealevel/environments/testnet3/warp-routes/nativetest/program-ids.json deleted file mode 100644 index a72aaccd1..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/nativetest/program-ids.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fuji": { - "hex": "0x000000000000000000000000ddd2e6d7cc3fa4599de681376690c8ba538dab51", - "base58": "11111111111146F2zy7DPHvybUST2mVP2A8mFRDi" - }, - "solanadevnet": { - "hex": "0x2a8cb95fbada0e0c294c85e739226f17adaca5ddf01ab99e0efaf8a73be32103", - "base58": "3s6afZYk3EmjsZQ33N9yPTdSk4cY5CKeQ5wtoBcWjFUn" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/nativetest/token-config.json b/rust/sealevel/environments/testnet3/warp-routes/nativetest/token-config.json deleted file mode 100644 index ea5c90fdc..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/nativetest/token-config.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "solanadevnet": { - "type": "native", - "decimals": 9 - }, - "fuji": { - "type": "synthetic", - "decimals": 9, - "name": "Solana (solanadevnet)", - "symbol": "SOL", - "foreignDeployment": "0xDDD2E6d7cC3Fa4599dE681376690c8ba538DaB51" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/proteustest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json b/rust/sealevel/environments/testnet3/warp-routes/proteustest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json deleted file mode 100644 index 4b523b6c5..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/proteustest/keys/hyperlane_sealevel_token_collateral-solanadevnet.json +++ /dev/null @@ -1 +0,0 @@ -[133,28,132,175,210,67,108,229,84,248,196,16,163,131,6,22,86,131,0,142,107,175,93,123,37,242,27,80,72,191,116,37,5,182,80,43,29,145,198,12,160,192,208,171,32,161,110,196,12,102,242,85,155,236,199,136,138,79,195,192,206,255,249,165] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json b/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json deleted file mode 100644 index f2372bc90..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/proteustest/program-ids.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "solanadevnet": { - "hex": "0x05b6502b1d91c60ca0c0d0ab20a16ec40c66f2559becc7888a4fc3c0cefff9a5", - "base58": "PJH5QAbxAqrrnSXfH3GHR8icua8CDFZmo97z91xmpvx" - }, - "proteustestnet": { - "hex": "0x00000000000000000000000034a9af13c5555bad0783c220911b9ef59cfdbcef", - "base58": "111111111111jZ775N1rpEpJ2M8RAzLNNr9Lh7U" - }, - "bsctestnet": { - "hex": "0x00000000000000000000000031b5234a896fbc4b3e2f7237592d054716762131", - "base58": "111111111111hAc1aTgvQGRBFHrYpXpfUqGyqgk" - } -} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/warp-routes/proteustest/token-config.json b/rust/sealevel/environments/testnet3/warp-routes/proteustest/token-config.json deleted file mode 100644 index c2424796a..000000000 --- a/rust/sealevel/environments/testnet3/warp-routes/proteustest/token-config.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "solanadevnet": { - "type": "collateral", - "decimals": 6, - "remoteDecimals": 18, - "token": "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr", - "splTokenProgram": "token" - }, - "bsctestnet": { - "type": "collateral", - "decimals": 18, - "token": "0x64544969ed7ebf5f083679233325356ebe738930", - "name": "USDC", - "symbol": "USDC", - "foreignDeployment": "0x31b5234A896FbC4b3e2F7237592D054716762131" - }, - "proteustestnet": { - "type": "native", - "decimals": 18, - "foreignDeployment": "0x34A9af13c5555BAD0783C220911b9ef59CfDBCEf" - } -} \ No newline at end of file diff --git a/rust/sealevel/libraries/account-utils/src/lib.rs b/rust/sealevel/libraries/account-utils/src/lib.rs index c312ab1e7..697075691 100644 --- a/rust/sealevel/libraries/account-utils/src/lib.rs +++ b/rust/sealevel/libraries/account-utils/src/lib.rs @@ -128,6 +128,7 @@ where data_len }; + #[allow(unexpected_cfgs)] // TODO: `rustc` 1.80.1 issue if cfg!(target_os = "solana") { account.realloc(data_len + realloc_increment, false)?; } else { diff --git a/rust/sealevel/libraries/ecdsa-signature/Cargo.toml b/rust/sealevel/libraries/ecdsa-signature/Cargo.toml index 845bd745c..11b25b0da 100644 --- a/rust/sealevel/libraries/ecdsa-signature/Cargo.toml +++ b/rust/sealevel/libraries/ecdsa-signature/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" solana-program.workspace = true thiserror.workspace = true -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } # Required to allow dependencies `getrandom` but to preserve determinism required by programs, see # https://github.com/solana-foundation/developer-content/blob/main/docs/programs/lang-rust.md#depending-on-rand getrandom = { workspace = true, features = ["custom"] } diff --git a/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml b/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml index 0a8cc148a..8c7f0001c 100644 --- a/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml +++ b/rust/sealevel/libraries/hyperlane-sealevel-connection-client/Cargo.toml @@ -10,9 +10,13 @@ borsh.workspace = true solana-program.workspace = true access-control = { path = "../access-control" } -hyperlane-core = { path = "../../../hyperlane-core" } -hyperlane-sealevel-mailbox = { path = "../../programs/mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-core = { path = "../../../main/hyperlane-core" } +hyperlane-sealevel-mailbox = { path = "../../programs/mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } [dev-dependencies] diff --git a/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml b/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml index d7e6c12b0..4394567be 100644 --- a/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml +++ b/rust/sealevel/libraries/hyperlane-sealevel-token/Cargo.toml @@ -14,14 +14,18 @@ thiserror.workspace = true spl-associated-token-account.workspace = true spl-noop.workspace = true spl-token.workspace = true -spl-token-2022.workspace = true # FIXME Should we actually use 2022 here or try normal token program? +spl-token-2022.workspace = true # FIXME Should we actually use 2022 here or try normal token program? access-control = { path = "../access-control" } account-utils = { path = "../account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-connection-client = { path = "../hyperlane-sealevel-connection-client" } -hyperlane-sealevel-mailbox = { path = "../../programs/mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../../programs/mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } hyperlane-sealevel-message-recipient-interface = { path = "../message-recipient-interface" } serializable-account-meta = { path = "../serializable-account-meta" } @@ -29,4 +33,3 @@ serializable-account-meta = { path = "../serializable-account-meta" } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml b/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml index 7b215d63c..350cf2e4c 100644 --- a/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml +++ b/rust/sealevel/libraries/interchain-security-module-interface/Cargo.toml @@ -12,4 +12,3 @@ spl-type-length-value.workspace = true [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/libraries/message-recipient-interface/Cargo.toml b/rust/sealevel/libraries/message-recipient-interface/Cargo.toml index ebb48ea00..7f9468cb8 100644 --- a/rust/sealevel/libraries/message-recipient-interface/Cargo.toml +++ b/rust/sealevel/libraries/message-recipient-interface/Cargo.toml @@ -10,11 +10,10 @@ borsh.workspace = true solana-program.workspace = true spl-type-length-value.workspace = true -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } # Required to allow dependencies `getrandom` but to preserve determinism required by programs, see # https://github.com/solana-foundation/developer-content/blob/main/docs/programs/lang-rust.md#depending-on-rand getrandom = { workspace = true, features = ["custom"] } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/libraries/multisig-ism/Cargo.toml b/rust/sealevel/libraries/multisig-ism/Cargo.toml index 1116acbbd..963db7a31 100644 --- a/rust/sealevel/libraries/multisig-ism/Cargo.toml +++ b/rust/sealevel/libraries/multisig-ism/Cargo.toml @@ -15,7 +15,7 @@ solana-program.workspace = true spl-type-length-value.workspace = true thiserror.workspace = true -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } ecdsa-signature = { path = "../ecdsa-signature" } [dev-dependencies] @@ -23,4 +23,3 @@ hex.workspace = true [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/libraries/serializable-account-meta/Cargo.toml b/rust/sealevel/libraries/serializable-account-meta/Cargo.toml index 6762a952e..cab84fe37 100644 --- a/rust/sealevel/libraries/serializable-account-meta/Cargo.toml +++ b/rust/sealevel/libraries/serializable-account-meta/Cargo.toml @@ -11,4 +11,3 @@ solana-program.workspace = true [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/libraries/test-transaction-utils/Cargo.toml b/rust/sealevel/libraries/test-transaction-utils/Cargo.toml index 2f9cc5634..418c7c38f 100644 --- a/rust/sealevel/libraries/test-transaction-utils/Cargo.toml +++ b/rust/sealevel/libraries/test-transaction-utils/Cargo.toml @@ -12,4 +12,3 @@ solana-sdk.workspace = true [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/libraries/test-utils/Cargo.toml b/rust/sealevel/libraries/test-utils/Cargo.toml index 3654a1327..4197771e7 100644 --- a/rust/sealevel/libraries/test-utils/Cargo.toml +++ b/rust/sealevel/libraries/test-utils/Cargo.toml @@ -13,15 +13,20 @@ solana-sdk.workspace = true spl-noop.workspace = true spl-token-2022.workspace = true -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-interchain-security-module-interface = { path = "../interchain-security-module-interface" } -hyperlane-sealevel-mailbox = { path = "../../programs/mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../../programs/mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../../programs/hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } hyperlane-sealevel-message-recipient-interface = { path = "../message-recipient-interface" } -hyperlane-sealevel-test-ism = { path = "../../programs/ism/test-ism", features = ["test-client"] } +hyperlane-sealevel-test-ism = { path = "../../programs/ism/test-ism", features = [ + "test-client", +] } hyperlane-test-transaction-utils = { path = "../test-transaction-utils" } serializable-account-meta = { path = "../serializable-account-meta" } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/helloworld/Cargo.toml b/rust/sealevel/programs/helloworld/Cargo.toml index b0cbf2eaf..7aa1fd699 100644 --- a/rust/sealevel/programs/helloworld/Cargo.toml +++ b/rust/sealevel/programs/helloworld/Cargo.toml @@ -7,7 +7,12 @@ edition = "2021" [features] no-entrypoint = [] -test-client = ["dep:solana-program-test", "dep:solana-sdk", "dep:hyperlane-test-utils", "dep:spl-noop"] +test-client = [ + "dep:solana-program-test", + "dep:solana-sdk", + "dep:hyperlane-test-utils", + "dep:spl-noop", +] [dependencies] borsh.workspace = true @@ -18,9 +23,13 @@ spl-noop = { workspace = true, optional = true } access-control = { path = "../../libraries/access-control" } account-utils = { path = "../../libraries/account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } -hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-core = { path = "../../../main/hyperlane-core" } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } hyperlane-sealevel-connection-client = { path = "../../libraries/hyperlane-sealevel-connection-client" } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } hyperlane-test-utils = { path = "../../libraries/test-utils", optional = true } diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml index bbc603f9e..b636c2692 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-igp-test/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" edition = "2021" [dev-dependencies] -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } access-control = { path = "../../libraries/access-control" } account-utils = { path = "../../libraries/account-utils" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } @@ -17,4 +17,3 @@ hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp" } solana-program-test.workspace = true solana-sdk.workspace = true hyperlane-test-utils = { path = "../../libraries/test-utils" } - diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml index 2bd41043c..256b21d76 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml @@ -11,7 +11,7 @@ no-spl-noop = [] serde = ["dep:serde"] [dependencies] -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } access-control = { path = "../../libraries/access-control" } account-utils = { path = "../../libraries/account-utils" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } @@ -27,4 +27,3 @@ serde = { workspace = true, optional = true } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml index d6378080b..c1fcf83ab 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-token-collateral/Cargo.toml @@ -15,15 +15,19 @@ num-traits.workspace = true solana-program.workspace = true spl-associated-token-account.workspace = true spl-noop.workspace = true -spl-token-2022.workspace = true # FIXME Should we actually use 2022 here or try normal token program? +spl-token-2022.workspace = true # FIXME Should we actually use 2022 here or try normal token program? spl-token.workspace = true thiserror.workspace = true account-utils = { path = "../../libraries/account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-connection-client = { path = "../../libraries/hyperlane-sealevel-connection-client" } -hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } hyperlane-sealevel-token-lib = { path = "../../libraries/hyperlane-sealevel-token" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } @@ -33,8 +37,9 @@ solana-program-test.workspace = true solana-sdk.workspace = true hyperlane-test-utils = { path = "../../libraries/test-utils" } -hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = ["no-entrypoint"] } +hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = [ + "no-entrypoint", +] } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml index 25f4777c9..84475f598 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-token-native/Cargo.toml @@ -17,10 +17,14 @@ spl-noop.workspace = true thiserror.workspace = true account-utils = { path = "../../libraries/account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-connection-client = { path = "../../libraries/hyperlane-sealevel-connection-client" } -hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } hyperlane-sealevel-token-lib = { path = "../../libraries/hyperlane-sealevel-token" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } @@ -30,11 +34,12 @@ solana-program-test.workspace = true solana-sdk.workspace = true hyperlane-test-utils = { path = "../../libraries/test-utils" } -hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = ["no-entrypoint"] } +hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = [ + "no-entrypoint", +] } # Unfortunately required for some functions in `solana-program-test`, and is not # re-exported tarpc = "~0.29" [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml b/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml index 5ddd28ed0..93f0e807b 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml +++ b/rust/sealevel/programs/hyperlane-sealevel-token/Cargo.toml @@ -20,10 +20,14 @@ spl-token.workspace = true thiserror.workspace = true account-utils = { path = "../../libraries/account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-connection-client = { path = "../../libraries/hyperlane-sealevel-connection-client" } -hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } -hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = [ + "no-entrypoint", +] } +hyperlane-sealevel-igp = { path = "../hyperlane-sealevel-igp", features = [ + "no-entrypoint", +] } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } hyperlane-sealevel-token-lib = { path = "../../libraries/hyperlane-sealevel-token" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } @@ -33,8 +37,9 @@ solana-program-test.workspace = true solana-sdk.workspace = true hyperlane-test-utils = { path = "../../libraries/test-utils" } -hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = ["no-entrypoint"] } +hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = [ + "no-entrypoint", +] } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/hyperlane-sealevel-token/src/lib.rs b/rust/sealevel/programs/hyperlane-sealevel-token/src/lib.rs index 69bcde823..b08a06f9f 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-token/src/lib.rs +++ b/rust/sealevel/programs/hyperlane-sealevel-token/src/lib.rs @@ -1,5 +1,5 @@ //! Hyperlane Token program for synthetic tokens. - +#![allow(unexpected_cfgs)] // TODO: `rustc` 1.80.1 clippy issue #![deny(warnings)] #![deny(missing_docs)] #![deny(unsafe_code)] diff --git a/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml b/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml index ea0fde75c..531367521 100644 --- a/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml +++ b/rust/sealevel/programs/ism/multisig-ism-message-id/Cargo.toml @@ -18,16 +18,20 @@ thiserror.workspace = true access-control = { path = "../../../libraries/access-control" } account-utils = { path = "../../../libraries/account-utils" } ecdsa-signature = { path = "../../../libraries/ecdsa-signature" } -hyperlane-core = { path = "../../../../hyperlane-core" } +hyperlane-core = { path = "../../../../main/hyperlane-core" } hyperlane-sealevel-interchain-security-module-interface = { path = "../../../libraries/interchain-security-module-interface" } -hyperlane-sealevel-mailbox = { path = "../../mailbox", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../../mailbox", features = [ + "no-entrypoint", +] } multisig-ism = { path = "../../../libraries/multisig-ism" } serializable-account-meta = { path = "../../../libraries/serializable-account-meta" } [dev-dependencies] hyperlane-sealevel-multisig-ism-message-id = { path = "../multisig-ism-message-id" } hyperlane-test-utils = { path = "../../../libraries/test-utils" } -multisig-ism = { path = "../../../libraries/multisig-ism", features = ["test-data"] } +multisig-ism = { path = "../../../libraries/multisig-ism", features = [ + "test-data", +] } solana-program-test.workspace = true solana-sdk.workspace = true hex.workspace = true @@ -37,4 +41,3 @@ rand = "0.8.5" [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/ism/test-ism/Cargo.toml b/rust/sealevel/programs/ism/test-ism/Cargo.toml index 69fe299cb..113fdede3 100644 --- a/rust/sealevel/programs/ism/test-ism/Cargo.toml +++ b/rust/sealevel/programs/ism/test-ism/Cargo.toml @@ -7,7 +7,11 @@ edition = "2021" [features] no-entrypoint = [] -test-client = ["dep:solana-program-test", "dep:solana-sdk", "dep:hyperlane-test-transaction-utils"] +test-client = [ + "dep:solana-program-test", + "dep:solana-sdk", + "dep:hyperlane-test-transaction-utils", +] [dependencies] borsh.workspace = true @@ -16,12 +20,13 @@ solana-program-test = { workspace = true, optional = true } solana-sdk = { workspace = true, optional = true } account-utils = { path = "../../../libraries/account-utils" } -hyperlane-core = { path = "../../../../hyperlane-core" } +hyperlane-core = { path = "../../../../main/hyperlane-core" } hyperlane-sealevel-interchain-security-module-interface = { path = "../../../libraries/interchain-security-module-interface" } -hyperlane-sealevel-mailbox = { path = "../../mailbox", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../../mailbox", features = [ + "no-entrypoint", +] } serializable-account-meta = { path = "../../../libraries/serializable-account-meta" } hyperlane-test-transaction-utils = { path = "../../../libraries/test-transaction-utils", optional = true } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/mailbox-test/Cargo.toml b/rust/sealevel/programs/mailbox-test/Cargo.toml index a166b6c68..a48891f06 100644 --- a/rust/sealevel/programs/mailbox-test/Cargo.toml +++ b/rust/sealevel/programs/mailbox-test/Cargo.toml @@ -23,15 +23,18 @@ thiserror.workspace = true access-control = { path = "../../libraries/access-control" } account-utils = { path = "../../libraries/account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-interchain-security-module-interface = { path = "../../libraries/interchain-security-module-interface" } hyperlane-sealevel-mailbox = { path = "../mailbox" } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } -hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = ["test-client"] } -hyperlane-sealevel-test-send-receiver = { path = "../test-send-receiver", features = ["test-client"] } +hyperlane-sealevel-test-ism = { path = "../ism/test-ism", features = [ + "test-client", +] } +hyperlane-sealevel-test-send-receiver = { path = "../test-send-receiver", features = [ + "test-client", +] } hyperlane-test-utils = { path = "../../libraries/test-utils" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/mailbox-test/src/functional.rs b/rust/sealevel/programs/mailbox-test/src/functional.rs index 3c4ace00e..0870ef2fa 100644 --- a/rust/sealevel/programs/mailbox-test/src/functional.rs +++ b/rust/sealevel/programs/mailbox-test/src/functional.rs @@ -1,3 +1,5 @@ +use std::thread::sleep; + use borsh::BorshDeserialize; use hyperlane_core::{ accumulator::incremental::IncrementalMerkle as MerkleTree, HyperlaneMessage, H256, @@ -976,6 +978,10 @@ async fn test_process_errors_if_message_already_processed() { .await .unwrap(); + // there's a race condition that isn't fixed by setting `CommitmentLevel::Confirmed` + // just wait a bit to ensure the message is processed + sleep(std::time::Duration::from_secs(1)); + let result = process( &mut banks_client, &payer, diff --git a/rust/sealevel/programs/mailbox/Cargo.toml b/rust/sealevel/programs/mailbox/Cargo.toml index 8b5f14d1c..6ca7bc9e2 100644 --- a/rust/sealevel/programs/mailbox/Cargo.toml +++ b/rust/sealevel/programs/mailbox/Cargo.toml @@ -22,11 +22,11 @@ spl-noop.workspace = true getrandom = { workspace = true, features = ["custom"] } proc-macro-crate = "~1.2.1" # TODO: remove this dependency once solana supports rust >=1.64 -blake3 = "=1.4.0" # TODO: update once solana supports rust >= 1.66 +blake3 = "=1.4.0" # TODO: update once solana supports rust >= 1.66 access-control = { path = "../../libraries/access-control" } account-utils = { path = "../../libraries/account-utils" } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-core = { path = "../../../main/hyperlane-core" } hyperlane-sealevel-interchain-security-module-interface = { path = "../../libraries/interchain-security-module-interface" } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } @@ -39,4 +39,3 @@ log.workspace = true [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/test-send-receiver/Cargo.toml b/rust/sealevel/programs/test-send-receiver/Cargo.toml index 9bfcb3015..785080d87 100644 --- a/rust/sealevel/programs/test-send-receiver/Cargo.toml +++ b/rust/sealevel/programs/test-send-receiver/Cargo.toml @@ -7,7 +7,12 @@ edition = "2021" [features] no-entrypoint = [] -test-client = ["dep:solana-program-test", "dep:solana-sdk", "dep:hyperlane-test-utils", "dep:spl-noop"] +test-client = [ + "dep:solana-program-test", + "dep:solana-sdk", + "dep:hyperlane-test-utils", + "dep:spl-noop", +] [dependencies] borsh.workspace = true @@ -17,11 +22,12 @@ solana-sdk = { workspace = true, optional = true } spl-noop = { workspace = true, optional = true } account-utils = { path = "../../libraries/account-utils" } -hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = [ + "no-entrypoint", +] } hyperlane-sealevel-message-recipient-interface = { path = "../../libraries/message-recipient-interface" } hyperlane-test-utils = { path = "../../libraries/test-utils", optional = true } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/validator-announce/Cargo.toml b/rust/sealevel/programs/validator-announce/Cargo.toml index 50bea99e6..d50834933 100644 --- a/rust/sealevel/programs/validator-announce/Cargo.toml +++ b/rust/sealevel/programs/validator-announce/Cargo.toml @@ -15,16 +15,17 @@ thiserror.workspace = true account-utils = { path = "../../libraries/account-utils" } ecdsa-signature = { path = "../../libraries/ecdsa-signature" } -hyperlane-sealevel-mailbox = { path = "../mailbox", features = ["no-entrypoint"] } -hyperlane-core = { path = "../../../hyperlane-core" } +hyperlane-sealevel-mailbox = { path = "../mailbox", features = [ + "no-entrypoint", +] } +hyperlane-core = { path = "../../../main/hyperlane-core" } serializable-account-meta = { path = "../../libraries/serializable-account-meta" } [dev-dependencies] hex.workspace = true solana-program-test.workspace = true solana-sdk.workspace = true -hyperlane-test-utils ={ path = "../../libraries/test-utils" } +hyperlane-test-utils = { path = "../../libraries/test-utils" } [lib] crate-type = ["cdylib", "lib"] - diff --git a/rust/sealevel/programs/validator-announce/tests/functional.rs b/rust/sealevel/programs/validator-announce/tests/functional.rs index cf1b5f0f5..450f88b88 100644 --- a/rust/sealevel/programs/validator-announce/tests/functional.rs +++ b/rust/sealevel/programs/validator-announce/tests/functional.rs @@ -1,6 +1,6 @@ use hyperlane_core::{Announcement, H160}; -use std::str::FromStr; +use std::{str::FromStr, thread::sleep}; use account_utils::SizedData; use borsh::BorshSerialize; @@ -300,12 +300,7 @@ async fn test_announce() { storage_location: announcement.storage_location, signature, }; - let ( - validator_storage_locations_key, - validator_storage_locations_bump_seed, - replay_protection_key, - _replay_protection_bump_seed, - ) = announce( + let announcement_res = announce( &mut banks_client, &payer, program_id, @@ -315,6 +310,17 @@ async fn test_announce() { .await .unwrap(); + // there's a race condition that isn't fixed by setting `CommitmentLevel::Confirmed` + // just wait a bit to ensure the account is created + sleep(std::time::Duration::from_secs(1)); + + let ( + validator_storage_locations_key, + validator_storage_locations_bump_seed, + replay_protection_key, + _replay_protection_bump_seed, + ) = announcement_res; + assert_successful_announcement( &mut banks_client, program_id, diff --git a/rust/sealevel/rust-toolchain b/rust/sealevel/rust-toolchain new file mode 100644 index 000000000..83e24cc56 --- /dev/null +++ b/rust/sealevel/rust-toolchain @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.72.1" +profile = "default" diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index 9b4b798c4..3bbfb9ebc 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,88 @@ # @hyperlane-xyz/core +## 5.6.1 + +### Patch Changes + +- a42616ff3: Added overrides for transferFrom, totalSupply to reflect the internal share based accounting for the 4626 mirror asset +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + +## 5.6.0 + +### Minor Changes + +- c55257cf5: Minor token related changes like adding custom hook to 4626 collateral, checking for ERC20 as valid contract in HypERC20Collateral, etc. +- 8cc0d9a4a: Added WHypERC4626 as a wrapper for rebasing HypERC4626 + +### Patch Changes + +- 8cc0d9a4a: Add wrapped HypERC4626 for easy defi use + - @hyperlane-xyz/utils@5.6.1 + +## 5.5.0 + +### Minor Changes + +- 72c23c0d6: Added PRECISION and rateUpdateNonce to ensure compatibility of HypERC4626 + +### Patch Changes + +- c9085afd9: Patched OPL2ToL1Ism to check for correct messageId for external call in verify +- ec6b874b1: Added nonce to HypERC4626 +- Updated dependencies [f1712deb7] +- Updated dependencies [29341950e] + - @hyperlane-xyz/utils@5.6.0 + +## 5.4.1 + +### Patch Changes + +- 92c86cca6: Forward value from ICA router to proxy for multicall +- Updated dependencies [2afc484a2] + - @hyperlane-xyz/utils@5.5.0 + +## 5.4.0 + +### Minor Changes + +- bb75eba74: fix: constrain rate limited ISM to a single message recipient +- c5c217f8e: Embed NPM package version in bytecode constant + +### Patch Changes + +- Updated dependencies [4415ac224] + - @hyperlane-xyz/utils@5.4.0 + +## 5.3.0 + +### Patch Changes + +- Updated dependencies [746eeb9d9] +- Updated dependencies [50319d8ba] + - @hyperlane-xyz/utils@5.3.0 + +## 5.2.1 + +### Patch Changes + +- eb5afcf3e: Patch `HypNative` with hook overrides `transferRemote` behavior + - @hyperlane-xyz/utils@5.2.1 + +## 5.2.0 + +### Minor Changes + +- 203084df2: Added sdk support for Stake weighted ISM +- 445b6222c: ArbL2ToL1Ism handles value via the executeTransaction branch + +### Patch Changes + +- Updated dependencies [d6de34ad5] +- Updated dependencies [291c5fe36] + - @hyperlane-xyz/utils@5.2.0 + ## 5.1.0 ### Minor Changes diff --git a/solidity/bytecodeversion.sh b/solidity/bytecodeversion.sh new file mode 100755 index 000000000..b30ae6979 --- /dev/null +++ b/solidity/bytecodeversion.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +FILEPATH="contracts/PackageVersioned.sol" +TEMPFILE=$(mktemp) + +# writes all but the last 2 lines to the temp file +head -n $(($(wc -l < $FILEPATH) - 2)) $FILEPATH > $TEMPFILE + +# writes generated last 2 lines to the temp file +cat <> $TEMPFILE + string public constant PACKAGE_VERSION = "$npm_package_version"; +} +EOF + +# overwrite the original file with the temp file +cat $TEMPFILE > $FILEPATH diff --git a/solidity/contracts/AttributeCheckpointFraud.sol b/solidity/contracts/AttributeCheckpointFraud.sol index 357612d04..33c533e37 100644 --- a/solidity/contracts/AttributeCheckpointFraud.sol +++ b/solidity/contracts/AttributeCheckpointFraud.sol @@ -5,6 +5,7 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; import {TREE_DEPTH} from "./libs/Merkle.sol"; import {CheckpointLib, Checkpoint} from "./libs/CheckpointLib.sol"; import {CheckpointFraudProofs} from "./CheckpointFraudProofs.sol"; @@ -26,7 +27,8 @@ struct Attribution { * @title AttributeCheckpointFraud * @dev The AttributeCheckpointFraud contract is used to attribute fraud to a specific ECDSA checkpoint signer. */ -contract AttributeCheckpointFraud is Ownable { + +contract AttributeCheckpointFraud is Ownable, PackageVersioned { using CheckpointLib for Checkpoint; using Address for address; diff --git a/solidity/contracts/CheckpointFraudProofs.sol b/solidity/contracts/CheckpointFraudProofs.sol index 55fa9fc73..adec53002 100644 --- a/solidity/contracts/CheckpointFraudProofs.sol +++ b/solidity/contracts/CheckpointFraudProofs.sol @@ -9,12 +9,14 @@ import {MerkleLib, TREE_DEPTH} from "./libs/Merkle.sol"; import {MerkleTreeHook} from "./hooks/MerkleTreeHook.sol"; import {IMailbox} from "./interfaces/IMailbox.sol"; +import {PackageVersioned} from "./PackageVersioned.sol"; + struct StoredIndex { uint32 index; bool exists; } -contract CheckpointFraudProofs { +contract CheckpointFraudProofs is PackageVersioned { using CheckpointLib for Checkpoint; using Address for address; diff --git a/solidity/contracts/Mailbox.sol b/solidity/contracts/Mailbox.sol index 338bdfc0e..472707684 100644 --- a/solidity/contracts/Mailbox.sol +++ b/solidity/contracts/Mailbox.sol @@ -10,12 +10,19 @@ import {IInterchainSecurityModule, ISpecifiesInterchainSecurityModule} from "./i import {IPostDispatchHook} from "./interfaces/hooks/IPostDispatchHook.sol"; import {IMessageRecipient} from "./interfaces/IMessageRecipient.sol"; import {IMailbox} from "./interfaces/IMailbox.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -contract Mailbox is IMailbox, Indexed, Versioned, OwnableUpgradeable { +contract Mailbox is + IMailbox, + Indexed, + Versioned, + OwnableUpgradeable, + PackageVersioned +{ // ============ Libraries ============ using Message for bytes; diff --git a/solidity/contracts/PackageVersioned.sol b/solidity/contracts/PackageVersioned.sol new file mode 100644 index 000000000..44913c2d9 --- /dev/null +++ b/solidity/contracts/PackageVersioned.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.6.11; + +/** + * @title PackageVersioned + * @notice Package version getter for contracts + **/ +abstract contract PackageVersioned { + // GENERATED CODE - DO NOT EDIT + string public constant PACKAGE_VERSION = "5.6.1"; +} diff --git a/solidity/contracts/avs/ECDSAStakeRegistry.sol b/solidity/contracts/avs/ECDSAStakeRegistry.sol index 6148c3c4e..11d1fa820 100644 --- a/solidity/contracts/avs/ECDSAStakeRegistry.sol +++ b/solidity/contracts/avs/ECDSAStakeRegistry.sol @@ -7,6 +7,8 @@ import {IDelegationManager} from "../interfaces/avs/vendored/IDelegationManager. import {ISignatureUtils} from "../interfaces/avs/vendored/ISignatureUtils.sol"; import {IServiceManager} from "../interfaces/avs/vendored/IServiceManager.sol"; +import {PackageVersioned} from "../PackageVersioned.sol"; + import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import {CheckpointsUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/CheckpointsUpgradeable.sol"; import {SignatureCheckerUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/SignatureCheckerUpgradeable.sol"; @@ -19,7 +21,8 @@ import {IERC1271Upgradeable} from "@openzeppelin/contracts-upgradeable/interface contract ECDSAStakeRegistry is IERC1271Upgradeable, OwnableUpgradeable, - ECDSAStakeRegistryStorage + ECDSAStakeRegistryStorage, + PackageVersioned { using SignatureCheckerUpgradeable for address; using CheckpointsUpgradeable for CheckpointsUpgradeable.History; diff --git a/solidity/contracts/avs/HyperlaneServiceManager.sol b/solidity/contracts/avs/HyperlaneServiceManager.sol index b663695dd..99f27c652 100644 --- a/solidity/contracts/avs/HyperlaneServiceManager.sol +++ b/solidity/contracts/avs/HyperlaneServiceManager.sol @@ -19,8 +19,9 @@ import {IAVSDirectory} from "../interfaces/avs/vendored/IAVSDirectory.sol"; import {IRemoteChallenger} from "../interfaces/avs/IRemoteChallenger.sol"; import {ISlasher} from "../interfaces/avs/vendored/ISlasher.sol"; import {ECDSAServiceManagerBase} from "./ECDSAServiceManagerBase.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; -contract HyperlaneServiceManager is ECDSAServiceManagerBase { +contract HyperlaneServiceManager is ECDSAServiceManagerBase, PackageVersioned { // ============ Libraries ============ using EnumerableMapEnrollment for EnumerableMapEnrollment.AddressToEnrollmentMap; diff --git a/solidity/contracts/client/GasRouter.sol b/solidity/contracts/client/GasRouter.sol index c5f9efd1c..f14d0b642 100644 --- a/solidity/contracts/client/GasRouter.sol +++ b/solidity/contracts/client/GasRouter.sol @@ -1,10 +1,25 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.6.11; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ import {Router} from "./Router.sol"; import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol"; abstract contract GasRouter is Router { + event GasSet(uint32 domain, uint256 gas); + // ============ Mutable Storage ============ mapping(uint32 => uint256) public destinationGas; @@ -56,6 +71,7 @@ abstract contract GasRouter is Router { function _setDestinationGas(uint32 domain, uint256 gas) internal { destinationGas[domain] = gas; + emit GasSet(domain, gas); } function _GasRouter_dispatch( diff --git a/solidity/contracts/client/MailboxClient.sol b/solidity/contracts/client/MailboxClient.sol index 3e0b751e6..9c986a0cc 100644 --- a/solidity/contracts/client/MailboxClient.sol +++ b/solidity/contracts/client/MailboxClient.sol @@ -1,19 +1,35 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.6.11; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + // ============ Internal Imports ============ import {IMailbox} from "../interfaces/IMailbox.sol"; import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol"; import {Message} from "../libs/Message.sol"; +import {PackageVersioned} from "../PackageVersioned.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -abstract contract MailboxClient is OwnableUpgradeable { +abstract contract MailboxClient is OwnableUpgradeable, PackageVersioned { using Message for bytes; + event HookSet(address _hook); + event IsmSet(address _ism); + IMailbox public immutable mailbox; uint32 public immutable localDomain; @@ -62,8 +78,11 @@ abstract contract MailboxClient is OwnableUpgradeable { * @notice Sets the address of the application's custom hook. * @param _hook The address of the hook contract. */ - function setHook(address _hook) public onlyContractOrNull(_hook) onlyOwner { + function setHook( + address _hook + ) public virtual onlyContractOrNull(_hook) onlyOwner { hook = IPostDispatchHook(_hook); + emit HookSet(_hook); } /** @@ -74,6 +93,7 @@ abstract contract MailboxClient is OwnableUpgradeable { address _module ) public onlyContractOrNull(_module) onlyOwner { interchainSecurityModule = IInterchainSecurityModule(_module); + emit IsmSet(_module); } // ======== Initializer ========= diff --git a/solidity/contracts/hooks/ArbL2ToL1Hook.sol b/solidity/contracts/hooks/ArbL2ToL1Hook.sol index 488dabc1e..9a6365b24 100644 --- a/solidity/contracts/hooks/ArbL2ToL1Hook.sol +++ b/solidity/contracts/hooks/ArbL2ToL1Hook.sol @@ -14,17 +14,16 @@ pragma solidity >=0.8.0; @@@@@@@@@ @@@@@@@@*/ // ============ Internal Imports ============ +import {Message} from "../libs/Message.sol"; +import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; import {AbstractPostDispatchHook} from "./libs/AbstractMessageIdAuthHook.sol"; import {AbstractMessageIdAuthHook} from "./libs/AbstractMessageIdAuthHook.sol"; -import {Mailbox} from "../Mailbox.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../isms/hook/AbstractMessageIdAuthorizedIsm.sol"; import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol"; import {Message} from "../libs/Message.sol"; import {TypeCasts} from "../libs/TypeCasts.sol"; -import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; -import {MailboxClient} from "../client/MailboxClient.sol"; // ============ External Imports ============ -import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {ArbSys} from "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; /** @@ -35,13 +34,14 @@ import {ArbSys} from "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; */ contract ArbL2ToL1Hook is AbstractMessageIdAuthHook { using StandardHookMetadata for bytes; + using Message for bytes; // ============ Constants ============ // precompile contract on L2 for sending messages to L1 ArbSys public immutable arbSys; - // Immutable quote amount - uint256 public immutable GAS_QUOTE; + // child hook to call first + IPostDispatchHook public immutable childHook; // ============ Constructor ============ @@ -50,21 +50,24 @@ contract ArbL2ToL1Hook is AbstractMessageIdAuthHook { uint32 _destinationDomain, bytes32 _ism, address _arbSys, - uint256 _gasQuote + address _childHook ) AbstractMessageIdAuthHook(_mailbox, _destinationDomain, _ism) { arbSys = ArbSys(_arbSys); - GAS_QUOTE = _gasQuote; + childHook = AbstractPostDispatchHook(_childHook); } + /// @inheritdoc IPostDispatchHook function hookType() external pure override returns (uint8) { return uint8(IPostDispatchHook.Types.ARB_L2_TO_L1); } + /// @inheritdoc AbstractPostDispatchHook function _quoteDispatch( - bytes calldata, - bytes calldata + bytes calldata metadata, + bytes calldata message ) internal view override returns (uint256) { - return GAS_QUOTE; + return + metadata.msgValue(0) + childHook.quoteDispatch(metadata, message); } // ============ Internal functions ============ @@ -72,8 +75,16 @@ contract ArbL2ToL1Hook is AbstractMessageIdAuthHook { /// @inheritdoc AbstractMessageIdAuthHook function _sendMessageId( bytes calldata metadata, - bytes memory payload + bytes calldata message ) internal override { + bytes memory payload = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (message.id(), metadata.msgValue(0)) + ); + + childHook.postDispatch{ + value: childHook.quoteDispatch(metadata, message) + }(metadata, message); arbSys.sendTxToL1{value: metadata.msgValue(0)}( TypeCasts.bytes32ToAddress(ism), payload diff --git a/solidity/contracts/hooks/OPL2ToL1Hook.sol b/solidity/contracts/hooks/OPL2ToL1Hook.sol index 7d0fcf36a..165289e57 100644 --- a/solidity/contracts/hooks/OPL2ToL1Hook.sol +++ b/solidity/contracts/hooks/OPL2ToL1Hook.sol @@ -14,10 +14,13 @@ pragma solidity >=0.8.0; @@@@@@@@@ @@@@@@@@*/ // ============ Internal Imports ============ +import {Message} from "../libs/Message.sol"; import {AbstractPostDispatchHook, AbstractMessageIdAuthHook} from "./libs/AbstractMessageIdAuthHook.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../isms/hook/AbstractMessageIdAuthorizedIsm.sol"; import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol"; import {TypeCasts} from "../libs/TypeCasts.sol"; import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; +import {InterchainGasPaymaster} from "./igp/InterchainGasPaymaster.sol"; // ============ External Imports ============ import {ICrossDomainMessenger} from "../interfaces/optimism/ICrossDomainMessenger.sol"; @@ -30,13 +33,16 @@ import {ICrossDomainMessenger} from "../interfaces/optimism/ICrossDomainMessenge */ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { using StandardHookMetadata for bytes; + using Message for bytes; // ============ Constants ============ // precompile contract on L2 for sending messages to L1 ICrossDomainMessenger public immutable l2Messenger; - // Immutable quote amount - uint32 public immutable GAS_QUOTE; + // child hook to call first + IPostDispatchHook public immutable childHook; + // Minimum gas limit that the message can be executed with - OP specific + uint32 public constant MIN_GAS_LIMIT = 300_000; // ============ Constructor ============ @@ -45,10 +51,10 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { uint32 _destinationDomain, bytes32 _ism, address _l2Messenger, - uint32 _gasQuote + address _childHook ) AbstractMessageIdAuthHook(_mailbox, _destinationDomain, _ism) { - GAS_QUOTE = _gasQuote; l2Messenger = ICrossDomainMessenger(_l2Messenger); + childHook = AbstractPostDispatchHook(_childHook); } /// @inheritdoc IPostDispatchHook @@ -58,10 +64,11 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { /// @inheritdoc AbstractPostDispatchHook function _quoteDispatch( - bytes calldata, - bytes calldata + bytes calldata metadata, + bytes calldata message ) internal view override returns (uint256) { - return GAS_QUOTE; + return + metadata.msgValue(0) + childHook.quoteDispatch(metadata, message); } // ============ Internal functions ============ @@ -69,16 +76,20 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook { /// @inheritdoc AbstractMessageIdAuthHook function _sendMessageId( bytes calldata metadata, - bytes memory payload + bytes calldata message ) internal override { - require( - msg.value >= metadata.msgValue(0) + GAS_QUOTE, - "OPL2ToL1Hook: insufficient msg.value" + bytes memory payload = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (message.id(), metadata.msgValue(0)) ); + + childHook.postDispatch{ + value: childHook.quoteDispatch(metadata, message) + }(metadata, message); l2Messenger.sendMessage{value: metadata.msgValue(0)}( TypeCasts.bytes32ToAddress(ism), payload, - GAS_QUOTE + MIN_GAS_LIMIT ); } } diff --git a/solidity/contracts/hooks/OPStackHook.sol b/solidity/contracts/hooks/OPStackHook.sol index a0a33dabc..695fab9cc 100644 --- a/solidity/contracts/hooks/OPStackHook.sol +++ b/solidity/contracts/hooks/OPStackHook.sol @@ -18,6 +18,7 @@ import {AbstractMessageIdAuthHook} from "./libs/AbstractMessageIdAuthHook.sol"; import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol"; import {TypeCasts} from "../libs/TypeCasts.sol"; import {Message} from "../libs/Message.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../isms/hook/AbstractMessageIdAuthorizedIsm.sol"; import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; // ============ External Imports ============ @@ -32,6 +33,7 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; */ contract OPStackHook is AbstractMessageIdAuthHook { using StandardHookMetadata for bytes; + using Message for bytes; // ============ Constants ============ @@ -60,21 +62,22 @@ contract OPStackHook is AbstractMessageIdAuthHook { // ============ Internal functions ============ function _quoteDispatch( - bytes calldata, + bytes calldata metadata, bytes calldata ) internal pure override returns (uint256) { - return 0; // gas subsidized by the L2 + return metadata.msgValue(0); // gas subsidized by the L2 } /// @inheritdoc AbstractMessageIdAuthHook function _sendMessageId( bytes calldata metadata, - bytes memory payload + bytes calldata message ) internal override { - require( - metadata.msgValue(0) < 2 ** 255, - "OPStackHook: msgValue must be less than 2 ** 255" + bytes memory payload = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (message.id(), metadata.msgValue(0)) ); + l1Messenger.sendMessage{value: metadata.msgValue(0)}( TypeCasts.bytes32ToAddress(ism), payload, diff --git a/solidity/contracts/hooks/PolygonPosHook.sol b/solidity/contracts/hooks/PolygonPosHook.sol index 6831f9a33..e3b1c7628 100644 --- a/solidity/contracts/hooks/PolygonPosHook.sol +++ b/solidity/contracts/hooks/PolygonPosHook.sol @@ -19,6 +19,7 @@ import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol"; import {TypeCasts} from "../libs/TypeCasts.sol"; import {Message} from "../libs/Message.sol"; import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../isms/hook/AbstractMessageIdAuthorizedIsm.sol"; // ============ External Imports ============ import {FxBaseRootTunnel} from "fx-portal/contracts/tunnel/FxBaseRootTunnel.sol"; @@ -31,6 +32,7 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; */ contract PolygonPosHook is AbstractMessageIdAuthHook, FxBaseRootTunnel { using StandardHookMetadata for bytes; + using Message for bytes; // ============ Constructor ============ @@ -56,22 +58,27 @@ contract PolygonPosHook is AbstractMessageIdAuthHook, FxBaseRootTunnel { // ============ Internal functions ============ function _quoteDispatch( - bytes calldata, + bytes calldata metadata, bytes calldata ) internal pure override returns (uint256) { - return 0; + return metadata.msgValue(0); } /// @inheritdoc AbstractMessageIdAuthHook function _sendMessageId( bytes calldata metadata, - bytes memory payload + bytes calldata message ) internal override { require( metadata.msgValue(0) == 0, "PolygonPosHook: does not support msgValue" ); require(msg.value == 0, "PolygonPosHook: does not support msgValue"); + + bytes memory payload = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (message.id(), metadata.msgValue(0)) + ); _sendMessageToChild(payload); } diff --git a/solidity/contracts/hooks/ProtocolFee.sol b/solidity/contracts/hooks/ProtocolFee.sol index 4e1f47048..b530b0d83 100644 --- a/solidity/contracts/hooks/ProtocolFee.sol +++ b/solidity/contracts/hooks/ProtocolFee.sol @@ -32,6 +32,9 @@ contract ProtocolFee is AbstractPostDispatchHook, Ownable { using Address for address payable; using Message for bytes; + event ProtocolFeeSet(uint256 protocolFee); + event BeneficiarySet(address beneficiary); + // ============ Constants ============ /// @notice The maximum protocol fee that can be set. @@ -126,6 +129,7 @@ contract ProtocolFee is AbstractPostDispatchHook, Ownable { "ProtocolFee: exceeds max protocol fee" ); protocolFee = _protocolFee; + emit ProtocolFeeSet(_protocolFee); } /** @@ -135,5 +139,6 @@ contract ProtocolFee is AbstractPostDispatchHook, Ownable { function _setBeneficiary(address _beneficiary) internal { require(_beneficiary != address(0), "ProtocolFee: invalid beneficiary"); beneficiary = _beneficiary; + emit BeneficiarySet(_beneficiary); } } diff --git a/solidity/contracts/hooks/aggregation/ERC5164Hook.sol b/solidity/contracts/hooks/aggregation/ERC5164Hook.sol index 8cfc80f06..e1156679c 100644 --- a/solidity/contracts/hooks/aggregation/ERC5164Hook.sol +++ b/solidity/contracts/hooks/aggregation/ERC5164Hook.sol @@ -15,9 +15,12 @@ pragma solidity >=0.8.0; // ============ Internal Imports ============ import {TypeCasts} from "../../libs/TypeCasts.sol"; +import {Message} from "../../libs/Message.sol"; +import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol"; import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol"; import {IMessageDispatcher} from "../../interfaces/hooks/IMessageDispatcher.sol"; import {AbstractMessageIdAuthHook} from "../libs/AbstractMessageIdAuthHook.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../../isms/hook/AbstractMessageIdAuthorizedIsm.sol"; // ============ External Imports ============ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; @@ -28,6 +31,9 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; * any of the 5164 adapters. */ contract ERC5164Hook is AbstractMessageIdAuthHook { + using StandardHookMetadata for bytes; + using Message for bytes; + IMessageDispatcher public immutable dispatcher; constructor( @@ -53,11 +59,15 @@ contract ERC5164Hook is AbstractMessageIdAuthHook { } function _sendMessageId( - bytes calldata, - /* metadata */ - bytes memory payload + bytes calldata metadata, + bytes calldata message ) internal override { require(msg.value == 0, "ERC5164Hook: no value allowed"); + + bytes memory payload = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (message.id(), metadata.msgValue(0)) + ); dispatcher.dispatchMessage( destinationDomain, TypeCasts.bytes32ToAddress(ism), diff --git a/solidity/contracts/hooks/igp/StorageGasOracle.sol b/solidity/contracts/hooks/igp/StorageGasOracle.sol index fb6ca2a83..1c5f9ad15 100644 --- a/solidity/contracts/hooks/igp/StorageGasOracle.sol +++ b/solidity/contracts/hooks/igp/StorageGasOracle.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0; // ============ Internal Imports ============ import {IGasOracle} from "../../interfaces/IGasOracle.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; // ============ External Imports ============ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; @@ -12,7 +13,7 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; * @dev This contract is intended to be owned by an address that will * update the stored remote gas data. */ -contract StorageGasOracle is IGasOracle, Ownable { +contract StorageGasOracle is IGasOracle, Ownable, PackageVersioned { // ============ Public Storage ============ /// @notice Keyed by remote domain, gas data on that remote domain. diff --git a/solidity/contracts/hooks/layer-zero/LayerZeroV2Hook.sol b/solidity/contracts/hooks/layer-zero/LayerZeroV2Hook.sol index 244fea5c7..5e0de4a8e 100644 --- a/solidity/contracts/hooks/layer-zero/LayerZeroV2Hook.sol +++ b/solidity/contracts/hooks/layer-zero/LayerZeroV2Hook.sol @@ -18,6 +18,7 @@ import {TypeCasts} from "../../libs/TypeCasts.sol"; import {Indexed} from "../../libs/Indexed.sol"; import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol"; import {AbstractMessageIdAuthHook} from "../libs/AbstractMessageIdAuthHook.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../../isms/hook/AbstractMessageIdAuthorizedIsm.sol"; import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol"; struct LayerZeroV2Metadata { @@ -55,8 +56,13 @@ contract LayerZeroV2Hook is AbstractMessageIdAuthHook { /// @inheritdoc AbstractMessageIdAuthHook function _sendMessageId( bytes calldata metadata, - bytes memory payload + bytes calldata message ) internal override { + bytes memory payload = abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (message.id(), metadata.msgValue(0)) + ); + bytes calldata lZMetadata = metadata.getCustomMetadata(); ( uint32 eid, @@ -72,7 +78,9 @@ contract LayerZeroV2Hook is AbstractMessageIdAuthHook { options, false // payInLzToken ); - lZEndpoint.send{value: msg.value}(msgParams, refundAddress); + + uint256 quote = _quoteDispatch(metadata, message); + lZEndpoint.send{value: quote}(msgParams, refundAddress); } /// @dev payInZRO is hardcoded to false because zro tokens should not be directly accepted @@ -96,7 +104,7 @@ contract LayerZeroV2Hook is AbstractMessageIdAuthHook { message.senderAddress() ); - return msgFee.nativeFee; + return metadata.msgValue(0) + msgFee.nativeFee; } /** diff --git a/solidity/contracts/hooks/libs/AbstractMessageIdAuthHook.sol b/solidity/contracts/hooks/libs/AbstractMessageIdAuthHook.sol index a9e3a41e9..05c2c1d08 100644 --- a/solidity/contracts/hooks/libs/AbstractMessageIdAuthHook.sol +++ b/solidity/contracts/hooks/libs/AbstractMessageIdAuthHook.sol @@ -22,6 +22,9 @@ import {Message} from "../../libs/Message.sol"; import {StandardHookMetadata} from "./StandardHookMetadata.sol"; import {MailboxClient} from "../../client/MailboxClient.sol"; +// ============ External Imports ============ +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; + /** * @title AbstractMessageIdAuthHook * @notice Message hook to inform an Abstract Message ID ISM of messages published through @@ -31,8 +34,10 @@ abstract contract AbstractMessageIdAuthHook is AbstractPostDispatchHook, MailboxClient { + using Address for address payable; using StandardHookMetadata for bytes; using Message for bytes; + using TypeCasts for bytes32; // ============ Constants ============ @@ -68,7 +73,7 @@ abstract contract AbstractMessageIdAuthHook is function _postDispatch( bytes calldata metadata, bytes calldata message - ) internal override { + ) internal virtual override { bytes32 id = message.id(); require( _isLatestDispatched(id), @@ -82,20 +87,29 @@ abstract contract AbstractMessageIdAuthHook is metadata.msgValue(0) < 2 ** 255, "AbstractMessageIdAuthHook: msgValue must be less than 2 ** 255" ); - bytes memory payload = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - id - ); - _sendMessageId(metadata, payload); + + _sendMessageId(metadata, message); + + uint256 _overpayment = msg.value - _quoteDispatch(metadata, message); + if (_overpayment > 0) { + address _refundAddress = metadata.refundAddress( + message.sender().bytes32ToAddress() + ); + require( + _refundAddress != address(0), + "AbstractPostDispatchHook: no refund address" + ); + payable(_refundAddress).sendValue(_overpayment); + } } /** * @notice Send a message to the ISM. * @param metadata The metadata for the hook caller - * @param payload The payload for call to the ISM + * @param message The message to send to the ISM */ function _sendMessageId( bytes calldata metadata, - bytes memory payload + bytes calldata message ) internal virtual; } diff --git a/solidity/contracts/hooks/libs/AbstractPostDispatchHook.sol b/solidity/contracts/hooks/libs/AbstractPostDispatchHook.sol index 5ad564976..2578af947 100644 --- a/solidity/contracts/hooks/libs/AbstractPostDispatchHook.sol +++ b/solidity/contracts/hooks/libs/AbstractPostDispatchHook.sol @@ -16,12 +16,16 @@ pragma solidity >=0.8.0; // ============ Internal Imports ============ import {StandardHookMetadata} from "./StandardHookMetadata.sol"; import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; /** * @title AbstractPostDispatch * @notice Abstract post dispatch hook supporting the current global hook metadata variant. */ -abstract contract AbstractPostDispatchHook is IPostDispatchHook { +abstract contract AbstractPostDispatchHook is + IPostDispatchHook, + PackageVersioned +{ using StandardHookMetadata for bytes; // ============ External functions ============ diff --git a/solidity/contracts/isms/NoopIsm.sol b/solidity/contracts/isms/NoopIsm.sol index 9e7a7f941..30ac76864 100644 --- a/solidity/contracts/isms/NoopIsm.sol +++ b/solidity/contracts/isms/NoopIsm.sol @@ -2,8 +2,9 @@ pragma solidity >=0.8.0; import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; -contract NoopIsm is IInterchainSecurityModule { +contract NoopIsm is IInterchainSecurityModule, PackageVersioned { uint8 public constant override moduleType = uint8(Types.NULL); function verify( diff --git a/solidity/contracts/isms/PausableIsm.sol b/solidity/contracts/isms/PausableIsm.sol index cbe696d2e..00868285e 100644 --- a/solidity/contracts/isms/PausableIsm.sol +++ b/solidity/contracts/isms/PausableIsm.sol @@ -7,8 +7,14 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; // ============ Internal Imports ============ import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; -contract PausableIsm is IInterchainSecurityModule, Ownable, Pausable { +contract PausableIsm is + IInterchainSecurityModule, + Ownable, + Pausable, + PackageVersioned +{ uint8 public constant override moduleType = uint8(Types.NULL); constructor(address owner) Ownable() Pausable() { diff --git a/solidity/contracts/isms/TrustedRelayerIsm.sol b/solidity/contracts/isms/TrustedRelayerIsm.sol index 89a8d07d1..87da1bb60 100644 --- a/solidity/contracts/isms/TrustedRelayerIsm.sol +++ b/solidity/contracts/isms/TrustedRelayerIsm.sol @@ -3,10 +3,12 @@ pragma solidity >=0.8.0; // ============ Internal Imports ============ import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {Message} from "../libs/Message.sol"; import {Mailbox} from "../Mailbox.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; -contract TrustedRelayerIsm is IInterchainSecurityModule { +contract TrustedRelayerIsm is IInterchainSecurityModule, PackageVersioned { using Message for bytes; uint8 public immutable moduleType = uint8(Types.NULL); @@ -14,6 +16,14 @@ contract TrustedRelayerIsm is IInterchainSecurityModule { address public immutable trustedRelayer; constructor(address _mailbox, address _trustedRelayer) { + require( + _trustedRelayer != address(0), + "TrustedRelayerIsm: invalid relayer" + ); + require( + Address.isContract(_mailbox), + "TrustedRelayerIsm: invalid mailbox" + ); mailbox = Mailbox(_mailbox); trustedRelayer = _trustedRelayer; } diff --git a/solidity/contracts/isms/aggregation/StaticAggregationIsm.sol b/solidity/contracts/isms/aggregation/StaticAggregationIsm.sol index 8e8bbb56e..7a64369af 100644 --- a/solidity/contracts/isms/aggregation/StaticAggregationIsm.sol +++ b/solidity/contracts/isms/aggregation/StaticAggregationIsm.sol @@ -5,13 +5,14 @@ pragma solidity >=0.8.0; import {AbstractAggregationIsm} from "./AbstractAggregationIsm.sol"; import {AggregationIsmMetadata} from "../../isms/libs/AggregationIsmMetadata.sol"; import {MetaProxy} from "../../libs/MetaProxy.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; /** * @title StaticAggregationIsm * @notice Manages per-domain m-of-n ISM sets that are used to verify * interchain messages. */ -contract StaticAggregationIsm is AbstractAggregationIsm { +contract StaticAggregationIsm is AbstractAggregationIsm, PackageVersioned { // ============ Public Functions ============ /** diff --git a/solidity/contracts/isms/aggregation/StaticAggregationIsmFactory.sol b/solidity/contracts/isms/aggregation/StaticAggregationIsmFactory.sol index da4209499..8fa18fa65 100644 --- a/solidity/contracts/isms/aggregation/StaticAggregationIsmFactory.sol +++ b/solidity/contracts/isms/aggregation/StaticAggregationIsmFactory.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; + // ============ Internal Imports ============ import {StaticAggregationIsm} from "./StaticAggregationIsm.sol"; import {StaticThresholdAddressSetFactory} from "../../libs/StaticAddressSetFactory.sol"; diff --git a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol index a8b8baee6..51a896129 100644 --- a/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol +++ b/solidity/contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol @@ -18,9 +18,9 @@ pragma solidity >=0.8.0; import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; import {LibBit} from "../../libs/LibBit.sol"; import {Message} from "../../libs/Message.sol"; +import {PackageVersioned} from "contracts/PackageVersioned.sol"; // ============ External Imports ============ - import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; @@ -31,7 +31,8 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini */ abstract contract AbstractMessageIdAuthorizedIsm is IInterchainSecurityModule, - Initializable + Initializable, + PackageVersioned { using Address for address payable; using LibBit for uint256; @@ -52,7 +53,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is // ============ Events ============ /// @notice Emitted when a message is received from the external bridge - event ReceivedMessage(bytes32 indexed messageId); + event ReceivedMessage(bytes32 indexed messageId, uint256 msgValue); // ============ Initializer ============ @@ -100,7 +101,7 @@ abstract contract AbstractMessageIdAuthorizedIsm is } /** - * @notice Check if a message is verified through verifyMessageId first. + * @notice Check if a message is verified through preVerifyMessage first. * @param message Message to check. */ function isVerified(bytes calldata message) public view returns (bool) { @@ -114,24 +115,31 @@ abstract contract AbstractMessageIdAuthorizedIsm is * @dev Only callable by the authorized hook. * @param messageId Hyperlane Id of the message. */ - function verifyMessageId(bytes32 messageId) public payable virtual { + function preVerifyMessage( + bytes32 messageId, + uint256 msgValue + ) public payable virtual { require( _isAuthorized(), "AbstractMessageIdAuthorizedIsm: sender is not the hook" ); require( - msg.value < 2 ** VERIFIED_MASK_INDEX, - "AbstractMessageIdAuthorizedIsm: msg.value must be less than 2^255" + msg.value < 2 ** VERIFIED_MASK_INDEX && msg.value == msgValue, + "AbstractMessageIdAuthorizedIsm: invalid msg.value" + ); + require( + verifiedMessages[messageId] == 0, + "AbstractMessageIdAuthorizedIsm: message already verified" ); verifiedMessages[messageId] = msg.value.setBit(VERIFIED_MASK_INDEX); - emit ReceivedMessage(messageId); + emit ReceivedMessage(messageId, msgValue); } // ============ Internal Functions ============ /** - * @notice Check if sender is authorized to message `verifyMessageId`. + * @notice Check if sender is authorized to message `preVerifyMessage`. */ function _isAuthorized() internal view virtual returns (bool); } diff --git a/solidity/contracts/isms/hook/ArbL2ToL1Ism.sol b/solidity/contracts/isms/hook/ArbL2ToL1Ism.sol index a7bd71447..42593f85c 100644 --- a/solidity/contracts/isms/hook/ArbL2ToL1Ism.sol +++ b/solidity/contracts/isms/hook/ArbL2ToL1Ism.sol @@ -44,6 +44,10 @@ contract ArbL2ToL1Ism is // arbitrum nitro contract on L1 to forward verification IOutbox public arbOutbox; + uint256 private constant DATA_LENGTH = 68; + + uint256 private constant MESSAGE_ID_END = 36; + // ============ Constructor ============ constructor(address _bridge) CrossChainEnabledArbitrumL1(_bridge) { @@ -63,6 +67,7 @@ contract ArbL2ToL1Ism is ) external override returns (bool) { if (!isVerified(message)) { _verifyWithOutboxCall(metadata, message); + require(isVerified(message), "ArbL2ToL1Ism: message not verified"); } releaseValueToRecipient(message); return true; @@ -109,13 +114,16 @@ contract ArbL2ToL1Ism is l2Sender == TypeCasts.bytes32ToAddress(authorizedHook), "ArbL2ToL1Ism: l2Sender != authorizedHook" ); - // this data is an abi encoded call of verifyMessageId(bytes32 messageId) - require(data.length == 36, "ArbL2ToL1Ism: invalid data length"); + // this data is an abi encoded call of preVerifyMessage(bytes32 messageId) + require( + data.length == DATA_LENGTH, + "ArbL2ToL1Ism: invalid data length" + ); bytes32 messageId = message.id(); bytes32 convertedBytes; assembly { // data = 0x[4 bytes function signature][32 bytes messageId] - convertedBytes := mload(add(data, 36)) + convertedBytes := mload(add(data, MESSAGE_ID_END)) } // check if the parsed message id matches the message id of the message require( diff --git a/solidity/contracts/isms/hook/ERC5164Ism.sol b/solidity/contracts/isms/hook/ERC5164Ism.sol index 0e0d788c5..d2735039c 100644 --- a/solidity/contracts/isms/hook/ERC5164Ism.sol +++ b/solidity/contracts/isms/hook/ERC5164Ism.sol @@ -44,7 +44,7 @@ contract ERC5164Ism is AbstractMessageIdAuthorizedIsm { } /** - * @notice Check if sender is authorized to message `verifyMessageId`. + * @notice Check if sender is authorized to message `preVerifyMessage`. */ function _isAuthorized() internal view override returns (bool) { return msg.sender == executor; diff --git a/solidity/contracts/isms/hook/OPL2ToL1Ism.sol b/solidity/contracts/isms/hook/OPL2ToL1Ism.sol index b333b15cd..ef3986861 100644 --- a/solidity/contracts/isms/hook/OPL2ToL1Ism.sol +++ b/solidity/contracts/isms/hook/OPL2ToL1Ism.sol @@ -66,9 +66,9 @@ contract OPL2ToL1Ism is bytes calldata metadata, bytes calldata message ) external override returns (bool) { - bool verified = isVerified(message); - if (!verified) { + if (!isVerified(message)) { _verifyWithPortalCall(metadata, message); + require(isVerified(message), "OPL2ToL1Ism: message not verified"); } releaseValueToRecipient(message); return true; diff --git a/solidity/contracts/isms/hook/OPStackIsm.sol b/solidity/contracts/isms/hook/OPStackIsm.sol index 1db350fec..504ed0d9d 100644 --- a/solidity/contracts/isms/hook/OPStackIsm.sol +++ b/solidity/contracts/isms/hook/OPStackIsm.sol @@ -49,7 +49,7 @@ contract OPStackIsm is // ============ Internal function ============ /** - * @notice Check if sender is authorized to message `verifyMessageId`. + * @notice Check if sender is authorized to message `preVerifyMessage`. */ function _isAuthorized() internal view override returns (bool) { return diff --git a/solidity/contracts/isms/hook/PolygonPosIsm.sol b/solidity/contracts/isms/hook/PolygonPosIsm.sol index 34a40360e..8a1471d1d 100644 --- a/solidity/contracts/isms/hook/PolygonPosIsm.sol +++ b/solidity/contracts/isms/hook/PolygonPosIsm.sol @@ -49,7 +49,7 @@ contract PolygonPosIsm is // ============ Internal function ============ /** - * @notice Check if sender is authorized to message `verifyMessageId`. + * @notice Check if sender is authorized to message `preVerifyMessage`. */ function _isAuthorized() internal view override returns (bool) { return diff --git a/solidity/contracts/isms/hook/layer-zero/LayerZeroV2Ism.sol b/solidity/contracts/isms/hook/layer-zero/LayerZeroV2Ism.sol index 1388f32cf..fedbffcda 100644 --- a/solidity/contracts/isms/hook/layer-zero/LayerZeroV2Ism.sol +++ b/solidity/contracts/isms/hook/layer-zero/LayerZeroV2Ism.sol @@ -57,7 +57,7 @@ contract LayerZeroV2Ism is AbstractMessageIdAuthorizedIsm { /** * @notice Entry point for receiving msg/packet from the LayerZero endpoint. * @param _lzMessage The payload of the received message. - * @dev Authorization verification is done within verifyMessageId() -> _isAuthorized() + * @dev Authorization verification is done within preVerifyMessage() -> _isAuthorized() */ function lzReceive( Origin calldata, @@ -66,25 +66,36 @@ contract LayerZeroV2Ism is AbstractMessageIdAuthorizedIsm { address, bytes calldata ) external payable { - verifyMessageId(_messageId(_lzMessage)); + preVerifyMessage(_messageId(_lzMessage), _msgValue(_lzMessage)); } // ============ Internal function ============ /** * @notice Slices the messageId from the message delivered from LayerZeroV2Hook - * @dev message is created as abi.encodeCall(AbstractMessageIdAuthorizedIsm.verifyMessageId, id) + * @dev message is created as abi.encodeCall(AbstractMessageIdAuthorizedIsm.preVerifyMessage, id) * @dev _message will be 36 bytes (4 bytes for function selector, and 32 bytes for messageId) */ function _messageId( bytes calldata _message ) internal pure returns (bytes32) { - return bytes32(_message[FUNC_SELECTOR_OFFSET:]); + return bytes32(_message[FUNC_SELECTOR_OFFSET:ORIGIN_SENDER_OFFSET]); + } + + /** + * @notice Slices the msgValue from the message delivered from LayerZeroV2Hook + * @dev message is created as abi.encodeCall(AbstractMessageIdAuthorizedIsm.preVerifyMessage, (id,msgValue)) + * @dev _message will be 68 bytes (4 bytes for function selector, and 32 bytes for messageId, another 32 for msgValue) + */ + function _msgValue( + bytes calldata _message + ) internal pure returns (uint256) { + return uint256(bytes32(_message[ORIGIN_SENDER_OFFSET:])); } /** * @notice Validates criteria to verify a message - * @dev this is called by AbstractMessageIdAuthorizedIsm.verifyMessageId + * @dev this is called by AbstractMessageIdAuthorizedIsm.preVerifyMessage * @dev parses msg.value to get parameters from lzReceive() */ function _isAuthorized() internal view override returns (bool) { diff --git a/solidity/contracts/isms/multisig/AbstractMultisigIsm.sol b/solidity/contracts/isms/multisig/AbstractMultisigIsm.sol index 0d56319b9..3b59d130c 100644 --- a/solidity/contracts/isms/multisig/AbstractMultisigIsm.sol +++ b/solidity/contracts/isms/multisig/AbstractMultisigIsm.sol @@ -21,6 +21,7 @@ import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityMod import {IMultisigIsm} from "../../interfaces/isms/IMultisigIsm.sol"; import {Message} from "../../libs/Message.sol"; import {MerkleLib} from "../../libs/Merkle.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; /** * @title AbstractMultisig @@ -29,7 +30,7 @@ import {MerkleLib} from "../../libs/Merkle.sol"; * for concrete implementations of `digest` and `signatureAt`. * @dev See ./StaticMultisigIsm.sol for concrete implementations. */ -abstract contract AbstractMultisig { +abstract contract AbstractMultisig is PackageVersioned { /** * @notice Returns the digest to be used for signature verification. * @param _metadata ABI encoded module metadata diff --git a/solidity/contracts/isms/multisig/AbstractWeightedMultisigIsm.sol b/solidity/contracts/isms/multisig/AbstractWeightedMultisigIsm.sol index 2264b192c..8b7fee049 100644 --- a/solidity/contracts/isms/multisig/AbstractWeightedMultisigIsm.sol +++ b/solidity/contracts/isms/multisig/AbstractWeightedMultisigIsm.sol @@ -73,11 +73,14 @@ abstract contract AbstractStaticWeightedMultisigIsm is // assumes that signatures are ordered by validator for ( - uint256 i = 0; - _totalWeight < _thresholdWeight && i < _validatorCount; - ++i + uint256 signatureIndex = 0; + _totalWeight < _thresholdWeight && signatureIndex < _validatorCount; + ++signatureIndex ) { - address _signer = ECDSA.recover(_digest, signatureAt(_metadata, i)); + address _signer = ECDSA.recover( + _digest, + signatureAt(_metadata, signatureIndex) + ); // loop through remaining validators until we find a match while ( _validatorIndex < _validatorCount && @@ -90,6 +93,7 @@ abstract contract AbstractStaticWeightedMultisigIsm is // add the weight of the current validator _totalWeight += _validators[_validatorIndex].weight; + ++_validatorIndex; } require( _totalWeight >= _thresholdWeight, diff --git a/solidity/contracts/isms/routing/DomainRoutingIsm.sol b/solidity/contracts/isms/routing/DomainRoutingIsm.sol index 3b4d753ae..4203d6348 100644 --- a/solidity/contracts/isms/routing/DomainRoutingIsm.sol +++ b/solidity/contracts/isms/routing/DomainRoutingIsm.sol @@ -12,11 +12,16 @@ import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityMod import {Message} from "../../libs/Message.sol"; import {TypeCasts} from "../../libs/TypeCasts.sol"; import {EnumerableMapExtended} from "../../libs/EnumerableMapExtended.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; /** * @title DomainRoutingIsm */ -contract DomainRoutingIsm is AbstractRoutingIsm, OwnableUpgradeable { +contract DomainRoutingIsm is + AbstractRoutingIsm, + OwnableUpgradeable, + PackageVersioned +{ using EnumerableMapExtended for EnumerableMapExtended.UintToBytes32Map; using Message for bytes; using TypeCasts for bytes32; diff --git a/solidity/contracts/isms/routing/DomainRoutingIsmFactory.sol b/solidity/contracts/isms/routing/DomainRoutingIsmFactory.sol index fc8b20383..8015fd8e2 100644 --- a/solidity/contracts/isms/routing/DomainRoutingIsmFactory.sol +++ b/solidity/contracts/isms/routing/DomainRoutingIsmFactory.sol @@ -6,8 +6,9 @@ import {DomainRoutingIsm} from "./DomainRoutingIsm.sol"; import {DefaultFallbackRoutingIsm} from "./DefaultFallbackRoutingIsm.sol"; import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; import {MinimalProxy} from "../../libs/MinimalProxy.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; -abstract contract AbstractDomainRoutingIsmFactory { +abstract contract AbstractDomainRoutingIsmFactory is PackageVersioned { /** * @notice Emitted when a routing module is deployed * @param module The deployed ISM diff --git a/solidity/contracts/isms/routing/InterchainAccountIsm.sol b/solidity/contracts/isms/routing/InterchainAccountIsm.sol index e509e603d..9adecfe88 100644 --- a/solidity/contracts/isms/routing/InterchainAccountIsm.sol +++ b/solidity/contracts/isms/routing/InterchainAccountIsm.sol @@ -6,11 +6,12 @@ import {IMailbox} from "../../interfaces/IMailbox.sol"; import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol"; import {Message} from "../../libs/Message.sol"; import {InterchainAccountMessage} from "../../middleware/libs/InterchainAccountMessage.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; /** * @title InterchainAccountIsm */ -contract InterchainAccountIsm is AbstractRoutingIsm { +contract InterchainAccountIsm is AbstractRoutingIsm, PackageVersioned { IMailbox private immutable mailbox; // ============ Constructor ============ diff --git a/solidity/contracts/isms/warp-route/RateLimitedIsm.sol b/solidity/contracts/isms/warp-route/RateLimitedIsm.sol index 221b6a5b3..0811cae4a 100644 --- a/solidity/contracts/isms/warp-route/RateLimitedIsm.sol +++ b/solidity/contracts/isms/warp-route/RateLimitedIsm.sol @@ -16,6 +16,8 @@ contract RateLimitedIsm is using Message for bytes; using TokenMessage for bytes; + address public immutable recipient; + mapping(bytes32 messageId => bool validated) public messageValidated; modifier validateMessageOnce(bytes calldata _message) { @@ -25,14 +27,22 @@ contract RateLimitedIsm is _; } + modifier onlyRecipient(bytes calldata _message) { + require(_message.recipientAddress() == recipient, "InvalidRecipient"); + _; + } + constructor( address _mailbox, - uint256 _maxCapacity - ) MailboxClient(_mailbox) RateLimited(_maxCapacity) {} + uint256 _maxCapacity, + address _recipient + ) MailboxClient(_mailbox) RateLimited(_maxCapacity) { + recipient = _recipient; + } /// @inheritdoc IInterchainSecurityModule function moduleType() external pure returns (uint8) { - return uint8(IInterchainSecurityModule.Types.UNUSED); + return uint8(IInterchainSecurityModule.Types.NULL); } /** @@ -42,7 +52,12 @@ contract RateLimitedIsm is function verify( bytes calldata, bytes calldata _message - ) external validateMessageOnce(_message) returns (bool) { + ) + external + onlyRecipient(_message) + validateMessageOnce(_message) + returns (bool) + { require(_isDelivered(_message.id()), "InvalidDeliveredMessage"); uint256 newAmount = _message.body().amount(); diff --git a/solidity/contracts/libs/OPL2ToL1Metadata.sol b/solidity/contracts/libs/OPL2ToL1Metadata.sol index 704ce3d4d..f0e933891 100644 --- a/solidity/contracts/libs/OPL2ToL1Metadata.sol +++ b/solidity/contracts/libs/OPL2ToL1Metadata.sol @@ -7,7 +7,7 @@ pragma solidity >=0.8.0; */ library OPL2ToL1Metadata { // bottom offset to the start of message id in the metadata - uint256 private constant MESSAGE_ID_OFFSET = 88; + uint256 private constant MESSAGE_ID_OFFSET = 120; // from IOptimismPortal.WithdrawalTransaction // Σ { // nonce = 32 bytes @@ -20,7 +20,7 @@ library OPL2ToL1Metadata { // LENGTH = 32 bytes // } = 252 bytes uint256 private constant FIXED_METADATA_LENGTH = 252; - // metadata here is double encoded call relayMessage(..., verifyMessageId) + // metadata here is double encoded call relayMessage(..., preVerifyMessage) // Σ { // _selector = 4 bytes // _nonce = 32 bytes @@ -31,9 +31,9 @@ library OPL2ToL1Metadata { // _data // OFFSET = 32 bytes // LENGTH = 32 bytes - // PADDING + verifyMessageId = 64 bytes - // } = 292 bytes - uint256 private constant MESSENGER_CALLDATA_LENGTH = 292; + // PADDING + preVerifyMessage = 96 bytes + // } = 324 bytes + uint256 private constant MESSENGER_CALLDATA_LENGTH = 324; /** * @notice Returns the message ID. diff --git a/solidity/contracts/libs/RateLimited.sol b/solidity/contracts/libs/RateLimited.sol index 4dfb8b262..c58c60379 100644 --- a/solidity/contracts/libs/RateLimited.sol +++ b/solidity/contracts/libs/RateLimited.sol @@ -1,5 +1,19 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; + +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ External Imports ============ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; /** @@ -7,16 +21,26 @@ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Own * @notice A contract used to keep track of an address sender's token amount limits. * @dev Implements a modified token bucket algorithm where the bucket is full in the beginning and gradually refills * See: https://dev.to/satrobit/rate-limiting-using-the-token-bucket-algorithm-3cjh - **/ + * + */ contract RateLimited is OwnableUpgradeable { uint256 public constant DURATION = 1 days; // 86400 - uint256 public filledLevel; /// @notice Current filled level - uint256 public refillRate; /// @notice Tokens per second refill rate - uint256 public lastUpdated; /// @notice Timestamp of the last time an action has been taken TODO prob can be uint40 + /// @notice Current filled level + uint256 public filledLevel; + /// @notice Tokens per second refill rate + uint256 public refillRate; + /// @notice Timestamp of the last time an action has been taken + uint256 public lastUpdated; event RateLimitSet(uint256 _oldCapacity, uint256 _newCapacity); + event ConsumedFilledLevel(uint256 filledLevel, uint256 lastUpdated); + constructor(uint256 _capacity) { + require( + _capacity >= DURATION, + "Capacity must be greater than DURATION" + ); _transferOwnership(msg.sender); setRefillRate(_capacity); filledLevel = _capacity; @@ -88,20 +112,22 @@ contract RateLimited is OwnableUpgradeable { /** * Validate an amount and decreases the currentCapacity - * @param _newAmount The amount to consume the fill level + * @param _consumedAmount The amount to consume the fill level * @return The new filled level */ function validateAndConsumeFilledLevel( - uint256 _newAmount + uint256 _consumedAmount ) public returns (uint256) { uint256 adjustedFilledLevel = calculateCurrentLevel(); - require(_newAmount <= adjustedFilledLevel, "RateLimitExceeded"); + require(_consumedAmount <= adjustedFilledLevel, "RateLimitExceeded"); // Reduce the filledLevel and update lastUpdated - uint256 _filledLevel = adjustedFilledLevel - _newAmount; + uint256 _filledLevel = adjustedFilledLevel - _consumedAmount; filledLevel = _filledLevel; lastUpdated = block.timestamp; + emit ConsumedFilledLevel(filledLevel, lastUpdated); + return _filledLevel; } } diff --git a/solidity/contracts/libs/StaticAddressSetFactory.sol b/solidity/contracts/libs/StaticAddressSetFactory.sol index 82df1e168..38da45b8b 100644 --- a/solidity/contracts/libs/StaticAddressSetFactory.sol +++ b/solidity/contracts/libs/StaticAddressSetFactory.sol @@ -6,8 +6,9 @@ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; // ============ Internal Imports ============ import {MetaProxy} from "./MetaProxy.sol"; +import {PackageVersioned} from "../PackageVersioned.sol"; -abstract contract StaticThresholdAddressSetFactory { +abstract contract StaticThresholdAddressSetFactory is PackageVersioned { // ============ Immutables ============ address public immutable implementation; diff --git a/solidity/contracts/libs/StaticWeightedValidatorSetFactory.sol b/solidity/contracts/libs/StaticWeightedValidatorSetFactory.sol index 9e9516dac..6cd849420 100644 --- a/solidity/contracts/libs/StaticWeightedValidatorSetFactory.sol +++ b/solidity/contracts/libs/StaticWeightedValidatorSetFactory.sol @@ -8,8 +8,9 @@ import {IStaticWeightedMultisigIsm} from "../interfaces/isms/IWeightedMultisigIs // ============ Internal Imports ============ import {MetaProxy} from "./MetaProxy.sol"; +import {PackageVersioned} from "../PackageVersioned.sol"; -abstract contract StaticWeightedValidatorSetFactory { +abstract contract StaticWeightedValidatorSetFactory is PackageVersioned { // ============ Immutables ============ address public immutable implementation; diff --git a/solidity/contracts/libs/TypeCasts.sol b/solidity/contracts/libs/TypeCasts.sol index 4440f63da..d156e9850 100644 --- a/solidity/contracts/libs/TypeCasts.sol +++ b/solidity/contracts/libs/TypeCasts.sol @@ -9,6 +9,10 @@ library TypeCasts { // alignment preserving cast function bytes32ToAddress(bytes32 _buf) internal pure returns (address) { + require( + uint256(_buf) <= uint256(type(uint160).max), + "TypeCasts: bytes32ToAddress overflow" + ); return address(uint160(uint256(_buf))); } } diff --git a/solidity/contracts/middleware/InterchainAccountRouter.sol b/solidity/contracts/middleware/InterchainAccountRouter.sol index 0c2277648..aa583f341 100644 --- a/solidity/contracts/middleware/InterchainAccountRouter.sol +++ b/solidity/contracts/middleware/InterchainAccountRouter.sol @@ -22,6 +22,7 @@ import {TypeCasts} from "../libs/TypeCasts.sol"; import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol"; import {EnumerableMapExtended} from "../libs/EnumerableMapExtended.sol"; import {Router} from "../client/Router.sol"; +import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; // ============ External Imports ============ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol"; @@ -160,6 +161,12 @@ contract InterchainAccountRouter is Router { } } + function setHook( + address _hook + ) public override onlyContractOrNull(_hook) onlyOwner { + hook = IPostDispatchHook(_hook); + } + // ============ External Functions ============ /** * @notice Dispatches a single remote call to be made by an owner's @@ -307,7 +314,7 @@ contract InterchainAccountRouter is Router { _sender, _ism.bytes32ToAddress() ); - _interchainAccount.multicall(_calls); + _interchainAccount.multicall{value: msg.value}(_calls); } /** @@ -600,7 +607,14 @@ contract InterchainAccountRouter is Router { ) private returns (bytes32) { require(_router != bytes32(0), "no router specified for destination"); emit RemoteCallDispatched(_destination, msg.sender, _router, _ism); - return mailbox.dispatch{value: msg.value}(_destination, _router, _body); + return + mailbox.dispatch{value: msg.value}( + _destination, + _router, + _body, + new bytes(0), + hook + ); } /** @@ -625,7 +639,8 @@ contract InterchainAccountRouter is Router { _destination, _router, _body, - _hookMetadata + _hookMetadata, + hook ); } @@ -665,7 +680,13 @@ contract InterchainAccountRouter is Router { function quoteGasPayment( uint32 _destination ) external view returns (uint256 _gasPayment) { - return _quoteDispatch(_destination, ""); + return + _Router_quoteDispatch( + _destination, + new bytes(0), + new bytes(0), + address(hook) + ); } /** @@ -679,13 +700,12 @@ contract InterchainAccountRouter is Router { bytes calldata _messageBody, uint256 gasLimit ) external view returns (uint256 _gasPayment) { - bytes32 _router = _mustHaveRemoteRouter(_destination); return - mailbox.quoteDispatch( + _Router_quoteDispatch( _destination, - _router, _messageBody, - StandardHookMetadata.overrideGasLimit(gasLimit) + StandardHookMetadata.overrideGasLimit(gasLimit), + address(hook) ); } } diff --git a/solidity/contracts/middleware/README.md b/solidity/contracts/middleware/README.md index 150b240cc..2a95ece96 100644 --- a/solidity/contracts/middleware/README.md +++ b/solidity/contracts/middleware/README.md @@ -3,13 +3,13 @@ ## Interchain Accounts An interchain account is a smart contract that is deployed on a remote chain controlled exclusively by the origin chain's deployer account. -Interchain accounts provide developers with a [transparent multicall API](../OwnableMulticall.sol) to remote smart contracts. +Interchain accounts provide developers with a [transparent multicall API](libs/OwnableMulticall.sol) to remote smart contracts. This avoids the need to deploy application specific smart contracts on remote chains while simultaneously enabling cross-chain composability. See [IBC Interchain Accounts](https://github.com/cosmos/ibc/blob/main/spec/app/ics-027-interchain-accounts/README.md) for the Cosmos ecosystem equivalent. ## Interchain Query System -The interchain query system generalizes view calls to contracts on remote chains. It is a [transparent multicall API](../OwnableMulticall.sol) that can be used to query remote smart contracts. This avoids the need to deploy application specific smart contracts on remote chains while simultaneously enabling cross-chain composability. +The interchain query system generalizes view calls to contracts on remote chains. It is a [transparent multicall API](libs/OwnableMulticall.sol) that can be used to query remote smart contracts. This avoids the need to deploy application specific smart contracts on remote chains while simultaneously enabling cross-chain composability. See [IBC Interchain Query System](https://github.com/cosmos/ibc/tree/main/spec/app/ics-031-crosschain-queries) for the Cosmos ecosystem equivalent. diff --git a/solidity/contracts/middleware/libs/OwnableMulticall.sol b/solidity/contracts/middleware/libs/OwnableMulticall.sol index ad67af036..981d7d35d 100644 --- a/solidity/contracts/middleware/libs/OwnableMulticall.sol +++ b/solidity/contracts/middleware/libs/OwnableMulticall.sol @@ -20,7 +20,9 @@ contract OwnableMulticall { _; } - function multicall(CallLib.Call[] calldata calls) external onlyOwner { + function multicall( + CallLib.Call[] calldata calls + ) external payable onlyOwner { return CallLib.multicall(calls); } diff --git a/solidity/contracts/mock/MockHyperlaneEnvironment.sol b/solidity/contracts/mock/MockHyperlaneEnvironment.sol index 583023cf9..9f28a1c31 100644 --- a/solidity/contracts/mock/MockHyperlaneEnvironment.sol +++ b/solidity/contracts/mock/MockHyperlaneEnvironment.sol @@ -38,8 +38,10 @@ contract MockHyperlaneEnvironment { mailboxes[_destinationDomain] = destinationMailbox; } - function processNextPendingMessage() public { - mailboxes[destinationDomain].processNextInboundMessage(); + function processNextPendingMessage() public payable { + mailboxes[destinationDomain].processNextInboundMessage{ + value: msg.value + }(); } function processNextPendingMessageFromDestination() public { diff --git a/solidity/contracts/mock/MockMailbox.sol b/solidity/contracts/mock/MockMailbox.sol index 68fefc521..c4b4b63e9 100644 --- a/solidity/contracts/mock/MockMailbox.sol +++ b/solidity/contracts/mock/MockMailbox.sol @@ -72,9 +72,14 @@ contract MockMailbox is Mailbox { inboundUnprocessedNonce++; } - function processNextInboundMessage() public { + function processNextInboundMessage() public payable { bytes memory _message = inboundMessages[inboundProcessedNonce]; - Mailbox(address(this)).process("", _message); + Mailbox(address(this)).process{value: msg.value}("", _message); inboundProcessedNonce++; } + + function processInboundMessage(uint32 _nonce) public { + bytes memory _message = inboundMessages[_nonce]; + Mailbox(address(this)).process("", _message); + } } diff --git a/solidity/contracts/test/TestPostDispatchHook.sol b/solidity/contracts/test/TestPostDispatchHook.sol index 34763b89c..84c84f379 100644 --- a/solidity/contracts/test/TestPostDispatchHook.sol +++ b/solidity/contracts/test/TestPostDispatchHook.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; + import {Message} from "../libs/Message.sol"; import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol"; @@ -35,15 +36,15 @@ contract TestPostDispatchHook is AbstractPostDispatchHook { // ============ Internal functions ============ function _postDispatch( - bytes calldata /*metadata*/, - bytes calldata message + bytes calldata, + /*metadata*/ bytes calldata message ) internal override { messageDispatched[message.id()] = true; } function _quoteDispatch( - bytes calldata /*metadata*/, - bytes calldata /*message*/ + bytes calldata, + /*metadata*/ bytes calldata /*message*/ ) internal view override returns (uint256) { return fee; } diff --git a/solidity/contracts/token/HypERC20Collateral.sol b/solidity/contracts/token/HypERC20Collateral.sol index 858a3140b..69b137e75 100644 --- a/solidity/contracts/token/HypERC20Collateral.sol +++ b/solidity/contracts/token/HypERC20Collateral.sol @@ -1,10 +1,25 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.8.0; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ import {TokenRouter} from "./libs/TokenRouter.sol"; import {TokenMessage} from "./libs/TokenMessage.sol"; import {MailboxClient} from "../client/MailboxClient.sol"; +// ============ External Imports ============ +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -22,6 +37,7 @@ contract HypERC20Collateral is TokenRouter { * @param erc20 Address of the token to keep as collateral */ constructor(address erc20, address _mailbox) TokenRouter(_mailbox) { + require(Address.isContract(erc20), "HypERC20Collateral: invalid token"); wrappedToken = IERC20(erc20); } diff --git a/solidity/contracts/token/HypNative.sol b/solidity/contracts/token/HypNative.sol index 5b620d00a..cfb526fa4 100644 --- a/solidity/contracts/token/HypNative.sol +++ b/solidity/contracts/token/HypNative.sol @@ -48,6 +48,30 @@ contract HypNative is TokenRouter { return _transferRemote(_destination, _recipient, _amount, _hookPayment); } + /** + * @inheritdoc TokenRouter + * @dev uses (`msg.value` - `_amount`) as hook payment. + */ + function transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amount, + bytes calldata _hookMetadata, + address _hook + ) external payable virtual override returns (bytes32 messageId) { + require(msg.value >= _amount, "Native: amount exceeds msg.value"); + uint256 _hookPayment = msg.value - _amount; + return + _transferRemote( + _destination, + _recipient, + _amount, + _hookPayment, + _hookMetadata, + _hook + ); + } + function balanceOf( address _account ) external view override returns (uint256) { diff --git a/solidity/contracts/token/README.md b/solidity/contracts/token/README.md index 99edbd11d..1fbc80cf7 100644 --- a/solidity/contracts/token/README.md +++ b/solidity/contracts/token/README.md @@ -45,9 +45,9 @@ graph LR The Token Router contract comes in several flavors and a warp route can be composed of a combination of these flavors. -- [`Native`](./contracts/HypNative.sol) - for warping native assets (e.g. ETH) from the canonical chain -- [`Collateral`](./contracts/HypERC20Collateral.sol) - for warping tokens, ERC20 or ERC721, from the canonical chain -- [`Synthetic`](./contracts/HypERC20.sol) - for representing tokens, Native/ERC20 or ERC721, on a non-canonical chain +- [`Native`](./HypNative.sol) - for warping native assets (e.g. ETH) from the canonical chain +- [`Collateral`](./HypERC20Collateral.sol) - for warping tokens, ERC20 or ERC721, from the canonical chain +- [`Synthetic`](./HypERC20.sol) - for representing tokens, Native/ERC20 or ERC721, on a non-canonical chain ## Interchain Security Models diff --git a/solidity/contracts/token/extensions/HypERC4626.sol b/solidity/contracts/token/extensions/HypERC4626.sol index 2252696fa..7ad9c1842 100644 --- a/solidity/contracts/token/extensions/HypERC4626.sol +++ b/solidity/contracts/token/extensions/HypERC4626.sol @@ -1,25 +1,46 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity >=0.8.0; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ import {IXERC20} from "../interfaces/IXERC20.sol"; import {HypERC20} from "../HypERC20.sol"; -import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {Message} from "../../libs/Message.sol"; import {TokenMessage} from "../libs/TokenMessage.sol"; import {TokenRouter} from "../libs/TokenRouter.sol"; +// ============ External Imports ============ +import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; + /** * @title Hyperlane ERC20 Rebasing Token * @author Abacus Works + * @notice This contract implements a rebasing token that reflects yields from the origin chain */ contract HypERC4626 is HypERC20 { using Math for uint256; using Message for bytes; using TokenMessage for bytes; + event ExchangeRateUpdated(uint256 newExchangeRate, uint32 rateUpdateNonce); + uint256 public constant PRECISION = 1e10; uint32 public immutable collateralDomain; uint256 public exchangeRate; // 1e10 + uint32 public previousNonce; constructor( uint8 _decimals, @@ -31,6 +52,67 @@ contract HypERC4626 is HypERC20 { _disableInitializers(); } + // ============ Public Functions ============ + + /// Override transfer to handle underlying amounts while using shares internally + /// @inheritdoc ERC20Upgradeable + /// @dev the Transfer event emitted from ERC20Upgradeable will be in terms of shares not assets, so it may be misleading + function transfer( + address to, + uint256 amount + ) public virtual override returns (bool) { + _transfer(_msgSender(), to, assetsToShares(amount)); + return true; + } + + /// Override transferFrom to handle underlying amounts while using shares internally + /// @inheritdoc ERC20Upgradeable + function transferFrom( + address sender, + address recipient, + uint256 amount + ) public virtual override returns (bool) { + address spender = _msgSender(); + uint256 shares = assetsToShares(amount); + _spendAllowance(sender, spender, amount); + _transfer(sender, recipient, shares); + return true; + } + + /// Override totalSupply to return the total assets instead of shares. This reflects the actual circulating supply in terms of assets, accounting for rebasing + /// @inheritdoc ERC20Upgradeable + function totalSupply() public view virtual override returns (uint256) { + return sharesToAssets(totalShares()); + } + + /// This returns the balance of the account in terms of assets, accounting for rebasing + /// @inheritdoc ERC20Upgradeable + function balanceOf( + address account + ) public view virtual override returns (uint256) { + return sharesToAssets(shareBalanceOf(account)); + } + + /// This function provides the total supply in terms of shares + function totalShares() public view returns (uint256) { + return super.totalSupply(); + } + + /// This returns the balance of the account in terms of shares + function shareBalanceOf(address account) public view returns (uint256) { + return super.balanceOf(account); + } + + function assetsToShares(uint256 _amount) public view returns (uint256) { + return _amount.mulDiv(PRECISION, exchangeRate); + } + + function sharesToAssets(uint256 _shares) public view returns (uint256) { + return _shares.mulDiv(exchangeRate, PRECISION); + } + + // ============ Internal Functions ============ + /// Override to send shares instead of assets from synthetic /// @inheritdoc TokenRouter function _transferRemote( @@ -60,43 +142,25 @@ contract HypERC4626 is HypERC20 { emit SentTransferRemote(_destination, _recipient, _amountOrId); } + /// override _handle to update exchange rate + /// @inheritdoc TokenRouter function _handle( uint32 _origin, bytes32 _sender, bytes calldata _message ) internal virtual override { if (_origin == collateralDomain) { - exchangeRate = abi.decode(_message.metadata(), (uint256)); + (uint256 newExchangeRate, uint32 rateUpdateNonce) = abi.decode( + _message.metadata(), + (uint256, uint32) + ); + // only update if the nonce is greater than the previous nonce + if (rateUpdateNonce > previousNonce) { + exchangeRate = newExchangeRate; + previousNonce = rateUpdateNonce; + emit ExchangeRateUpdated(exchangeRate, rateUpdateNonce); + } } super._handle(_origin, _sender, _message); } - - // Override to send shares locally instead of assets - function transfer( - address to, - uint256 amount - ) public virtual override returns (bool) { - address owner = _msgSender(); - _transfer(owner, to, assetsToShares(amount)); - return true; - } - - function shareBalanceOf(address account) public view returns (uint256) { - return super.balanceOf(account); - } - - function balanceOf( - address account - ) public view virtual override returns (uint256) { - uint256 _balance = super.balanceOf(account); - return sharesToAssets(_balance); - } - - function assetsToShares(uint256 _amount) public view returns (uint256) { - return _amount.mulDiv(PRECISION, exchangeRate); - } - - function sharesToAssets(uint256 _shares) public view returns (uint256) { - return _shares.mulDiv(exchangeRate, PRECISION); - } } diff --git a/solidity/contracts/token/extensions/HypERC4626Collateral.sol b/solidity/contracts/token/extensions/HypERC4626Collateral.sol index 8a084134c..1f164563e 100644 --- a/solidity/contracts/token/extensions/HypERC4626Collateral.sol +++ b/solidity/contracts/token/extensions/HypERC4626Collateral.sol @@ -1,11 +1,26 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.8.0; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ import {TokenMessage} from "../libs/TokenMessage.sol"; import {HypERC20Collateral} from "../HypERC20Collateral.sol"; import {TypeCasts} from "../../libs/TypeCasts.sol"; +// ============ External Imports ============ +import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; + /** * @title Hyperlane ERC4626 Token Collateral with deposits collateral to a vault * @author Abacus Works @@ -17,9 +32,13 @@ contract HypERC4626Collateral is HypERC20Collateral { // Address of the ERC4626 compatible vault ERC4626 public immutable vault; + // Precision for the exchange rate uint256 public constant PRECISION = 1e10; + // Null recipient for rebase transfer bytes32 public constant NULL_RECIPIENT = 0x0000000000000000000000000000000000000000000000000000000000000001; + // Nonce for the rate update, to ensure sequential updates + uint32 public rateUpdateNonce; constructor( ERC4626 _vault, @@ -47,12 +66,13 @@ contract HypERC4626Collateral is HypERC20Collateral { // Can't override _transferFromSender only because we need to pass shares in the token message _transferFromSender(_amount); uint256 _shares = _depositIntoVault(_amount); - uint256 _exchangeRate = PRECISION.mulDiv( - vault.totalAssets(), - vault.totalSupply(), - Math.Rounding.Down + uint256 _exchangeRate = vault.convertToAssets(PRECISION); + + rateUpdateNonce++; + bytes memory _tokenMetadata = abi.encode( + _exchangeRate, + rateUpdateNonce ); - bytes memory _tokenMetadata = abi.encode(_exchangeRate); bytes memory _tokenMessage = TokenMessage.format( _recipient, @@ -97,15 +117,19 @@ contract HypERC4626Collateral is HypERC20Collateral { * @dev Update the exchange rate on the synthetic token by accounting for additional yield accrued to the underlying vault * @param _destinationDomain domain of the vault */ - function rebase(uint32 _destinationDomain) public payable { + function rebase( + uint32 _destinationDomain, + bytes calldata _hookMetadata, + address _hook + ) public payable { // force a rebase with an empty transfer to 0x1 _transferRemote( _destinationDomain, NULL_RECIPIENT, 0, msg.value, - bytes(""), - address(0) + _hookMetadata, + _hook ); } } diff --git a/solidity/contracts/token/extensions/HypERC4626OwnerCollateral.sol b/solidity/contracts/token/extensions/HypERC4626OwnerCollateral.sol index 1d4d64b0b..42d52f42c 100644 --- a/solidity/contracts/token/extensions/HypERC4626OwnerCollateral.sol +++ b/solidity/contracts/token/extensions/HypERC4626OwnerCollateral.sol @@ -1,9 +1,24 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity >=0.8.0; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +// ============ Internal Imports ============ import {HypERC20Collateral} from "../HypERC20Collateral.sol"; +// ============ External Imports ============ +import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol"; + /** * @title Hyperlane ERC20 Token Collateral with deposits collateral to a vault, the yield goes to the owner * @author ltyu @@ -11,9 +26,12 @@ import {HypERC20Collateral} from "../HypERC20Collateral.sol"; contract HypERC4626OwnerCollateral is HypERC20Collateral { // Address of the ERC4626 compatible vault ERC4626 public immutable vault; - + // standby precision for exchange rate + uint256 public constant PRECISION = 1e10; // Internal balance of total asset deposited uint256 public assetDeposited; + // Nonce for the rate update, to ensure sequential updates (not necessary for Owner variant but for compatibility with HypERC4626) + uint32 public rateUpdateNonce; event ExcessSharesSwept(uint256 amount, uint256 assetsRedeemed); @@ -40,8 +58,11 @@ contract HypERC4626OwnerCollateral is HypERC20Collateral { function _transferFromSender( uint256 _amount ) internal override returns (bytes memory metadata) { - metadata = super._transferFromSender(_amount); + super._transferFromSender(_amount); _depositIntoVault(_amount); + rateUpdateNonce++; + + return abi.encode(PRECISION, rateUpdateNonce); } /** diff --git a/solidity/contracts/token/extensions/HypNativeScaled.sol b/solidity/contracts/token/extensions/HypNativeScaled.sol index 88ccaa468..ad129fce8 100644 --- a/solidity/contracts/token/extensions/HypNativeScaled.sol +++ b/solidity/contracts/token/extensions/HypNativeScaled.sol @@ -38,6 +38,31 @@ contract HypNativeScaled is HypNative { ); } + /** + * @inheritdoc TokenRouter + * @dev uses (`msg.value` - `_amount`) as hook payment. + */ + function transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amount, + bytes calldata _hookMetadata, + address _hook + ) external payable override returns (bytes32 messageId) { + require(msg.value >= _amount, "Native: amount exceeds msg.value"); + uint256 _hookPayment = msg.value - _amount; + uint256 _scaledAmount = _amount / scale; + return + _transferRemote( + _destination, + _recipient, + _scaledAmount, + _hookPayment, + _hookMetadata, + _hook + ); + } + /** * @dev Sends scaled `_amount` (multiplied by `scale`) to `_recipient`. * @inheritdoc TokenRouter diff --git a/solidity/contracts/token/extensions/WHypERC4626.sol b/solidity/contracts/token/extensions/WHypERC4626.sol new file mode 100644 index 000000000..08cb59732 --- /dev/null +++ b/solidity/contracts/token/extensions/WHypERC4626.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import {HypERC4626} from "./HypERC4626.sol"; +import {PackageVersioned} from "../../PackageVersioned.sol"; + +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + +/** + * @title WHypERC4626 + * @author Abacus Works + * @notice A wrapper for HypERC4626 that allows for wrapping and unwrapping of underlying rebasing tokens + */ +contract WHypERC4626 is ERC20, PackageVersioned { + HypERC4626 public immutable underlying; + + constructor( + HypERC4626 _underlying, + string memory name, + string memory symbol + ) ERC20(name, symbol) { + underlying = _underlying; + } + + /* + * @notice Wraps an amount of underlying tokens into wrapped tokens + * @param _underlyingAmount The amount of underlying tokens to wrap + * @return The amount of wrapped tokens + */ + function wrap(uint256 _underlyingAmount) external returns (uint256) { + require( + _underlyingAmount > 0, + "WHypERC4626: wrap amount must be greater than 0" + ); + uint256 wrappedAmount = underlying.assetsToShares(_underlyingAmount); + _mint(msg.sender, wrappedAmount); + underlying.transferFrom(msg.sender, address(this), _underlyingAmount); + return wrappedAmount; + } + + /* + * @notice Unwraps an amount of wrapped tokens into underlying tokens + * @param _wrappedAmount The amount of wrapped tokens to unwrap + * @return The amount of underlying tokens + */ + function unwrap(uint256 _wrappedAmount) external returns (uint256) { + require( + _wrappedAmount > 0, + "WHypERC4626: unwrap amount must be greater than 0" + ); + uint256 underlyingAmount = underlying.sharesToAssets(_wrappedAmount); + _burn(msg.sender, _wrappedAmount); + underlying.transfer(msg.sender, underlyingAmount); + return underlyingAmount; + } + + /* + * @notice Gets the amount of wrapped tokens for a given amount of underlying tokens + * @param _underlyingAmount The amount of underlying tokens + * @return The amount of wrapped tokens + */ + function getWrappedAmount( + uint256 _underlyingAmount + ) external view returns (uint256) { + return underlying.assetsToShares(_underlyingAmount); + } + + /* + * @notice Gets the amount of underlying tokens for a given amount of wrapped tokens + * @param _wrappedAmount The amount of wrapped tokens + * @return The amount of underlying tokens + */ + function getUnderlyingAmount( + uint256 _wrappedAmount + ) external view returns (uint256) { + return underlying.sharesToAssets(_wrappedAmount); + } + + /* + * @notice Gets the amount of wrapped tokens for 1 unit of underlying tokens + * @return The amount of wrapped tokens + */ + function wrappedPerUnderlying() external view returns (uint256) { + return underlying.assetsToShares(1 * 10 ** underlying.decimals()); + } + + /* + * @notice Gets the amount of underlying tokens for 1 unit of wrapped tokens + * @return The amount of underlying tokens + */ + function underlyingPerWrapped() external view returns (uint256) { + return underlying.sharesToAssets(1 * 10 ** decimals()); + } + + /* + * @notice Gets the decimals of the wrapped token + * @return The decimals of the wrapped token + */ + function decimals() public view override returns (uint8) { + return underlying.decimals(); + } +} diff --git a/solidity/package.json b/solidity/package.json index cc55e943b..5f7eef442 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "5.1.0", + "version": "5.6.1", "dependencies": { "@arbitrum/nitro-contracts": "^1.2.1", "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "5.1.0", + "@hyperlane-xyz/utils": "5.6.2", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", @@ -19,7 +19,7 @@ "@typechain/ethers-v6": "^0.5.1", "@typechain/hardhat": "^9.1.0", "@types/node": "^18.14.5", - "chai": "^4.3.6", + "chai": "4.5.0", "ethereum-waffle": "^4.0.10", "ethers": "^5.7.2", "hardhat": "^2.22.2", @@ -33,6 +33,7 @@ "solidity-coverage": "^0.8.3", "ts-generator": "^0.1.1", "ts-node": "^10.8.0", + "tsx": "^4.19.1", "typechain": "patch:typechain@npm%3A8.3.2#~/.yarn/patches/typechain-npm-8.3.2-b02e27439e.patch", "typescript": "5.3.3" }, @@ -63,7 +64,7 @@ ], "license": "Apache-2.0", "scripts": { - "build": "yarn hardhat-esm compile && tsc && ./exportBuildArtifact.sh", + "build": "yarn version:update && yarn hardhat-esm compile && tsc && ./exportBuildArtifact.sh", "lint": "solhint contracts/**/*.sol", "clean": "yarn hardhat-esm clean && rm -rf ./dist ./cache ./types ./coverage ./out ./forge-cache ./fixtures", "coverage": "yarn fixtures && ./coverage.sh", @@ -71,14 +72,17 @@ "fixtures": "mkdir -p ./fixtures/aggregation ./fixtures/multisig", "hardhat-esm": "NODE_OPTIONS='--experimental-loader ts-node/esm/transpile-only --no-warnings=ExperimentalWarning' hardhat --config hardhat.config.cts", "prettier": "prettier --write ./contracts ./test", - "test": "yarn hardhat-esm test && yarn test:forge", + "test": "yarn version:exhaustive && yarn hardhat-esm test && yarn test:forge", "test:hardhat": "yarn hardhat-esm test", "test:forge": "yarn fixtures && forge test -vvv", - "test:ci": "yarn test:hardhat && yarn test:forge --no-match-test testFork", + "test:ci": "yarn version:changed && yarn test:hardhat && yarn test:forge --no-match-test testFork", "gas": "forge snapshot", "gas-ci": "yarn gas --check --tolerance 2 || (echo 'Manually update gas snapshot' && exit 1)", "slither": "slither .", - "storage": "./storage.sh" + "storage": "./storage.sh", + "version:update": "sh ./bytecodeversion.sh", + "version:changed": "yarn version:update && git diff --exit-code", + "version:exhaustive": "yarn tsx ./test/exhaustiveversion.test.ts" }, "peerDependencies": { "@ethersproject/abi": "*", diff --git a/solidity/script/DeployArbHook.s.sol b/solidity/script/DeployArbHook.s.sol deleted file mode 100644 index 89f35f8d4..000000000 --- a/solidity/script/DeployArbHook.s.sol +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity >=0.8.0; - -import "forge-std/Script.sol"; - -import {Mailbox} from "../../contracts/Mailbox.sol"; -import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; -import {ArbL2ToL1Hook} from "../../contracts/hooks/ArbL2ToL1Hook.sol"; -import {ArbL2ToL1Ism} from "../../contracts/isms/hook/ArbL2ToL1Ism.sol"; -import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; -import {TestIsm} from "../../contracts/test/TestIsm.sol"; - -contract DeployArbHook is Script { - uint256 deployerPrivateKey; - - ArbL2ToL1Hook hook; - ArbL2ToL1Ism ism; - - uint32 constant L1_DOMAIN = 11155111; - address constant L1_MAILBOX = 0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766; - address constant L1_BRIDGE = 0x38f918D0E9F1b721EDaA41302E399fa1B79333a9; - address constant L1_ISM = 0x096A1c034c7Ad113B6dB786b7BA852cB67025458; // placeholder - bytes32 TEST_RECIPIENT = - 0x000000000000000000000000155b1cd2f7cbc58d403b9be341fab6cd77425175; // placeholder - - address constant ARBSYS = 0x0000000000000000000000000000000000000064; - address constant L2_MAILBOX = 0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8; - address constant L2_HOOK = 0xd9d99AC1C645563576b8Df22cBebFC23FB60Ec73; // placeholder - - function deployIsm() external { - deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - ism = new ArbL2ToL1Ism(L1_BRIDGE); - - TestRecipient testRecipient = new TestRecipient(); - testRecipient.setInterchainSecurityModule(address(ism)); - - vm.stopBroadcast(); - } - - function deployHook() external { - deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - hook = new ArbL2ToL1Hook( - L2_MAILBOX, - L1_DOMAIN, - TypeCasts.addressToBytes32(L1_ISM), - ARBSYS, - 200_000 // estimated gas amount used for verify - ); - - vm.stopBroadcast(); - } - - function deployTestRecipient() external { - deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - TestIsm noopIsm = new TestIsm(); - noopIsm.setVerify(true); - TestRecipient testRecipient = new TestRecipient(); - testRecipient.setInterchainSecurityModule(address(noopIsm)); - - console.log("TestRecipient address: %s", address(testRecipient)); - - vm.stopBroadcast(); - } - - function setAuthorizedHook() external { - deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); - - vm.startBroadcast(deployerPrivateKey); - - ism = ArbL2ToL1Ism(L1_ISM); - ism.setAuthorizedHook(TypeCasts.addressToBytes32(L2_HOOK)); - - vm.stopBroadcast(); - } -} diff --git a/solidity/test/GasRouter.t.sol b/solidity/test/GasRouter.t.sol index aaf73b2a3..becd43cf3 100644 --- a/solidity/test/GasRouter.t.sol +++ b/solidity/test/GasRouter.t.sol @@ -68,6 +68,8 @@ contract GasRouterTest is Test { } function testSetDestinationGas(uint256 gas) public { + vm.expectEmit(true, true, true, true); + emit GasRouter.GasSet(originDomain, gas); setDestinationGas(remoteRouter, originDomain, gas); assertEq(remoteRouter.destinationGas(originDomain), gas); diff --git a/solidity/test/InterchainAccountRouter.t.sol b/solidity/test/InterchainAccountRouter.t.sol index fecfe96a4..65d804caa 100644 --- a/solidity/test/InterchainAccountRouter.t.sol +++ b/solidity/test/InterchainAccountRouter.t.sol @@ -13,12 +13,16 @@ import {TestInterchainGasPaymaster} from "../contracts/test/TestInterchainGasPay import {IPostDispatchHook} from "../contracts/interfaces/hooks/IPostDispatchHook.sol"; import {CallLib, OwnableMulticall, InterchainAccountRouter} from "../contracts/middleware/InterchainAccountRouter.sol"; import {InterchainAccountIsm} from "../contracts/isms/routing/InterchainAccountIsm.sol"; +import {AbstractPostDispatchHook} from "../contracts/hooks/libs/AbstractPostDispatchHook.sol"; +import {TestPostDispatchHook} from "../contracts/test/TestPostDispatchHook.sol"; contract Callable { mapping(address => bytes32) public data; + mapping(address => uint256) public value; - function set(bytes32 _data) external { + function set(bytes32 _data) external payable { data[msg.sender] = _data; + value[msg.sender] = msg.value; } } @@ -106,10 +110,11 @@ contract InterchainAccountRouterTestBase is Test { address owner = address(this); originIcaRouter = deployProxiedIcaRouter( environment.mailboxes(origin), - environment.igps(destination), + environment.igps(origin), icaIsm, owner ); + destinationIcaRouter = deployProxiedIcaRouter( environment.mailboxes(destination), environment.igps(destination), @@ -298,13 +303,14 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { } function getCalls( - bytes32 data + bytes32 data, + uint256 value ) private view returns (CallLib.Call[] memory) { vm.assume(data != bytes32(0)); CallLib.Call memory call = CallLib.Call( TypeCasts.addressToBytes32(address(target)), - 0, + value, abi.encodeCall(target.set, (data)) ); CallLib.Call[] memory calls = new CallLib.Call[](1); @@ -312,8 +318,10 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { return calls; } - function assertRemoteCallReceived(bytes32 data) private { + function assertRemoteCallReceived(bytes32 data, uint256 value) private { assertEq(target.data(address(this)), bytes32(0)); + assertEq(target.value(address(this)), 0); + vm.expectEmit(true, true, false, true, address(destinationIcaRouter)); emit InterchainAccountCreated( origin, @@ -321,8 +329,11 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { TypeCasts.bytes32ToAddress(ismOverride), address(ica) ); - environment.processNextPendingMessage(); + vm.deal(address(this), value); + environment.processNextPendingMessage{value: value}(); + assertEq(target.data(address(ica)), data); + assertEq(target.value(address(ica)), value); } function assertIgpPayment( @@ -384,7 +395,29 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { ); } - function testFuzz_singleCallRemoteWithDefault(bytes32 data) public { + function test_quoteDispatch_differentHook() public { + // arrange + TestPostDispatchHook testHook = new TestPostDispatchHook(); + originIcaRouter = deployProxiedIcaRouter( + environment.mailboxes(origin), + testHook, + icaIsm, + address(this) + ); + originIcaRouter.enrollRemoteRouterAndIsm( + destination, + routerOverride, + ismOverride + ); + + // assert + assertEq(originIcaRouter.quoteGasPayment(destination), 0); + } + + function testFuzz_singleCallRemoteWithDefault( + bytes32 data, + uint256 value + ) public { // arrange originIcaRouter.enrollRemoteRouterAndIsm( destination, @@ -394,7 +427,7 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { uint256 balanceBefore = address(this).balance; // act - CallLib.Call[] memory calls = getCalls(data); + CallLib.Call[] memory calls = getCalls(data, value); originIcaRouter.callRemote{value: gasPaymentQuote}( destination, TypeCasts.bytes32ToAddress(calls[0].to), @@ -404,11 +437,15 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { // assert uint256 balanceAfter = address(this).balance; - assertRemoteCallReceived(data); assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); + + assertRemoteCallReceived(data, value); } - function testFuzz_callRemoteWithDefault(bytes32 data) public { + function testFuzz_callRemoteWithDefault( + bytes32 data, + uint256 value + ) public { // arrange originIcaRouter.enrollRemoteRouterAndIsm( destination, @@ -420,16 +457,48 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { // act originIcaRouter.callRemote{value: gasPaymentQuote}( destination, - getCalls(data) + getCalls(data, value) ); // assert uint256 balanceAfter = address(this).balance; - assertRemoteCallReceived(data); + assertRemoteCallReceived(data, value); assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); } - function testFuzz_overrideAndCallRemote(bytes32 data) public { + function testFuzz_callRemoteWithDefault_differentHook( + bytes32 data, + uint256 value + ) public { + // arrange + TestPostDispatchHook testHook = new TestPostDispatchHook(); + originIcaRouter = deployProxiedIcaRouter( + environment.mailboxes(origin), + testHook, + icaIsm, + address(this) + ); + originIcaRouter.enrollRemoteRouterAndIsm( + destination, + routerOverride, + ismOverride + ); + + // assert + vm.expectCall( + address(testHook), + 0, + abi.encodePacked(AbstractPostDispatchHook.postDispatch.selector) + ); + + // act + originIcaRouter.callRemote(destination, getCalls(data, value)); + } + + function testFuzz_overrideAndCallRemote( + bytes32 data, + uint256 value + ) public { // arrange originIcaRouter.enrollRemoteRouterAndIsm( destination, @@ -441,20 +510,21 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { // act originIcaRouter.callRemote{value: gasPaymentQuote}( destination, - getCalls(data) + getCalls(data, value) ); // assert uint256 balanceAfter = address(this).balance; - assertRemoteCallReceived(data); + assertRemoteCallReceived(data, value); assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); } function testFuzz_callRemoteWithoutDefaults_revert_noRouter( - bytes32 data + bytes32 data, + uint256 value ) public { // assert error - CallLib.Call[] memory calls = getCalls(data); + CallLib.Call[] memory calls = getCalls(data, value); vm.expectRevert(bytes("no router specified for destination")); originIcaRouter.callRemote(destination, calls); } @@ -462,7 +532,8 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { function testFuzz_customMetadata_forIgp( uint64 gasLimit, uint64 overpayment, - bytes32 data + bytes32 data, + uint256 value ) public { // arrange bytes memory metadata = StandardHookMetadata.formatMetadata( @@ -481,20 +552,21 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { // act originIcaRouter.callRemote{ value: gasLimit * igp.gasPrice() + overpayment - }(destination, getCalls(data), metadata); + }(destination, getCalls(data, value), metadata); // assert uint256 balanceAfter = address(this).balance; - assertRemoteCallReceived(data); + assertRemoteCallReceived(data, value); assertIgpPayment(balanceBefore, balanceAfter, gasLimit); } function testFuzz_customMetadata_reverts_underpayment( uint64 gasLimit, uint64 payment, - bytes32 data + bytes32 data, + uint256 value ) public { - CallLib.Call[] memory calls = getCalls(data); + CallLib.Call[] memory calls = getCalls(data, value); vm.assume(payment < gasLimit * igp.gasPrice()); // arrange bytes memory metadata = StandardHookMetadata.formatMetadata( @@ -518,7 +590,10 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { ); } - function testFuzz_callRemoteWithOverrides_default(bytes32 data) public { + function testFuzz_callRemoteWithOverrides_default( + bytes32 data, + uint256 value + ) public { // arrange uint256 balanceBefore = address(this).balance; @@ -527,18 +602,20 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { destination, routerOverride, ismOverride, - getCalls(data) + getCalls(data, value) ); // assert uint256 balanceAfter = address(this).balance; - assertRemoteCallReceived(data); + assertRemoteCallReceived(data, value); assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); + assertEq(address(originIcaRouter.hook()), address(0)); } function testFuzz_callRemoteWithOverrides_metadata( uint64 gasLimit, - bytes32 data + bytes32 data, + uint256 value ) public { // arrange bytes memory metadata = StandardHookMetadata.formatMetadata( @@ -552,15 +629,56 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { // act originIcaRouter.callRemoteWithOverrides{ value: gasLimit * igp.gasPrice() - }(destination, routerOverride, ismOverride, getCalls(data), metadata); + }( + destination, + routerOverride, + ismOverride, + getCalls(data, value), + metadata + ); // assert uint256 balanceAfter = address(this).balance; - assertRemoteCallReceived(data); + assertRemoteCallReceived(data, value); assertIgpPayment(balanceBefore, balanceAfter, gasLimit); } - function testFuzz_callRemoteWithFailingIsmOverride(bytes32 data) public { + function testFuzz_callRemoteWithOverrides_withHook( + bytes32 data, + uint256 value + ) public { + TestPostDispatchHook testHook = new TestPostDispatchHook(); + + originIcaRouter = deployProxiedIcaRouter( + environment.mailboxes(origin), + testHook, + icaIsm, + address(this) + ); + originIcaRouter.enrollRemoteRouterAndIsm( + destination, + routerOverride, + ismOverride + ); + + vm.expectCall( + address(testHook), + 0, + abi.encodePacked(AbstractPostDispatchHook.postDispatch.selector) + ); + originIcaRouter.callRemoteWithOverrides( + destination, + routerOverride, + ismOverride, + getCalls(data, value), + new bytes(0) + ); + } + + function testFuzz_callRemoteWithFailingIsmOverride( + bytes32 data, + uint256 value + ) public { // arrange string memory failureMessage = "failing ism"; bytes32 failingIsm = TypeCasts.addressToBytes32( @@ -572,7 +690,7 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { destination, routerOverride, failingIsm, - getCalls(data), + getCalls(data, value), "" ); @@ -581,7 +699,10 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { environment.processNextPendingMessage(); } - function testFuzz_callRemoteWithFailingDefaultIsm(bytes32 data) public { + function testFuzz_callRemoteWithFailingDefaultIsm( + bytes32 data, + uint256 value + ) public { // arrange string memory failureMessage = "failing ism"; FailingIsm failingIsm = new FailingIsm(failureMessage); @@ -592,7 +713,7 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { destination, routerOverride, bytes32(0), - getCalls(data), + getCalls(data, value), "" ); @@ -601,7 +722,10 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { environment.processNextPendingMessage(); } - function testFuzz_getLocalInterchainAccount(bytes32 data) public { + function testFuzz_getLocalInterchainAccount( + bytes32 data, + uint256 value + ) public { // check OwnableMulticall destinationIca = destinationIcaRouter .getLocalInterchainAccount( @@ -628,12 +752,12 @@ contract InterchainAccountRouterTest is InterchainAccountRouterTestBase { destination, routerOverride, ismOverride, - getCalls(data), + getCalls(data, value), "" ); // recheck - assertRemoteCallReceived(data); + assertRemoteCallReceived(data, value); assert(address(destinationIca).code.length != 0); } diff --git a/solidity/test/exhaustiveversion.test.ts b/solidity/test/exhaustiveversion.test.ts new file mode 100644 index 000000000..e43909927 --- /dev/null +++ b/solidity/test/exhaustiveversion.test.ts @@ -0,0 +1,63 @@ +import { CompilerOutputContract } from 'hardhat/types'; +import { readFile, readdir } from 'node:fs/promises'; +import { basename, join } from 'node:path'; + +const EXCLUDE_PATTERNS: RegExp[] = [ + /\.dbg/g, + /interfaces\//g, + /libs\//g, + /Abstract/g, + /Test/g, + /Mock/g, + /Versioned/g, + // also abstract + /ECDSAServiceManagerBase/g, + /ECDSAStakeRegistryStorage/g, +]; +const REQUIRED_METHOD = 'PACKAGE_VERSION'; + +// https://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search +const walk = async (dirPath) => + Promise.all( + await readdir(dirPath, { withFileTypes: true }).then((entries) => + entries.map((entry) => { + const childPath = join(dirPath, entry.name); + return entry.isDirectory() ? walk(childPath) : childPath; + }), + ), + ); + +const artifacts = (await walk('artifacts/contracts')).flat( + Number.POSITIVE_INFINITY, +); + +const filtered = artifacts.filter((path: string) => + EXCLUDE_PATTERNS.every((excluded) => path.match(excluded) === null), +); + +const results = await Promise.all( + filtered.map(async (path) => { + const content = await readFile(path, 'utf-8'); + const compilerOutput: CompilerOutputContract = JSON.parse(content); + return [ + path, + compilerOutput.abi && + compilerOutput.abi.some((elem) => elem.name === REQUIRED_METHOD), + ]; + }), +); + +const missing = results.filter(([, included]) => !included); + +if (missing.length > 0) { + console.error( + `Missing ${REQUIRED_METHOD} method in the following contracts:`, + ); + const contracts = missing.map(([path]) => + basename(path).replace('.json', ''), + ); + console.error(contracts.map((contract) => ` - ${contract}`).join('\n')); + process.exit(1); +} + +console.log('All contracts have the required method'); diff --git a/solidity/test/hooks/ProtocolFee.t.sol b/solidity/test/hooks/ProtocolFee.t.sol index df0d3471c..161d9e9c8 100644 --- a/solidity/test/hooks/ProtocolFee.t.sol +++ b/solidity/test/hooks/ProtocolFee.t.sol @@ -42,6 +42,9 @@ contract ProtocolFeeTest is Test { function testSetProtocolFee(uint256 fee) public { fee = bound(fee, 0, fees.MAX_PROTOCOL_FEE()); + + vm.expectEmit(true, true, true, true); + emit ProtocolFee.ProtocolFeeSet(fee); fees.setProtocolFee(fee); assertEq(fees.protocolFee(), fee); } @@ -69,6 +72,14 @@ contract ProtocolFeeTest is Test { assertEq(fees.protocolFee(), FEE); } + function testSetBeneficiary(address beneficiary) public { + vm.assume(beneficiary != address(0)); + vm.expectEmit(true, true, true, true); + emit ProtocolFee.BeneficiarySet(beneficiary); + fees.setBeneficiary(beneficiary); + assertEq(fees.beneficiary(), beneficiary); + } + function testSetBeneficiary_revertWhen_notOwner() public { vm.prank(charlie); diff --git a/solidity/test/hooks/layerzero/LayerZeroV2Hook.t.sol b/solidity/test/hooks/layerzero/LayerZeroV2Hook.t.sol index 94225d287..8b5d6004c 100644 --- a/solidity/test/hooks/layerzero/LayerZeroV2Hook.t.sol +++ b/solidity/test/hooks/layerzero/LayerZeroV2Hook.t.sol @@ -147,15 +147,7 @@ contract LayerZeroV2HookTest is Test { vm.assume(balance < nativeFee - 1); vm.deal(address(this), balance); - vm.expectRevert( - abi.encodeWithSelector( - Errors.InsufficientFee.selector, - 100, - balance, - 0, - 0 - ) - ); + vm.expectRevert(); // OutOfFunds mailbox.dispatch{value: balance}( HYPERLANE_DEST_DOMAIN, address(crossChainCounterApp).addressToBytes32(), diff --git a/solidity/test/isms/ArbL2ToL1Ism.t.sol b/solidity/test/isms/ArbL2ToL1Ism.t.sol index f34c1a74e..ec0fd9392 100644 --- a/solidity/test/isms/ArbL2ToL1Ism.t.sol +++ b/solidity/test/isms/ArbL2ToL1Ism.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT or Apache-2.0 pragma solidity ^0.8.13; -import {Test} from "forge-std/Test.sol"; - import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; import {MessageUtils} from "./IsmTestUtils.sol"; import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; @@ -13,13 +11,10 @@ import {ArbL2ToL1Hook} from "../../contracts/hooks/ArbL2ToL1Hook.sol"; import {ArbL2ToL1Ism} from "../../contracts/isms/hook/ArbL2ToL1Ism.sol"; import {MockArbBridge, MockArbSys} from "../../contracts/mock/MockArbBridge.sol"; import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; +import {ExternalBridgeTest} from "./ExternalBridgeTest.sol"; +import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol"; -contract ArbL2ToL1IsmTest is Test { - uint8 internal constant HYPERLANE_VERSION = 1; - uint32 internal constant MAINNET_DOMAIN = 1; - uint32 internal constant ARBITRUM_DOMAIN = 42161; - uint256 internal constant GAS_QUOTE = 120_000; - +contract ArbL2ToL1IsmTest is ExternalBridgeTest { uint256 internal constant MOCK_LEAF_INDEX = 40160; uint256 internal constant MOCK_L2_BLOCK = 54220000; uint256 internal constant MOCK_L1_BLOCK = 6098300; @@ -28,26 +23,14 @@ contract ArbL2ToL1IsmTest is Test { 0x0000000000000000000000000000000000000064; MockArbBridge internal arbBridge; - TestMailbox public l2Mailbox; - ArbL2ToL1Hook public hook; - ArbL2ToL1Ism public ism; + TestInterchainGasPaymaster internal mockOverheadIgp; - TestRecipient internal testRecipient; - bytes internal testMessage = - abi.encodePacked("Hello from the other chain!"); - bytes internal encodedMessage; - bytes internal testMetadata = - StandardHookMetadata.overrideRefundAddress(address(this)); - bytes32 internal messageId; - - function setUp() public { + function setUp() public override { // Arbitrum bridge mock setup vm.etch(L2_ARBSYS_ADDRESS, address(new MockArbSys()).code); - testRecipient = new TestRecipient(); - - encodedMessage = _encodeTestMessage(); - messageId = Message.id(encodedMessage); + deployAll(); + super.setUp(); } /////////////////////////////////////////////////////////////////// @@ -55,19 +38,19 @@ contract ArbL2ToL1IsmTest is Test { /////////////////////////////////////////////////////////////////// function deployHook() public { - l2Mailbox = new TestMailbox(ARBITRUM_DOMAIN); + originMailbox = new TestMailbox(ORIGIN_DOMAIN); + mockOverheadIgp = new TestInterchainGasPaymaster(); hook = new ArbL2ToL1Hook( - address(l2Mailbox), - MAINNET_DOMAIN, + address(originMailbox), + DESTINATION_DOMAIN, TypeCasts.addressToBytes32(address(ism)), L2_ARBSYS_ADDRESS, - GAS_QUOTE + address(mockOverheadIgp) ); } function deployIsm() public { arbBridge = new MockArbBridge(); - ism = new ArbL2ToL1Ism(address(arbBridge)); } @@ -75,196 +58,53 @@ contract ArbL2ToL1IsmTest is Test { deployIsm(); deployHook(); + arbBridge.setL2ToL1Sender(address(hook)); ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); } - function test_postDispatch() public { - deployAll(); + function test_postDispatch_childHook() public { + bytes memory encodedHookData = _encodeHookData(messageId, 0); + originMailbox.updateLatestDispatchedId(messageId); + _expectOriginExternalBridgeCall(encodedHookData); - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) + bytes memory igpMetadata = StandardHookMetadata.overrideGasLimit( + 78_000 ); - l2Mailbox.updateLatestDispatchedId(messageId); + uint256 quote = hook.quoteDispatch(igpMetadata, encodedMessage); + assertEq(quote, mockOverheadIgp.quoteGasPayment(ORIGIN_DOMAIN, 78_000)); + hook.postDispatch{value: quote}(igpMetadata, encodedMessage); + } + + /* ============ helper functions ============ */ + function _expectOriginExternalBridgeCall( + bytes memory _encodedHookData + ) internal override { vm.expectCall( L2_ARBSYS_ADDRESS, abi.encodeCall( MockArbSys.sendTxToL1, - (address(ism), encodedHookData) + (address(ism), _encodedHookData) ) ); - hook.postDispatch(testMetadata, encodedMessage); - } - - function testFork_postDispatch_revertWhen_chainIDNotSupported() public { - deployAll(); - - bytes memory message = MessageUtils.formatMessage( - 0, - uint32(0), - ARBITRUM_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - 2, // wrong domain - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); - - l2Mailbox.updateLatestDispatchedId(Message.id(message)); - vm.expectRevert( - "AbstractMessageIdAuthHook: invalid destination domain" - ); - hook.postDispatch(testMetadata, message); - } - - function test_postDispatch_revertWhen_notLastDispatchedMessage() public { - deployAll(); - - vm.expectRevert( - "AbstractMessageIdAuthHook: message not latest dispatched" - ); - hook.postDispatch(testMetadata, encodedMessage); - } - - function test_verify_outboxCall() public { - deployAll(); - - bytes memory encodedOutboxTxMetadata = _encodeOutboxTx( - address(hook), - address(ism), - messageId, - 1 ether - ); - - vm.deal(address(arbBridge), 1 ether); - arbBridge.setL2ToL1Sender(address(hook)); - assertTrue(ism.verify(encodedOutboxTxMetadata, encodedMessage)); - assertEq(address(testRecipient).balance, 1 ether); } - function test_verify_statefulVerify() public { - deployAll(); - - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) - ); - - arbBridge.setL2ToL1Sender(address(hook)); - arbBridge.executeTransaction{value: 1 ether}( - new bytes32[](0), - MOCK_LEAF_INDEX, - address(hook), - address(ism), - MOCK_L2_BLOCK, - MOCK_L1_BLOCK, - block.timestamp, - 1 ether, - encodedHookData - ); - - vm.etch(address(arbBridge), new bytes(0)); // this is a way to test that the arbBridge isn't called again - assertTrue(ism.verify(new bytes(0), encodedMessage)); - assertEq(address(testRecipient).balance, 1 ether); + function _encodeExternalDestinationBridgeCall( + address _from, + address _to, + uint256 _msgValue, + bytes32 _messageId + ) internal override returns (bytes memory) { + vm.deal(address(arbBridge), _msgValue); + return _encodeOutboxTx(_from, _to, _messageId, _msgValue); } - function test_verify_statefulAndOutbox() public { - deployAll(); - - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) - ); - - arbBridge.setL2ToL1Sender(address(hook)); - arbBridge.executeTransaction{value: 1 ether}( - new bytes32[](0), - MOCK_LEAF_INDEX, - address(hook), - address(ism), - MOCK_L2_BLOCK, - MOCK_L1_BLOCK, - block.timestamp, - 1 ether, - encodedHookData - ); - - bytes memory encodedOutboxTxMetadata = _encodeOutboxTx( - address(hook), - address(ism), - messageId, - 1 ether - ); - - vm.etch(address(arbBridge), new bytes(0)); // this is a way to test that the arbBridge isn't called again - assertTrue(ism.verify(encodedOutboxTxMetadata, encodedMessage)); - assertEq(address(testRecipient).balance, 1 ether); - } - - function test_verify_revertsWhen_noStatefulOrOutbox() public { - deployAll(); - - vm.expectRevert(); - ism.verify(new bytes(0), encodedMessage); - } - - function test_verify_revertsWhen_notAuthorizedHook() public { - deployAll(); - - bytes memory encodedOutboxTxMetadata = _encodeOutboxTx( - address(this), - address(ism), - messageId, - 0 - ); - - arbBridge.setL2ToL1Sender(address(this)); - - vm.expectRevert("ArbL2ToL1Ism: l2Sender != authorizedHook"); - ism.verify(encodedOutboxTxMetadata, encodedMessage); - } - - function test_verify_revertsWhen_invalidIsm() public { - deployAll(); - - bytes memory encodedOutboxTxMetadata = _encodeOutboxTx( - address(hook), - address(this), - messageId, - 0 - ); - - arbBridge.setL2ToL1Sender(address(hook)); - - vm.expectRevert(); // BridgeCallFailed() - ism.verify(encodedOutboxTxMetadata, encodedMessage); - } - - function test_verify_revertsWhen_incorrectMessageId() public { - deployAll(); - - bytes32 incorrectMessageId = keccak256("incorrect message id"); - - bytes memory encodedOutboxTxMetadata = _encodeOutboxTx( - address(hook), - address(ism), - incorrectMessageId, - 0 - ); - - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (incorrectMessageId) - ); - - arbBridge.setL2ToL1Sender(address(hook)); - - // through outbox call - vm.expectRevert("ArbL2ToL1Ism: invalid message id"); - ism.verify(encodedOutboxTxMetadata, encodedMessage); - - // through statefulVerify + function _externalBridgeDestinationCall( + bytes memory _encodedHookData, + uint256 _msgValue + ) internal override { + vm.deal(address(arbBridge), _msgValue); arbBridge.executeTransaction( new bytes32[](0), MOCK_LEAF_INDEX, @@ -273,16 +113,17 @@ contract ArbL2ToL1IsmTest is Test { MOCK_L2_BLOCK, MOCK_L1_BLOCK, block.timestamp, - 0, - encodedHookData + _msgValue, + _encodedHookData ); - - vm.etch(address(arbBridge), new bytes(0)); // to stop the outbox route - vm.expectRevert(); - assertFalse(ism.verify(new bytes(0), encodedMessage)); } - /* ============ helper functions ============ */ + function _setExternalOriginSender( + address _sender + ) internal override returns (bytes memory unauthorizedHookErrorMsg) { + arbBridge.setL2ToL1Sender(_sender); + return "ArbL2ToL1Ism: l2Sender != authorizedHook"; + } function _encodeOutboxTx( address _hook, @@ -290,10 +131,7 @@ contract ArbL2ToL1IsmTest is Test { bytes32 _messageId, uint256 _value ) internal view returns (bytes memory) { - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (_messageId) - ); + bytes memory encodedHookData = _encodeHookData(_messageId, _value); bytes32[] memory proof = new bytes32[](16); return @@ -309,17 +147,4 @@ contract ArbL2ToL1IsmTest is Test { encodedHookData ); } - - function _encodeTestMessage() internal view returns (bytes memory) { - return - MessageUtils.formatMessage( - HYPERLANE_VERSION, - uint32(0), - ARBITRUM_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - MAINNET_DOMAIN, - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); - } } diff --git a/solidity/test/isms/ERC5164ISM.t.sol b/solidity/test/isms/ERC5164ISM.t.sol index c94052e25..f3900e621 100644 --- a/solidity/test/isms/ERC5164ISM.t.sol +++ b/solidity/test/isms/ERC5164ISM.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT or Apache-2.0 pragma solidity ^0.8.13; -import {Test} from "forge-std/Test.sol"; - import {LibBit} from "../../contracts/libs/LibBit.sol"; import {Message} from "../../contracts/libs/Message.sol"; import {MessageUtils} from "./IsmTestUtils.sol"; @@ -17,8 +15,9 @@ import {ERC5164Ism} from "../../contracts/isms/hook/ERC5164Ism.sol"; import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; import {MockMessageDispatcher, MockMessageExecutor} from "../../contracts/mock/MockERC5164.sol"; +import {ExternalBridgeTest} from "./ExternalBridgeTest.sol"; -contract ERC5164IsmTest is Test { +contract ERC5164IsmTest is ExternalBridgeTest { using LibBit for uint256; using TypeCasts for address; using Message for bytes; @@ -27,23 +26,8 @@ contract ERC5164IsmTest is Test { IMessageDispatcher internal dispatcher; MockMessageExecutor internal executor; - ERC5164Hook internal hook; - ERC5164Ism internal ism; - TestMailbox internal originMailbox; - TestRecipient internal testRecipient; - - uint32 internal constant TEST1_DOMAIN = 1; - uint32 internal constant TEST2_DOMAIN = 2; - - uint8 internal constant VERSION = 0; - bytes internal testMessage = - abi.encodePacked("Hello from the other chain!"); address internal alice = address(0x1); - // req for most tests - bytes encodedMessage = _encodeTestMessage(0, address(testRecipient)); - bytes32 messageId = encodedMessage.id(); - event MessageDispatched( bytes32 indexed messageId, address indexed from, @@ -56,19 +40,19 @@ contract ERC5164IsmTest is Test { /// SETUP /// /////////////////////////////////////////////////////////////////// - function setUp() public { + function setUp() public override { dispatcher = new MockMessageDispatcher(); executor = new MockMessageExecutor(); - testRecipient = new TestRecipient(); - originMailbox = new TestMailbox(TEST1_DOMAIN); + originMailbox = new TestMailbox(ORIGIN_DOMAIN); ism = new ERC5164Ism(address(executor)); hook = new ERC5164Hook( address(originMailbox), - TEST2_DOMAIN, + DESTINATION_DOMAIN, address(ism).addressToBytes32(), address(dispatcher) ); ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); + super.setUp(); } /////////////////////////////////////////////////////////////////// @@ -100,7 +84,7 @@ contract ERC5164IsmTest is Test { vm.expectRevert("AbstractMessageIdAuthHook: invalid ISM"); hook = new ERC5164Hook( address(originMailbox), - TEST2_DOMAIN, + DESTINATION_DOMAIN, address(0).addressToBytes32(), address(dispatcher) ); @@ -108,125 +92,89 @@ contract ERC5164IsmTest is Test { vm.expectRevert("ERC5164Hook: invalid dispatcher"); hook = new ERC5164Hook( address(originMailbox), - TEST2_DOMAIN, + DESTINATION_DOMAIN, address(ism).addressToBytes32(), address(0) ); } - function test_postDispatch() public { - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) - ); - originMailbox.updateLatestDispatchedId(messageId); + function testTypes() public view { + assertEq(hook.hookType(), uint8(IPostDispatchHook.Types.ID_AUTH_ISM)); + assertEq(ism.moduleType(), uint8(IInterchainSecurityModule.Types.NULL)); + } - // note: not checking for messageId since this is implementation dependent on each vendor + function _expectOriginExternalBridgeCall( + bytes memory _encodedHookData + ) internal override { vm.expectEmit(false, true, true, true, address(dispatcher)); emit MessageDispatched( messageId, address(hook), - TEST2_DOMAIN, + DESTINATION_DOMAIN, address(ism), - encodedHookData + _encodedHookData ); - - hook.postDispatch(bytes(""), encodedMessage); } - function testTypes() public { - assertEq(hook.hookType(), uint8(IPostDispatchHook.Types.ID_AUTH_ISM)); - assertEq(ism.moduleType(), uint8(IInterchainSecurityModule.Types.NULL)); + function test_verify_revertWhen_invalidMetadata() public override { + assertFalse(ism.verify(new bytes(0), encodedMessage)); } - function test_postDispatch_RevertWhen_ChainIDNotSupported() public { - encodedMessage = MessageUtils.formatMessage( - VERSION, - 0, - TEST1_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - 3, // unsupported chain id - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); - originMailbox.updateLatestDispatchedId(Message.id(encodedMessage)); - - vm.expectRevert( - "AbstractMessageIdAuthHook: invalid destination domain" - ); - hook.postDispatch(bytes(""), encodedMessage); - } - - function test_postDispatch_RevertWhen_msgValueNotAllowed() public payable { + function test_postDispatch_revertWhen_msgValueNotAllowed() public payable { originMailbox.updateLatestDispatchedId(messageId); vm.expectRevert("ERC5164Hook: no value allowed"); hook.postDispatch{value: 1}(bytes(""), encodedMessage); } - /* ============ ISM.verifyMessageId ============ */ - - function test_verifyMessageId() public { - vm.startPrank(address(executor)); - - ism.verifyMessageId(messageId); - assertTrue(ism.verifiedMessages(messageId).isBitSet(255)); - - vm.stopPrank(); - } + // override to omit direct external bridge call + function test_verify_revertsWhen_notAuthorizedHook() public override { + vm.prank(alice); - function test_verifyMessageId_RevertWhen_NotAuthorized() public { - vm.startPrank(alice); - - // needs to be called by the authorized hook contract on Ethereum vm.expectRevert( "AbstractMessageIdAuthorizedIsm: sender is not the hook" ); - ism.verifyMessageId(messageId); - - vm.stopPrank(); + ism.preVerifyMessage(messageId, 0); + assertFalse(ism.isVerified(encodedMessage)); } - /* ============ ISM.verify ============ */ + // SKIP - duplicate of test_verify_revertWhen_invalidMetadata + function test_verify_revertsWhen_incorrectMessageId() public override {} - function test_verify() public { - vm.startPrank(address(executor)); + function test_verify_revertsWhen_invalidIsm() public override {} - ism.verifyMessageId(messageId); + // SKIP - 5164 ism does not support msg.value + function test_verify_msgValue_asyncCall() public override {} - bool verified = ism.verify(new bytes(0), encodedMessage); - assertTrue(verified); + function test_verify_msgValue_externalBridgeCall() public override {} - vm.stopPrank(); - } + function test_verify_valueAlreadyClaimed(uint256) public override {} - function test_verify_RevertWhen_InvalidMessage() public { - vm.startPrank(address(executor)); + function test_verify_override_msgValue() public override {} - ism.verifyMessageId(messageId); + function testFuzz_postDispatch_refundsExtraValue(uint256) public override {} - bytes memory invalidMessage = _encodeTestMessage(0, address(this)); - bool verified = ism.verify(new bytes(0), invalidMessage); - assertFalse(verified); - - vm.stopPrank(); - } + function test_verify_false_arbitraryCall() public override {} /* ============ helper functions ============ */ - function _encodeTestMessage( - uint32 _msgCount, - address _receipient - ) internal view returns (bytes memory) { - return - MessageUtils.formatMessage( - VERSION, - _msgCount, - TEST1_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - TEST2_DOMAIN, - TypeCasts.addressToBytes32(_receipient), - testMessage - ); + function _externalBridgeDestinationCall( + bytes memory _encodedHookData, + uint256 _msgValue + ) internal override { + vm.prank(address(executor)); + ism.preVerifyMessage(messageId, 0); + } + + function _encodeExternalDestinationBridgeCall( + address _from, + address _to, + uint256 _msgValue, + bytes32 _messageId + ) internal override returns (bytes memory) { + if (_from == address(hook)) { + vm.prank(address(executor)); + ism.preVerifyMessage{value: _msgValue}(messageId, 0); + } } } diff --git a/solidity/test/isms/ExternalBridgeTest.sol b/solidity/test/isms/ExternalBridgeTest.sol new file mode 100644 index 000000000..937e39311 --- /dev/null +++ b/solidity/test/isms/ExternalBridgeTest.sol @@ -0,0 +1,333 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; +import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; +import {Message} from "../../contracts/libs/Message.sol"; +import {MessageUtils} from "./IsmTestUtils.sol"; +import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; +import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol"; +import {AbstractMessageIdAuthHook} from "../../contracts/hooks/libs/AbstractMessageIdAuthHook.sol"; + +abstract contract ExternalBridgeTest is Test { + using TypeCasts for address; + using MessageUtils for bytes; + + uint8 internal constant HYPERLANE_VERSION = 1; + uint32 internal constant ORIGIN_DOMAIN = 1; + uint32 internal constant DESTINATION_DOMAIN = 2; + uint256 internal constant MSG_VALUE = 1 ether; + uint256 internal constant MAX_MSG_VALUE = 2 ** 255 - 1; + uint256 internal GAS_QUOTE; + + TestMailbox internal originMailbox; + TestRecipient internal testRecipient; + + AbstractMessageIdAuthHook internal hook; + AbstractMessageIdAuthorizedIsm internal ism; + + bytes internal testMessage = + abi.encodePacked("Hello from the other chain!"); + bytes internal testMetadata = + StandardHookMetadata.overrideRefundAddress(address(this)); + bytes internal encodedMessage; + bytes32 internal messageId; + + function setUp() public virtual { + testRecipient = new TestRecipient(); + encodedMessage = _encodeTestMessage(); + messageId = Message.id(encodedMessage); + } + + /* ============ hook.quoteDispatch ============ */ + + function test_quoteDispatch() public view { + assertEq(hook.quoteDispatch(testMetadata, encodedMessage), GAS_QUOTE); + } + + /* ============ Hook.postDispatch ============ */ + + function test_postDispatch() public { + bytes memory hookMetadata = testMetadata; + bytes memory encodedHookData = _encodeHookData(messageId, 0); + originMailbox.updateLatestDispatchedId(messageId); + _expectOriginExternalBridgeCall(encodedHookData); + + uint256 quote = hook.quoteDispatch(testMetadata, encodedMessage); + hook.postDispatch{value: quote}(testMetadata, encodedMessage); + } + + function test_postDispatch_revertWhen_chainIDNotSupported() public { + bytes memory message = originMailbox.buildOutboundMessage( + 3, + TypeCasts.addressToBytes32(address(this)), + testMessage + ); + + originMailbox.updateLatestDispatchedId(Message.id(message)); + vm.expectRevert( + "AbstractMessageIdAuthHook: invalid destination domain" + ); + hook.postDispatch(testMetadata, message); + } + + function test_postDispatch_revertWhen_notLastDispatchedMessage() public { + vm.expectRevert( + "AbstractMessageIdAuthHook: message not latest dispatched" + ); + hook.postDispatch(testMetadata, encodedMessage); + } + + function test_postDispatch_revertWhen_tooMuchValue() public { + vm.deal(address(this), uint256(MAX_MSG_VALUE + 1)); + bytes memory excessValueMetadata = StandardHookMetadata + .overrideMsgValue(uint256(2 ** 255 + 1)); + + originMailbox.updateLatestDispatchedId(messageId); + vm.expectRevert( + "AbstractMessageIdAuthHook: msgValue must be less than 2 ** 255" + ); + hook.postDispatch(excessValueMetadata, encodedMessage); + } + + function testFuzz_postDispatch_refundsExtraValue( + uint256 extraValue + ) public virtual { + vm.assume(extraValue < MAX_MSG_VALUE); + vm.deal(address(this), address(this).balance + extraValue); + uint256 valueBefore = address(this).balance; + + bytes memory encodedHookData = _encodeHookData(messageId, 0); + originMailbox.updateLatestDispatchedId(messageId); + _expectOriginExternalBridgeCall(encodedHookData); + + uint256 quote = hook.quoteDispatch(testMetadata, encodedMessage); + hook.postDispatch{value: quote + extraValue}( + testMetadata, + encodedMessage + ); + + assertEq(address(this).balance, valueBefore - quote); + } + + function test_postDispatch_revertWhen_insufficientValue() public { + bytes memory encodedHookData = _encodeHookData(messageId, 0); + originMailbox.updateLatestDispatchedId(messageId); + _expectOriginExternalBridgeCall(encodedHookData); + + uint256 quote = hook.quoteDispatch(testMetadata, encodedMessage); + + vm.expectRevert(); //arithmetic underflow + hook.postDispatch{value: quote - 1}(testMetadata, encodedMessage); + } + + /* ============ ISM.preVerifyMessage ============ */ + + function test_preVerifyMessage_asyncCall() public { + bytes memory encodedHookData = _encodeHookData(messageId, 0); + _externalBridgeDestinationCall(encodedHookData, 0); + + assertTrue(ism.isVerified(encodedMessage)); + } + + function test_preVerifyMessage_externalBridgeCall() public virtual { + bytes memory externalCalldata = _encodeExternalDestinationBridgeCall( + address(hook), + address(ism), + 0, + messageId + ); + + assertTrue(ism.verify(externalCalldata, encodedMessage)); + assertTrue(ism.isVerified(encodedMessage)); + } + + /* ============ ISM.verify ============ */ + + function test_verify_revertWhen_invalidMetadata() public virtual { + vm.expectRevert(); + assertFalse(ism.verify(new bytes(0), encodedMessage)); + } + + function test_verify_msgValue_asyncCall() public virtual { + bytes memory encodedHookData = _encodeHookData(messageId, MSG_VALUE); + _externalBridgeDestinationCall(encodedHookData, MSG_VALUE); + + assertTrue(ism.verify(new bytes(0), encodedMessage)); + assertEq(address(testRecipient).balance, MSG_VALUE); + } + + function test_verify_msgValue_externalBridgeCall() public virtual { + bytes memory externalCalldata = _encodeExternalDestinationBridgeCall( + address(hook), + address(ism), + MSG_VALUE, + messageId + ); + assertTrue(ism.verify(externalCalldata, encodedMessage)); + assertEq(address(testRecipient).balance, 1 ether); + } + + function test_verify_revertsWhen_invalidIsm() public virtual { + bytes memory externalCalldata = _encodeExternalDestinationBridgeCall( + address(hook), + address(hook), + 0, + messageId + ); + + vm.expectRevert(); + assertFalse(ism.verify(externalCalldata, encodedMessage)); + } + + function test_verify_revertsWhen_notAuthorizedHook() public virtual { + bytes memory unauthorizedHookErrorMsg = _setExternalOriginSender( + address(this) + ); + + bytes memory externalCalldata = _encodeExternalDestinationBridgeCall( + address(this), + address(ism), + 0, + messageId + ); + + // external call + vm.expectRevert(unauthorizedHookErrorMsg); + assertFalse(ism.verify(externalCalldata, encodedMessage)); + + // async call vm.expectRevert(NotCrossChainCall.selector); + vm.expectRevert(); + _externalBridgeDestinationCall(externalCalldata, 0); + assertFalse(ism.isVerified(encodedMessage)); + } + + function test_verify_revertsWhen_incorrectMessageId() public virtual { + bytes32 incorrectMessageId = keccak256("incorrect message id"); + bytes memory externalCalldata = _encodeExternalDestinationBridgeCall( + address(hook), + address(ism), + 0, + incorrectMessageId + ); + + // external call + vm.expectRevert(); + assertFalse(ism.verify(externalCalldata, encodedMessage)); + + // async call - native bridges might have try catch block to prevent revert + try + this.externalBridgeDestinationCallWrapper( + _encodeHookData(incorrectMessageId, 0), + 0 + ) + {} catch {} + assertFalse(ism.isVerified(testMessage)); + } + + /// forge-config: default.fuzz.runs = 10 + function test_verify_valueAlreadyClaimed(uint256 _msgValue) public virtual { + _msgValue = bound(_msgValue, 0, MAX_MSG_VALUE); + _externalBridgeDestinationCall( + _encodeHookData(messageId, _msgValue), + _msgValue + ); + + bool verified = ism.verify(new bytes(0), encodedMessage); + assertTrue(verified); + assertEq(address(ism).balance, 0); + assertEq(address(testRecipient).balance, _msgValue); + + // send more value to the ISM + vm.deal(address(ism), _msgValue); + + // verified still true + verified = ism.verify(new bytes(0), encodedMessage); + assertTrue(verified); + // value which was already sent + assertEq(address(ism).balance, _msgValue); + assertEq(address(testRecipient).balance, _msgValue); + } + + function test_verify_override_msgValue() public virtual { + bytes memory encodedHookData = _encodeHookData(messageId, MSG_VALUE); + + _externalBridgeDestinationCall(encodedHookData, MSG_VALUE); + + vm.expectRevert("AbstractMessageIdAuthorizedIsm: invalid msg.value"); + _externalBridgeDestinationCall(encodedHookData, 0); + + assertTrue(ism.verify(new bytes(0), encodedMessage)); + assertEq(address(testRecipient).balance, MSG_VALUE); + } + + function test_verify_false_arbitraryCall() public virtual { + bytes memory incorrectCalldata = _encodeExternalDestinationBridgeCall( + address(hook), + address(this), + 0, + messageId + ); + + vm.expectRevert(); + ism.verify(incorrectCalldata, encodedMessage); + assertFalse(ism.isVerified(encodedMessage)); + } + + /* ============ helper functions ============ */ + + function _encodeTestMessage() internal view returns (bytes memory) { + return + originMailbox.buildOutboundMessage( + DESTINATION_DOMAIN, + TypeCasts.addressToBytes32(address(testRecipient)), + testMessage + ); + } + + function _encodeHookData( + bytes32 _messageId, + uint256 _msgValue + ) internal pure returns (bytes memory) { + return + abi.encodeCall( + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (_messageId, _msgValue) + ); + } + + // wrapper function needed for _externalBridgeDestinationCall because try catch cannot call an internal function + function externalBridgeDestinationCallWrapper( + bytes memory _encodedHookData, + uint256 _msgValue + ) external { + _externalBridgeDestinationCall(_encodedHookData, _msgValue); + } + + function _expectOriginExternalBridgeCall( + bytes memory _encodedHookData + ) internal virtual; + + function _externalBridgeDestinationCall( + bytes memory _encodedHookData, + uint256 _msgValue + ) internal virtual; + + function _encodeExternalDestinationBridgeCall( + address _from, + address _to, + uint256 _msgValue, + bytes32 _messageId + ) internal virtual returns (bytes memory); + + function _setExternalOriginSender( + address _sender + ) internal virtual returns (bytes memory) {} + + receive() external payable {} + + // meant to be mock an arbitrary successful call made by the external bridge + function preVerifyMessage(bytes32 /*messageId*/) public payable {} +} diff --git a/solidity/test/isms/MultisigIsm.t.sol b/solidity/test/isms/MultisigIsm.t.sol index 8c0b61d38..29098475f 100644 --- a/solidity/test/isms/MultisigIsm.t.sol +++ b/solidity/test/isms/MultisigIsm.t.sol @@ -186,6 +186,32 @@ abstract contract AbstractMultisigIsmTest is Test { metadata[index] = ~metadata[index]; assertFalse(ism.verify(metadata, message)); } + + function test_verify_revertWhen_duplicateSignatures( + uint32 destination, + bytes32 recipient, + bytes calldata body, + uint8 m, + uint8 n, + bytes32 seed + ) public virtual { + vm.assume(1 < m && m <= n && n < 10); + bytes memory message = getMessage(destination, recipient, body); + bytes memory metadata = getMetadata(m, n, seed, message); + + bytes memory duplicateMetadata = new bytes(metadata.length); + for (uint256 i = 0; i < metadata.length - 65; i++) { + duplicateMetadata[i] = metadata[i]; + } + for (uint256 i = 0; i < 65; i++) { + duplicateMetadata[metadata.length - 65 + i] = metadata[ + metadata.length - 130 + i + ]; + } + + vm.expectRevert("!threshold"); + ism.verify(duplicateMetadata, message); + } } contract MerkleRootMultisigIsmTest is AbstractMultisigIsmTest { diff --git a/solidity/test/isms/OPL2ToL1Ism.t.sol b/solidity/test/isms/OPL2ToL1Ism.t.sol index 9466a7c93..9322713d5 100644 --- a/solidity/test/isms/OPL2ToL1Ism.t.sol +++ b/solidity/test/isms/OPL2ToL1Ism.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: MIT or Apache-2.0 pragma solidity ^0.8.13; -import {Test} from "forge-std/Test.sol"; - import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; import {Message} from "../../contracts/libs/Message.sol"; import {MessageUtils} from "./IsmTestUtils.sol"; @@ -15,57 +13,44 @@ import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; import {MockOptimismMessenger, MockOptimismPortal} from "../../contracts/mock/MockOptimism.sol"; import {OPL2ToL1Hook} from "../../contracts/hooks/OPL2ToL1Hook.sol"; import {OPL2ToL1Ism} from "../../contracts/isms/hook/OPL2ToL1Ism.sol"; +import {ExternalBridgeTest} from "./ExternalBridgeTest.sol"; +import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol"; -contract OPL2ToL1IsmTest is Test { - uint8 internal constant HYPERLANE_VERSION = 1; - uint32 internal constant MAINNET_DOMAIN = 1; - uint32 internal constant OPTIMISM_DOMAIN = 10; - uint32 internal constant GAS_QUOTE = 120_000; - +contract OPL2ToL1IsmTest is ExternalBridgeTest { address internal constant L2_MESSENGER_ADDRESS = 0x4200000000000000000000000000000000000007; uint256 internal constant MOCK_NONCE = 0; - TestMailbox public l2Mailbox; - TestRecipient internal testRecipient; - bytes internal testMessage = - abi.encodePacked("Hello from the other chain!"); - bytes internal encodedMessage; - bytes internal testMetadata = - StandardHookMetadata.overrideRefundAddress(address(this)); - bytes32 internal messageId; - + TestInterchainGasPaymaster internal mockOverheadIgp; MockOptimismPortal internal portal; MockOptimismMessenger internal l1Messenger; - OPL2ToL1Hook public hook; - OPL2ToL1Ism public ism; /////////////////////////////////////////////////////////////////// /// SETUP /// /////////////////////////////////////////////////////////////////// - function setUp() public { + function setUp() public override { // Optimism messenger mock setup + // GAS_QUOTE = 300_000; vm.etch( L2_MESSENGER_ADDRESS, address(new MockOptimismMessenger()).code ); - testRecipient = new TestRecipient(); - - encodedMessage = _encodeTestMessage(); - messageId = Message.id(encodedMessage); + deployAll(); + super.setUp(); } function deployHook() public { - l2Mailbox = new TestMailbox(OPTIMISM_DOMAIN); + originMailbox = new TestMailbox(ORIGIN_DOMAIN); + mockOverheadIgp = new TestInterchainGasPaymaster(); hook = new OPL2ToL1Hook( - address(l2Mailbox), - MAINNET_DOMAIN, + address(originMailbox), + DESTINATION_DOMAIN, TypeCasts.addressToBytes32(address(ism)), L2_MESSENGER_ADDRESS, - GAS_QUOTE + address(mockOverheadIgp) ); } @@ -85,209 +70,78 @@ contract OPL2ToL1IsmTest is Test { ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); } - function test_postDispatch() public { - deployAll(); + function test_postDispatch_childHook() public { + bytes memory encodedHookData = _encodeHookData(messageId, 0); + originMailbox.updateLatestDispatchedId(messageId); + _expectOriginExternalBridgeCall(encodedHookData); - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) + bytes memory igpMetadata = StandardHookMetadata.overrideGasLimit( + 78_000 ); - l2Mailbox.updateLatestDispatchedId(messageId); + uint256 quote = hook.quoteDispatch(igpMetadata, encodedMessage); + assertEq(quote, mockOverheadIgp.quoteGasPayment(ORIGIN_DOMAIN, 78_000)); + hook.postDispatch{value: quote}(igpMetadata, encodedMessage); + } + + /* ============ helper functions ============ */ + function _expectOriginExternalBridgeCall( + bytes memory _encodedHookData + ) internal override { vm.expectCall( L2_MESSENGER_ADDRESS, abi.encodeCall( ICrossDomainMessenger.sendMessage, - (address(ism), encodedHookData, GAS_QUOTE) + (address(ism), _encodedHookData, uint32(300_000)) ) ); - - hook.postDispatch{value: GAS_QUOTE}(testMetadata, encodedMessage); - } - - function testFork_postDispatch_revertWhen_chainIDNotSupported() public { - deployAll(); - - bytes memory message = MessageUtils.formatMessage( - 0, - uint32(0), - OPTIMISM_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - 2, // wrong domain - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); - - l2Mailbox.updateLatestDispatchedId(Message.id(message)); - vm.expectRevert( - "AbstractMessageIdAuthHook: invalid destination domain" - ); - hook.postDispatch(testMetadata, message); - } - - function test_postDispatch_revertWhen_notLastDispatchedMessage() public { - deployAll(); - - vm.expectRevert( - "AbstractMessageIdAuthHook: message not latest dispatched" - ); - hook.postDispatch(testMetadata, encodedMessage); } - function test_verify_directWithdrawalCall() public { - deployAll(); - - bytes memory encodedWithdrawalTx = _encodeFinalizeWithdrawalTx( - address(ism), - 0, - messageId - ); - - assertTrue(ism.verify(encodedWithdrawalTx, encodedMessage)); - } - - function test_verify_directWithdrawalCall_revertsWhen_invalidSender() - public - { - deployAll(); - l1Messenger.setXDomainMessageSender(address(this)); - - bytes memory encodedWithdrawalTx = _encodeFinalizeWithdrawalTx( - address(ism), - 0, - messageId - ); - - vm.expectRevert(); // evmRevert in MockOptimismPortal - ism.verify(encodedWithdrawalTx, encodedMessage); - } - - function test_verify_statefulVerify() public { - deployAll(); - - vm.deal(address(portal), 1 ether); - IOptimismPortal.WithdrawalTransaction - memory withdrawal = IOptimismPortal.WithdrawalTransaction({ - nonce: MOCK_NONCE, - sender: L2_MESSENGER_ADDRESS, - target: address(l1Messenger), - value: 1 ether, - gasLimit: uint256(GAS_QUOTE), - data: _encodeMessengerCalldata(address(ism), 1 ether, messageId) - }); - portal.finalizeWithdrawalTransaction(withdrawal); - - vm.etch(address(portal), new bytes(0)); // this is a way to test that the portal isn't called again - assertTrue(ism.verify(new bytes(0), encodedMessage)); - assertEq(address(testRecipient).balance, 1 ether); // testing msg.value - } - - function test_verify_statefulAndDirectWithdrawal() public { - deployAll(); - - IOptimismPortal.WithdrawalTransaction - memory withdrawal = IOptimismPortal.WithdrawalTransaction({ - nonce: MOCK_NONCE, - sender: L2_MESSENGER_ADDRESS, - target: address(l1Messenger), - value: 0, - gasLimit: uint256(GAS_QUOTE), - data: _encodeMessengerCalldata(address(ism), 0, messageId) - }); - portal.finalizeWithdrawalTransaction(withdrawal); - - bytes memory encodedWithdrawalTx = _encodeFinalizeWithdrawalTx( - address(ism), - 0, - messageId - ); - - vm.etch(address(portal), new bytes(0)); // this is a way to test that the portal isn't called again - assertTrue(ism.verify(encodedWithdrawalTx, encodedMessage)); - } - - function test_verify_revertsWhen_noStatefulAndDirectWithdrawal() public { - deployAll(); - - vm.expectRevert(); - ism.verify(new bytes(0), encodedMessage); + function _encodeExternalDestinationBridgeCall( + address, + /*_from*/ + address _to, + uint256 _msgValue, + bytes32 _messageId + ) internal override returns (bytes memory) { + vm.deal(address(portal), _msgValue); + return _encodeFinalizeWithdrawalTx(_to, _msgValue, _messageId); } - function test_verify_revertsWhen_invalidIsm() public { - deployAll(); - - bytes memory encodedWithdrawalTx = _encodeFinalizeWithdrawalTx( - address(this), - 0, - messageId - ); - - vm.expectRevert(); // evmRevert in MockOptimismPortal - ism.verify(encodedWithdrawalTx, encodedMessage); + function _setExternalOriginSender( + address _sender + ) internal override returns (bytes memory) { + l1Messenger.setXDomainMessageSender(_sender); + return "AbstractMessageIdAuthorizedIsm: sender is not the hook"; } - function test_verify_revertsWhen_incorrectMessageId() public { - deployAll(); - - bytes32 incorrectMessageId = keccak256("incorrect message id"); - - bytes memory encodedWithdrawalTx = _encodeFinalizeWithdrawalTx( - address(this), - 0, - incorrectMessageId - ); - - // through portal call - vm.expectRevert("OPL2ToL1Ism: invalid message id"); - ism.verify(encodedWithdrawalTx, encodedMessage); - - // through statefulVerify + function _externalBridgeDestinationCall( + bytes memory _encodedHookData, + uint256 _msgValue + ) internal override { + vm.deal(address(portal), _msgValue); IOptimismPortal.WithdrawalTransaction memory withdrawal = IOptimismPortal.WithdrawalTransaction({ nonce: MOCK_NONCE, sender: L2_MESSENGER_ADDRESS, target: address(l1Messenger), - value: 0, + value: _msgValue, gasLimit: uint256(GAS_QUOTE), data: _encodeMessengerCalldata( address(ism), - 0, - incorrectMessageId + _msgValue, + _encodedHookData ) }); portal.finalizeWithdrawalTransaction(withdrawal); - - vm.etch(address(portal), new bytes(0)); // to stop the portal route - vm.expectRevert(); // evmRevert() - assertFalse(ism.verify(new bytes(0), encodedMessage)); - } - - /* ============ helper functions ============ */ - - function _encodeTestMessage() internal view returns (bytes memory) { - return - MessageUtils.formatMessage( - HYPERLANE_VERSION, - uint32(0), - OPTIMISM_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - MAINNET_DOMAIN, - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); } function _encodeMessengerCalldata( address _ism, uint256 _value, - bytes32 _messageId + bytes memory _encodedHookData ) internal view returns (bytes memory) { - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (_messageId) - ); - return abi.encodeCall( ICrossDomainMessenger.relayMessage, @@ -297,7 +151,7 @@ contract OPL2ToL1IsmTest is Test { _ism, _value, uint256(GAS_QUOTE), - encodedHookData + _encodedHookData ) ); } @@ -307,6 +161,7 @@ contract OPL2ToL1IsmTest is Test { uint256 _value, bytes32 _messageId ) internal view returns (bytes memory) { + bytes memory encodedHookData = _encodeHookData(_messageId, _value); return abi.encode( MOCK_NONCE, @@ -314,7 +169,7 @@ contract OPL2ToL1IsmTest is Test { l1Messenger, _value, uint256(GAS_QUOTE), - _encodeMessengerCalldata(_ism, _value, _messageId) + _encodeMessengerCalldata(_ism, _value, encodedHookData) ); } } diff --git a/solidity/test/isms/OPStackIsm.t.sol b/solidity/test/isms/OPStackIsm.t.sol index 9862717ad..8371c3b68 100644 --- a/solidity/test/isms/OPStackIsm.t.sol +++ b/solidity/test/isms/OPStackIsm.t.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MIT or Apache-2.0 pragma solidity ^0.8.13; -import {Test} from "forge-std/Test.sol"; +import "forge-std/console.sol"; import {LibBit} from "../../contracts/libs/LibBit.sol"; import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol"; +import {MockOptimismMessenger} from "../../contracts/mock/MockOptimism.sol"; import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; import {Message} from "../../contracts/libs/Message.sol"; import {MessageUtils} from "./IsmTestUtils.sol"; @@ -18,16 +19,14 @@ import {TestRecipient} from "../../contracts/test/TestRecipient.sol"; import {NotCrossChainCall} from "@openzeppelin/contracts/crosschain/errors.sol"; import {AddressAliasHelper} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol"; -import {ICrossDomainMessenger, IL2CrossDomainMessenger} from "../../contracts/interfaces/optimism/ICrossDomainMessenger.sol"; +import {ICrossDomainMessenger} from "../../contracts/interfaces/optimism/ICrossDomainMessenger.sol"; +import {ExternalBridgeTest} from "./ExternalBridgeTest.sol"; -contract OPStackIsmTest is Test { +contract OPStackIsmTest is ExternalBridgeTest { using LibBit for uint256; using TypeCasts for address; using MessageUtils for bytes; - uint256 internal mainnetFork; - uint256 internal optimismFork; - address internal constant L1_MESSENGER_ADDRESS = 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1; address internal constant L1_CANNONICAL_CHAIN = @@ -36,36 +35,12 @@ contract OPStackIsmTest is Test { 0x4200000000000000000000000000000000000007; uint8 internal constant OPTIMISM_VERSION = 0; - uint8 internal constant HYPERLANE_VERSION = 1; uint256 internal constant DEFAULT_GAS_LIMIT = 1_920_000; address internal alice = address(0x1); - ICrossDomainMessenger internal l1Messenger; - IL2CrossDomainMessenger internal l2Messenger; - TestMailbox internal l1Mailbox; - OPStackIsm internal opISM; - OPStackHook internal opHook; - - TestRecipient internal testRecipient; - bytes internal testMessage = - abi.encodePacked("Hello from the other chain!"); - bytes internal testMetadata = - StandardHookMetadata.overrideRefundAddress(address(this)); - - bytes internal encodedMessage; - bytes32 internal messageId; - - uint32 internal constant MAINNET_DOMAIN = 1; - uint32 internal constant OPTIMISM_DOMAIN = 10; - - event SentMessage( - address indexed target, - address sender, - bytes message, - uint256 messageNonce, - uint256 gasLimit - ); + MockOptimismMessenger internal l1Messenger; + MockOptimismMessenger internal l2Messenger; event RelayedMessage(bytes32 indexed msgHash); @@ -73,469 +48,151 @@ contract OPStackIsmTest is Test { event ReceivedMessage(bytes32 indexed messageId); - function setUp() public { - // block numbers to fork from, chain data is cached to ../../forge-cache/ - mainnetFork = vm.createFork(vm.rpcUrl("mainnet"), 18_992_500); - optimismFork = vm.createFork(vm.rpcUrl("optimism"), 114_696_811); + function setUp() public override { + GAS_QUOTE = 0; - testRecipient = new TestRecipient(); + vm.etch( + L1_MESSENGER_ADDRESS, + address(new MockOptimismMessenger()).code + ); + vm.etch( + L2_MESSENGER_ADDRESS, + address(new MockOptimismMessenger()).code + ); + l1Messenger = MockOptimismMessenger(L1_MESSENGER_ADDRESS); + l2Messenger = MockOptimismMessenger(L2_MESSENGER_ADDRESS); - encodedMessage = _encodeTestMessage(); - messageId = Message.id(encodedMessage); + deployAll(); + super.setUp(); } /////////////////////////////////////////////////////////////////// /// SETUP /// /////////////////////////////////////////////////////////////////// - function deployOptimismHook() public { - vm.selectFork(mainnetFork); - - l1Messenger = ICrossDomainMessenger(L1_MESSENGER_ADDRESS); - l1Mailbox = new TestMailbox(MAINNET_DOMAIN); - - opHook = new OPStackHook( - address(l1Mailbox), - OPTIMISM_DOMAIN, - TypeCasts.addressToBytes32(address(opISM)), + function deployHook() public { + originMailbox = new TestMailbox(ORIGIN_DOMAIN); + hook = new OPStackHook( + address(originMailbox), + DESTINATION_DOMAIN, + TypeCasts.addressToBytes32(address(ism)), L1_MESSENGER_ADDRESS ); - - vm.makePersistent(address(opHook)); } - function deployOPStackIsm() public { - vm.selectFork(optimismFork); - - l2Messenger = IL2CrossDomainMessenger(L2_MESSENGER_ADDRESS); - opISM = new OPStackIsm(L2_MESSENGER_ADDRESS); - - vm.makePersistent(address(opISM)); + function deployIsm() public { + ism = new OPStackIsm(L2_MESSENGER_ADDRESS); } function deployAll() public { - deployOPStackIsm(); - deployOptimismHook(); - - vm.selectFork(optimismFork); + deployIsm(); + deployHook(); - opISM.setAuthorizedHook(TypeCasts.addressToBytes32(address(opHook))); - // for sending value - vm.deal( - AddressAliasHelper.applyL1ToL2Alias(L1_MESSENGER_ADDRESS), - 2 ** 255 - ); + ism.setAuthorizedHook(TypeCasts.addressToBytes32(address(hook))); + l2Messenger.setXDomainMessageSender(address(hook)); } - /////////////////////////////////////////////////////////////////// - /// FORK TESTS /// - /////////////////////////////////////////////////////////////////// - - /* ============ hook.quoteDispatch ============ */ - - function testFork_quoteDispatch() public { - deployAll(); - - vm.selectFork(mainnetFork); - - assertEq(opHook.quoteDispatch(testMetadata, encodedMessage), 0); + function test_verify_revertWhen_invalidMetadata() public override { + assertFalse(ism.verify(new bytes(0), encodedMessage)); } - /* ============ hook.postDispatch ============ */ - - function testFork_postDispatch() public { - deployAll(); + function test_verify_revertsWhen_incorrectMessageId() public override { + bytes32 incorrectMessageId = keccak256("incorrect message id"); - vm.selectFork(mainnetFork); - - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) - ); - - uint40 testNonce = 123; - l1Mailbox.updateLatestDispatchedId(messageId); - - vm.expectEmit(true, true, true, false, L1_MESSENGER_ADDRESS); - emit SentMessage( - address(opISM), - address(opHook), - encodedHookData, - testNonce, - DEFAULT_GAS_LIMIT + _externalBridgeDestinationCall( + _encodeHookData(incorrectMessageId, 0), + 0 ); - opHook.postDispatch(testMetadata, encodedMessage); + assertFalse(ism.isVerified(testMessage)); } - function testFork_postDispatch_RevertWhen_ChainIDNotSupported() public { - deployAll(); - - vm.selectFork(mainnetFork); - - bytes memory message = MessageUtils.formatMessage( - OPTIMISM_VERSION, - uint32(0), - MAINNET_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - 11, // wrong domain - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); + /* ============ helper functions ============ */ - l1Mailbox.updateLatestDispatchedId(Message.id(message)); - vm.expectRevert( - "AbstractMessageIdAuthHook: invalid destination domain" + function _expectOriginExternalBridgeCall( + bytes memory _encodedHookData + ) internal override { + vm.expectCall( + L1_MESSENGER_ADDRESS, + abi.encodeCall( + ICrossDomainMessenger.sendMessage, + (address(ism), _encodedHookData, uint32(DEFAULT_GAS_LIMIT)) + ) ); - opHook.postDispatch(testMetadata, message); } - function testFork_postDispatch_RevertWhen_TooMuchValue() public { - deployAll(); - - vm.selectFork(mainnetFork); - - vm.deal(address(this), uint256(2 ** 255 + 1)); - bytes memory excessValueMetadata = StandardHookMetadata - .overrideMsgValue(uint256(2 ** 255 + 1)); - - l1Mailbox.updateLatestDispatchedId(messageId); - vm.expectRevert( - "AbstractMessageIdAuthHook: msgValue must be less than 2 ** 255" + function _externalBridgeDestinationCall( + bytes memory _encodedHookData, + uint256 _msgValue + ) internal override { + vm.deal(L2_MESSENGER_ADDRESS, _msgValue); + l2Messenger.relayMessage( + 0, + address(hook), + address(ism), + _msgValue, + uint32(GAS_QUOTE), + _encodedHookData ); - opHook.postDispatch(excessValueMetadata, encodedMessage); } - function testFork_postDispatch_RevertWhen_NotLastDispatchedMessage() - public - { - deployAll(); - - vm.selectFork(mainnetFork); - - vm.expectRevert( - "AbstractMessageIdAuthHook: message not latest dispatched" - ); - opHook.postDispatch(testMetadata, encodedMessage); + function _encodeExternalDestinationBridgeCall( + address /*_from*/, + address /*_to*/, + uint256 /*_msgValue*/, + bytes32 /*_messageId*/ + ) internal pure override returns (bytes memory) { + return new bytes(0); } - /* ============ ISM.verifyMessageId ============ */ - - function testFork_verifyMessageId() public { - deployAll(); - - vm.selectFork(optimismFork); - - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) - ); - - (uint240 nonce, uint16 version) = decodeVersionedNonce( - l2Messenger.messageNonce() - ); - uint256 versionedNonce = encodeVersionedNonce(nonce + 1, version); - - bytes32 versionedHash = hashCrossDomainMessageV1( - versionedNonce, - address(opHook), - address(opISM), - 0, - DEFAULT_GAS_LIMIT, - encodedHookData - ); - - vm.startPrank( - AddressAliasHelper.applyL1ToL2Alias(L1_MESSENGER_ADDRESS) - ); - - vm.expectEmit(true, false, false, false, address(opISM)); - emit ReceivedMessage(messageId); - - vm.expectEmit(true, false, false, false, L2_MESSENGER_ADDRESS); - emit RelayedMessage(versionedHash); + // SKIP - no external bridge call + function test_preVerifyMessage_externalBridgeCall() public override {} - l2Messenger.relayMessage( - versionedNonce, - address(opHook), - address(opISM), - 0, - DEFAULT_GAS_LIMIT, - encodedHookData - ); + function test_verify_msgValue_externalBridgeCall() public override {} - assertTrue(opISM.verifiedMessages(messageId).isBitSet(255)); - vm.stopPrank(); - } + function test_verify_revertsWhen_invalidIsm() public override {} - function testFork_verifyMessageId_RevertWhen_NotAuthorized() public { - deployAll(); + function test_verify_false_arbitraryCall() public override {} - vm.selectFork(optimismFork); + /* ============ ISM.preVerifyMessage ============ */ + function test_verify_revertsWhen_notAuthorizedHook() public override { // needs to be called by the canonical messenger on Optimism vm.expectRevert(NotCrossChainCall.selector); - opISM.verifyMessageId(messageId); - - // set the xDomainMessageSender storage slot as alice - bytes32 key = bytes32(uint256(204)); - bytes32 value = TypeCasts.addressToBytes32(alice); - vm.store(address(l2Messenger), key, value); + ism.preVerifyMessage(messageId, 0); vm.startPrank(L2_MESSENGER_ADDRESS); + _setExternalOriginSender(address(this)); // needs to be called by the authorized hook contract on Ethereum vm.expectRevert( "AbstractMessageIdAuthorizedIsm: sender is not the hook" ); - opISM.verifyMessageId(messageId); + ism.preVerifyMessage(messageId, 0); } - /* ============ ISM.verify ============ */ - - function testFork_verify() public { - deployAll(); - - vm.selectFork(optimismFork); - - orchestrateRelayMessage(0, messageId); - - bool verified = opISM.verify(new bytes(0), encodedMessage); - assertTrue(verified); + function _setExternalOriginSender( + address _sender + ) internal override returns (bytes memory) { + l2Messenger.setXDomainMessageSender(_sender); + return ""; } - /// forge-config: default.fuzz.runs = 10 - function testFork_verify_WithValue(uint256 _msgValue) public { - _msgValue = bound(_msgValue, 0, 2 ** 254); - deployAll(); - - orchestrateRelayMessage(_msgValue, messageId); - - bool verified = opISM.verify(new bytes(0), encodedMessage); - assertTrue(verified); - - assertEq(address(opISM).balance, 0); - assertEq(address(testRecipient).balance, _msgValue); - } - - /// forge-config: default.fuzz.runs = 10 - function testFork_verify_valueAlreadyClaimed(uint256 _msgValue) public { - _msgValue = bound(_msgValue, 0, 2 ** 254); - deployAll(); - - orchestrateRelayMessage(_msgValue, messageId); - - bool verified = opISM.verify(new bytes(0), encodedMessage); - assertTrue(verified); - - assertEq(address(opISM).balance, 0); - assertEq(address(testRecipient).balance, _msgValue); - - // send more value to the ISM - vm.deal(address(opISM), _msgValue); - - verified = opISM.verify(new bytes(0), encodedMessage); - // verified still true - assertTrue(verified); - - assertEq(address(opISM).balance, _msgValue); - // value which was already sent - assertEq(address(testRecipient).balance, _msgValue); - } - - function testFork_verify_tooMuchValue() public { - deployAll(); + /* ============ ISM.verify ============ */ + function test_verify_tooMuchValue() public { uint256 _msgValue = 2 ** 255 + 1; - vm.expectEmit(false, false, false, false, address(l2Messenger)); - emit FailedRelayedMessage(messageId); - orchestrateRelayMessage(_msgValue, messageId); - - bool verified = opISM.verify(new bytes(0), encodedMessage); - assertFalse(verified); - - assertEq(address(opISM).balance, 0); - assertEq(address(testRecipient).balance, 0); - } - - // sending over invalid message - function testFork_verify_RevertWhen_HyperlaneInvalidMessage() public { - deployAll(); - - orchestrateRelayMessage(0, messageId); - - bytes memory invalidMessage = MessageUtils.formatMessage( - HYPERLANE_VERSION, - uint8(0), - MAINNET_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - OPTIMISM_DOMAIN, - TypeCasts.addressToBytes32(address(this)), // wrong recipient - testMessage + vm.expectRevert("AbstractMessageIdAuthorizedIsm: invalid msg.value"); + _externalBridgeDestinationCall( + _encodeHookData(messageId, _msgValue), + _msgValue ); - bool verified = opISM.verify(new bytes(0), invalidMessage); - assertFalse(verified); - } - // invalid messageID in postDispatch - function testFork_verify_RevertWhen_InvalidOptimismMessageID() public { - deployAll(); - vm.selectFork(optimismFork); - - bytes memory invalidMessage = MessageUtils.formatMessage( - HYPERLANE_VERSION, - uint8(0), - MAINNET_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - OPTIMISM_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - testMessage - ); - bytes32 _messageId = Message.id(invalidMessage); - orchestrateRelayMessage(0, _messageId); + assertFalse(ism.isVerified(encodedMessage)); - bool verified = opISM.verify(new bytes(0), encodedMessage); - assertFalse(verified); + assertEq(address(ism).balance, 0); + assertEq(address(testRecipient).balance, 0); } /* ============ helper functions ============ */ - - function _encodeTestMessage() internal view returns (bytes memory) { - return - MessageUtils.formatMessage( - HYPERLANE_VERSION, - uint32(0), - MAINNET_DOMAIN, - TypeCasts.addressToBytes32(address(this)), - OPTIMISM_DOMAIN, - TypeCasts.addressToBytes32(address(testRecipient)), - testMessage - ); - } - - /// @dev from eth-optimism/contracts-bedrock/contracts/libraries/Hashing.sol - /// @notice Hashes a cross domain message based on the V1 (current) encoding. - /// @param _nonce Message nonce. - /// @param _sender Address of the sender of the message. - /// @param _target Address of the target of the message. - /// @param _value ETH value to send to the target. - /// @param _gasLimit Gas limit to use for the message. - /// @param _data Data to send with the message. - /// @return Hashed cross domain message. - function hashCrossDomainMessageV1( - uint256 _nonce, - address _sender, - address _target, - uint256 _value, - uint256 _gasLimit, - bytes memory _data - ) internal pure returns (bytes32) { - return - keccak256( - encodeCrossDomainMessageV1( - _nonce, - _sender, - _target, - _value, - _gasLimit, - _data - ) - ); - } - - /// @dev from eth-optimism/contracts-bedrock/contracts/libraries/Encoding.sol - /// @notice Encodes a cross domain message based on the V1 (current) encoding. - /// @param _nonce Message nonce. - /// @param _sender Address of the sender of the message. - /// @param _target Address of the target of the message. - /// @param _value ETH value to send to the target. - /// @param _gasLimit Gas limit to use for the message. - /// @param _data Data to send with the message. - /// @return Encoded cross domain message. - function encodeCrossDomainMessageV1( - uint256 _nonce, - address _sender, - address _target, - uint256 _value, - uint256 _gasLimit, - bytes memory _data - ) internal pure returns (bytes memory) { - return - abi.encodeWithSignature( - "relayMessage(uint256,address,address,uint256,uint256,bytes)", - _nonce, - _sender, - _target, - _value, - _gasLimit, - _data - ); - } - - /// @dev from eth-optimism/contracts-bedrock/contracts/libraries/Encoding.sol - /// @notice Adds a version number into the first two bytes of a message nonce. - /// @param _nonce Message nonce to encode into. - /// @param _version Version number to encode into the message nonce. - /// @return Message nonce with version encoded into the first two bytes. - function encodeVersionedNonce( - uint240 _nonce, - uint16 _version - ) internal pure returns (uint256) { - uint256 nonce; - assembly { - nonce := or(shl(240, _version), _nonce) - } - return nonce; - } - - /// @dev from eth-optimism/contracts-bedrock/contracts/libraries/Encoding.sol - /// @notice Pulls the version out of a version-encoded nonce. - /// @param _nonce Message nonce with version encoded into the first two bytes. - /// @return Nonce without encoded version. - /// @return Version of the message. - function decodeVersionedNonce( - uint256 _nonce - ) internal pure returns (uint240, uint16) { - uint240 nonce; - uint16 version; - assembly { - nonce := and( - _nonce, - 0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - ) - version := shr(240, _nonce) - } - return (nonce, version); - } - - function orchestrateRelayMessage( - uint256 _msgValue, - bytes32 _messageId - ) internal { - vm.selectFork(optimismFork); - - bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (_messageId) - ); - - (uint240 nonce, uint16 version) = decodeVersionedNonce( - l2Messenger.messageNonce() - ); - uint256 versionedNonce = encodeVersionedNonce(nonce + 1, version); - - vm.deal( - AddressAliasHelper.applyL1ToL2Alias(L1_MESSENGER_ADDRESS), - 2 ** 256 - 1 - ); - vm.prank(AddressAliasHelper.applyL1ToL2Alias(L1_MESSENGER_ADDRESS)); - l2Messenger.relayMessage{value: _msgValue}( - versionedNonce, - address(opHook), - address(opISM), - _msgValue, - DEFAULT_GAS_LIMIT, - encodedHookData - ); - } } diff --git a/solidity/test/isms/PolygonPosIsm.t.sol b/solidity/test/isms/PolygonPosIsm.t.sol index fd26afea2..7d30dca4f 100644 --- a/solidity/test/isms/PolygonPosIsm.t.sol +++ b/solidity/test/isms/PolygonPosIsm.t.sol @@ -78,7 +78,7 @@ contract PolygonPosIsmTest is Test { bytes data ); - event ReceivedMessage(bytes32 indexed messageId); + event ReceivedMessage(bytes32 indexed messageId, uint256 msgValue); function setUp() public { // block numbers to fork from, chain data is cached to ../../forge-cache/ @@ -155,8 +155,8 @@ contract PolygonPosIsmTest is Test { vm.selectFork(mainnetFork); bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (messageId, 0) ); l1Mailbox.updateLatestDispatchedId(messageId); @@ -228,22 +228,22 @@ contract PolygonPosIsmTest is Test { polygonPosHook.postDispatch(testMetadata, encodedMessage); } - /* ============ ISM.verifyMessageId ============ */ + /* ============ ISM.preVerifyMessage ============ */ - function testFork_verifyMessageId() public { + function testFork_preVerifyMessage() public { deployAll(); vm.selectFork(polygonPosFork); bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (messageId) + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (messageId, 0) ); vm.startPrank(POLYGON_CROSSCHAIN_SYSTEM_ADDR); vm.expectEmit(true, false, false, false, address(polygonPosISM)); - emit ReceivedMessage(messageId); + emit ReceivedMessage(messageId, 0); // FIX: expect other events fxChild.onStateReceive( @@ -259,14 +259,14 @@ contract PolygonPosIsmTest is Test { vm.stopPrank(); } - function testFork_verifyMessageId_RevertWhen_NotAuthorized() public { + function testFork_preVerifyMessage_RevertWhen_NotAuthorized() public { deployAll(); vm.selectFork(polygonPosFork); // needs to be called by the fxchild on Polygon vm.expectRevert(NotCrossChainCall.selector); - polygonPosISM.verifyMessageId(messageId); + polygonPosISM.preVerifyMessage(messageId, 0); vm.startPrank(MAINNET_FX_CHILD); @@ -274,7 +274,7 @@ contract PolygonPosIsmTest is Test { vm.expectRevert( "AbstractMessageIdAuthorizedIsm: sender is not the hook" ); - polygonPosISM.verifyMessageId(messageId); + polygonPosISM.preVerifyMessage(messageId, 0); } /* ============ ISM.verify ============ */ @@ -349,8 +349,8 @@ contract PolygonPosIsmTest is Test { vm.selectFork(polygonPosFork); bytes memory encodedHookData = abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (_messageId) + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (_messageId, 0) ); vm.prank(POLYGON_CROSSCHAIN_SYSTEM_ADDR); diff --git a/solidity/test/isms/RateLimitedIsm.t.sol b/solidity/test/isms/RateLimitedIsm.t.sol index a514d75c9..481bbaa49 100644 --- a/solidity/test/isms/RateLimitedIsm.t.sol +++ b/solidity/test/isms/RateLimitedIsm.t.sol @@ -25,18 +25,20 @@ contract RateLimitedIsmTest is Test { function setUp() external { localMailbox = new TestMailbox(ORIGIN); + testRecipient = new TestRecipient(); rateLimitedIsm = new RateLimitedIsm( address(localMailbox), - MAX_CAPACITY + MAX_CAPACITY, + address(testRecipient) ); - testRecipient = new TestRecipient(); testRecipient.setInterchainSecurityModule(address(rateLimitedIsm)); } function testRateLimitedIsm_revertsIDeliveredFalse( - bytes calldata _message + uint256 _amount ) external { + bytes memory _message = _encodeTestMessage(_amount); vm.prank(address(localMailbox)); vm.expectRevert("InvalidDeliveredMessage"); rateLimitedIsm.verify(bytes(""), _message); @@ -62,6 +64,21 @@ contract RateLimitedIsmTest is Test { rateLimitedIsm.verify(bytes(""), encodedMessage); } + function test_verifyOnlyRecipient(uint128 _amount) external { + bytes memory _message = MessageUtils.formatMessage( + uint8(3), + uint32(1), + ORIGIN, + WARP_ROUTE_ADDR.addressToBytes32(), + ORIGIN, + ~address(testRecipient).addressToBytes32(), // bad recipient + TokenMessage.format(bytes32(""), _amount, bytes("")) + ); + + vm.expectRevert("TypeCasts: bytes32ToAddress overflow"); + rateLimitedIsm.verify(bytes(""), _message); + } + function _encodeTestMessage( uint256 _amount ) internal view returns (bytes memory) { diff --git a/solidity/test/isms/TrustedRelayerIsm.t.sol b/solidity/test/isms/TrustedRelayerIsm.t.sol index 51c574ba1..f630b6474 100644 --- a/solidity/test/isms/TrustedRelayerIsm.t.sol +++ b/solidity/test/isms/TrustedRelayerIsm.t.sol @@ -29,6 +29,13 @@ contract TrustedRelayerIsmTest is Test { recipient.setInterchainSecurityModule(address(ism)); } + function test_revertsWhen_invalidMailboxOrRelayer() public { + vm.expectRevert("TrustedRelayerIsm: invalid relayer"); + new TrustedRelayerIsm(address(mailbox), address(0)); + vm.expectRevert("TrustedRelayerIsm: invalid mailbox"); + new TrustedRelayerIsm(relayer, relayer); + } + function test_verify( uint32 origin, bytes32 sender, diff --git a/solidity/test/isms/WeightedMultisigIsm.t.sol b/solidity/test/isms/WeightedMultisigIsm.t.sol index df2a3d0ea..0c5fd7ee4 100644 --- a/solidity/test/isms/WeightedMultisigIsm.t.sol +++ b/solidity/test/isms/WeightedMultisigIsm.t.sol @@ -65,7 +65,6 @@ abstract contract AbstractStaticWeightedMultisigIsmTest is } } - // ism = IInterchainSecurityModule(deployedIsm); ism = IInterchainSecurityModule( weightedFactory.deploy(validators, threshold) ); @@ -136,7 +135,7 @@ abstract contract AbstractStaticWeightedMultisigIsmTest is return metadata; } - function testVerify_revertInsufficientWeight( + function test_verify_revertInsufficientWeight( uint32 destination, bytes32 recipient, bytes calldata body, @@ -161,6 +160,34 @@ abstract contract AbstractStaticWeightedMultisigIsmTest is vm.expectRevert("Insufficient validator weight"); ism.verify(insufficientMetadata, message); } + + function test_verify_revertWhen_duplicateSignatures( + uint32 destination, + bytes32 recipient, + bytes calldata body, + uint8 m, + uint8 n, + bytes32 seed + ) public virtual override { + vm.assume(1 < m && m <= n && n < 10); + bytes memory message = getMessage(destination, recipient, body); + bytes memory metadata = getMetadata(m, n, seed, message); + + bytes memory duplicateMetadata = new bytes(metadata.length); + for (uint256 i = 0; i < metadata.length - 65; i++) { + duplicateMetadata[i] = metadata[i]; + } + for (uint256 i = 0; i < 65; i++) { + duplicateMetadata[metadata.length - 65 + i] = metadata[ + metadata.length - 130 + i + ]; + } + + if (weightedIsm.signatureCount(metadata) >= 2) { + vm.expectRevert("Invalid signer"); + ism.verify(duplicateMetadata, message); + } + } } contract StaticMerkleRootWeightedMultisigIsmTest is @@ -194,6 +221,28 @@ contract StaticMerkleRootWeightedMultisigIsmTest is message ); } + + function test_verify_revertWhen_duplicateSignatures( + uint32 destination, + bytes32 recipient, + bytes calldata body, + uint8 m, + uint8 n, + bytes32 seed + ) + public + override(AbstractMultisigIsmTest, AbstractStaticWeightedMultisigIsmTest) + { + AbstractStaticWeightedMultisigIsmTest + .test_verify_revertWhen_duplicateSignatures( + destination, + recipient, + body, + m, + n, + seed + ); + } } contract StaticMessageIdWeightedMultisigIsmTest is @@ -227,4 +276,26 @@ contract StaticMessageIdWeightedMultisigIsmTest is message ); } + + function test_verify_revertWhen_duplicateSignatures( + uint32 destination, + bytes32 recipient, + bytes calldata body, + uint8 m, + uint8 n, + bytes32 seed + ) + public + override(AbstractMultisigIsmTest, AbstractStaticWeightedMultisigIsmTest) + { + AbstractStaticWeightedMultisigIsmTest + .test_verify_revertWhen_duplicateSignatures( + destination, + recipient, + body, + m, + n, + seed + ); + } } diff --git a/solidity/test/isms/layer-zero/LayerZeroV2Ism.t.sol b/solidity/test/isms/layer-zero/LayerZeroV2Ism.t.sol index 0d778a4d9..ac3ff38cf 100644 --- a/solidity/test/isms/layer-zero/LayerZeroV2Ism.t.sol +++ b/solidity/test/isms/layer-zero/LayerZeroV2Ism.t.sol @@ -23,8 +23,8 @@ contract LayerZeroV2IsmTest is Test { ) internal pure returns (bytes memory) { return abi.encodeCall( - AbstractMessageIdAuthorizedIsm.verifyMessageId, - (_messageId) + AbstractMessageIdAuthorizedIsm.preVerifyMessage, + (_messageId, 0) ); } @@ -133,7 +133,7 @@ contract LayerZeroV2IsmTest is Test { vm.stopPrank(); } - function testLzV2Ism_verifyMessageId_SetsCorrectMessageId( + function testLzV2Ism_preVerifyMessage_SetsCorrectMessageId( bytes32 messageId ) public { lZIsm.setAuthorizedHook(hook.addressToBytes32()); diff --git a/solidity/test/lib/RateLimited.t.sol b/solidity/test/lib/RateLimited.t.sol index 1276c86af..d0ea273de 100644 --- a/solidity/test/lib/RateLimited.t.sol +++ b/solidity/test/lib/RateLimited.t.sol @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MIT or Apache-2.0 pragma solidity ^0.8.13; + import {Test} from "forge-std/Test.sol"; import {RateLimited} from "../../contracts/libs/RateLimited.sol"; @@ -13,8 +14,13 @@ contract RateLimitLibTest is Test { rateLimited = new RateLimited(MAX_CAPACITY); } + function testConstructor_revertsWhen_lowCapacity() public { + vm.expectRevert("Capacity must be greater than DURATION"); + new RateLimited(1 days - 1); + } + function testRateLimited_setsNewLimit() external { - rateLimited.setRefillRate(2 ether); + assert(rateLimited.setRefillRate(2 ether) > 0); assertApproxEqRel(rateLimited.maxCapacity(), 2 ether, ONE_PERCENT); assertEq(rateLimited.refillRate(), uint256(2 ether) / 1 days); // 2 ether / 1 day } @@ -45,6 +51,25 @@ contract RateLimitLibTest is Test { rateLimited.setRefillRate(1 ether); } + function testConsumedFilledLevelEvent() public { + uint256 consumeAmount = 0.5 ether; + + vm.expectEmit(true, true, false, true); + emit RateLimited.ConsumedFilledLevel( + 499999999999993600, + block.timestamp + ); // precision loss + rateLimited.validateAndConsumeFilledLevel(consumeAmount); + + assertApproxEqRelDecimal( + rateLimited.filledLevel(), + MAX_CAPACITY - consumeAmount, + 1e14, + 0 + ); + assertEq(rateLimited.lastUpdated(), block.timestamp); + } + function testRateLimited_neverReturnsGtMaxLimit( uint256 _newAmount, uint40 _newTime @@ -104,4 +129,24 @@ contract RateLimitLibTest is Test { currentTargetLimit = rateLimited.calculateCurrentLevel(); assertApproxEqRel(currentTargetLimit, MAX_CAPACITY, ONE_PERCENT); } + + function testCalculateCurrentLevel_revertsWhenCapacityIsZero() public { + rateLimited.setRefillRate(0); + + vm.expectRevert("RateLimitNotSet"); + rateLimited.calculateCurrentLevel(); + } + + function testValidateAndConsumeFilledLevel_revertsWhenExceedingLimit() + public + { + vm.warp(1 days); + uint256 initialLevel = rateLimited.calculateCurrentLevel(); + + uint256 excessAmount = initialLevel + 1 ether; + + vm.expectRevert("RateLimitExceeded"); + rateLimited.validateAndConsumeFilledLevel(excessAmount); + assertEq(rateLimited.calculateCurrentLevel(), initialLevel); + } } diff --git a/solidity/test/token/HypERC20.t.sol b/solidity/test/token/HypERC20.t.sol index ce50c611e..c3b7da74c 100644 --- a/solidity/test/token/HypERC20.t.sol +++ b/solidity/test/token/HypERC20.t.sol @@ -49,7 +49,7 @@ abstract contract HypTokenTest is Test { uint256 internal constant TOTAL_SUPPLY = 1_000_000e18; uint256 internal REQUIRED_VALUE; // initialized in setUp uint256 internal constant GAS_LIMIT = 10_000; - uint256 internal constant TRANSFER_AMT = 100e18; + uint256 internal TRANSFER_AMT = 100e18; string internal constant NAME = "HyperlaneInu"; string internal constant SYMBOL = "HYP"; address internal constant ALICE = address(0x1); @@ -406,6 +406,11 @@ contract HypERC20CollateralTest is HypTokenTest { _enrollRemoteTokenRouter(); } + function test_constructor_revert_ifInvalidToken() public { + vm.expectRevert("HypERC20Collateral: invalid token"); + new HypERC20Collateral(address(0), address(localMailbox)); + } + function testInitialize_revert_ifAlreadyInitialized() public {} function testRemoteTransfer() public { @@ -640,7 +645,25 @@ contract HypNativeTest is HypTokenTest { _enrollRemoteTokenRouter(); } - function testInitialize_revert_ifAlreadyInitialized() public {} + function testTransfer_withHookSpecified( + uint256 fee, + bytes calldata metadata + ) public override { + TestPostDispatchHook hook = new TestPostDispatchHook(); + hook.setFee(fee); + + uint256 value = REQUIRED_VALUE + TRANSFER_AMT; + + vm.prank(ALICE); + primaryToken.approve(address(localToken), TRANSFER_AMT); + bytes32 messageId = _performRemoteTransferWithHook( + value, + TRANSFER_AMT, + address(hook), + metadata + ); + assertTrue(hook.messageDispatched(messageId)); + } function testRemoteTransfer() public { _performRemoteTransferWithEmit( @@ -668,4 +691,28 @@ contract HypNativeTest is HypTokenTest { TRANSFER_AMT + GAS_LIMIT * igp.gasPrice() ); } + + function test_transferRemote_reverts_whenAmountExceedsValue( + uint256 nativeValue + ) public { + vm.assume(nativeValue < address(this).balance); + + address recipient = address(0xdeadbeef); + bytes32 bRecipient = TypeCasts.addressToBytes32(recipient); + vm.expectRevert("Native: amount exceeds msg.value"); + nativeToken.transferRemote{value: nativeValue}( + DESTINATION, + bRecipient, + nativeValue + 1 + ); + + vm.expectRevert("Native: amount exceeds msg.value"); + nativeToken.transferRemote{value: nativeValue}( + DESTINATION, + bRecipient, + nativeValue + 1, + bytes(""), + address(0) + ); + } } diff --git a/solidity/test/token/HypERC20CollateralVaultDeposit.t.sol b/solidity/test/token/HypERC20CollateralVaultDeposit.t.sol index 3dba941b3..8d2f9226e 100644 --- a/solidity/test/token/HypERC20CollateralVaultDeposit.t.sol +++ b/solidity/test/token/HypERC20CollateralVaultDeposit.t.sol @@ -16,8 +16,11 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {HypERC4626} from "../../contracts/token/extensions/HypERC4626.sol"; + import {ERC4626Test} from "../../contracts/test/ERC4626/ERC4626Test.sol"; import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {TokenMessage} from "../../contracts/token/libs/TokenMessage.sol"; import {HypTokenTest} from "./HypERC20.t.sol"; import {HypERC4626OwnerCollateral} from "../../contracts/token/extensions/HypERC4626OwnerCollateral.sol"; @@ -227,6 +230,20 @@ contract HypERC4626OwnerCollateralTest is HypTokenTest { ); } + function testERC4626VaultDeposit_TransferFromSender_CorrectMetadata() + public + { + remoteToken = new HypERC4626(18, address(remoteMailbox), ORIGIN); + _enrollRemoteTokenRouter(); + vm.prank(ALICE); + + primaryToken.approve(address(localToken), TRANSFER_AMT); + _performRemoteTransfer(0, TRANSFER_AMT, 1); + + assertEq(HypERC4626(address(remoteToken)).exchangeRate(), 1e10); + assertEq(HypERC4626(address(remoteToken)).previousNonce(), 1); + } + function testBenchmark_overheadGasUsage() public override { vm.prank(ALICE); primaryToken.approve(address(localToken), TRANSFER_AMT); @@ -243,4 +260,34 @@ contract HypERC4626OwnerCollateralTest is HypTokenTest { uint256 gasAfter = gasleft(); console.log("Overhead gas usage: %d", gasBefore - gasAfter); } + + function _performRemoteTransfer( + uint256 _msgValue, + uint256 _amount, + uint32 _nonce + ) internal { + vm.prank(ALICE); + localToken.transferRemote{value: _msgValue}( + DESTINATION, + BOB.addressToBytes32(), + _amount + ); + + vm.expectEmit(true, true, false, true); + emit ReceivedTransferRemote(ORIGIN, BOB.addressToBytes32(), _amount); + bytes memory _tokenMessage = TokenMessage.format( + BOB.addressToBytes32(), + _amount, + abi.encode(uint256(1e10), _nonce) + ); + + vm.prank(address(remoteMailbox)); + remoteToken.handle( + ORIGIN, + address(localToken).addressToBytes32(), + _tokenMessage + ); + + assertEq(remoteToken.balanceOf(BOB), _amount); + } } diff --git a/solidity/test/token/HypERC4626Test.t.sol b/solidity/test/token/HypERC4626Test.t.sol index d09e0aae6..d5aecb8e2 100644 --- a/solidity/test/token/HypERC4626Test.t.sol +++ b/solidity/test/token/HypERC4626Test.t.sol @@ -24,7 +24,9 @@ import {MockMailbox} from "../../contracts/mock/MockMailbox.sol"; import {HypERC20} from "../../contracts/token/HypERC20.sol"; import {HypERC4626Collateral} from "../../contracts/token/extensions/HypERC4626Collateral.sol"; import {HypERC4626} from "../../contracts/token/extensions/HypERC4626.sol"; +import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol"; import "../../contracts/test/ERC4626/ERC4626Test.sol"; +import {ProtocolFee} from "../../contracts/hooks/ProtocolFee.sol"; contract HypERC4626CollateralTest is HypTokenTest { using TypeCasts for address; @@ -43,6 +45,8 @@ contract HypERC4626CollateralTest is HypTokenTest { HypERC4626 remoteRebasingToken; HypERC4626 peerRebasingToken; + event ExchangeRateUpdated(uint256 newExchangeRate, uint32 rateUpdateNonce); + function setUp() public override { super.setUp(); @@ -95,6 +99,7 @@ contract HypERC4626CollateralTest is HypTokenTest { peerRebasingToken = HypERC4626(address(peerToken)); primaryToken.transfer(ALICE, 1000e18); + primaryToken.transfer(BOB, 1000e18); uint32[] memory domains = new uint32[](3); domains[0] = ORIGIN; @@ -108,6 +113,11 @@ contract HypERC4626CollateralTest is HypTokenTest { _connectRouters(domains, addresses); } + function testDisableInitializers() public { + vm.expectRevert("Initializable: contract is already initialized"); + remoteToken.initialize(0, "", "", address(0), address(0), address(0)); + } + function test_collateralDomain() public view { assertEq( remoteRebasingToken.collateralDomain(), @@ -121,12 +131,36 @@ contract HypERC4626CollateralTest is HypTokenTest { _accrueYield(); - localRebasingToken.rebase(DESTINATION); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); remoteMailbox.processNextInboundMessage(); - assertEq( + assertApproxEqRelDecimal( remoteToken.balanceOf(BOB), - transferAmount + _discountedYield() + transferAmount + _discountedYield(), + 1e14, + 0 + ); + } + + function testRemoteTransfer_rebaseWithCustomHook() public { + _performRemoteTransferWithoutExpectation(0, transferAmount); + assertEq(remoteToken.balanceOf(BOB), transferAmount); + + _accrueYield(); + + uint256 FEE = 1e18; + ProtocolFee customHook = new ProtocolFee( + FEE, + FEE, + address(this), + address(this) + ); + + localRebasingToken.rebase{value: FEE}( + DESTINATION, + StandardHookMetadata.overrideMsgValue(FEE), + address(customHook) ); + assertEq(address(customHook).balance, FEE); } function testRebaseWithTransfer() public { @@ -146,6 +180,47 @@ contract HypERC4626CollateralTest is HypTokenTest { ); } + function testRebase_exchangeRateUpdateInSequence() public { + _performRemoteTransferWithoutExpectation(0, transferAmount); + _accrueYield(); + + uint256 exchangeRateInitially = remoteRebasingToken.exchangeRate(); + + vm.startPrank(BOB); + primaryToken.approve(address(localToken), transferAmount); + localToken.transferRemote( + DESTINATION, + BOB.addressToBytes32(), + transferAmount + ); + vm.stopPrank(); + + _accrueYield(); + + vm.startPrank(ALICE); + primaryToken.approve(address(localToken), transferAmount); + localToken.transferRemote( + DESTINATION, + BOB.addressToBytes32(), + transferAmount + ); + vm.stopPrank(); + + // process ALICE's transfer + + vm.expectEmit(true, true, true, true); + emit ExchangeRateUpdated(10721400472, 3); + remoteMailbox.processInboundMessage(2); + uint256 exchangeRateBefore = remoteRebasingToken.exchangeRate(); + + // process BOB's transfer + remoteMailbox.processInboundMessage(1); + uint256 exchangeRateAfter = remoteRebasingToken.exchangeRate(); + + assertLt(exchangeRateInitially, exchangeRateBefore); // updates bc nonce=2 is after nonce=0 + assertEq(exchangeRateBefore, exchangeRateAfter); // doesn't update bc nonce=1 is before nonce=0 + } + function testSyntheticTransfers_withRebase() public { _performRemoteTransferWithoutExpectation(0, transferAmount); assertEq(remoteToken.balanceOf(BOB), transferAmount); @@ -172,7 +247,110 @@ contract HypERC4626CollateralTest is HypTokenTest { ); } + function testTransferFrom() public { + _performRemoteTransferWithoutExpectation(0, transferAmount); + assertEq(remoteToken.balanceOf(BOB), transferAmount); + + uint256 transferAmount2 = 50e18; + vm.prank(BOB); + remoteToken.approve(CAROL, transferAmount2); + + vm.prank(CAROL); + bool success = remoteToken.transferFrom(BOB, DANIEL, transferAmount2); + assertTrue(success, "TransferFrom should succeed"); + + assertEq( + remoteToken.balanceOf(BOB), + transferAmount - transferAmount2, + "BOB's balance should decrease" + ); + assertEq( + remoteToken.balanceOf(DANIEL), + transferAmount2, + "DANIEL's balance should increase" + ); + assertEq( + remoteToken.allowance(BOB, CAROL), + 0, + "Allowance should be zero after transfer" + ); + } + + event Transfer(address indexed from, address indexed to, uint256 value); + + function testTransferEvent() public { + _performRemoteTransferWithoutExpectation(0, transferAmount); + assertEq(remoteToken.balanceOf(BOB), transferAmount); + + uint256 transferAmount2 = 50e18; + vm.expectEmit(true, true, false, true); + emit Transfer(BOB, CAROL, transferAmount2); + + vm.prank(BOB); + remoteToken.transfer(CAROL, transferAmount2); + + assertEq( + remoteToken.balanceOf(BOB), + transferAmount - transferAmount2, + "BOB's balance should decrease" + ); + assertEq( + remoteToken.balanceOf(CAROL), + transferAmount2, + "CAROL's balance should increase" + ); + } + + function testTotalShares() public { + uint256 initialShares = remoteRebasingToken.totalShares(); + assertEq(initialShares, 0, "Initial shares should be zero"); + + _performRemoteTransferWithoutExpectation(0, transferAmount); + uint256 sharesAfterTransfer = remoteRebasingToken.totalShares(); + assertEq( + sharesAfterTransfer, + remoteRebasingToken.assetsToShares(transferAmount), + "Shares should match transferred amount converted to shares" + ); + + _accrueYield(); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); + remoteMailbox.processNextInboundMessage(); + + uint256 sharesAfterYield = remoteRebasingToken.totalShares(); + assertEq( + sharesAfterYield, + sharesAfterTransfer, + "Total shares should remain constant after yield accrual" + ); + } + + function testShareBalanceOf() public { + _performRemoteTransferWithoutExpectation(0, transferAmount); + + uint256 bobShareBalance = remoteRebasingToken.shareBalanceOf(BOB); + assertEq( + bobShareBalance, + remoteRebasingToken.assetsToShares(transferAmount), + "Bob's share balance should match transferred amount converted to shares" + ); + + _accrueYield(); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); + remoteMailbox.processNextInboundMessage(); + + uint256 bobShareBalanceAfterYield = remoteRebasingToken.shareBalanceOf( + BOB + ); + assertEq( + bobShareBalanceAfterYield, + bobShareBalance, + "Bob's share balance should remain constant after yield accrual" + ); + } + function testWithdrawalWithoutYield() public { + uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB); _performRemoteTransferWithoutExpectation(0, transferAmount); assertEq(remoteToken.balanceOf(BOB), transferAmount); @@ -183,10 +361,14 @@ contract HypERC4626CollateralTest is HypTokenTest { transferAmount ); localMailbox.processNextInboundMessage(); - assertEq(primaryToken.balanceOf(BOB), transferAmount); + assertEq( + primaryToken.balanceOf(BOB) - bobPrimaryBefore, + transferAmount + ); } function testWithdrawalWithYield() public { + uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB); _performRemoteTransferWithoutExpectation(0, transferAmount); assertEq(remoteToken.balanceOf(BOB), transferAmount); @@ -205,19 +387,28 @@ contract HypERC4626CollateralTest is HypTokenTest { uint256 _expectedBal = transferAmount + _discountedYield(); // BOB gets the yield even though it didn't rebase - assertApproxEqRelDecimal(_bobBal, _expectedBal, 1e14, 0); - assertTrue(_bobBal < _expectedBal, "Transfer remote should round down"); + assertApproxEqRelDecimal( + _bobBal - bobPrimaryBefore, + _expectedBal, + 1e14, + 0 + ); + assertTrue( + _bobBal - bobPrimaryBefore < _expectedBal, + "Transfer remote should round down" + ); assertEq(vault.accumulatedFees(), YIELD / 10); } function testWithdrawalAfterYield() public { + uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB); _performRemoteTransferWithoutExpectation(0, transferAmount); assertEq(remoteToken.balanceOf(BOB), transferAmount); _accrueYield(); - localRebasingToken.rebase(DESTINATION); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); remoteMailbox.processNextInboundMessage(); // Use balance here since it might be off by <1bp @@ -230,7 +421,7 @@ contract HypERC4626CollateralTest is HypTokenTest { ); localMailbox.processNextInboundMessage(); assertApproxEqRelDecimal( - primaryToken.balanceOf(BOB), + primaryToken.balanceOf(BOB) - bobPrimaryBefore, transferAmount + _discountedYield(), 1e14, 0 @@ -256,7 +447,7 @@ contract HypERC4626CollateralTest is HypTokenTest { _accrueYield(); _accrueYield(); // earning 2x yield to be split - localRebasingToken.rebase(DESTINATION); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); vm.prank(CAROL); remoteToken.transferRemote( @@ -287,13 +478,14 @@ contract HypERC4626CollateralTest is HypTokenTest { } function testWithdrawalAfterDrawdown() public { + uint256 bobPrimaryBefore = primaryToken.balanceOf(BOB); _performRemoteTransferWithoutExpectation(0, transferAmount); assertEq(remoteToken.balanceOf(BOB), transferAmount); // decrease collateral in vault by 10% uint256 drawdown = 5e18; primaryToken.burnFrom(address(vault), drawdown); - localRebasingToken.rebase(DESTINATION); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); remoteMailbox.processNextInboundMessage(); // Use balance here since it might be off by <1bp @@ -306,7 +498,7 @@ contract HypERC4626CollateralTest is HypTokenTest { ); localMailbox.processNextInboundMessage(); assertApproxEqRelDecimal( - primaryToken.balanceOf(BOB), + primaryToken.balanceOf(BOB) - bobPrimaryBefore, transferAmount - drawdown, 1e14, 0 @@ -319,7 +511,7 @@ contract HypERC4626CollateralTest is HypTokenTest { _accrueYield(); - localRebasingToken.rebase(DESTINATION); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); remoteMailbox.processNextInboundMessage(); vm.prank(BOB); @@ -330,13 +522,23 @@ contract HypERC4626CollateralTest is HypTokenTest { ); peerMailbox.processNextInboundMessage(); - assertEq(remoteRebasingToken.exchangeRate(), 1045e7); // 5 * 0.9 = 4.5% yield + assertApproxEqRelDecimal( + remoteRebasingToken.exchangeRate(), + 1045e7, + 1e14, + 0 + ); // 5 * 0.9 = 4.5% yield assertEq(peerRebasingToken.exchangeRate(), 1e10); // assertingthat transfers by the synthetic variant don't impact the exchang rate - localRebasingToken.rebase(PEER_DESTINATION); + localRebasingToken.rebase(PEER_DESTINATION, bytes(""), address(0)); peerMailbox.processNextInboundMessage(); - assertEq(peerRebasingToken.exchangeRate(), 1045e7); // asserting that the exchange rate is set finally by the collateral variant + assertApproxEqRelDecimal( + peerRebasingToken.exchangeRate(), + 1045e7, + 1e14, + 0 + ); // asserting that the exchange rate is set finally by the collateral variant } function test_cyclicTransfers() public { @@ -346,7 +548,7 @@ contract HypERC4626CollateralTest is HypTokenTest { _accrueYield(); - localRebasingToken.rebase(DESTINATION); // yield is added + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); // yield is added remoteMailbox.processNextInboundMessage(); // BOB: remote -> peer(BOB) (yield is leftover) @@ -358,7 +560,7 @@ contract HypERC4626CollateralTest is HypTokenTest { ); peerMailbox.processNextInboundMessage(); - localRebasingToken.rebase(PEER_DESTINATION); + localRebasingToken.rebase(PEER_DESTINATION, bytes(""), address(0)); peerMailbox.processNextInboundMessage(); // BOB: peer -> local(CAROL) @@ -385,6 +587,32 @@ contract HypERC4626CollateralTest is HypTokenTest { ); } + function testTotalSupply() public { + uint256 initialSupply = remoteToken.totalSupply(); + assertEq(initialSupply, 0, "Initial supply should be zero"); + + _performRemoteTransferWithoutExpectation(0, transferAmount); + uint256 supplyAfterTransfer = remoteToken.totalSupply(); + assertEq( + supplyAfterTransfer, + transferAmount, + "Supply should match transferred amount" + ); + + _accrueYield(); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); + remoteMailbox.processNextInboundMessage(); + + uint256 supplyAfterYield = remoteToken.totalSupply(); + assertApproxEqRelDecimal( + supplyAfterYield, + transferAmount + _discountedYield(), + 1e14, + 0, + "Supply should include yield" + ); + } + function testTransfer_withHookSpecified( uint256, bytes calldata @@ -398,11 +626,13 @@ contract HypERC4626CollateralTest is HypTokenTest { _accrueYield(); - localRebasingToken.rebase(DESTINATION); + localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); remoteMailbox.processNextInboundMessage(); - assertEq( + assertApproxEqRelDecimal( remoteToken.balanceOf(BOB), - transferAmount + _discountedYield() + transferAmount + _discountedYield(), + 1e14, + 0 ); vm.prank(address(localMailbox)); diff --git a/solidity/test/token/HypNativeScaled.t.sol b/solidity/test/token/HypNativeScaled.t.sol index a9af062ae..ffded6565 100644 --- a/solidity/test/token/HypNativeScaled.t.sol +++ b/solidity/test/token/HypNativeScaled.t.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.0; import "forge-std/Test.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; import {HypNativeScaled} from "../../contracts/token/extensions/HypNativeScaled.sol"; import {HypERC20} from "../../contracts/token/HypERC20.sol"; import {HypNative} from "../../contracts/token/HypNative.sol"; @@ -14,6 +15,8 @@ contract HypNativeScaledTest is Test { uint32 nativeDomain = 1; uint32 synthDomain = 2; + address internal constant ALICE = address(0x1); + uint8 decimals = 9; uint256 mintAmount = 123456789; uint256 nativeDecimals = 18; @@ -149,7 +152,7 @@ contract HypNativeScaledTest is Test { address recipient = address(0xdeadbeef); bytes32 bRecipient = TypeCasts.addressToBytes32(recipient); - vm.assume(nativeValue < address(this).balance); + vm.deal(address(this), nativeValue); vm.expectEmit(true, true, true, true); emit SentTransferRemote(synthDomain, bRecipient, synthAmount); native.transferRemote{value: nativeValue}( @@ -161,6 +164,33 @@ contract HypNativeScaledTest is Test { assertEq(synth.balanceOf(recipient), synthAmount); } + function testTransfer_withHookSpecified( + uint256 amount, + bytes calldata metadata + ) public { + vm.assume(amount <= mintAmount); + + uint256 nativeValue = amount * (10 ** nativeDecimals); + uint256 synthAmount = amount * (10 ** decimals); + address recipient = address(0xdeadbeef); + bytes32 bRecipient = TypeCasts.addressToBytes32(recipient); + + TestPostDispatchHook hook = new TestPostDispatchHook(); + + vm.deal(address(this), nativeValue); + vm.expectEmit(true, true, true, true); + emit SentTransferRemote(synthDomain, bRecipient, synthAmount); + native.transferRemote{value: nativeValue}( + synthDomain, + bRecipient, + nativeValue, + metadata, + address(hook) + ); + environment.processNextPendingMessageFromDestination(); + assertEq(synth.balanceOf(recipient), synthAmount); + } + function test_transferRemote_reverts_whenAmountExceedsValue( uint256 nativeValue ) public { @@ -174,5 +204,14 @@ contract HypNativeScaledTest is Test { bRecipient, nativeValue + 1 ); + + vm.expectRevert("Native: amount exceeds msg.value"); + native.transferRemote{value: nativeValue}( + synthDomain, + bRecipient, + nativeValue + 1, + bytes(""), + address(0) + ); } } diff --git a/solidity/test/token/WHypERC4626.t.sol b/solidity/test/token/WHypERC4626.t.sol new file mode 100644 index 000000000..5684eeee2 --- /dev/null +++ b/solidity/test/token/WHypERC4626.t.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {MockMailbox} from "../../contracts/mock/MockMailbox.sol"; +import {WHypERC4626} from "../../contracts/token/extensions/WHypERC4626.sol"; +import {HypERC4626} from "../../contracts/token/extensions/HypERC4626.sol"; +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MockHypERC4626 is HypERC4626 { + constructor(address _mailbox) HypERC4626(18, _mailbox, 2) {} + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } +} + +contract WHypERC4626Test is Test { + WHypERC4626 public wHypERC4626; + MockHypERC4626 public underlyingToken; + address public alice = address(0x1); + address public bob = address(0x2); + + function setUp() public { + MockMailbox mailbox = new MockMailbox(1); + underlyingToken = new MockHypERC4626(address(mailbox)); + wHypERC4626 = new WHypERC4626( + underlyingToken, + "Wrapped Rebasing Token", + "WRT" + ); + + underlyingToken.mint(alice, 1000 * 10 ** 18); + underlyingToken.mint(bob, 1000 * 10 ** 18); + } + + function test_wrap() public { + uint256 amount = 100 * 10 ** 18; + + vm.startPrank(alice); + underlyingToken.approve(address(wHypERC4626), amount); + uint256 wrappedAmount = wHypERC4626.wrap(amount); + + assertEq(wHypERC4626.balanceOf(alice), wrappedAmount); + assertEq(underlyingToken.balanceOf(alice), 900 * 10 ** 18); + vm.stopPrank(); + } + + function test_wrap_revertsWhen_zeroAmount() public { + vm.startPrank(alice); + underlyingToken.approve(address(wHypERC4626), 0); + vm.expectRevert("WHypERC4626: wrap amount must be greater than 0"); + wHypERC4626.wrap(0); + vm.stopPrank(); + } + + function test_unwrap() public { + uint256 amount = 100 * 10 ** 18; + + vm.startPrank(alice); + underlyingToken.approve(address(wHypERC4626), amount); + uint256 wrappedAmount = wHypERC4626.wrap(amount); + + uint256 unwrappedAmount = wHypERC4626.unwrap(wrappedAmount); + + assertEq(wHypERC4626.balanceOf(alice), 0); + assertEq(underlyingToken.balanceOf(alice), 1000 * 10 ** 18); + assertEq(unwrappedAmount, amount); + vm.stopPrank(); + } + + function test_unwrap_revertsWhen_zeroAmount() public { + vm.startPrank(alice); + vm.expectRevert("WHypERC4626: unwrap amount must be greater than 0"); + wHypERC4626.unwrap(0); + vm.stopPrank(); + } + + function test_getWrappedAmount() public view { + uint256 amount = 100 * 10 ** 18; + uint256 wrappedAmount = wHypERC4626.getWrappedAmount(amount); + + assertEq(wrappedAmount, underlyingToken.assetsToShares(amount)); + } + + function test_getUnderlyingAmount() public view { + uint256 amount = 100 * 10 ** 18; + uint256 underlyingAmount = wHypERC4626.getUnderlyingAmount(amount); + + assertEq(underlyingAmount, underlyingToken.sharesToAssets(amount)); + } + + function test_wrappedPerUnderlying() public view { + uint256 wrappedPerUnderlying = wHypERC4626.wrappedPerUnderlying(); + + assertEq( + wrappedPerUnderlying, + underlyingToken.assetsToShares(1 * 10 ** underlyingToken.decimals()) + ); + } + + function test_underlyingPerWrapped() public view { + uint256 underlyingPerWrapped = wHypERC4626.underlyingPerWrapped(); + + assertEq( + underlyingPerWrapped, + underlyingToken.sharesToAssets(1 * 10 ** underlyingToken.decimals()) + ); + } + + function test_decimals() public view { + uint8 decimals = wHypERC4626.decimals(); + + assertEq(decimals, underlyingToken.decimals()); + } +} diff --git a/solidity/update_abis.sh b/solidity/update_abis.sh index 94e95ed98..1095eb95c 100755 --- a/solidity/update_abis.sh +++ b/solidity/update_abis.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh # Must be ran from the `solidity` directory @@ -6,7 +6,7 @@ copy() { # Optionally allow path to be passed in, and extract the contract name # as the string following the last instance of `/` CONTRACT_NAME="${1##*/}" - jq .abi < artifacts/contracts/"$1".sol/"$CONTRACT_NAME".json > ../rust/chains/hyperlane-ethereum/abis/"$CONTRACT_NAME".abi.json + jq .abi < artifacts/contracts/"$1".sol/"$CONTRACT_NAME".json > ../rust/main/chains/hyperlane-ethereum/abis/"$CONTRACT_NAME".abi.json } copy interfaces/IMailbox && \ diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 6ac6fea3f..96e835745 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,25 @@ # @hyperlane-xyz/ccip-server +## 5.6.2 + +## 5.6.1 + +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + +## 5.3.0 + +## 5.2.1 + +## 5.2.0 + +### Patch Changes + +- 5a0d68bdc: replace import console module with direct console + ## 5.1.0 ## 5.0.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index faaaf2cbb..35442643c 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "5.1.0", + "version": "5.6.2", "description": "CCIP server", "typings": "dist/index.d.ts", "typedocMain": "src/index.ts", diff --git a/typescript/ccip-server/src/services/HyperlaneService.ts b/typescript/ccip-server/src/services/HyperlaneService.ts index 47d86d6ee..a3c8b3b3f 100644 --- a/typescript/ccip-server/src/services/HyperlaneService.ts +++ b/typescript/ccip-server/src/services/HyperlaneService.ts @@ -1,5 +1,3 @@ -import { info } from 'console'; - import { Message, MessageTx } from './explorerTypes'; // These types are copied from hyperlane-explorer. TODO: export them so this file can use them directly. @@ -21,7 +19,7 @@ class HyperlaneService { * @param id: Message id to look up */ async getOriginBlockByMessageId(id: string): Promise { - info(`Fetching block for id: ${id}`); + console.info(`Fetching block for id: ${id}`); const response = await fetch( `${this.baseUrl}?module=message&action=${API_ACTION.GetMessages}&id=${id}`, ); diff --git a/typescript/cli/.mocharc-e2e.json b/typescript/cli/.mocharc-e2e.json new file mode 100644 index 000000000..ecabac62f --- /dev/null +++ b/typescript/cli/.mocharc-e2e.json @@ -0,0 +1,8 @@ +{ + "extensions": ["ts"], + "spec": ["src/**/*.e2e-test.ts"], + "node-option": [ + "experimental-specifier-resolution=node", + "loader=ts-node/esm" + ] +} diff --git a/typescript/cli/.mocharc.json b/typescript/cli/.mocharc.json index 17ba33e2f..2d7433a2f 100644 --- a/typescript/cli/.mocharc.json +++ b/typescript/cli/.mocharc.json @@ -1,6 +1,6 @@ { "extensions": ["ts"], - "spec": ["src/**/*.test.*", "src/**/*.e2e-test.ts"], + "spec": ["src/**/*.test.*"], "node-option": [ "experimental-specifier-resolution=node", "loader=ts-node/esm" diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index 85209edd1..dfe8451c3 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,142 @@ # @hyperlane-xyz/cli +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/sdk@5.6.2 + +## 5.6.1 + +### Patch Changes + +- 3474a8450: Explicitly define inquirer/core and inquirier/figures dependencies + - @hyperlane-xyz/sdk@5.6.1 + - @hyperlane-xyz/utils@5.6.1 + +## 5.6.0 + +### Minor Changes + +- 41035aac8: Add strategyUrl detect and validation in the beginning of `warp apply` + Remove yaml transactions print from `warp apply` +- 29341950e: Adds new `core check` command to compare local configuration and on chain deployments. Adds memoization to the EvmHookReader to avoid repeating configuration derivation +- 32d0a67c2: Adds the warp check command to compare warp routes config files with on chain warp route deployments +- 3662297fc: Add prompt in `warp init` command to choose if a trusted relayer should be used instead of making the choice by default for the user and enable the `--yes` flag to default to a trusted ISM +- b1ff48bd1: Add rebasing yield route support into CLI/SDK +- d41aa6928: Add `EthJsonRpcBlockParameterTag` enum for validating reorgPeriod +- c3e9268f1: Add support for an arbitrary string in `reorgPeriod`, which is used as a block tag to get the finalized block. +- a4d5d692f: Update `warp apply` such that it updates in place AND extends in a single call +- 01e7070eb: updates the multi chain selection prompt by adding search functionality and an optional confirmation prompt for the current selection + +### Patch Changes + +- e89f9e35d: Update registry to v4.7.0 +- Updated dependencies [f1712deb7] +- Updated dependencies [46044a2e9] +- Updated dependencies [02a5b92ba] +- Updated dependencies [29341950e] +- Updated dependencies [8001bbbd6] +- Updated dependencies [32d0a67c2] +- Updated dependencies [b1ff48bd1] +- Updated dependencies [d41aa6928] +- Updated dependencies [c3e9268f1] +- Updated dependencies [7d7bcc1a3] +- Updated dependencies [7f3e0669d] +- Updated dependencies [2317eca3c] + - @hyperlane-xyz/utils@5.6.0 + - @hyperlane-xyz/sdk@5.6.0 + +## 5.5.0 + +### Patch Changes + +- fcfe91113: Reuse SDK transaction typings in tx submitters +- Updated dependencies [2afc484a2] +- Updated dependencies [2afc484a2] +- Updated dependencies [3254472e0] +- Updated dependencies [fcfe91113] +- Updated dependencies [6176c9861] + - @hyperlane-xyz/sdk@5.5.0 + - @hyperlane-xyz/utils@5.5.0 + +## 5.4.0 + +### Minor Changes + +- 4415ac224: Add Gnosis safe transaction builder to warp apply + +### Patch Changes + +- Updated dependencies [4415ac224] + - @hyperlane-xyz/utils@5.4.0 + - @hyperlane-xyz/sdk@5.4.0 + +## 5.3.0 + +### Minor Changes + +- 35d4503b9: Update to registry v4.3.6 +- aef3dbf4d: Remove mailbox choice prompt if it can be automatically detected from the registry + +### Patch Changes + +- a513e1b51: Override default with merkle hook for self relay +- Updated dependencies [eb47aaee8] +- Updated dependencies [50319d8ba] +- Updated dependencies [8de531fa4] +- Updated dependencies [746eeb9d9] +- Updated dependencies [fd536a79a] +- Updated dependencies [50319d8ba] + - @hyperlane-xyz/sdk@5.3.0 + - @hyperlane-xyz/utils@5.3.0 + +## 5.2.1 + +### Patch Changes + +- @hyperlane-xyz/sdk@5.2.1 +- @hyperlane-xyz/utils@5.2.1 + +## 5.2.0 + +### Minor Changes + +- a5afd20f3: Add CLI e2e typescript tests +- 203084df2: Added sdk support for Stake weighted ISM +- a46fe434a: Add hyperlane registry rpc and addresses --contract utils +- f2783c03b: Add ChainSubmissionStrategySchema +- 3c07ded5b: Add Safe submit functionality to warp apply + +### Patch Changes + +- Updated dependencies [a19e882fd] +- Updated dependencies [d6de34ad5] +- Updated dependencies [518a1bef9] +- Updated dependencies [203084df2] +- Updated dependencies [74a592e58] +- Updated dependencies [739af9a34] +- Updated dependencies [44588c31d] +- Updated dependencies [2bd540e0f] +- Updated dependencies [291c5fe36] +- Updated dependencies [69f17d99a] +- Updated dependencies [3ad5918da] +- Updated dependencies [291c5fe36] +- Updated dependencies [9563a8beb] +- Updated dependencies [73c232b3a] +- Updated dependencies [445b6222c] +- Updated dependencies [d6de34ad5] +- Updated dependencies [2e6176f67] +- Updated dependencies [f2783c03b] +- Updated dependencies [2ffb78f5c] +- Updated dependencies [3c07ded5b] +- Updated dependencies [815542dd7] + - @hyperlane-xyz/sdk@5.2.0 + - @hyperlane-xyz/utils@5.2.0 + ## 5.1.0 ### Minor Changes diff --git a/typescript/cli/ci-advanced-test.sh b/typescript/cli/ci-advanced-test.sh deleted file mode 100755 index eaf9cfabf..000000000 --- a/typescript/cli/ci-advanced-test.sh +++ /dev/null @@ -1,374 +0,0 @@ -#!/usr/bin/env bash - -_main() { - export LOG_LEVEL=DEBUG - - # set script location as repo root - cd "$(dirname "$0")/../.." - - TEST_TYPE_PRESET_HOOK="preset_hook_enabled" - TEST_TYPE_CONFIGURED_HOOK="configure_hook_enabled" - TEST_TYPE_PI_CORE="pi_with_core_chain" - - # set the first arg to 'configured_hook' to set the hook config as part of core deployment - # motivation is to test both the bare bone deployment (included in the docs) and the deployment - # with the routing over igp hook (which is closer to production deployment) - TEST_TYPE=$1 - if [ -z "$TEST_TYPE" ]; then - echo "Usage: ci-advanced-test.sh <$TEST_TYPE_PRESET_HOOK | $TEST_TYPE_CONFIGURED_HOOK | $TEST_TYPE_PI_CORE>" - exit 1 - fi - - prepare_environment_vars; - - prepare_anvil; - - DEPLOYER=$(cast rpc eth_accounts | jq -r '.[0]'); - - run_hyperlane_deploy_core_dry_run; - run_hyperlane_deploy_warp_dry_run; - - reset_anvil; - - run_hyperlane_deploy_core; - run_hyperlane_deploy_warp; - run_hyperlane_send_message; - - # cd ./rust; - - # run_validator; - # run_relayer; - # run_hyperlane_status; - - kill_anvil; - - echo "Done"; -} - -prepare_environment_vars() { - ANVIL_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 - CHAIN1=anvil1 - CHAIN2=anvil2 - EXAMPLES_PATH=./examples - TEST_CONFIGS_PATH=./test-configs - CLI_PATH=./typescript/cli - REGISTRY_PATH="$TEST_CONFIGS_PATH/anvil" - CORE_ISM_PATH="$EXAMPLES_PATH/ism.yaml" - WARP_DEPLOY_CONFIG_PATH="$EXAMPLES_PATH/warp-route-deployment.yaml" - DEPLOY_ERC20_PATH=./src/tests/deployTestErc20.ts - - # use different chain names and config for pi<>core test - if [ "$TEST_TYPE" == $TEST_TYPE_PI_CORE ]; then - CHAIN2=ethereum - REGISTRY_PATH="$TEST_CONFIGS_PATH/fork" - CORE_ISM_PATH="$REGISTRY_PATH/ism.yaml" - WARP_DEPLOY_CONFIG_PATH="$REGISTRY_PATH/warp-route-deployment.yaml" - fi - - CHAIN1_CAPS=$(echo "${CHAIN1}" | tr '[:lower:]' '[:upper:]') - CHAIN2_CAPS=$(echo "${CHAIN2}" | tr '[:lower:]' '[:upper:]') - - HOOK_FLAG=false - if [ "$TEST_TYPE" == $TEST_TYPE_CONFIGURED_HOOK ]; then - HOOK_FLAG=true - fi -} - -prepare_anvil() { - - CHAIN1_PORT=8545 - CHAIN2_PORT=8555 - - # Optional cleanup for previous runs, useful when running locally - pkill -f anvil - rm -rf /tmp/${CHAIN1}* - rm -rf /tmp/${CHAIN2}* - rm -rf /tmp/relayer - rm -f $CLI_PATH/$TEST_CONFIGS_PATH/*/chains/*/addresses.yaml - rm -rf $CLI_PATH/$TEST_CONFIGS_PATH/*/deployments - - if [[ $OSTYPE == 'darwin'* ]]; then - # kill child processes on exit, but only locally because - # otherwise it causes the script exit code to be non-zero - trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT - fi - - # Setup directories for anvil chains - for CHAIN in ${CHAIN1} ${CHAIN2} - do - mkdir -p /tmp/$CHAIN /tmp/$CHAIN/state /tmp/$CHAIN/validator /tmp/relayer - chmod -R 777 /tmp/relayer /tmp/$CHAIN - done - - # run the PI chain - anvil --chain-id 31337 -p ${CHAIN1_PORT} --state /tmp/${CHAIN1}/state --gas-price 1 > /dev/null & - sleep 1 - - # use different chain names for pi<>core test - if [ "$TEST_TYPE" == $TEST_TYPE_PI_CORE ]; then - # Fetch the RPC of chain to fork - cd typescript/infra - RPC_URL=$(LOG_LEVEL=error yarn tsx scripts/print-chain-metadatas.ts -e mainnet3 | jq -r ".${CHAIN2}.rpcUrls[0].http") - cd ../../ - - # run the fork chain - anvil -p ${CHAIN2_PORT} --state /tmp/${CHAIN2}/state --gas-price 1 --fork-url $RPC_URL --fork-retry-backoff 3 --compute-units-per-second 200 > /dev/null & - - # wait for fork to be ready - while ! cast bn --rpc-url http://127.0.0.1:${CHAIN2_PORT} &> /dev/null; do - sleep 1 - done - else - # run a second PI chain - anvil --chain-id 31338 -p ${CHAIN2_PORT} --state /tmp/${CHAIN2}/state --gas-price 1 > /dev/null & - sleep 1 - fi - - set -e -} - -reset_anvil() { - prepare_anvil -} - -kill_anvil() { - pkill -f anvil -} - -run_hyperlane_deploy_core_dry_run() { - if [ "$TEST_TYPE" == $TEST_TYPE_PI_CORE ]; then - return; - fi - - update_deployer_balance; - - echo -e "\nDry-running contract deployments to Alfajores" - yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ - --dry-run alfajores \ - --registry ${TEST_CONFIGS_PATH}/dry-run \ - --overrides " " \ - --config ${EXAMPLES_PATH}/core-config.yaml \ - --from-address 0xfaD1C94469700833717Fa8a3017278BC1cA8031C \ - --yes - - check_deployer_balance; -} - -run_hyperlane_deploy_warp_dry_run() { - if [ "$TEST_TYPE" == $TEST_TYPE_PI_CORE ]; then - return; - fi - - update_deployer_balance; - - echo -e "\nDry-running warp route deployments to Alfajores" - yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \ - --dry-run alfajores \ - --overrides ${TEST_CONFIGS_PATH}/dry-run \ - --config ${TEST_CONFIGS_PATH}/dry-run/warp-route-deployment.yaml \ - --from-address 0xfaD1C94469700833717Fa8a3017278BC1cA8031C \ - --yes - - check_deployer_balance; -} - -run_hyperlane_deploy_core() { - update_deployer_balance; - - echo -e "\nDeploying contracts to ${CHAIN1}" - yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ - --registry $REGISTRY_PATH \ - --overrides " " \ - --config ${EXAMPLES_PATH}/core-config.yaml \ - --chain $CHAIN1 \ - --key $ANVIL_KEY \ - --yes - - echo -e "\nDeploying contracts to ${CHAIN2}" - yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ - --registry $REGISTRY_PATH \ - --overrides " " \ - --config ${EXAMPLES_PATH}/core-config.yaml \ - --chain $CHAIN2 \ - --key $ANVIL_KEY \ - --yes - - check_deployer_balance; -} - -run_hyperlane_deploy_warp() { - update_deployer_balance; - - echo -e "\nDeploying hypNative warp route" - yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \ - --registry $REGISTRY_PATH \ - --overrides " " \ - --config $WARP_DEPLOY_CONFIG_PATH \ - --key $ANVIL_KEY \ - --yes - - yarn workspace @hyperlane-xyz/cli run tsx $DEPLOY_ERC20_PATH \ - http://127.0.0.1:$CHAIN1_PORT \ - $CHAIN1 $CHAIN2 $ANVIL_KEY \ - /tmp/warp-collateral-deployment.json \ - - echo "Deploying hypCollateral warp route" - yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \ - --registry $REGISTRY_PATH \ - --overrides " " \ - --config /tmp/warp-collateral-deployment.json \ - --key $ANVIL_KEY \ - --yes - - check_deployer_balance; -} - -run_hyperlane_send_message() { - update_deployer_balance; - - echo -e "\nSending test message" - yarn workspace @hyperlane-xyz/cli run hyperlane send message \ - --registry $REGISTRY_PATH \ - --overrides " " \ - --origin ${CHAIN1} \ - --destination ${CHAIN2} \ - --body "Howdy!" \ - --quick \ - --key $ANVIL_KEY \ - | tee /tmp/message1 - - check_deployer_balance; - - MESSAGE1_ID=`cat /tmp/message1 | grep "Message ID" | grep -E -o '0x[0-9a-f]+'` - echo "Message 1 ID: $MESSAGE1_ID" - - WARP_CONFIG_FILE="$REGISTRY_PATH/deployments/warp_routes/FAKE/${CHAIN1}-${CHAIN2}-config.yaml" - - echo -e "\nSending test warp transfer" - yarn workspace @hyperlane-xyz/cli run hyperlane warp send \ - --registry $REGISTRY_PATH \ - --overrides " " \ - --origin ${CHAIN1} \ - --destination ${CHAIN2} \ - --warp ${WARP_CONFIG_FILE} \ - --quick \ - --relay \ - --key $ANVIL_KEY \ - | tee /tmp/message2 - - MESSAGE2_ID=`cat /tmp/message2 | grep "Message ID" | grep -E -o '0x[0-9a-f]+'` - echo "Message 2 ID: $MESSAGE2_ID" -} - -run_validator() { - echo -e "\nPre-building validator with cargo" - cargo build --bin validator --features test-utils - - # set some default agent env vars, used by both validators and relayer - export HYP_CHAINS_${CHAIN1_CAPS}_BLOCKS_REORGPERIOD=0 - export HYP_CHAINS_${CHAIN1_CAPS}_CUSTOMRPCURLS="http://127.0.0.1:${CHAIN1_PORT}" - export HYP_CHAINS_${CHAIN2_CAPS}_BLOCKS_REORGPERIOD=0 - export HYP_CHAINS_${CHAIN2_CAPS}_CUSTOMRPCURLS="http://127.0.0.1:${CHAIN2_PORT}" - - VALIDATOR_PORT=9091 - - for CHAIN in ${CHAIN1} ${CHAIN2} - do - # don't need the second validator for pi<>core test - if [ "$CHAIN" == "$CHAIN2" ] && [ "$TEST_TYPE" == "$TEST_TYPE_PI_CORE" ]; then - echo "Skipping validator for $CHAIN2 due to $TEST_TYPE_PI_CORE test type" - continue - fi - - VALIDATOR_PORT=$((VALIDATOR_PORT+1)) - echo "Running validator on $CHAIN on port $VALIDATOR_PORT" - export CONFIG_FILES=/tmp/agent-config.json - export HYP_ORIGINCHAINNAME=${CHAIN} - export HYP_VALIDATOR_INTERVAL=1 - export HYP_VALIDATOR_TYPE=hexKey - export HYP_VALIDATOR_KEY=0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 - export HYP_CHECKPOINTSYNCER_TYPE=localStorage - export HYP_CHECKPOINTSYNCER_PATH=/tmp/${CHAIN}/validator - export HYP_TRACING_LEVEL=debug - export HYP_TRACING_FMT=compact - export HYP_METRICSPORT=$VALIDATOR_PORT - - cargo run --bin validator > /tmp/${CHAIN}/validator-logs.txt & - done - - echo "Validator running, sleeping to let it sync" - # This needs to be long to allow time for the cargo build to finish - sleep 20 - echo "Done sleeping" - - for CHAIN in ${CHAIN1} ${CHAIN2} - do - # only have one validator announce in pi<>core test - if [ "$CHAIN" == "$CHAIN2" ] && [ "$TEST_TYPE" == "$TEST_TYPE_PI_CORE" ]; then - echo "Skipping validator for $CHAIN2 due to $TEST_TYPE_PI_CORE test type" - continue - fi - - echo "Validator Announcement for ${CHAIN}:" - cat /tmp/${CHAIN}/validator/announcement.json - done -} - -run_relayer() { - echo -e "\nPre-building relayer with cargo" - cargo build --bin relayer --features test-utils - - echo "Running relayer" - export CONFIG_FILES=/tmp/agent-config.json - export HYP_RELAYCHAINS=${CHAIN1},${CHAIN2} - export HYP_ALLOWLOCALCHECKPOINTSYNCERS=true - export HYP_DB=/tmp/relayer - export HYP_GASPAYMENTENFORCEMENT='[{"type":"none"}]' - export HYP_CHAINS_${CHAIN1_CAPS}_SIGNER_TYPE=hexKey - export HYP_CHAINS_${CHAIN1_CAPS}_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 - export HYP_CHAINS_${CHAIN2_CAPS}_SIGNER_TYPE=hexKey - export HYP_CHAINS_${CHAIN2_CAPS}_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 - export HYP_METRICSPORT=9090 - - cargo run --bin relayer > /tmp/relayer/relayer-logs.txt & - - # This needs to be long to allow time for the cargo build to finish - echo "Waiting for relayer..." - sleep 20 - echo "Done running relayer, checking message delivery statuses" -} - -run_hyperlane_status() { - for i in "1 $MESSAGE1_ID" "2 $MESSAGE2_ID" - do - set -- $i - echo "Checking delivery status of $1: $2" - yarn workspace @hyperlane-xyz/cli run hyperlane status \ - --id $2 \ - --destination ${CHAIN2} \ - --registry $REGISTRY_PATH \ - --overrides " " \ - | tee /tmp/message-status-$1 - if ! grep -q "$2 was delivered" /tmp/message-status-$1; then - echo "ERROR: Message $1 was not delivered" - exit 1 - else - echo "Message $1 was delivered!" - fi - done -} - -update_deployer_balance() { - OLD_BALANCE=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT}); -} - -check_deployer_balance() { - NEW_BALANCE=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT}) - GAS_PRICE=$(cast gas-price --rpc-url http://127.0.0.1:${CHAIN1_PORT}) - GAS_USED=$(bc <<< "($OLD_BALANCE - $NEW_BALANCE) / $GAS_PRICE") - echo "Gas used: $GAS_USED" -} - -_main "$@"; - -exit; diff --git a/typescript/cli/cli.ts b/typescript/cli/cli.ts index d122f293d..77be0b86f 100644 --- a/typescript/cli/cli.ts +++ b/typescript/cli/cli.ts @@ -12,6 +12,7 @@ import { deployCommand } from './src/commands/deploy.js'; import { hookCommand } from './src/commands/hook.js'; import { ismCommand } from './src/commands/ism.js'; import { + disableProxyCommandOption, keyCommandOption, logFormatCommandOption, logLevelCommandOption, @@ -46,6 +47,7 @@ try { .option('registry', registryUriCommandOption) .option('overrides', overrideRegistryUriCommandOption) .option('key', keyCommandOption) + .option('disableProxy', disableProxyCommandOption) .option('yes', skipConfirmationOption) .global(['log', 'verbosity', 'registry', 'overrides', 'yes']) .middleware([ diff --git a/typescript/cli/examples/submit/strategy/gnosis-chain-tx-builder-strategy.yaml b/typescript/cli/examples/submit/strategy/gnosis-chain-tx-builder-strategy.yaml new file mode 100644 index 000000000..e681027e8 --- /dev/null +++ b/typescript/cli/examples/submit/strategy/gnosis-chain-tx-builder-strategy.yaml @@ -0,0 +1,6 @@ +basesepolia: + submitter: + chain: 'basesepolia' + type: gnosisSafeTxBuilder + version: '1.0' + safeAddress: '0x7232Ad76d905ae9D8D00379359DDa744a7A21C46' diff --git a/typescript/cli/examples/submit/strategy/json-rpc-chain-strategy.yaml b/typescript/cli/examples/submit/strategy/json-rpc-chain-strategy.yaml index 6e4c2ce6f..20197ffd6 100644 --- a/typescript/cli/examples/submit/strategy/json-rpc-chain-strategy.yaml +++ b/typescript/cli/examples/submit/strategy/json-rpc-chain-strategy.yaml @@ -1,3 +1,6 @@ -anvil1: +anvil2: + submitter: + type: jsonRpc +anvil3: submitter: type: jsonRpc diff --git a/typescript/cli/package.json b/typescript/cli/package.json index b95502542..50d2d4f49 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,14 +1,17 @@ { "name": "@hyperlane-xyz/cli", - "version": "5.1.0", + "version": "5.6.2", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", - "@hyperlane-xyz/registry": "2.5.0", - "@hyperlane-xyz/sdk": "5.1.0", - "@hyperlane-xyz/utils": "5.1.0", + "@hyperlane-xyz/registry": "4.7.0", + "@hyperlane-xyz/sdk": "5.6.2", + "@hyperlane-xyz/utils": "5.6.2", + "@inquirer/core": "9.0.10", + "@inquirer/figures": "1.0.5", "@inquirer/prompts": "^3.0.0", + "ansi-escapes": "^7.0.0", "asn1.js": "^5.4.1", "bignumber.js": "^9.1.1", "chalk": "^5.3.0", @@ -16,7 +19,7 @@ "latest-version": "^8.0.0", "terminal-link": "^3.0.0", "tsx": "^4.7.1", - "yaml": "^2.4.1", + "yaml": "2.4.5", "yargs": "^17.7.2", "zod": "^3.21.2", "zod-validation-error": "^3.3.0", @@ -25,17 +28,19 @@ "devDependencies": { "@ethersproject/abi": "*", "@ethersproject/providers": "*", + "@types/chai-as-promised": "^8", "@types/mocha": "^10.0.1", "@types/node": "^18.14.5", "@types/yargs": "^17.0.24", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", - "chai": "^4.3.6", + "chai": "^4.5.0", + "chai-as-promised": "^8.0.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "mocha": "^10.2.0", "prettier": "^2.8.8", - "typescript": "^5.1.6" + "typescript": "5.3.3" }, "scripts": { "hyperlane": "node ./dist/cli.js", @@ -44,7 +49,8 @@ "clean": "rm -rf ./dist", "lint": "eslint . --ext .ts", "prettier": "prettier --write ./src ./examples", - "test:ci": "./scripts/all-test.sh", + "test:ci": "yarn mocha --config .mocharc.json", + "test:e2e": "./scripts/run-e2e-test.sh", "version:update": "echo \"export const VERSION = '$npm_package_version';\" > src/version.ts" }, "files": [ diff --git a/typescript/cli/scripts/all-test.sh b/typescript/cli/scripts/run-e2e-test.sh similarity index 71% rename from typescript/cli/scripts/all-test.sh rename to typescript/cli/scripts/run-e2e-test.sh index f33060987..e398253b2 100755 --- a/typescript/cli/scripts/all-test.sh +++ b/typescript/cli/scripts/run-e2e-test.sh @@ -1,24 +1,24 @@ #!/usr/bin/env bash function cleanup() { - set +e + set +e pkill -f anvil rm -rf /tmp/anvil2 rm -rf /tmp/anvil3 rm -f ./test-configs/anvil/chains/anvil2/addresses.yaml rm -f ./test-configs/anvil/chains/anvil3/addresses.yaml - set -e + set -e } cleanup -echo "Starting anvil2 and anvil3 chain" +echo "Starting anvil2 and anvil3 chain for E2E tests" anvil --chain-id 31338 -p 8555 --state /tmp/anvil2/state --gas-price 1 > /dev/null & anvil --chain-id 31347 -p 8600 --state /tmp/anvil3/state --gas-price 1 > /dev/null & -echo "Running all tests" -yarn mocha --config .mocharc.json +echo "Running E2E tests" +yarn mocha --config .mocharc-e2e.json cleanup -echo "Done all tests" \ No newline at end of file +echo "Completed E2E tests" diff --git a/typescript/cli/src/check/warp.ts b/typescript/cli/src/check/warp.ts new file mode 100644 index 000000000..a31fac62e --- /dev/null +++ b/typescript/cli/src/check/warp.ts @@ -0,0 +1,41 @@ +import { stringify as yamlStringify } from 'yaml'; + +import { WarpRouteDeployConfig, normalizeConfig } from '@hyperlane-xyz/sdk'; +import { ObjectDiff, diffObjMerge } from '@hyperlane-xyz/utils'; + +import { log, logGreen } from '../logger.js'; +import '../utils/output.js'; +import { formatYamlViolationsOutput } from '../utils/output.js'; + +export async function runWarpRouteCheck({ + warpRouteConfig, + onChainWarpConfig, +}: { + warpRouteConfig: WarpRouteDeployConfig; + onChainWarpConfig: WarpRouteDeployConfig; +}): Promise { + // Go through each chain and only add to the output the chains that have mismatches + const [violations, isInvalid] = Object.keys(warpRouteConfig).reduce( + (acc, chain) => { + const { mergedObject, isInvalid } = diffObjMerge( + normalizeConfig(onChainWarpConfig[chain]), + normalizeConfig(warpRouteConfig[chain]), + ); + + if (isInvalid) { + acc[0][chain] = mergedObject; + acc[1] ||= isInvalid; + } + + return acc; + }, + [{}, false] as [{ [index: string]: ObjectDiff }, boolean], + ); + + if (isInvalid) { + log(formatYamlViolationsOutput(yamlStringify(violations, null, 2))); + process.exit(1); + } + + logGreen(`No violations found`); +} diff --git a/typescript/cli/src/commands/config.ts b/typescript/cli/src/commands/config.ts index 7b145ca44..e72b72452 100644 --- a/typescript/cli/src/commands/config.ts +++ b/typescript/cli/src/commands/config.ts @@ -41,7 +41,7 @@ const validateChainCommand: CommandModuleWithContext<{ path: string }> = { command: 'chain', describe: 'Validate a chain config file', builder: { - path: inputFileCommandOption, + path: inputFileCommandOption(), }, handler: async ({ path }) => { readChainConfigs(path); @@ -54,7 +54,7 @@ const validateIsmCommand: CommandModuleWithContext<{ path: string }> = { command: 'ism', describe: 'Validate the basic ISM config file', builder: { - path: inputFileCommandOption, + path: inputFileCommandOption(), }, handler: async ({ path }) => { readMultisigConfig(path); @@ -67,7 +67,7 @@ const validateIsmAdvancedCommand: CommandModuleWithContext<{ path: string }> = { command: 'ism-advanced', describe: 'Validate the advanced ISM config file', builder: { - path: inputFileCommandOption, + path: inputFileCommandOption(), }, handler: async ({ path }) => { readIsmConfig(path); @@ -80,7 +80,7 @@ const validateWarpCommand: CommandModuleWithContext<{ path: string }> = { command: 'warp', describe: 'Validate a Warp Route deployment config file', builder: { - path: inputFileCommandOption, + path: inputFileCommandOption(), }, handler: async ({ path }) => { await readWarpRouteDeployConfig(path); diff --git a/typescript/cli/src/commands/core.ts b/typescript/cli/src/commands/core.ts index f691de750..60d84f877 100644 --- a/typescript/cli/src/commands/core.ts +++ b/typescript/cli/src/commands/core.ts @@ -1,10 +1,13 @@ +import { stringify as yamlStringify } from 'yaml'; import { CommandModule } from 'yargs'; import { + CoreConfig, DeployedCoreAddresses, DeployedCoreAddressesSchema, - EvmCoreReader, + normalizeConfig, } from '@hyperlane-xyz/sdk'; +import { diffObjMerge } from '@hyperlane-xyz/utils'; import { createCoreDeployConfig, @@ -16,17 +19,21 @@ import { } from '../context/types.js'; import { runCoreApply, runCoreDeploy } from '../deploy/core.js'; import { evaluateIfDryRunFailure } from '../deploy/dry-run.js'; -import { errorRed, log, logGray, logGreen } from '../logger.js'; +import { log, logCommandHeader, logGreen } from '../logger.js'; +import { executeCoreRead } from '../read/core.js'; import { logYamlIfUnderMaxLines, readYamlOrJson, writeYamlOrJson, } from '../utils/files.js'; +import { formatYamlViolationsOutput } from '../utils/output.js'; import { + DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, chainCommandOption, dryRunCommandOption, fromAddressCommandOption, + inputFileCommandOption, outputFileCommandOption, skipConfirmationOption, } from './options.js'; @@ -40,6 +47,7 @@ export const coreCommand: CommandModule = { builder: (yargs) => yargs .command(apply) + .command(check) .command(deploy) .command(init) .command(read) @@ -47,6 +55,7 @@ export const coreCommand: CommandModule = { .demandCommand(), handler: () => log('Command required'), }; + export const apply: CommandModuleWithWriteContext<{ chain: string; config: string; @@ -60,14 +69,13 @@ export const apply: CommandModuleWithWriteContext<{ demandOption: true, }, config: outputFileCommandOption( - './configs/core-config.yaml', + DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, true, 'The path to output a Core Config JSON or YAML file.', ), }, handler: async ({ context, chain, config: configFilePath }) => { - logGray(`Hyperlane Core Apply`); - logGray('--------------------'); + logCommandHeader(`Hyperlane Core Apply`); const addresses = (await context.registry.getChainAddresses( chain, @@ -103,7 +111,7 @@ export const deploy: CommandModuleWithWriteContext<{ builder: { chain: chainCommandOption, config: outputFileCommandOption( - './configs/core-config.yaml', + DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, false, 'The path to a JSON or YAML file with a core deployment config.', ), @@ -112,8 +120,7 @@ export const deploy: CommandModuleWithWriteContext<{ 'skip-confirmation': skipConfirmationOption, }, handler: async ({ context, chain, config: configFilePath, dryRun }) => { - logGray(`Hyperlane Core deployment${dryRun ? ' dry-run' : ''}`); - logGray(`------------------------------------------------`); + logCommandHeader(`Hyperlane Core deployment${dryRun ? ' dry-run' : ''}`); try { await runCoreDeploy({ @@ -142,14 +149,13 @@ export const init: CommandModuleWithContext<{ default: false, }, config: outputFileCommandOption( - './configs/core-config.yaml', + DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, false, 'The path to output a Core Config JSON or YAML file.', ), }, handler: async ({ context, advanced, config: configFilePath }) => { - logGray('Hyperlane Core Configure'); - logGray('------------------------'); + logCommandHeader('Hyperlane Core Configure'); await createCoreDeployConfig({ context, @@ -178,39 +184,70 @@ export const read: CommandModuleWithContext<{ description: 'Mailbox address used to derive the core config', }, config: outputFileCommandOption( - './configs/core-config.yaml', + DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, false, 'The path to output a Core Config JSON or YAML file.', ), }, handler: async ({ context, chain, mailbox, config: configFilePath }) => { - if (!mailbox) { - const addresses = await context.registry.getChainAddresses(chain); - mailbox = addresses?.mailbox; - if (!mailbox) { - throw new Error( - `${chain} mailbox not provided and none found in registry.`, - ); - } - } + logCommandHeader('Hyperlane Core Read'); - logGray('Hyperlane Core Read'); - logGray('-------------------'); + const coreConfig = await executeCoreRead({ context, chain, mailbox }); - const evmCoreReader = new EvmCoreReader(context.multiProvider, chain); - try { - const coreConfig = await evmCoreReader.deriveCoreConfig(mailbox); - writeYamlOrJson(configFilePath, coreConfig, 'yaml'); - logGreen(`✅ Core config written successfully to ${configFilePath}:\n`); - logYamlIfUnderMaxLines(coreConfig); - } catch (e: any) { - errorRed( - `❌ Failed to read core config for mailbox ${mailbox} on ${chain}:`, - e, - ); + writeYamlOrJson(configFilePath, coreConfig, 'yaml'); + logGreen(`✅ Core config written successfully to ${configFilePath}:\n`); + logYamlIfUnderMaxLines(coreConfig); + + process.exit(0); + }, +}; + +export const check: CommandModuleWithContext<{ + chain: string; + config: string; + mailbox?: string; +}> = { + command: 'check', + describe: + 'Reads onchain Core configuration for a given mailbox address and compares it with a provided file', + builder: { + chain: { + ...chainCommandOption, + demandOption: true, + }, + mailbox: { + type: 'string', + description: + 'Mailbox address used to derive the core config. If not provided it will be inferred from the registry', + }, + config: inputFileCommandOption({ + defaultPath: DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH, + description: 'The path to a a Core Config JSON or YAML file.', + demandOption: false, + }), + }, + handler: async ({ context, chain, mailbox, config: configFilePath }) => { + logCommandHeader('Hyperlane Core Check'); + + const expectedCoreConfig: CoreConfig = await readYamlOrJson(configFilePath); + const onChainCoreConfig = await executeCoreRead({ + context, + chain, + mailbox, + }); + + const { mergedObject, isInvalid } = diffObjMerge( + normalizeConfig(onChainCoreConfig), + normalizeConfig(expectedCoreConfig), + ); + + if (isInvalid) { + log(formatYamlViolationsOutput(yamlStringify(mergedObject, null, 2))); process.exit(1); } + logGreen(`No violations found`); + process.exit(0); }, }; diff --git a/typescript/cli/src/commands/options.ts b/typescript/cli/src/commands/options.ts index 7b772b601..f23194c80 100644 --- a/typescript/cli/src/commands/options.ts +++ b/typescript/cli/src/commands/options.ts @@ -54,6 +54,13 @@ export const keyCommandOption: Options = { defaultDescription: 'process.env.HYP_KEY', }; +export const disableProxyCommandOption: Options = { + type: 'boolean', + description: + 'Disable routing of Github API requests through the Hyperlane registry proxy.', + default: false, +}; + /* Command-specific options */ export const coreTargetsCommandOption: Options = { @@ -84,11 +91,16 @@ export const hookCommandOption: Options = { 'A path to a JSON or YAML file with Hook configs (for every chain)', }; +export const DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH = + './configs/warp-route-deployment.yaml'; + +export const DEFAULT_CORE_DEPLOYMENT_CONFIG_PATH = './configs/core-config.yaml'; + export const warpDeploymentConfigCommandOption: Options = { type: 'string', description: 'A path to a JSON or YAML file with a warp route deployment config.', - default: './configs/warp-route-deployment.yaml', + default: DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, alias: 'wd', }; @@ -127,12 +139,23 @@ export const outputFileCommandOption = ( demandOption, }); -export const inputFileCommandOption: Options = { +interface InputFileCommandOptionConfig + extends Pick { + defaultPath?: string; +} + +export const inputFileCommandOption = ({ + defaultPath, + demandOption = true, + description = 'Input file path', + alias = 'i', +}: InputFileCommandOptionConfig = {}): Options => ({ type: 'string', - description: 'Input file path', - alias: 'i', - demandOption: true, -}; + description, + default: defaultPath, + alias, + demandOption, +}); export const fromAddressCommandOption: Options = { type: 'string', diff --git a/typescript/cli/src/commands/registry.ts b/typescript/cli/src/commands/registry.ts index 46f96ada0..933024de5 100644 --- a/typescript/cli/src/commands/registry.ts +++ b/typescript/cli/src/commands/registry.ts @@ -20,6 +20,7 @@ export const registryCommand: CommandModule = { builder: (yargs) => yargs .command(addressesCommand) + .command(rpcCommand) .command(createAgentConfigCommand) .command(initCommand) .command(listCommand) @@ -71,8 +72,12 @@ const listCommand: CommandModuleWithContext<{ type: ChainType }> = { /** * Addresses command */ -const addressesCommand: CommandModuleWithContext<{ name: string }> = { +const addressesCommand: CommandModuleWithContext<{ + name: string; + contract: string; +}> = { command: 'addresses', + aliases: ['address', 'addy'], describe: 'Display the addresses of core Hyperlane contracts', builder: { name: { @@ -80,10 +85,21 @@ const addressesCommand: CommandModuleWithContext<{ name: string }> = { description: 'Chain to display addresses for', alias: 'chain', }, + contract: { + type: 'string', + description: 'Specific contract name to print addresses for', + implies: 'name', + }, }, - handler: async ({ name, context }) => { + handler: async ({ name, context, contract }) => { if (name) { const result = await context.registry.getChainAddresses(name); + if (contract && result?.[contract.toLowerCase()]) { + // log only contract address for machine readability + log(result[contract]); + return; + } + logBlue('Hyperlane contract addresses for:', name); logGray('---------------------------------'); log(JSON.stringify(result, null, 2)); @@ -96,6 +112,38 @@ const addressesCommand: CommandModuleWithContext<{ name: string }> = { }, }; +const rpcCommand: CommandModuleWithContext<{ + name: string; + index: number; +}> = { + command: 'rpc', + describe: 'Display the public rpc of a Hyperlane chain', + builder: { + name: { + type: 'string', + description: 'Chain to display addresses for', + alias: 'chain', + demandOption: true, + }, + index: { + type: 'number', + description: 'Index of the rpc to display', + default: 0, + demandOption: false, + }, + }, + handler: async ({ name, context, index }) => { + const result = await context.registry.getChainMetadata(name); + const rpcUrl = result?.rpcUrls[index]?.http; + if (!rpcUrl) { + errorRed(`❌ No rpc found for chain ${name}`); + process.exit(1); + } + + log(rpcUrl); + }, +}; + /** * agent-config command */ diff --git a/typescript/cli/src/commands/warp.ts b/typescript/cli/src/commands/warp.ts index da15e62a2..b7fb45624 100644 --- a/typescript/cli/src/commands/warp.ts +++ b/typescript/cli/src/commands/warp.ts @@ -1,23 +1,11 @@ -import { ethers } from 'ethers'; import { stringify as yamlStringify } from 'yaml'; import { CommandModule } from 'yargs'; -import { - HypXERC20Lockbox__factory, - HypXERC20__factory, - IXERC20__factory, -} from '@hyperlane-xyz/core'; -import { - ChainMap, - EvmERC20WarpRouteReader, - TokenStandard, - WarpCoreConfig, -} from '@hyperlane-xyz/sdk'; -import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; +import { ChainSubmissionStrategySchema } from '@hyperlane-xyz/sdk'; +import { runWarpRouteCheck } from '../check/warp.js'; import { createWarpRouteDeployConfig, - readWarpCoreConfig, readWarpRouteDeployConfig, } from '../config/warp.js'; import { @@ -26,16 +14,24 @@ import { } from '../context/types.js'; import { evaluateIfDryRunFailure } from '../deploy/dry-run.js'; import { runWarpRouteApply, runWarpRouteDeploy } from '../deploy/warp.js'; -import { log, logGray, logGreen, logRed, logTable } from '../logger.js'; +import { log, logCommandHeader, logGreen } from '../logger.js'; +import { runWarpRouteRead } from '../read/warp.js'; import { sendTestTransfer } from '../send/transfer.js'; -import { indentYamlOrJson, writeYamlOrJson } from '../utils/files.js'; -import { selectRegistryWarpRoute } from '../utils/tokens.js'; +import { + indentYamlOrJson, + readYamlOrJson, + removeEndingSlash, + writeYamlOrJson, +} from '../utils/files.js'; +import { getWarpCoreConfigOrExit } from '../utils/input.js'; import { + DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, addressCommandOption, chainCommandOption, dryRunCommandOption, fromAddressCommandOption, + inputFileCommandOption, outputFileCommandOption, strategyCommandOption, symbolCommandOption, @@ -53,6 +49,7 @@ export const warpCommand: CommandModule = { builder: (yargs) => yargs .command(apply) + .command(check) .command(deploy) .command(init) .command(read) @@ -68,6 +65,7 @@ export const apply: CommandModuleWithWriteContext<{ symbol?: string; warp: string; strategy?: string; + receiptsDir: string; }> = { command: 'apply', describe: 'Update Warp Route contracts', @@ -82,19 +80,31 @@ export const apply: CommandModuleWithWriteContext<{ demandOption: false, }, strategy: { ...strategyCommandOption, demandOption: false }, + 'receipts-dir': { + type: 'string', + description: 'The directory to output transaction receipts.', + default: './generated/transactions', + coerce: (dir) => removeEndingSlash(dir), + }, }, - handler: async ({ context, config, symbol, warp, strategy: strategyUrl }) => { - logGray(`Hyperlane Warp Apply`); - logGray('--------------------'); // @TODO consider creating a helper function for these dashes - let warpCoreConfig: WarpCoreConfig; - if (symbol) { - warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); - } else if (warp) { - warpCoreConfig = readWarpCoreConfig(warp); - } else { - logRed(`Please specify either a symbol or warp config`); - process.exit(0); - } + handler: async ({ + context, + config, + symbol, + warp, + strategy: strategyUrl, + receiptsDir, + }) => { + logCommandHeader('Hyperlane Warp Apply'); + + const warpCoreConfig = await getWarpCoreConfigOrExit({ + symbol, + warp, + context, + }); + + if (strategyUrl) + ChainSubmissionStrategySchema.parse(readYamlOrJson(strategyUrl)); const warpDeployConfig = await readWarpRouteDeployConfig(config); await runWarpRouteApply({ @@ -102,6 +112,7 @@ export const apply: CommandModuleWithWriteContext<{ warpDeployConfig, warpCoreConfig, strategyUrl, + receiptsDir, }); process.exit(0); }, @@ -120,8 +131,9 @@ export const deploy: CommandModuleWithWriteContext<{ 'from-address': fromAddressCommandOption, }, handler: async ({ context, config, dryRun }) => { - logGray(`Hyperlane Warp Route Deployment${dryRun ? ' Dry-Run' : ''}`); - logGray('------------------------------------------------'); + logCommandHeader( + `Hyperlane Warp Route Deployment${dryRun ? ' Dry-Run' : ''}`, + ); try { await runWarpRouteDeploy({ @@ -148,11 +160,10 @@ export const init: CommandModuleWithContext<{ describe: 'Create an advanced ISM', default: false, }, - out: outputFileCommandOption('./configs/warp-route-deployment.yaml'), + out: outputFileCommandOption(DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH), }, handler: async ({ context, advanced, out }) => { - logGray('Hyperlane Warp Configure'); - logGray('------------------------'); + logCommandHeader('Hyperlane Warp Configure'); await createWarpRouteDeployConfig({ context, @@ -185,7 +196,7 @@ export const read: CommandModuleWithContext<{ false, ), config: outputFileCommandOption( - './configs/warp-route-deployment.yaml', + DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, false, 'The path to output a Warp Config JSON or YAML file.', ), @@ -197,75 +208,14 @@ export const read: CommandModuleWithContext<{ config: configFilePath, symbol, }) => { - logGray('Hyperlane Warp Reader'); - logGray('---------------------'); - - const { multiProvider } = context; - - let addresses: ChainMap; - if (symbol) { - const warpCoreConfig = await selectRegistryWarpRoute( - context.registry, - symbol, - ); - - // TODO: merge with XERC20TokenAdapter and WarpRouteReader - const xerc20Limits = await Promise.all( - warpCoreConfig.tokens - .filter( - (t) => - t.standard === TokenStandard.EvmHypXERC20 || - t.standard === TokenStandard.EvmHypXERC20Lockbox, - ) - .map(async (t) => { - const provider = multiProvider.getProvider(t.chainName); - const router = t.addressOrDenom!; - const xerc20Address = - t.standard === TokenStandard.EvmHypXERC20Lockbox - ? await HypXERC20Lockbox__factory.connect( - router, - provider, - ).xERC20() - : await HypXERC20__factory.connect( - router, - provider, - ).wrappedToken(); - - const xerc20 = IXERC20__factory.connect(xerc20Address, provider); - const mint = await xerc20.mintingCurrentLimitOf(router); - const burn = await xerc20.burningCurrentLimitOf(router); - - const formattedLimits = objMap({ mint, burn }, (_, v) => - ethers.utils.formatUnits(v, t.decimals), - ); + logCommandHeader('Hyperlane Warp Reader'); - return [t.chainName, formattedLimits]; - }), - ); - if (xerc20Limits.length > 0) { - logGray('xERC20 Limits:'); - logTable(Object.fromEntries(xerc20Limits)); - } - - addresses = Object.fromEntries( - warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom!]), - ); - } else if (chain && address) { - addresses = { - [chain]: address, - }; - } else { - logGreen(`Please specify either a symbol or chain and address`); - process.exit(0); - } - - const config = await promiseObjAll( - objMap(addresses, async (chain, address) => - new EvmERC20WarpRouteReader(multiProvider, chain).deriveWarpRouteConfig( - address, - ), - ), - ); + const config = await runWarpRouteRead({ + context, + chain, + address, + symbol, + }); if (configFilePath) { writeYamlOrJson(configFilePath, config, 'yaml'); @@ -323,15 +273,11 @@ const send: CommandModuleWithWriteContext< amount, recipient, }) => { - let warpCoreConfig: WarpCoreConfig; - if (symbol) { - warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); - } else if (warp) { - warpCoreConfig = readWarpCoreConfig(warp); - } else { - logRed(`Please specify either a symbol or warp config`); - process.exit(0); - } + const warpCoreConfig = await getWarpCoreConfigOrExit({ + symbol, + warp, + context, + }); await sendTestTransfer({ context, @@ -347,3 +293,44 @@ const send: CommandModuleWithWriteContext< process.exit(0); }, }; + +export const check: CommandModuleWithContext<{ + config: string; + symbol?: string; + warp?: string; +}> = { + command: 'check', + describe: + 'Verifies that a warp route configuration matches the on chain configuration.', + builder: { + symbol: { + ...symbolCommandOption, + demandOption: false, + }, + warp: { + ...warpCoreConfigCommandOption, + demandOption: false, + }, + config: inputFileCommandOption({ + defaultPath: DEFAULT_WARP_ROUTE_DEPLOYMENT_CONFIG_PATH, + description: 'The path to a warp route deployment configuration file', + }), + }, + handler: async ({ context, config, symbol, warp }) => { + logCommandHeader('Hyperlane Warp Check'); + + const warpRouteConfig = await readWarpRouteDeployConfig(config, context); + const onChainWarpConfig = await runWarpRouteRead({ + context, + warp, + symbol, + }); + + await runWarpRouteCheck({ + onChainWarpConfig, + warpRouteConfig, + }); + + process.exit(0); + }, +}; diff --git a/typescript/cli/src/config/agent.ts b/typescript/cli/src/config/agent.ts index dff8f2255..05fa16559 100644 --- a/typescript/cli/src/config/agent.ts +++ b/typescript/cli/src/config/agent.ts @@ -87,7 +87,7 @@ async function getStartBlocks( chainAddresses: ChainMap, core: HyperlaneCore, chainMetadata: any, -) { +): Promise> { return promiseObjAll( objMap(chainAddresses, async (chain, _) => { const indexFrom = chainMetadata[chain].index?.from; @@ -103,6 +103,7 @@ async function getStartBlocks( errorRed( `❌ Failed to get deployed block to set an index for ${chain}, this is potentially an issue with rpc provider or a misconfiguration`, ); + return undefined; } }), ); diff --git a/typescript/cli/src/config/chain.ts b/typescript/cli/src/config/chain.ts index 199e026c8..e710b6cf0 100644 --- a/typescript/cli/src/config/chain.ts +++ b/typescript/cli/src/config/chain.ts @@ -5,6 +5,7 @@ import { stringify as yamlStringify } from 'yaml'; import { ChainMetadata, ChainMetadataSchema, + EthJsonRpcBlockParameterTag, ExplorerFamily, ZChainName, } from '@hyperlane-xyz/sdk'; @@ -168,6 +169,13 @@ async function addBlockOrGasConfig(metadata: ChainMetadata): Promise { } async function addBlockConfig(metadata: ChainMetadata): Promise { + const parseReorgPeriod = ( + value: string, + ): number | EthJsonRpcBlockParameterTag => { + const parsed = parseInt(value, 10); + return isNaN(parsed) ? (value as EthJsonRpcBlockParameterTag) : parsed; + }; + const wantBlockConfig = await confirm({ message: 'Do you want to add block config for this chain', }); @@ -179,8 +187,16 @@ async function addBlockConfig(metadata: ChainMetadata): Promise { }); const blockReorgPeriod = await input({ message: - 'Enter no. of blocks before a transaction has a near-zero chance of reverting (0-500):', - validate: (value) => parseInt(value) >= 0 && parseInt(value) <= 500, + 'Enter no. of blocks before a transaction has a near-zero chance of reverting (0-500) or block tag (earliest, latest, safe, finalized, pending):', + validate: (value) => { + const parsedInt = parseInt(value, 10); + return ( + Object.values(EthJsonRpcBlockParameterTag).includes( + value as EthJsonRpcBlockParameterTag, + ) || + (!isNaN(parsedInt) && parsedInt >= 0 && parsedInt <= 500) + ); + }, }); const blockTimeEstimate = await input({ message: 'Enter the rough estimate of time per block in seconds (0-20):', @@ -188,7 +204,7 @@ async function addBlockConfig(metadata: ChainMetadata): Promise { }); metadata.blocks = { confirmations: parseInt(blockConfirmation, 10), - reorgPeriod: parseInt(blockReorgPeriod, 10), + reorgPeriod: parseReorgPeriod(blockReorgPeriod), estimateBlockTime: parseInt(blockTimeEstimate, 10), }; } diff --git a/typescript/cli/src/config/hooks.ts b/typescript/cli/src/config/hooks.ts index 30294d19d..0bfd8cb1f 100644 --- a/typescript/cli/src/config/hooks.ts +++ b/typescript/cli/src/config/hooks.ts @@ -4,22 +4,33 @@ import { ethers } from 'ethers'; import { z } from 'zod'; import { + ChainGasOracleParams, ChainMap, + ChainMetadata, ChainName, HookConfig, HookConfigSchema, HookType, + IgpHookConfig, + MultiProtocolProvider, + getCoingeckoTokenPrices, + getGasPrice, + getLocalStorageGasOracleConfig, } from '@hyperlane-xyz/sdk'; import { Address, normalizeAddressEvm, + objFilter, objMap, toWei, } from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; import { errorRed, logBlue, logGreen, logRed } from '../logger.js'; -import { runMultiChainSelectionStep } from '../utils/chains.js'; +import { + runMultiChainSelectionStep, + runSingleChainSelectionStep, +} from '../utils/chains.js'; import { readYamlOrJson } from '../utils/files.js'; import { detectAndConfirmOrPrompt, inputWithInfo } from '../utils/input.js'; @@ -96,6 +107,11 @@ export async function createHookConfig({ name: HookType.PROTOCOL_FEE, description: 'Charge fees for each message dispatch from this chain', }, + { + value: HookType.INTERCHAIN_GAS_PAYMASTER, + name: HookType.INTERCHAIN_GAS_PAYMASTER, + description: 'Pay for gas on remote chains', + }, ], pageSize: 10, }); @@ -107,6 +123,8 @@ export async function createHookConfig({ return createMerkleTreeConfig(); case HookType.PROTOCOL_FEE: return createProtocolFeeConfig(context, advanced); + case HookType.INTERCHAIN_GAS_PAYMASTER: + return createIGPConfig(context, advanced); default: throw new Error(`Invalid hook type: ${hookType}`); } @@ -124,30 +142,13 @@ export const createProtocolFeeConfig = callWithConfigCreationLogs( context: CommandContext, advanced: boolean = false, ): Promise => { - const unnormalizedOwner = - !advanced && context.signer - ? await context.signer.getAddress() - : await detectAndConfirmOrPrompt( - async () => context.signer?.getAddress(), - 'For protocol fee hook, enter', - 'owner address', - 'signer', - ); - const owner = normalizeAddressEvm(unnormalizedOwner); - let beneficiary = owner; - - const isBeneficiarySameAsOwner = advanced - ? await confirm({ - message: `Use this same address (${owner}) for the beneficiary?`, - }) - : true; - - if (!isBeneficiarySameAsOwner) { - const unnormalizedBeneficiary = await input({ - message: 'Enter beneficiary address for protocol fee hook:', - }); - beneficiary = normalizeAddressEvm(unnormalizedBeneficiary); - } + // Get owner and beneficiary + const { owner, beneficiary } = await getOwnerAndBeneficiary( + 'Protocol Fee Hook', + context, + advanced, + ); + // TODO: input in gwei, wei, etc const maxProtocolFee = advanced ? toWei( @@ -182,51 +183,167 @@ export const createProtocolFeeConfig = callWithConfigCreationLogs( HookType.PROTOCOL_FEE, ); -// TODO: make this usable export const createIGPConfig = callWithConfigCreationLogs( - async (remotes: ChainName[]): Promise => { - const unnormalizedOwner = await input({ - message: 'Enter owner address for IGP hook', - }); - const owner = normalizeAddressEvm(unnormalizedOwner); - let beneficiary = owner; - let oracleKey = owner; + async ( + context: CommandContext, + advanced: boolean = false, + ): Promise => { + // Get owner and beneficiary + const { owner, beneficiary } = await getOwnerAndBeneficiary( + 'Interchain Gas Paymaster', + context, + advanced, + ); + + // Determine local and remote chains + const { localChain, remoteChains } = await selectIgpChains(context); + + // Get overhead, defaulting to 75000 + const overhead = await getIgpOverheads(remoteChains); + + // Only get prices for local and remote chains + const filteredMetadata = objFilter( + context.chainMetadata, + (_, metadata): metadata is ChainMetadata => + remoteChains.includes(metadata.name) || metadata.name === localChain, + ); + const prices = await getIgpTokenPrices(context, filteredMetadata); + + // Get exchange rate margin percentage, defaulting to 10 + const exchangeRateMarginPct = parseInt( + await input({ + message: `Enter IGP margin percentage (e.g. 10 for 10%)`, + default: '10', + }), + 10, + ); - const beneficiarySameAsOwner = await confirm({ - message: 'Use this same address for the beneficiary and gasOracleKey?', + // Calculate storage gas oracle config + const oracleConfig = getLocalStorageGasOracleConfig({ + local: localChain, + gasOracleParams: prices, + exchangeRateMarginPct, }); - if (!beneficiarySameAsOwner) { - const unnormalizedBeneficiary = await input({ - message: 'Enter beneficiary address for IGP hook', - }); - beneficiary = normalizeAddressEvm(unnormalizedBeneficiary); - const unnormalizedOracleKey = await input({ - message: 'Enter gasOracleKey address for IGP hook', - }); - oracleKey = normalizeAddressEvm(unnormalizedOracleKey); - } - const overheads: ChainMap = {}; - for (const chain of remotes) { - const overhead = parseInt( - await input({ - message: `Enter overhead for ${chain} (eg 75000) for IGP hook`, - }), - ); - overheads[chain] = overhead; - } return { type: HookType.INTERCHAIN_GAS_PAYMASTER, beneficiary, owner, - oracleKey, - overhead: overheads, - oracleConfig: {}, + oracleKey: owner, + overhead, + oracleConfig, }; }, HookType.INTERCHAIN_GAS_PAYMASTER, ); +async function getOwnerAndBeneficiary( + module: string, + context: CommandContext, + advanced: boolean, +) { + const unnormalizedOwner = + !advanced && context.signer + ? await context.signer.getAddress() + : await detectAndConfirmOrPrompt( + async () => context.signer?.getAddress(), + `For ${module}, enter`, + 'owner address', + 'signer', + ); + const owner = normalizeAddressEvm(unnormalizedOwner); + + let beneficiary = owner; + const beneficiarySameAsOwner = await confirm({ + message: `Use this same address (${owner}) for the beneficiary?`, + }); + if (!beneficiarySameAsOwner) { + const unnormalizedBeneficiary = await input({ + message: `Enter beneficiary address for ${module}`, + }); + beneficiary = normalizeAddressEvm(unnormalizedBeneficiary); + } + + return { owner, beneficiary }; +} + +async function selectIgpChains(context: CommandContext) { + const localChain = await runSingleChainSelectionStep( + context.chainMetadata, + 'Select local chain for IGP hook', + ); + const isTestnet = context.chainMetadata[localChain].isTestnet; + const remoteChains = await runMultiChainSelectionStep({ + chainMetadata: objFilter( + context.chainMetadata, + (_, metadata): metadata is ChainMetadata => metadata.name !== localChain, + ), + message: 'Select remote destination chains for IGP hook', + requireNumber: 1, + networkType: isTestnet ? 'testnet' : 'mainnet', + }); + + return { localChain, remoteChains }; +} + +async function getIgpOverheads(remoteChains: ChainName[]) { + const overhead: ChainMap = {}; + for (const chain of remoteChains) { + overhead[chain] = parseInt( + await input({ + message: `Enter overhead for ${chain} (e.g., 75000) for IGP hook`, + default: '75000', + }), + ); + } + return overhead; +} + +async function getIgpTokenPrices( + context: CommandContext, + filteredMetadata: ChainMap, +) { + const isTestnet = + context.chainMetadata[Object.keys(filteredMetadata)[0]].isTestnet; + const fetchedPrices = isTestnet + ? objMap(filteredMetadata, () => '10') + : await getCoingeckoTokenPrices(filteredMetadata); + + logBlue( + isTestnet + ? `Hardcoding all gas token prices to 10 USD for testnet...` + : `Getting gas token prices for all chains from Coingecko...`, + ); + + const mpp = new MultiProtocolProvider(context.chainMetadata); + const prices: ChainMap = {}; + + for (const chain of Object.keys(filteredMetadata)) { + const gasPrice = await getGasPrice(mpp, chain); + logBlue(`Gas price for ${chain} is ${gasPrice.amount}`); + + let tokenPrice = fetchedPrices[chain]; + if (!tokenPrice) { + tokenPrice = await input({ + message: `Enter the price of ${chain}'s token in USD`, + }); + } else { + logBlue(`Gas token price for ${chain} is $${tokenPrice}`); + } + + const decimals = context.chainMetadata[chain].nativeToken?.decimals; + if (!decimals) { + throw new Error(`No decimals found in metadata for ${chain}`); + } + prices[chain] = { + gasPrice, + nativeToken: { price: tokenPrice, decimals }, + }; + } + + return prices; +} + export const createAggregationConfig = callWithConfigCreationLogs( async ( context: CommandContext, @@ -265,11 +382,11 @@ export const createRoutingConfig = callWithConfigCreationLogs( message: 'Enter owner address for routing Hook', }); const ownerAddress = owner; - const chains = await runMultiChainSelectionStep( - context.chainMetadata, - 'Select chains for routing Hook', - 1, - ); + const chains = await runMultiChainSelectionStep({ + chainMetadata: context.chainMetadata, + message: 'Select chains for routing Hook', + requireNumber: 1, + }); const domainsMap: ChainMap = {}; for (const chain of chains) { diff --git a/typescript/cli/src/config/ism.ts b/typescript/cli/src/config/ism.ts index c975bb895..e40a00c26 100644 --- a/typescript/cli/src/config/ism.ts +++ b/typescript/cli/src/config/ism.ts @@ -226,11 +226,11 @@ export const createRoutingConfig = callWithConfigCreationLogs( message: 'Enter owner address for routing ISM', }); const ownerAddress = owner; - const chains = await runMultiChainSelectionStep( - context.chainMetadata, - 'Select chains to configure routing ISM for', - 1, - ); + const chains = await runMultiChainSelectionStep({ + chainMetadata: context.chainMetadata, + message: 'Select chains to configure routing ISM for', + requireNumber: 1, + }); const domainsMap: ChainMap = {}; for (const chain of chains) { @@ -249,11 +249,11 @@ export const createRoutingConfig = callWithConfigCreationLogs( export const createFallbackRoutingConfig = callWithConfigCreationLogs( async (context: CommandContext): Promise => { - const chains = await runMultiChainSelectionStep( - context.chainMetadata, - 'Select chains to configure fallback routing ISM for', - 1, - ); + const chains = await runMultiChainSelectionStep({ + chainMetadata: context.chainMetadata, + message: 'Select chains to configure fallback routing ISM for', + requireNumber: 1, + }); const domainsMap: ChainMap = {}; for (const chain of chains) { diff --git a/typescript/cli/src/config/multisig.ts b/typescript/cli/src/config/multisig.ts index bb2e0ebbf..28648b271 100644 --- a/typescript/cli/src/config/multisig.ts +++ b/typescript/cli/src/config/multisig.ts @@ -72,7 +72,9 @@ export async function createMultisigConfig({ log( 'Select your own chain below to run your own validators. If you want to reuse existing Hyperlane validators instead of running your own, do not select additional mainnet or testnet chains.', ); - const chains = await runMultiChainSelectionStep(context.chainMetadata); + const chains = await runMultiChainSelectionStep({ + chainMetadata: context.chainMetadata, + }); const chainAddresses = await context.registry.getAddresses(); const result: MultisigConfigMap = {}; diff --git a/typescript/cli/src/config/submit.ts b/typescript/cli/src/config/submit.ts index e2f66a003..45f3367b0 100644 --- a/typescript/cli/src/config/submit.ts +++ b/typescript/cli/src/config/submit.ts @@ -1,11 +1,9 @@ import { stringify as yamlStringify } from 'yaml'; import { - PopulatedTransactions, - PopulatedTransactionsSchema, + AnnotatedEV5Transaction, SubmissionStrategy, } from '@hyperlane-xyz/sdk'; -import { PopulatedTransaction } from '@hyperlane-xyz/sdk'; import { MultiProvider } from '@hyperlane-xyz/sdk'; import { assert, errorToString } from '@hyperlane-xyz/utils'; @@ -72,20 +70,20 @@ export async function runSubmit({ */ function getChainFromTxs( multiProvider: MultiProvider, - transactions: PopulatedTransactions, + transactions: AnnotatedEV5Transaction[], ) { const firstTransaction = transactions[0]; + assert(firstTransaction.chainId, 'Invalid transaction: chainId is required'); const sameChainIds = transactions.every( - (t: PopulatedTransaction) => t.chainId === firstTransaction.chainId, + (t: AnnotatedEV5Transaction) => t.chainId === firstTransaction.chainId, ); assert(sameChainIds, 'Transactions must be submitted on the same chains'); return multiProvider.getChainName(firstTransaction.chainId); } -function getTransactions(transactionsFilepath: string): PopulatedTransactions { - const transactionsFileContent = readYamlOrJson( - transactionsFilepath.trim(), - ); - return PopulatedTransactionsSchema.parse(transactionsFileContent); +function getTransactions( + transactionsFilepath: string, +): AnnotatedEV5Transaction[] { + return readYamlOrJson(transactionsFilepath.trim()); } diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 240e0e39d..dd3a23713 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -1,4 +1,4 @@ -import { input, select } from '@inquirer/prompts'; +import { confirm, input, select } from '@inquirer/prompts'; import { stringify as yamlStringify } from 'yaml'; import { @@ -12,7 +12,13 @@ import { WarpRouteDeployConfig, WarpRouteDeployConfigSchema, } from '@hyperlane-xyz/sdk'; -import { Address, assert, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; +import { + Address, + assert, + isAddress, + objMap, + promiseObjAll, +} from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; import { errorRed, log, logBlue, logGreen } from '../logger.js'; @@ -28,12 +34,15 @@ import { createAdvancedIsmConfig } from './ism.js'; const TYPE_DESCRIPTIONS: Record = { [TokenType.synthetic]: 'A new ERC20 with remote transfer functionality', + [TokenType.syntheticRebase]: `A rebasing ERC20 with remote transfer functionality. Must be paired with ${TokenType.collateralVaultRebase}`, [TokenType.collateral]: 'Extends an existing ERC20 with remote transfer functionality', [TokenType.native]: 'Extends the native token with remote transfer functionality', [TokenType.collateralVault]: - 'Extends an existing ERC4626 with remote transfer functionality', + 'Extends an existing ERC4626 with remote transfer functionality. Yields are manually claimed by owner.', + [TokenType.collateralVaultRebase]: + 'Extends an existing ERC4626 with remote transfer functionality. Rebases yields to token holders.', [TokenType.collateralFiat]: 'Extends an existing FiatToken with remote transfer functionality', [TokenType.XERC20]: @@ -116,38 +125,60 @@ export async function createWarpRouteDeployConfig({ 'signer', ); - const warpChains = await runMultiChainSelectionStep( - context.chainMetadata, - 'Select chains to connect', - 1, - ); + const warpChains = await runMultiChainSelectionStep({ + chainMetadata: context.chainMetadata, + message: 'Select chains to connect', + requireNumber: 1, + requiresConfirmation: true, + }); const result: WarpRouteDeployConfig = {}; + let typeChoices = TYPE_CHOICES; for (const chain of warpChains) { logBlue(`${chain}: Configuring warp route...`); + + // default to the mailbox from the registry and if not found ask to the user to submit one + const chainAddresses = await context.registry.getChainAddresses(chain); + + const mailbox = + chainAddresses?.mailbox ?? + (await input({ + validate: isAddress, + message: `Could not retrieve mailbox address from the registry for chain "${chain}". Please enter a valid mailbox address:`, + })); + + /** + * The logic from the cli is as follows: + * --advanced flag is provided: the user will have to build their own configuration using the available ISM types + * --yes flag is provided: the default ISM config will be used (Trusted ISM + Default fallback ISM) + * -- no flag is provided: the user must choose if the default ISM config should be used: + * - yes: the default ISM config will be used (Trusted ISM + Default fallback ISM) + * - no: the default fallback ISM will be used + */ + let interchainSecurityModule: IsmConfig; + if (advanced) { + interchainSecurityModule = await createAdvancedIsmConfig(context); + } else if (context.skipConfirmation) { + interchainSecurityModule = createDefaultWarpIsmConfig(owner); + } else if ( + await confirm({ + message: 'Do you want to use a trusted ISM for warp route?', + }) + ) { + interchainSecurityModule = createDefaultWarpIsmConfig(owner); + } else { + interchainSecurityModule = createFallbackRoutingConfig(owner); + } + const type = await select({ message: `Select ${chain}'s token type`, - choices: TYPE_CHOICES, + choices: typeChoices, }); // TODO: restore NFT prompting const isNft = type === TokenType.syntheticUri || type === TokenType.collateralUri; - const mailbox = await detectAndConfirmOrPrompt( - async () => { - const addresses = await context.registry.getChainAddresses(chain); - return addresses?.mailbox; - }, - `For ${chain}, enter the`, - 'mailbox address', - 'hyperlane-registry', - ); - - const interchainSecurityModule = advanced - ? await createAdvancedIsmConfig(context) - : createDefaultWarpIsmConfig(owner); - switch (type) { case TokenType.collateral: case TokenType.XERC20: @@ -166,6 +197,34 @@ export async function createWarpRouteDeployConfig({ }), }; break; + case TokenType.syntheticRebase: + result[chain] = { + mailbox, + type, + owner, + isNft, + collateralChainName: '', // This will be derived correctly by zod.parse() below + interchainSecurityModule, + }; + typeChoices = restrictChoices([ + TokenType.syntheticRebase, + TokenType.collateralVaultRebase, + ]); + break; + case TokenType.collateralVaultRebase: + result[chain] = { + mailbox, + type, + owner, + isNft, + interchainSecurityModule, + token: await input({ + message: `Enter the ERC-4626 vault address on chain ${chain}`, + }), + }; + + typeChoices = restrictChoices([TokenType.syntheticRebase]); + break; case TokenType.collateralVault: result[chain] = { mailbox, @@ -203,6 +262,10 @@ export async function createWarpRouteDeployConfig({ } } +function restrictChoices(typeChoices: TokenType[]) { + return TYPE_CHOICES.filter((choice) => typeChoices.includes(choice.name)); +} + // Note, this is different than the function above which reads a config // for a DEPLOYMENT. This gets a config for using a warp route (aka WarpCoreConfig) export function readWarpCoreConfig(filePath: string): WarpCoreConfig { @@ -227,12 +290,22 @@ function createDefaultWarpIsmConfig(owner: Address): IsmConfig { type: IsmType.TRUSTED_RELAYER, relayer: owner, }, - { - type: IsmType.FALLBACK_ROUTING, - domains: {}, - owner, - }, + createFallbackRoutingConfig(owner), ], threshold: 1, }; } + +/** + * Creates a fallback configuration for an ISM with a FALLBACK_ROUTING and the provided `owner`. + * + * @param owner - The address of the owner of the ISM. + * @returns The Fallback Routing ISM configuration. + */ +function createFallbackRoutingConfig(owner: Address): IsmConfig { + return { + type: IsmType.FALLBACK_ROUTING, + domains: {}, + owner, + }; +} diff --git a/typescript/cli/src/consts.ts b/typescript/cli/src/consts.ts index 37bada31d..674f7dd15 100644 --- a/typescript/cli/src/consts.ts +++ b/typescript/cli/src/consts.ts @@ -2,3 +2,4 @@ export const MINIMUM_CORE_DEPLOY_GAS = (1e8).toString(); export const MINIMUM_WARP_DEPLOY_GAS = (6e8).toString(); // Rough calculation through deployments to testnets with 2x buffer export const MINIMUM_TEST_SEND_GAS = (3e5).toString(); export const MINIMUM_AVS_GAS = (3e6).toString(); +export const PROXY_DEPLOYED_URL = 'https://proxy.hyperlane.xyz'; diff --git a/typescript/cli/src/context/context.ts b/typescript/cli/src/context/context.ts index fb50d37bb..30390f4b4 100644 --- a/typescript/cli/src/context/context.ts +++ b/typescript/cli/src/context/context.ts @@ -2,6 +2,7 @@ import { confirm } from '@inquirer/prompts'; import { ethers } from 'ethers'; import { + DEFAULT_GITHUB_REGISTRY, GithubRegistry, IRegistry, MergedRegistry, @@ -16,6 +17,7 @@ import { import { isHttpsUrl, isNullish, rootLogger } from '@hyperlane-xyz/utils'; import { isSignCommand } from '../commands/signCommands.js'; +import { PROXY_DEPLOYED_URL } from '../consts.js'; import { forkNetworkToMultiProvider, verifyAnvil } from '../deploy/dry-run.js'; import { logBlue } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; @@ -37,6 +39,7 @@ export async function contextMiddleware(argv: Record) { key: argv.key, fromAddress: argv.fromAddress, requiresKey, + disableProxy: argv.disableProxy, skipConfirmation: argv.yes, }; if (!isDryRun && settings.fromAddress) @@ -59,8 +62,9 @@ export async function getContext({ key, requiresKey, skipConfirmation, + disableProxy = false, }: ContextSettings): Promise { - const registry = getRegistry(registryUri, registryOverrideUri); + const registry = getRegistry(registryUri, registryOverrideUri, !disableProxy); let signer: ethers.Wallet | undefined = undefined; if (key || requiresKey) { @@ -89,10 +93,11 @@ export async function getDryRunContext( key, fromAddress, skipConfirmation, + disableProxy = false, }: ContextSettings, chain?: ChainName, ): Promise { - const registry = getRegistry(registryUri, registryOverrideUri); + const registry = getRegistry(registryUri, registryOverrideUri, !disableProxy); const chainMetadata = await registry.getMetadata(); if (!chain) { @@ -137,6 +142,7 @@ export async function getDryRunContext( function getRegistry( primaryRegistryUri: string, overrideRegistryUri: string, + enableProxy: boolean, ): IRegistry { const logger = rootLogger.child({ module: 'MergedRegistry' }); const registries = [primaryRegistryUri, overrideRegistryUri] @@ -145,7 +151,14 @@ function getRegistry( .map((uri, index) => { const childLogger = logger.child({ uri, index }); if (isHttpsUrl(uri)) { - return new GithubRegistry({ uri, logger: childLogger }); + return new GithubRegistry({ + uri, + logger: childLogger, + proxyUrl: + enableProxy && isCanonicalRepoUrl(uri) + ? PROXY_DEPLOYED_URL + : undefined, + }); } else { return new FileSystemRegistry({ uri, @@ -159,6 +172,10 @@ function getRegistry( }); } +function isCanonicalRepoUrl(url: string) { + return url === DEFAULT_GITHUB_REGISTRY; +} + /** * Retrieves a new MultiProvider based on all known chain metadata & custom user chains * @param customChains Custom chains specified by the user @@ -178,6 +195,10 @@ export async function getOrRequestApiKeys( const apiKeys: ChainMap = {}; for (const chain of chains) { + if (chainMetadata[chain]?.blockExplorers?.[0]?.apiKey) { + apiKeys[chain] = chainMetadata[chain]!.blockExplorers![0]!.apiKey!; + continue; + } const wantApiKey = await confirm({ default: false, message: `Do you want to use an API key to verify on this (${chain}) chain's block explorer`, diff --git a/typescript/cli/src/context/types.ts b/typescript/cli/src/context/types.ts index 80f3121fb..6c3a17c5f 100644 --- a/typescript/cli/src/context/types.ts +++ b/typescript/cli/src/context/types.ts @@ -14,6 +14,7 @@ export interface ContextSettings { key?: string; fromAddress?: string; requiresKey?: boolean; + disableProxy?: boolean; skipConfirmation?: boolean; } diff --git a/typescript/cli/src/deploy/agent.ts b/typescript/cli/src/deploy/agent.ts index 5b93e10c4..ca490fc5f 100644 --- a/typescript/cli/src/deploy/agent.ts +++ b/typescript/cli/src/deploy/agent.ts @@ -28,11 +28,11 @@ export async function runKurtosisAgentDeploy({ ); } if (!relayChains) { - const selectedRelayChains = await runMultiChainSelectionStep( - context.chainMetadata, - 'Select chains to relay between', - 2, - ); + const selectedRelayChains = await runMultiChainSelectionStep({ + chainMetadata: context.chainMetadata, + message: 'Select chains to relay between', + requireNumber: 2, + }); relayChains = selectedRelayChains.join(','); } diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 0d8b00584..f0c74abc6 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -1,4 +1,5 @@ import { confirm } from '@inquirer/prompts'; +import { groupBy } from 'lodash-es'; import { stringify as yamlStringify } from 'yaml'; import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; @@ -11,6 +12,7 @@ import { ChainSubmissionStrategy, ChainSubmissionStrategySchema, ContractVerifier, + DestinationGas, EvmERC20WarpModule, EvmERC20WarpRouteReader, EvmIsmModule, @@ -57,26 +59,21 @@ import { objKeys, objMap, promiseObjAll, + retryAsync, } from '@hyperlane-xyz/utils'; import { readWarpRouteDeployConfig } from '../config/warp.js'; import { MINIMUM_WARP_DEPLOY_GAS } from '../consts.js'; import { getOrRequestApiKeys } from '../context/context.js'; import { WriteCommandContext } from '../context/types.js'; -import { - log, - logBlue, - logGray, - logGreen, - logRed, - logTable, -} from '../logger.js'; +import { log, logBlue, logGray, logGreen, logTable } from '../logger.js'; import { getSubmitterBuilder } from '../submit/submit.js'; import { indentYamlOrJson, isFile, readYamlOrJson, runFileSelectionStep, + writeYamlOrJson, } from '../utils/files.js'; import { @@ -93,6 +90,7 @@ interface DeployParams { interface WarpApplyParams extends DeployParams { warpCoreConfig: WarpCoreConfig; strategyUrl?: string; + receiptsDir: string; } export async function runWarpRouteDeploy({ @@ -438,18 +436,14 @@ function fullyConnectTokens(warpCoreConfig: WarpCoreConfig): void { export async function runWarpRouteApply( params: WarpApplyParams, ): Promise { - const { warpDeployConfig, warpCoreConfig, context, strategyUrl } = params; - const { registry, multiProvider, chainMetadata, skipConfirmation } = context; + const { warpDeployConfig, warpCoreConfig, context } = params; + const { chainMetadata, skipConfirmation } = context; WarpRouteDeployConfigSchema.parse(warpDeployConfig); WarpCoreConfigSchema.parse(warpCoreConfig); - const addresses = await registry.getAddresses(); const warpCoreConfigByChain = Object.fromEntries( - warpCoreConfig.tokens.map((token) => [ - token.chainName, - token, - ]) /* Necessary for O(1) reads below */, + warpCoreConfig.tokens.map((token) => [token.chainName, token]), ); const chains = Object.keys(warpDeployConfig); @@ -458,107 +452,126 @@ export async function runWarpRouteApply( if (!skipConfirmation) apiKeys = await getOrRequestApiKeys(chains, chainMetadata); - const contractVerifier = new ContractVerifier( - multiProvider, - apiKeys, - coreBuildArtifact, - ExplorerLicenseType.MIT, - ); + const transactions: AnnotatedEV5Transaction[] = [ + ...(await extendWarpRoute( + params, + apiKeys, + warpDeployConfig, + warpCoreConfigByChain, + )), + ...(await updateExistingWarpRoute( + params, + apiKeys, + warpDeployConfig, + warpCoreConfigByChain, + )), + ]; + if (transactions.length == 0) + return logGreen(`Warp config is the same as target. No updates needed.`); + + await submitWarpApplyTransactions(params, groupBy(transactions, 'chainId')); +} - const warpDeployChains = Object.keys(warpDeployConfig); +async function extendWarpRoute( + params: WarpApplyParams, + apiKeys: ChainMap, + warpDeployConfig: WarpRouteDeployConfig, + warpCoreConfigByChain: ChainMap, +) { + const { multiProvider } = params.context; const warpCoreChains = Object.keys(warpCoreConfigByChain); - if (warpDeployChains.length === warpCoreChains.length) { - logGray('Updating deployed Warp Routes'); - await promiseObjAll( - objMap(warpDeployConfig, async (chain, config) => { - try { - config.ismFactoryAddresses = addresses[ - chain - ] as ProxyFactoryFactoriesAddresses; - const evmERC20WarpModule = new EvmERC20WarpModule( - multiProvider, - { - config, - chain, - addresses: { - deployedTokenRoute: - warpCoreConfigByChain[chain].addressOrDenom!, - }, - }, - contractVerifier, - ); - const transactions = await evmERC20WarpModule.update(config); - if (transactions.length == 0) - return logGreen( - `Warp config on ${chain} is the same as target. No updates needed.`, - ); - const submitter: TxSubmitterBuilder = - await getWarpApplySubmitter({ - chain, - context, - strategyUrl, - }); - const transactionReceipts = await submitter.submit(...transactions); + // Split between the existing and additional config + const existingConfigs: WarpRouteDeployConfig = objFilter( + warpDeployConfig, + (chain, _config): _config is any => warpCoreChains.includes(chain), + ); - if (transactionReceipts && transactionReceipts.length > 0) { - return logGreen( - `✅ Warp config update successfully submitted with ${submitter.txSubmitterType} on ${chain}:\n\n`, - indentYamlOrJson(yamlStringify(transactionReceipts, null, 2), 4), - ); - } - } catch (e) { - logRed(`Warp config on ${chain} failed to update.`, e); - } - }), - ); - } else if (warpDeployChains.length > warpCoreChains.length) { - logGray('Extending deployed Warp configs'); + let extendedConfigs: WarpRouteDeployConfig = objFilter( + warpDeployConfig, + (chain, _config): _config is any => !warpCoreChains.includes(chain), + ); - // Split between the existing and additional config - const existingConfigs: WarpRouteDeployConfig = objFilter( - warpDeployConfig, - (chain, _config): _config is any => warpCoreChains.includes(chain), - ); + const extendedChains = Object.keys(extendedConfigs); + if (extendedChains.length === 0) return []; - let extendedConfigs: WarpRouteDeployConfig = objFilter( - warpDeployConfig, - (chain, _config): _config is any => !warpCoreChains.includes(chain), - ); + logBlue(`Extending Warp Route to ${extendedChains.join(', ')}`); - extendedConfigs = await deriveMetadataFromExisting( - multiProvider, - existingConfigs, - extendedConfigs, - ); + extendedConfigs = await deriveMetadataFromExisting( + multiProvider, + existingConfigs, + extendedConfigs, + ); - const newDeployedContracts = await executeDeploy( - { - // TODO: use EvmERC20WarpModule when it's ready - context, - warpDeployConfig: extendedConfigs, - }, - apiKeys, - ); + const newDeployedContracts = await executeDeploy( + { + // TODO: use EvmERC20WarpModule when it's ready + context: params.context, + warpDeployConfig: extendedConfigs, + }, + apiKeys, + ); - const mergedRouters = mergeAllRouters( - multiProvider, - existingConfigs, - newDeployedContracts, - warpCoreConfigByChain, - ); + const mergedRouters = mergeAllRouters( + multiProvider, + existingConfigs, + newDeployedContracts, + warpCoreConfigByChain, + ); - await enrollRemoteRouters(context, mergedRouters, strategyUrl); + const updatedWarpCoreConfig = await getWarpCoreConfig(params, mergedRouters); + WarpCoreConfigSchema.parse(updatedWarpCoreConfig); + await writeDeploymentArtifacts(updatedWarpCoreConfig, params.context); - const updatedWarpCoreConfig = await getWarpCoreConfig( - params, - mergedRouters, - ); - WarpCoreConfigSchema.parse(updatedWarpCoreConfig); - await writeDeploymentArtifacts(updatedWarpCoreConfig, context); - } else { - throw new Error('Unenrolling warp routes is currently not supported'); - } + return enrollRemoteRouters(params, mergedRouters); +} + +async function updateExistingWarpRoute( + params: WarpApplyParams, + apiKeys: ChainMap, + warpDeployConfig: WarpRouteDeployConfig, + warpCoreConfigByChain: ChainMap, +) { + logBlue('Updating deployed Warp Routes'); + const { multiProvider, registry } = params.context; + const addresses = await registry.getAddresses(); + const contractVerifier = new ContractVerifier( + multiProvider, + apiKeys, + coreBuildArtifact, + ExplorerLicenseType.MIT, + ); + const transactions: AnnotatedEV5Transaction[] = []; + + await promiseObjAll( + objMap(warpDeployConfig, async (chain, config) => { + await retryAsync(async () => { + logGray(`Update existing warp route for chain ${chain}`); + const deployedConfig = warpCoreConfigByChain[chain]; + if (!deployedConfig) + return logGray( + `Missing artifacts for ${chain}. Probably new deployment. Skipping update...`, + ); + + config.ismFactoryAddresses = addresses[ + chain + ] as ProxyFactoryFactoriesAddresses; + const evmERC20WarpModule = new EvmERC20WarpModule( + multiProvider, + { + config, + chain, + addresses: { + deployedTokenRoute: deployedConfig.addressOrDenom!, + }, + }, + contractVerifier, + ); + transactions.push(...(await evmERC20WarpModule.update(config))); + }); + }), + ); + return transactions; } /** @@ -629,65 +642,105 @@ function mergeAllRouters( * @param multiProvider - A MultiProvider instance to interact with multiple chains. */ async function enrollRemoteRouters( - context: WriteCommandContext, + params: WarpApplyParams, deployedContractsMap: HyperlaneContractsMap, - strategyUrl?: string, -): Promise { - logBlue(`Enrolling deployed routers with each other (if not already)...`); - const { multiProvider } = context; - const deployedRouters: ChainMap
= objMap( +): Promise { + logBlue(`Enrolling deployed routers with each other...`); + const { multiProvider } = params.context; + const deployedRoutersAddresses: ChainMap
= objMap( deployedContractsMap, (_, contracts) => getRouter(contracts).address, ); - const allChains = Object.keys(deployedRouters); + const deployedDestinationGas: DestinationGas = await populateDestinationGas( + multiProvider, + params.warpDeployConfig, + deployedContractsMap, + ); + const deployedChains = Object.keys(deployedRoutersAddresses); + const transactions: AnnotatedEV5Transaction[] = []; await promiseObjAll( objMap(deployedContractsMap, async (chain, contracts) => { - const router = getRouter(contracts); // Assume deployedContract always has 1 value - - // Mutate the config.remoteRouters by setting it to all other routers to update - const warpRouteReader = new EvmERC20WarpRouteReader(multiProvider, chain); - const mutatedWarpRouteConfig = - await warpRouteReader.deriveWarpRouteConfig(router.address); - const evmERC20WarpModule = new EvmERC20WarpModule(multiProvider, { - config: mutatedWarpRouteConfig, - chain, - addresses: { deployedTokenRoute: router.address }, - }); - - const otherChains = multiProvider - .getRemoteChains(chain) - .filter((c) => allChains.includes(c)); - - mutatedWarpRouteConfig.remoteRouters = otherChains.reduce( - (remoteRouters, chain) => { - remoteRouters[multiProvider.getDomainId(chain)] = - deployedRouters[chain]; - return remoteRouters; - }, - {}, - ); - const mutatedConfigTxs: AnnotatedEV5Transaction[] = - await evmERC20WarpModule.update(mutatedWarpRouteConfig); + await retryAsync(async () => { + const router = getRouter(contracts); // Assume deployedContract always has 1 value - if (mutatedConfigTxs.length == 0) - return logGreen( - `Mutated warp config on ${chain} is the same as target. No updates needed.`, + // Mutate the config.remoteRouters by setting it to all other routers to update + const warpRouteReader = new EvmERC20WarpRouteReader( + multiProvider, + chain, ); - const submitter: TxSubmitterBuilder = - await getWarpApplySubmitter({ + const mutatedWarpRouteConfig = + await warpRouteReader.deriveWarpRouteConfig(router.address); + const evmERC20WarpModule = new EvmERC20WarpModule(multiProvider, { + config: mutatedWarpRouteConfig, chain, - context, - strategyUrl, + addresses: { deployedTokenRoute: router.address }, }); - const transactionReceipts = await submitter.submit(...mutatedConfigTxs); - return logGreen( - `✅ Router enrollment update successfully submitted with ${submitter.txSubmitterType} on ${chain}:\n\n`, - indentYamlOrJson(yamlStringify(transactionReceipts, null, 2), 4), - ); + const otherChains = multiProvider + .getRemoteChains(chain) + .filter((c) => deployedChains.includes(c)); + + mutatedWarpRouteConfig.remoteRouters = + otherChains.reduce((remoteRouters, otherChain) => { + remoteRouters[multiProvider.getDomainId(otherChain)] = + deployedRoutersAddresses[otherChain]; + return remoteRouters; + }, {}); + + mutatedWarpRouteConfig.destinationGas = + otherChains.reduce((destinationGas, otherChain) => { + const otherChainDomain = multiProvider.getDomainId(otherChain); + destinationGas[otherChainDomain] = + deployedDestinationGas[otherChainDomain]; + return destinationGas; + }, {}); + + const mutatedConfigTxs: AnnotatedEV5Transaction[] = + await evmERC20WarpModule.update(mutatedWarpRouteConfig); + + if (mutatedConfigTxs.length == 0) + return logGreen( + `Warp config on ${chain} is the same as target. No updates needed.`, + ); + transactions.push(...mutatedConfigTxs); + }); }), ); + + return transactions; +} + +/** + * Populates the destination gas amounts for each chain using warpConfig.gas OR querying other router's destinationGas + */ +async function populateDestinationGas( + multiProvider: MultiProvider, + warpDeployConfig: WarpRouteDeployConfig, + deployedContractsMap: HyperlaneContractsMap, +): Promise { + const destinationGas: DestinationGas = {}; + const deployedChains = Object.keys(deployedContractsMap); + await promiseObjAll( + objMap(deployedContractsMap, async (chain, contracts) => { + await retryAsync(async () => { + const router = getRouter(contracts); + + const otherChains = multiProvider + .getRemoteChains(chain) + .filter((c) => deployedChains.includes(c)); + + for (const otherChain of otherChains) { + const otherDomain = multiProvider.getDomainId(otherChain); + if (!destinationGas[otherDomain]) + destinationGas[otherDomain] = + warpDeployConfig[otherChain].gas?.toString() || + (await router.destinationGas(otherDomain)).toString(); + } + }); + }), + ); + return destinationGas; } function getRouter(contracts: HyperlaneContracts) { @@ -825,6 +878,43 @@ function transformIsmConfigForDisplay(ismConfig: IsmConfig): any[] { } } +/** + * Submits a set of transactions to the specified chain and outputs transaction receipts + */ +async function submitWarpApplyTransactions( + params: WarpApplyParams, + chainTransactions: Record, +): Promise { + const { multiProvider } = params.context; + await promiseObjAll( + objMap(chainTransactions, async (chainId, transactions) => { + await retryAsync( + async () => { + const chain = multiProvider.getChainName(chainId); + const submitter: TxSubmitterBuilder = + await getWarpApplySubmitter({ + chain, + context: params.context, + strategyUrl: params.strategyUrl, + }); + const transactionReceipts = await submitter.submit(...transactions); + if (transactionReceipts) { + const receiptPath = `${params.receiptsDir}/${chain}-${ + submitter.txSubmitterType + }-${Date.now()}-receipts.json`; + writeYamlOrJson(receiptPath, transactionReceipts); + logGreen( + `Transactions receipts successfully written to ${receiptPath}`, + ); + } + }, + 5, // attempts + 100, // baseRetryMs + ); + }), + ); +} + /** * Helper function to get warp apply specific submitter. * diff --git a/typescript/cli/src/logger.ts b/typescript/cli/src/logger.ts index d5347c66d..621d70e4d 100644 --- a/typescript/cli/src/logger.ts +++ b/typescript/cli/src/logger.ts @@ -57,5 +57,9 @@ export const errorRed = (...args: any) => logColor('error', chalk.red, ...args); export const logDebug = (msg: string, ...args: any) => logger.debug(msg, ...args); +export const logCommandHeader = (msg: string) => { + logGray(`${msg}\n${'_'.repeat(msg.length)}`); +}; + // No support for table in pino so print directly to console export const logTable = (...args: any) => console.table(...args); diff --git a/typescript/cli/src/read/core.ts b/typescript/cli/src/read/core.ts new file mode 100644 index 000000000..b7f1f94b2 --- /dev/null +++ b/typescript/cli/src/read/core.ts @@ -0,0 +1,36 @@ +import { ChainName, CoreConfig, EvmCoreReader } from '@hyperlane-xyz/sdk'; +import { Address, assert } from '@hyperlane-xyz/utils'; + +import { CommandContext } from '../context/types.js'; +import { errorRed } from '../logger.js'; + +export async function executeCoreRead({ + context, + chain, + mailbox, +}: { + context: CommandContext; + chain: ChainName; + mailbox?: Address; +}): Promise { + if (!mailbox) { + const addresses = await context.registry.getChainAddresses(chain); + mailbox = addresses?.mailbox; + + assert( + mailbox, + `${chain} mailbox not provided and none found in registry.`, + ); + } + + const evmCoreReader = new EvmCoreReader(context.multiProvider, chain); + try { + return evmCoreReader.deriveCoreConfig(mailbox); + } catch (e: any) { + errorRed( + `❌ Failed to read core config for mailbox ${mailbox} on ${chain}:`, + e, + ); + process.exit(1); + } +} diff --git a/typescript/cli/src/read/warp.ts b/typescript/cli/src/read/warp.ts new file mode 100644 index 000000000..9139d890c --- /dev/null +++ b/typescript/cli/src/read/warp.ts @@ -0,0 +1,117 @@ +import { ethers } from 'ethers'; + +import { + HypXERC20Lockbox__factory, + HypXERC20__factory, + IXERC20__factory, +} from '@hyperlane-xyz/core'; +import { + ChainMap, + ChainName, + EvmERC20WarpRouteReader, + TokenStandard, +} from '@hyperlane-xyz/sdk'; +import { isAddressEvm, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; + +import { CommandContext } from '../context/types.js'; +import { logGray, logRed, logTable } from '../logger.js'; +import { getWarpCoreConfigOrExit } from '../utils/input.js'; + +export async function runWarpRouteRead({ + context, + chain, + address, + warp, + symbol, +}: { + context: CommandContext; + chain?: ChainName; + warp?: string; + address?: string; + symbol?: string; +}): Promise> { + const { multiProvider } = context; + + let addresses: ChainMap; + if (symbol || warp) { + const warpCoreConfig = await getWarpCoreConfigOrExit({ + context, + warp, + symbol, + }); + + // TODO: merge with XERC20TokenAdapter and WarpRouteReader + const xerc20Limits = await Promise.all( + warpCoreConfig.tokens + .filter( + (t) => + t.standard === TokenStandard.EvmHypXERC20 || + t.standard === TokenStandard.EvmHypXERC20Lockbox, + ) + .map(async (t) => { + const provider = multiProvider.getProvider(t.chainName); + const router = t.addressOrDenom!; + const xerc20Address = + t.standard === TokenStandard.EvmHypXERC20Lockbox + ? await HypXERC20Lockbox__factory.connect( + router, + provider, + ).xERC20() + : await HypXERC20__factory.connect( + router, + provider, + ).wrappedToken(); + + const xerc20 = IXERC20__factory.connect(xerc20Address, provider); + const mint = await xerc20.mintingCurrentLimitOf(router); + const burn = await xerc20.burningCurrentLimitOf(router); + + const formattedLimits = objMap({ mint, burn }, (_, v) => + ethers.utils.formatUnits(v, t.decimals), + ); + + return [t.chainName, formattedLimits]; + }), + ); + + if (xerc20Limits.length > 0) { + logGray('xERC20 Limits:'); + logTable(Object.fromEntries(xerc20Limits)); + } + + addresses = Object.fromEntries( + warpCoreConfig.tokens.map((t) => [t.chainName, t.addressOrDenom!]), + ); + } else if (chain && address) { + addresses = { + [chain]: address, + }; + } else { + logRed(`Please specify either a symbol, chain and address or warp file`); + process.exit(1); + } + + // Check if there any non-EVM chains in the config and exit + const nonEvmChains = Object.entries(addresses) + .filter(([_, address]) => !isAddressEvm(address)) + .map(([chain]) => chain); + if (nonEvmChains.length > 0) { + const chainList = nonEvmChains.join(', '); + logRed( + `${chainList} ${ + nonEvmChains.length > 1 ? 'are' : 'is' + } non-EVM and not compatible with the cli`, + ); + process.exit(1); + } + + const config = await promiseObjAll( + objMap(addresses, async (chain, address) => + new EvmERC20WarpRouteReader(multiProvider, chain).deriveWarpRouteConfig( + address, + ), + ), + ); + + return config; +} diff --git a/typescript/cli/src/send/message.ts b/typescript/cli/src/send/message.ts index eaa69dcce..430d3b7bc 100644 --- a/typescript/cli/src/send/message.ts +++ b/typescript/cli/src/send/message.ts @@ -84,11 +84,6 @@ async function executeDelivery({ const chainAddresses = await registry.getAddresses(); const core = HyperlaneCore.fromAddressesMap(chainAddresses, multiProvider); - const hook = chainAddresses[origin]?.customHook; - if (hook) { - logBlue(`Using custom hook ${hook} for ${origin} -> ${destination}`); - } - try { const recipient = chainAddresses[destination].testRecipient; if (!recipient) { @@ -102,8 +97,8 @@ async function executeDelivery({ destination, formattedRecipient, messageBody, - hook, - undefined, + // override the the default hook (with IGP) for self-relay to avoid race condition with the production relayer + selfRelay ? chainAddresses[origin].merkleTreeHook : undefined, ); logBlue(`Sent message from ${origin} to ${recipient} on ${destination}.`); logBlue(`Message ID: ${message.id}`); diff --git a/typescript/cli/src/send/transfer.ts b/typescript/cli/src/send/transfer.ts index 3816a78a6..1afddb596 100644 --- a/typescript/cli/src/send/transfer.ts +++ b/typescript/cli/src/send/transfer.ts @@ -12,7 +12,7 @@ import { WarpCore, WarpCoreConfig, } from '@hyperlane-xyz/sdk'; -import { timeout } from '@hyperlane-xyz/utils'; +import { parseWarpRouteMessage, timeout } from '@hyperlane-xyz/utils'; import { MINIMUM_TEST_SEND_GAS } from '../consts.js'; import { WriteCommandContext } from '../context/types.js'; @@ -23,6 +23,10 @@ import { indentYamlOrJson } from '../utils/files.js'; import { stubMerkleTreeConfig } from '../utils/relay.js'; import { runTokenSelectionStep } from '../utils/tokens.js'; +export const WarpSendLogs = { + SUCCESS: 'Transfer was self-relayed!', +}; + export async function sendTestTransfer({ context, warpCoreConfig, @@ -145,6 +149,7 @@ async function executeDelivery({ throw new Error('Error validating transfer'); } + // TODO: override hook address for self-relay const transferTxs = await warpCore.getTransferRemoteTxs({ originTokenAmount: new TokenAmount(amount, token), destination, @@ -165,11 +170,14 @@ async function executeDelivery({ const message: DispatchedMessage = HyperlaneCore.getDispatchedMessages(transferTxReceipt)[messageIndex]; + const parsed = parseWarpRouteMessage(message.parsed.body); + logBlue( `Sent transfer from sender (${senderAddress}) on ${origin} to recipient (${recipient}) on ${destination}.`, ); logBlue(`Message ID: ${message.id}`); log(`Message:\n${indentYamlOrJson(yamlStringify(message, null, 2), 4)}`); + log(`Body:\n${indentYamlOrJson(yamlStringify(parsed, null, 2), 4)}`); if (selfRelay) { const relayer = new HyperlaneRelayer({ core }); @@ -180,7 +188,7 @@ async function executeDelivery({ log('Attempting self-relay of transfer...'); await relayer.relayMessage(transferTxReceipt, messageIndex, message); - logGreen('Transfer was self-relayed!'); + logGreen(WarpSendLogs.SUCCESS); return; } diff --git a/typescript/cli/src/status/message.ts b/typescript/cli/src/status/message.ts index 63012499a..2c1e9af96 100644 --- a/typescript/cli/src/status/message.ts +++ b/typescript/cli/src/status/message.ts @@ -2,10 +2,10 @@ import type { TransactionReceipt } from '@ethersproject/providers'; import { input } from '@inquirer/prompts'; import { ChainName, HyperlaneCore, HyperlaneRelayer } from '@hyperlane-xyz/sdk'; -import { assert } from '@hyperlane-xyz/utils'; +import { assert, parseWarpRouteMessage } from '@hyperlane-xyz/utils'; import { WriteCommandContext } from '../context/types.js'; -import { log, logBlue, logGreen, logRed } from '../logger.js'; +import { log, logBlue, logGray, logGreen, logRed } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { stubMerkleTreeConfig } from '../utils/relay.js'; @@ -68,6 +68,11 @@ export async function checkMessageStatus({ const match = messages.find((m) => m.id === messageId); assert(match, `Message ${messageId} not found in dispatch tx ${dispatchTx}`); const message = match; + try { + const { amount, recipient } = parseWarpRouteMessage(message.parsed.body); + logGray(`Warping ${amount} to ${recipient}`); + // eslint-disable-next-line no-empty + } catch {} let deliveredTx: TransactionReceipt; diff --git a/typescript/cli/src/submit/submit.ts b/typescript/cli/src/submit/submit.ts index 6c2cca501..29d1f8a05 100644 --- a/typescript/cli/src/submit/submit.ts +++ b/typescript/cli/src/submit/submit.ts @@ -1,4 +1,5 @@ import { + EV5GnosisSafeTxBuilder, EV5GnosisSafeTxSubmitter, EV5ImpersonatedAccountTxSubmitter, EV5InterchainAccountTxTransformer, @@ -47,6 +48,10 @@ async function getSubmitter( return EV5GnosisSafeTxSubmitter.create(multiProvider, { ...submitterMetadata, }); + case TxSubmitterType.GNOSIS_TX_BUILDER: + return EV5GnosisSafeTxBuilder.create(multiProvider, { + ...submitterMetadata, + }); default: throw new Error(`Invalid TxSubmitterType.`); } diff --git a/typescript/cli/src/tests/commands/core.ts b/typescript/cli/src/tests/commands/core.ts index a0924514c..b28dbafd2 100644 --- a/typescript/cli/src/tests/commands/core.ts +++ b/typescript/cli/src/tests/commands/core.ts @@ -11,7 +11,6 @@ export async function hyperlaneCoreDeploy( ) { return $`yarn workspace @hyperlane-xyz/cli run hyperlane core deploy \ --registry ${REGISTRY_PATH} \ - --overrides " " \ --config ${coreInputPath} \ --chain ${chain} \ --key ${ANVIL_KEY} \ diff --git a/typescript/cli/src/tests/commands/helpers.ts b/typescript/cli/src/tests/commands/helpers.ts index 47fb9a11c..c4ad03651 100644 --- a/typescript/cli/src/tests/commands/helpers.ts +++ b/typescript/cli/src/tests/commands/helpers.ts @@ -1,3 +1,4 @@ +import { ERC20Test__factory, ERC4626Test__factory } from '@hyperlane-xyz/core'; import { ChainAddresses } from '@hyperlane-xyz/registry'; import { TokenRouterConfig, @@ -10,7 +11,11 @@ import { getContext } from '../../context/context.js'; import { readYamlOrJson, writeYamlOrJson } from '../../utils/files.js'; import { hyperlaneCoreDeploy } from './core.js'; -import { hyperlaneWarpApply, readWarpConfig } from './warp.js'; +import { + hyperlaneWarpApply, + hyperlaneWarpSendRelay, + readWarpConfig, +} from './warp.js'; export const TEST_CONFIGS_PATH = './test-configs'; export const REGISTRY_PATH = `${TEST_CONFIGS_PATH}/anvil`; @@ -43,7 +48,7 @@ export async function updateWarpOwnerConfig( warpDeployPath, ); warpDeployConfig[chain].owner = owner; - writeYamlOrJson(warpDeployPath, warpDeployConfig); + await writeYamlOrJson(warpDeployPath, warpDeployConfig); return warpDeployPath; } @@ -64,13 +69,22 @@ export async function updateOwner( /** * Extends the Warp route deployment with a new warp config */ -export async function extendWarpConfig( - chain: string, - chainToExtend: string, - extendedConfig: TokenRouterConfig, - warpCorePath: string, - warpDeployPath: string, -): Promise { +export async function extendWarpConfig(params: { + chain: string; + chainToExtend: string; + extendedConfig: TokenRouterConfig; + warpCorePath: string; + warpDeployPath: string; + strategyUrl?: string; +}): Promise { + const { + chain, + chainToExtend, + extendedConfig, + warpCorePath, + warpDeployPath, + strategyUrl, + } = params; const warpDeployConfig = await readWarpConfig( chain, warpCorePath, @@ -78,7 +92,7 @@ export async function extendWarpConfig( ); warpDeployConfig[chainToExtend] = extendedConfig; writeYamlOrJson(warpDeployPath, warpDeployConfig); - await hyperlaneWarpApply(warpDeployPath, warpCorePath); + await hyperlaneWarpApply(warpDeployPath, warpCorePath, strategyUrl); return warpDeployPath; } @@ -114,3 +128,54 @@ export async function getChainId(chainName: string, key: string) { const chainMetadata = await registry.getChainMetadata(chainName); return String(chainMetadata?.chainId); } + +export async function deployToken(privateKey: string, chain: string) { + const { multiProvider } = await getContext({ + registryUri: REGISTRY_PATH, + registryOverrideUri: '', + key: privateKey, + }); + + const token = await new ERC20Test__factory( + multiProvider.getSigner(chain), + ).deploy('token', 'token', '100000000000000000000', 18); + await token.deployed(); + + return token; +} + +export async function deploy4626Vault( + privateKey: string, + chain: string, + tokenAddress: string, +) { + const { multiProvider } = await getContext({ + registryUri: REGISTRY_PATH, + registryOverrideUri: '', + key: privateKey, + }); + + const vault = await new ERC4626Test__factory( + multiProvider.getSigner(chain), + ).deploy(tokenAddress, 'VAULT', 'VAULT'); + await vault.deployed(); + + return vault; +} + +/** + * Performs a round-trip warp relay between two chains using the specified warp core config. + * + * @param chain1 - The first chain to send the warp relay from. + * @param chain2 - The second chain to send the warp relay to and back from. + * @param warpCoreConfigPath - The path to the warp core config file. + * @returns A promise that resolves when the round-trip warp relay is complete. + */ +export async function sendWarpRouteMessageRoundTrip( + chain1: string, + chain2: string, + warpCoreConfigPath: string, +) { + await hyperlaneWarpSendRelay(chain1, chain2, warpCoreConfigPath); + return hyperlaneWarpSendRelay(chain2, chain1, warpCoreConfigPath); +} diff --git a/typescript/cli/src/tests/commands/warp.ts b/typescript/cli/src/tests/commands/warp.ts index fe7e7a509..3f3eec338 100644 --- a/typescript/cli/src/tests/commands/warp.ts +++ b/typescript/cli/src/tests/commands/warp.ts @@ -12,6 +12,7 @@ $.verbose = true; * Deploys the Warp route to the specified chain using the provided config. */ export async function hyperlaneWarpDeploy(warpCorePath: string) { + // --overrides is " " to allow local testing to work return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp deploy \ --registry ${REGISTRY_PATH} \ --overrides " " \ @@ -27,6 +28,7 @@ export async function hyperlaneWarpDeploy(warpCorePath: string) { export async function hyperlaneWarpApply( warpDeployPath: string, warpCorePath: string, + strategyUrl = '', ) { return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp apply \ --registry ${REGISTRY_PATH} \ @@ -35,6 +37,7 @@ export async function hyperlaneWarpApply( --warp ${warpCorePath} \ --key ${ANVIL_KEY} \ --verbosity debug \ + --strategy ${strategyUrl} \ --yes`; } @@ -53,6 +56,23 @@ export async function hyperlaneWarpRead( --config ${warpDeployPath}`; } +export async function hyperlaneWarpSendRelay( + origin: string, + destination: string, + warpCorePath: string, +) { + return $`yarn workspace @hyperlane-xyz/cli run hyperlane warp send \ + --relay \ + --registry ${REGISTRY_PATH} \ + --overrides " " \ + --origin ${origin} \ + --destination ${destination} \ + --warp ${warpCorePath} \ + --key ${ANVIL_KEY} \ + --verbosity debug \ + --yes`; +} + /** * Reads the Warp route deployment config to specified output path. * @param warpCorePath path to warp core diff --git a/typescript/cli/src/tests/warp-apply.e2e-test.ts b/typescript/cli/src/tests/warp-apply.e2e-test.ts index 23a135d8f..ff891448d 100644 --- a/typescript/cli/src/tests/warp-apply.e2e-test.ts +++ b/typescript/cli/src/tests/warp-apply.e2e-test.ts @@ -18,7 +18,11 @@ import { getChainId, updateOwner, } from './commands/helpers.js'; -import { hyperlaneWarpDeploy, readWarpConfig } from './commands/warp.js'; +import { + hyperlaneWarpApply, + hyperlaneWarpDeploy, + readWarpConfig, +} from './commands/warp.js'; const CHAIN_NAME_2 = 'anvil2'; const CHAIN_NAME_3 = 'anvil3'; @@ -32,7 +36,7 @@ const TEMP_PATH = '/tmp'; // /temp gets removed at the end of all-test.sh const WARP_CONFIG_PATH_2 = `${TEMP_PATH}/anvil2/warp-route-deployment-anvil2.yaml`; const WARP_CORE_CONFIG_PATH_2 = `${REGISTRY_PATH}/deployments/warp_routes/ETH/anvil2-config.yaml`; -const TEST_TIMEOUT = 60_000; // Long timeout since these tests can take a while +const TEST_TIMEOUT = 100_000; // Long timeout since these tests can take a while describe('WarpApply e2e tests', async function () { let chain2Addresses: ChainAddresses = {}; this.timeout(TEST_TIMEOUT); @@ -86,9 +90,8 @@ describe('WarpApply e2e tests', async function () { warpConfigPath, WARP_CORE_CONFIG_PATH_2, ); - expect(stdout).to.include( - 'Warp config on anvil2 is the same as target. No updates needed.', + 'Warp config is the same as target. No updates needed.', ); }); @@ -108,14 +111,68 @@ describe('WarpApply e2e tests', async function () { type: TokenType.native, }; - await extendWarpConfig( + await extendWarpConfig({ + chain: CHAIN_NAME_2, + chainToExtend: CHAIN_NAME_3, + extendedConfig: config, + warpCorePath: WARP_CORE_CONFIG_PATH_2, + warpDeployPath: warpConfigPath, + }); + + const COMBINED_WARP_CORE_CONFIG_PATH = `${REGISTRY_PATH}/deployments/warp_routes/ETH/anvil2-anvil3-config.yaml`; + + // Check that chain2 is enrolled in chain1 + const updatedWarpDeployConfig1 = await readWarpConfig( CHAIN_NAME_2, + COMBINED_WARP_CORE_CONFIG_PATH, + warpConfigPath, + ); + + const chain2Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY); + const remoteRouterKeys1 = Object.keys( + updatedWarpDeployConfig1[CHAIN_NAME_2].remoteRouters!, + ); + expect(remoteRouterKeys1).to.include(chain2Id); + + // Check that chain1 is enrolled in chain2 + const updatedWarpDeployConfig2 = await readWarpConfig( CHAIN_NAME_3, - config, - WARP_CORE_CONFIG_PATH_2, + COMBINED_WARP_CORE_CONFIG_PATH, warpConfigPath, ); + const chain1Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY); + const remoteRouterKeys2 = Object.keys( + updatedWarpDeployConfig2[CHAIN_NAME_3].remoteRouters!, + ); + expect(remoteRouterKeys2).to.include(chain1Id); + }); + + it('should extend an existing warp route with json strategy', async () => { + // Read existing config into a file + const warpConfigPath = `${TEMP_PATH}/warp-route-deployment-2.yaml`; + await readWarpConfig(CHAIN_NAME_2, WARP_CORE_CONFIG_PATH_2, warpConfigPath); + + // Extend with new config + const config: TokenRouterConfig = { + decimals: 18, + mailbox: chain2Addresses!.mailbox, + name: 'Ether', + owner: new Wallet(ANVIL_KEY).address, + symbol: 'ETH', + totalSupply: 0, + type: TokenType.native, + }; + + await extendWarpConfig({ + chain: CHAIN_NAME_2, + chainToExtend: CHAIN_NAME_3, + extendedConfig: config, + warpCorePath: WARP_CORE_CONFIG_PATH_2, + warpDeployPath: warpConfigPath, + strategyUrl: `${EXAMPLES_PATH}/submit/strategy/json-rpc-chain-strategy.yaml`, + }); + const COMBINED_WARP_CORE_CONFIG_PATH = `${REGISTRY_PATH}/deployments/warp_routes/ETH/anvil2-anvil3-config.yaml`; // Check that chain2 is enrolled in chain1 @@ -144,4 +201,118 @@ describe('WarpApply e2e tests', async function () { ); expect(remoteRouterKeys2).to.include(chain1Id); }); + + it('should extend an existing warp route and update the owner', async () => { + const warpDeployPath = `${TEMP_PATH}/warp-route-deployment-2.yaml`; + // Burn anvil2 owner in config + const warpDeployConfig = await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2, + warpDeployPath, + ); + warpDeployConfig[CHAIN_NAME_2].owner = BURN_ADDRESS; + + // Extend with new config + const randomOwner = new Wallet(ANVIL_KEY).address; + const extendedConfig: TokenRouterConfig = { + decimals: 18, + mailbox: chain2Addresses!.mailbox, + name: 'Ether', + owner: randomOwner, + symbol: 'ETH', + totalSupply: 0, + type: TokenType.native, + }; + + warpDeployConfig[CHAIN_NAME_3] = extendedConfig; + writeYamlOrJson(warpDeployPath, warpDeployConfig); + await hyperlaneWarpApply(warpDeployPath, WARP_CORE_CONFIG_PATH_2); + + const COMBINED_WARP_CORE_CONFIG_PATH = `${REGISTRY_PATH}/deployments/warp_routes/ETH/anvil2-anvil3-config.yaml`; + + const updatedWarpDeployConfig_2 = await readWarpConfig( + CHAIN_NAME_2, + COMBINED_WARP_CORE_CONFIG_PATH, + warpDeployPath, + ); + const updatedWarpDeployConfig_3 = await readWarpConfig( + CHAIN_NAME_3, + COMBINED_WARP_CORE_CONFIG_PATH, + warpDeployPath, + ); + // Check that anvil2 owner is burned + expect(updatedWarpDeployConfig_2.anvil2.owner).to.equal(BURN_ADDRESS); + + // Also, anvil3 owner is not burned + expect(updatedWarpDeployConfig_3.anvil3.owner).to.equal(randomOwner); + + // Check that both chains enrolled + const chain2Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY); + const chain3Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY); + + const remoteRouterKeys2 = Object.keys( + updatedWarpDeployConfig_2[CHAIN_NAME_2].remoteRouters!, + ); + const remoteRouterKeys3 = Object.keys( + updatedWarpDeployConfig_3[CHAIN_NAME_3].remoteRouters!, + ); + expect(remoteRouterKeys2).to.include(chain3Id); + expect(remoteRouterKeys3).to.include(chain2Id); + }); + + it('should extend an existing warp route and update all destination domains', async () => { + // Read existing config into a file + const warpConfigPath = `${TEMP_PATH}/warp-route-deployment-2.yaml`; + const warpDeployConfig = await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2, + warpConfigPath, + ); + warpDeployConfig[CHAIN_NAME_2].gas = 7777; + + // Extend with new config + const GAS = 694200; + const extendedConfig: TokenRouterConfig = { + decimals: 18, + mailbox: chain2Addresses!.mailbox, + name: 'Ether', + owner: new Wallet(ANVIL_KEY).address, + symbol: 'ETH', + totalSupply: 0, + type: TokenType.native, + gas: GAS, + }; + warpDeployConfig[CHAIN_NAME_3] = extendedConfig; + writeYamlOrJson(warpConfigPath, warpDeployConfig); + await hyperlaneWarpApply(warpConfigPath, WARP_CORE_CONFIG_PATH_2); + + const COMBINED_WARP_CORE_CONFIG_PATH = `${REGISTRY_PATH}/deployments/warp_routes/ETH/anvil2-anvil3-config.yaml`; + + // Check that chain2 is enrolled in chain1 + const updatedWarpDeployConfig_2 = await readWarpConfig( + CHAIN_NAME_2, + COMBINED_WARP_CORE_CONFIG_PATH, + warpConfigPath, + ); + + const chain2Id = await getChainId(CHAIN_NAME_2, ANVIL_KEY); + const chain3Id = await getChainId(CHAIN_NAME_3, ANVIL_KEY); + + // Destination gas should be set in the existing chain (chain2) to include the extended chain (chain3) + const destinationGas_2 = + updatedWarpDeployConfig_2[CHAIN_NAME_2].destinationGas!; + expect(Object.keys(destinationGas_2)).to.include(chain3Id); + expect(destinationGas_2[chain3Id]).to.equal(GAS.toString()); + + // Destination gas should be set for the extended chain (chain3) + const updatedWarpDeployConfig_3 = await readWarpConfig( + CHAIN_NAME_3, + COMBINED_WARP_CORE_CONFIG_PATH, + warpConfigPath, + ); + const destinationGas_3 = + updatedWarpDeployConfig_3[CHAIN_NAME_3].destinationGas!; + expect(Object.keys(destinationGas_3)).to.include(chain2Id); + expect(destinationGas_3[chain2Id]).to.equal('7777'); + }); }); diff --git a/typescript/cli/src/tests/warp-deploy.e2e-test.ts b/typescript/cli/src/tests/warp-deploy.e2e-test.ts new file mode 100644 index 000000000..6263f70cb --- /dev/null +++ b/typescript/cli/src/tests/warp-deploy.e2e-test.ts @@ -0,0 +1,114 @@ +import * as chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; + +import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { TokenType, WarpRouteDeployConfig } from '@hyperlane-xyz/sdk'; + +import { WarpSendLogs } from '../send/transfer.js'; +import { writeYamlOrJson } from '../utils/files.js'; + +import { + ANVIL_KEY, + REGISTRY_PATH, + deploy4626Vault, + deployOrUseExistingCore, + deployToken, + sendWarpRouteMessageRoundTrip, +} from './commands/helpers.js'; +import { hyperlaneWarpDeploy, readWarpConfig } from './commands/warp.js'; + +chai.use(chaiAsPromised); +const expect = chai.expect; +chai.should(); + +const CHAIN_NAME_2 = 'anvil2'; +const CHAIN_NAME_3 = 'anvil3'; + +const EXAMPLES_PATH = './examples'; +const TEMP_PATH = '/tmp'; // /temp gets removed at the end of all-test.sh + +const CORE_CONFIG_PATH = `${EXAMPLES_PATH}/core-config.yaml`; +const WARP_CONFIG_PATH = `${TEMP_PATH}/warp-route-deployment-deploy.yaml`; +const WARP_CORE_CONFIG_PATH_2_3 = `${REGISTRY_PATH}/deployments/warp_routes/VAULT/anvil2-anvil3-config.yaml`; + +const TEST_TIMEOUT = 60_000; // Long timeout since these tests can take a while +describe('WarpDeploy e2e tests', async function () { + let chain2Addresses: ChainAddresses = {}; + let token: any; + let vault: any; + + this.timeout(TEST_TIMEOUT); + + before(async function () { + chain2Addresses = await deployOrUseExistingCore( + CHAIN_NAME_2, + CORE_CONFIG_PATH, + ANVIL_KEY, + ); + + await deployOrUseExistingCore(CHAIN_NAME_3, CORE_CONFIG_PATH, ANVIL_KEY); + + token = await deployToken(ANVIL_KEY, CHAIN_NAME_2); + vault = await deploy4626Vault(ANVIL_KEY, CHAIN_NAME_2, token.address); + }); + + it('should only allow rebasing yield route to be deployed with rebasing synthetic', async function () { + const warpConfig: WarpRouteDeployConfig = { + [CHAIN_NAME_2]: { + type: TokenType.collateralVaultRebase, + token: vault.address, + mailbox: chain2Addresses.mailbox, + owner: chain2Addresses.mailbox, + }, + [CHAIN_NAME_3]: { + type: TokenType.synthetic, + mailbox: chain2Addresses.mailbox, + owner: chain2Addresses.mailbox, + }, + }; + + writeYamlOrJson(WARP_CONFIG_PATH, warpConfig); + await hyperlaneWarpDeploy(WARP_CONFIG_PATH).should.be.rejected; // TODO: revisit this to figure out how to parse the error. + }); + + it(`should be able to bridge between ${TokenType.collateralVaultRebase} and ${TokenType.syntheticRebase}`, async function () { + const warpConfig: WarpRouteDeployConfig = { + [CHAIN_NAME_2]: { + type: TokenType.collateralVaultRebase, + token: vault.address, + mailbox: chain2Addresses.mailbox, + owner: chain2Addresses.mailbox, + }, + [CHAIN_NAME_3]: { + type: TokenType.syntheticRebase, + mailbox: chain2Addresses.mailbox, + owner: chain2Addresses.mailbox, + collateralChainName: CHAIN_NAME_2, + }, + }; + + writeYamlOrJson(WARP_CONFIG_PATH, warpConfig); + await hyperlaneWarpDeploy(WARP_CONFIG_PATH); + + // Check collateralRebase + const collateralRebaseConfig = ( + await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2_3, + WARP_CONFIG_PATH, + ) + )[CHAIN_NAME_2]; + + expect(collateralRebaseConfig.type).to.equal( + TokenType.collateralVaultRebase, + ); + + // Try to send a transaction + const { stdout } = await sendWarpRouteMessageRoundTrip( + CHAIN_NAME_2, + CHAIN_NAME_3, + WARP_CORE_CONFIG_PATH_2_3, + ); + expect(stdout).to.include(WarpSendLogs.SUCCESS); + }); +}); diff --git a/typescript/cli/src/tests/warp-read.e2e-test.ts b/typescript/cli/src/tests/warp-read.e2e-test.ts new file mode 100644 index 000000000..1cbf336d5 --- /dev/null +++ b/typescript/cli/src/tests/warp-read.e2e-test.ts @@ -0,0 +1,54 @@ +import { expect } from 'chai'; + +import { WarpRouteDeployConfig } from '@hyperlane-xyz/sdk'; + +import { readYamlOrJson, writeYamlOrJson } from '../utils/files.js'; + +import { + ANVIL_KEY, + REGISTRY_PATH, + deployOrUseExistingCore, +} from './commands/helpers.js'; +import { hyperlaneWarpDeploy, readWarpConfig } from './commands/warp.js'; + +const CHAIN_NAME_2 = 'anvil2'; + +const EXAMPLES_PATH = './examples'; +const CORE_CONFIG_PATH = `${EXAMPLES_PATH}/core-config.yaml`; +const WARP_CONFIG_PATH_EXAMPLE = `${EXAMPLES_PATH}/warp-route-deployment.yaml`; + +const TEMP_PATH = '/tmp'; // /temp gets removed at the end of all-test.sh +const WARP_CONFIG_PATH_2 = `${TEMP_PATH}/anvil2/warp-route-deployment-anvil2.yaml`; +const WARP_CORE_CONFIG_PATH_2 = `${REGISTRY_PATH}/deployments/warp_routes/ETH/anvil2-config.yaml`; + +const TEST_TIMEOUT = 60_000; // Long timeout since these tests can take a while +describe('WarpRead e2e tests', async function () { + let anvil2Config: WarpRouteDeployConfig; + this.timeout(TEST_TIMEOUT); + before(async function () { + await deployOrUseExistingCore(CHAIN_NAME_2, CORE_CONFIG_PATH, ANVIL_KEY); + + // Create a new warp config using the example + const exampleWarpConfig: WarpRouteDeployConfig = readYamlOrJson( + WARP_CONFIG_PATH_EXAMPLE, + ); + anvil2Config = { anvil2: { ...exampleWarpConfig.anvil1 } }; + writeYamlOrJson(WARP_CONFIG_PATH_2, anvil2Config); + }); + + beforeEach(async function () { + await hyperlaneWarpDeploy(WARP_CONFIG_PATH_2); + }); + + it('should be able to read a warp route', async function () { + const warpConfigPath = `${TEMP_PATH}/warp-route-deployment-2.yaml`; + const warpConfig = await readWarpConfig( + CHAIN_NAME_2, + WARP_CORE_CONFIG_PATH_2, + warpConfigPath, + ); + expect(warpConfig[CHAIN_NAME_2].type).to.be.equal( + anvil2Config[CHAIN_NAME_2].type, + ); + }); +}); diff --git a/typescript/cli/src/utils/chains.ts b/typescript/cli/src/utils/chains.ts index fe226b1b8..f5fb2b341 100644 --- a/typescript/cli/src/utils/chains.ts +++ b/typescript/cli/src/utils/chains.ts @@ -1,13 +1,14 @@ -import { Separator, checkbox } from '@inquirer/prompts'; +import { Separator, confirm } from '@inquirer/prompts'; import select from '@inquirer/select'; import chalk from 'chalk'; import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; import { toTitleCase } from '@hyperlane-xyz/utils'; -import { log, logRed, logTip } from '../logger.js'; +import { log } from '../logger.js'; import { calculatePageSize } from './cli-options.js'; +import { SearchableCheckboxChoice, searchableCheckBox } from './input.js'; // A special value marker to indicate user selected // a new chain in the list @@ -18,37 +19,109 @@ export async function runSingleChainSelectionStep( message = 'Select chain', ) { const networkType = await selectNetworkType(); - const choices = getChainChoices(chainMetadata, networkType); + const { choices, networkTypeSeparator } = getChainChoices( + chainMetadata, + networkType, + ); const chain = (await select({ message, - choices, + choices: [networkTypeSeparator, ...choices], pageSize: calculatePageSize(2), })) as string; handleNewChain([chain]); return chain; } -export async function runMultiChainSelectionStep( - chainMetadata: ChainMap, +type RunMultiChainSelectionStepOptions = { + /** + * The metadata of the chains that will be displayed to the user + */ + chainMetadata: ChainMap; + + /** + * The message to display to the user + * + * @default 'Select chains' + */ + message?: string; + + /** + * The minimum number of chains that must be selected + * + * @default 0 + */ + requireNumber?: number; + + /** + * Whether to ask for confirmation after the selection + * + * @default false + */ + requiresConfirmation?: boolean; + + /** + * The network type to filter the chains by + * + * @default undefined + */ + networkType?: 'mainnet' | 'testnet'; +}; + +export async function runMultiChainSelectionStep({ + chainMetadata, message = 'Select chains', requireNumber = 0, -) { - const networkType = await selectNetworkType(); - const choices = getChainChoices(chainMetadata, networkType); + requiresConfirmation = false, + networkType = undefined, +}: RunMultiChainSelectionStepOptions) { + const selectedNetworkType = networkType ?? (await selectNetworkType()); + const { choices, networkTypeSeparator } = getChainChoices( + chainMetadata, + selectedNetworkType, + ); + + let currentChoiceSelection = new Set(); while (true) { - logTip( - `Use SPACE key to select at least ${requireNumber} chains, then press ENTER`, - ); - const chains = (await checkbox({ + const chains = await searchableCheckBox({ message, - choices, + selectableOptionsSeparator: networkTypeSeparator, + choices: choices.map((choice) => + currentChoiceSelection.has(choice.name) + ? { ...choice, checked: true } + : choice, + ), + instructions: `Use TAB key to select at least ${requireNumber} chains, then press ENTER to proceed. Type to search for a specific chain.`, + theme: { + style: { + // The leading space is needed because the help tip will be tightly close to the message header + helpTip: (text: string) => ` ${chalk.bgYellow(text)}`, + }, + helpMode: 'always', + }, pageSize: calculatePageSize(2), - })) as string[]; + validate: (answer): string | boolean => { + if (answer.length < requireNumber) { + return `Please select at least ${requireNumber} chains`; + } + + return true; + }, + }); + handleNewChain(chains); - if (chains?.length < requireNumber) { - logRed(`Please select at least ${requireNumber} chains`); + + const confirmed = requiresConfirmation + ? await confirm({ + message: `Is this chain selection correct?: ${chalk.cyan( + chains.join(', '), + )}`, + }) + : true; + if (!confirmed) { + currentChoiceSelection = new Set(chains); continue; } + return chains; } } @@ -75,12 +148,17 @@ function getChainChoices( const filteredChains = chains.filter((c) => networkType === 'mainnet' ? !c.isTestnet : !!c.isTestnet, ); - const choices: Parameters['0']['choices'] = [ + const choices: SearchableCheckboxChoice[] = [ { name: '(New custom chain)', value: NEW_CHAIN_MARKER }, - new Separator(`--${toTitleCase(networkType)} Chains--`), ...chainsToChoices(filteredChains), ]; - return choices; + + return { + choices, + networkTypeSeparator: new Separator( + `--${toTitleCase(networkType)} Chains--`, + ), + }; } function handleNewChain(chainNames: string[]) { diff --git a/typescript/cli/src/utils/files.ts b/typescript/cli/src/utils/files.ts index 30360ddd8..9c7cb6216 100644 --- a/typescript/cli/src/utils/files.ts +++ b/typescript/cli/src/utils/files.ts @@ -23,6 +23,13 @@ export type ArtifactsFile = { description: string; }; +export function removeEndingSlash(dirPath: string): string { + if (dirPath.endsWith('/')) { + return dirPath.slice(0, -1); + } + return dirPath; +} + export function resolvePath(filePath: string): string { if (filePath.startsWith('~')) { const homedir = os.homedir(); diff --git a/typescript/cli/src/utils/input.ts b/typescript/cli/src/utils/input.ts index 0f8c9ef66..251c6c0c5 100644 --- a/typescript/cli/src/utils/input.ts +++ b/typescript/cli/src/utils/input.ts @@ -1,8 +1,31 @@ -import { confirm, input } from '@inquirer/prompts'; +import { + Separator, + type Theme, + createPrompt, + isEnterKey, + makeTheme, + useEffect, + useKeypress, + useMemo, + usePagination, + usePrefix, + useRef, + useState, +} from '@inquirer/core'; +import figures from '@inquirer/figures'; +import { KeypressEvent, confirm, input } from '@inquirer/prompts'; +import type { PartialDeep } from '@inquirer/type'; +import ansiEscapes from 'ansi-escapes'; +import chalk from 'chalk'; -import { logGray } from '../logger.js'; +import { WarpCoreConfig } from '@hyperlane-xyz/sdk'; + +import { readWarpCoreConfig } from '../config/warp.js'; +import { CommandContext } from '../context/types.js'; +import { logGray, logRed } from '../logger.js'; import { indentYamlOrJson } from './files.js'; +import { selectRegistryWarpRoute } from './tokens.js'; export async function detectAndConfirmOrPrompt( detect: () => Promise, @@ -53,3 +76,520 @@ export async function inputWithInfo({ } while (answer === INFO_COMMAND); return answer; } + +/** + * Gets a {@link WarpCoreConfig} based on the provided path or prompts the user to choose one: + * - if `symbol` is provided the user will have to select one of the available warp routes. + * - if `warp` is provided the config will be read by the provided file path. + * - if none is provided the CLI will exit. + */ +export async function getWarpCoreConfigOrExit({ + context, + symbol, + warp, +}: { + context: CommandContext; + symbol?: string; + warp?: string; +}): Promise { + let warpCoreConfig: WarpCoreConfig; + if (symbol) { + warpCoreConfig = await selectRegistryWarpRoute(context.registry, symbol); + } else if (warp) { + warpCoreConfig = readWarpCoreConfig(warp); + } else { + logRed(`Please specify either a symbol or warp config`); + process.exit(0); + } + + return warpCoreConfig; +} + +/** + * Searchable checkbox code + * + * Note that the code below hab been implemented by taking inspiration from + * the @inquirer/prompt package search and checkbox prompts + * + * - https://github.com/SBoudrias/Inquirer.js/blob/main/packages/search/src/index.mts + * - https://github.com/SBoudrias/Inquirer.js/blob/main/packages/checkbox/src/index.mts + */ + +type Status = 'loading' | 'idle' | 'done'; + +type SearchableCheckboxTheme = { + icon: { + checked: string; + unchecked: string; + cursor: string; + }; + style: { + disabledChoice: (text: string) => string; + renderSelectedChoices: ( + selectedChoices: ReadonlyArray>, + allChoices: ReadonlyArray | Separator>, + ) => string; + description: (text: string) => string; + helpTip: (text: string) => string; + }; + helpMode: 'always' | 'never' | 'auto'; +}; + +const checkboxTheme: SearchableCheckboxTheme = { + icon: { + checked: chalk.green(figures.circleFilled), + unchecked: figures.circle, + cursor: figures.pointer, + }, + style: { + disabledChoice: (text: string) => chalk.dim(`- ${text}`), + renderSelectedChoices: (selectedChoices) => + selectedChoices.map((choice) => choice.short).join(', '), + description: (text: string) => chalk.cyan(text), + helpTip: (text) => ` ${text}`, + }, + helpMode: 'always', +}; + +export type SearchableCheckboxChoice = { + value: Value; + name?: string; + description?: string; + short?: string; + disabled?: boolean | string; + checked?: boolean; +}; + +type NormalizedChoice = Required< + Omit, 'description'> +> & { + description?: string; +}; + +type SearchableCheckboxConfig = { + message: string; + prefix?: string; + pageSize?: number; + instructions?: string; + choices: ReadonlyArray>; + loop?: boolean; + required?: boolean; + selectableOptionsSeparator?: Separator; + validate?: ( + choices: ReadonlyArray>, + ) => boolean | string | Promise; + theme?: PartialDeep>; +}; + +type Item = NormalizedChoice | Separator; + +type SearchableCheckboxState = { + options: Item[]; + currentOptionState: Record>; +}; + +function isSelectable( + item: Item, +): item is NormalizedChoice { + return !Separator.isSeparator(item) && !item.disabled; +} + +function isChecked(item: Item): item is NormalizedChoice { + return isSelectable(item) && Boolean(item.checked); +} + +function toggle(item: Item): Item { + return isSelectable(item) ? { ...item, checked: !item.checked } : item; +} + +function normalizeChoices( + choices: ReadonlyArray>, +): NormalizedChoice[] { + return choices.map((choice) => { + const name = choice.name ?? String(choice.value); + return { + value: choice.value, + name, + short: choice.short ?? name, + description: choice.description, + disabled: choice.disabled ?? false, + checked: choice.checked ?? false, + }; + }); +} + +function sortNormalizedItems( + a: NormalizedChoice, + b: NormalizedChoice, +): number { + return a.name.localeCompare(b.name); +} + +function organizeItems( + items: Array>, + selectableOptionsSeparator?: Separator, +): Array | Separator> { + const orderedItems = []; + + const checkedItems = items.filter( + (item) => !Separator.isSeparator(item) && item.checked, + ) as NormalizedChoice[]; + + if (checkedItems.length !== 0) { + orderedItems.push(new Separator('--Selected Options--')); + + orderedItems.push(...checkedItems.sort(sortNormalizedItems)); + } + + orderedItems.push( + selectableOptionsSeparator ?? new Separator('--Available Options--'), + ); + + const nonCheckedItems = items.filter( + (item) => !Separator.isSeparator(item) && !item.checked, + ) as NormalizedChoice[]; + + orderedItems.push(...nonCheckedItems.sort(sortNormalizedItems)); + + if (orderedItems.length === 1) { + return []; + } + + return orderedItems; +} + +interface BuildViewOptions { + theme: Readonly>; + pageSize: number; + firstRender: { current: boolean }; + page: string; + currentOptions: ReadonlyArray>; + prefix: string; + message: string; + errorMsg?: string; + status: Status; + searchTerm: string; + description?: string; + instructions?: string; +} + +interface GetErrorMessageOptions + extends Pick< + BuildViewOptions, + 'theme' | 'errorMsg' | 'status' | 'searchTerm' + > { + currentItemCount: number; +} + +function getErrorMessage({ + theme, + errorMsg, + currentItemCount, + status, + searchTerm, +}: GetErrorMessageOptions): string { + if (errorMsg) { + return `${theme.style.error(errorMsg)}`; + } else if (currentItemCount === 0 && searchTerm !== '' && status === 'idle') { + return theme.style.error('No results found'); + } + + return ''; +} + +interface GetHelpTipsOptions + extends Pick< + BuildViewOptions, + 'theme' | 'pageSize' | 'firstRender' | 'instructions' + > { + currentItemCount: number; +} + +function getHelpTips({ + theme, + instructions, + currentItemCount, + pageSize, + firstRender, +}: GetHelpTipsOptions): { helpTipTop: string; helpTipBottom: string } { + let helpTipTop = ''; + let helpTipBottom = ''; + const defaultTopHelpTip = + instructions ?? + `(Press ${theme.style.key('tab')} to select, and ${theme.style.key( + 'enter', + )} to proceed`; + const defaultBottomHelpTip = `\n${theme.style.help( + '(Use arrow keys to reveal more choices)', + )}`; + + if (theme.helpMode === 'always') { + helpTipTop = theme.style.helpTip(defaultTopHelpTip); + helpTipBottom = currentItemCount > pageSize ? defaultBottomHelpTip : ''; + firstRender.current = false; + } else if (theme.helpMode === 'auto' && firstRender.current) { + helpTipTop = theme.style.helpTip(defaultTopHelpTip); + helpTipBottom = currentItemCount > pageSize ? defaultBottomHelpTip : ''; + firstRender.current = false; + } + + return { helpTipBottom, helpTipTop }; +} + +function formatRenderedItem( + item: Readonly>, + isActive: boolean, + theme: Readonly>, +): string { + if (Separator.isSeparator(item)) { + return ` ${item.separator}`; + } + + if (item.disabled) { + const disabledLabel = + typeof item.disabled === 'string' ? item.disabled : '(disabled)'; + return theme.style.disabledChoice(`${item.name} ${disabledLabel}`); + } + + const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked; + const color = isActive ? theme.style.highlight : (x: string) => x; + const cursor = isActive ? theme.icon.cursor : ' '; + return color(`${cursor}${checkbox} ${item.name}`); +} + +function getListBounds(items: ReadonlyArray>): { + first: number; + last: number; +} { + const first = items.findIndex(isSelectable); + // findLastIndex replacement as the project must support older ES versions + let last = -1; + for (let i = items.length; i >= 0; --i) { + if (items[i] && isSelectable(items[i])) { + last = i; + break; + } + } + + return { first, last }; +} + +function buildView({ + page, + prefix, + theme, + status, + message, + errorMsg, + pageSize, + firstRender, + searchTerm, + description, + instructions, + currentOptions, +}: BuildViewOptions): string { + message = theme.style.message(message); + if (status === 'done') { + const selection = currentOptions.filter(isChecked); + const answer = theme.style.answer( + theme.style.renderSelectedChoices(selection, currentOptions), + ); + + return `${prefix} ${message} ${answer}`; + } + + const currentItemCount = currentOptions.length; + const { helpTipBottom, helpTipTop } = getHelpTips({ + theme, + instructions, + currentItemCount, + pageSize, + firstRender, + }); + + const choiceDescription = description + ? `\n${theme.style.description(description)}` + : ``; + + const error = getErrorMessage({ + theme, + errorMsg, + currentItemCount, + status, + searchTerm, + }); + + return `${prefix} ${message}${helpTipTop} ${searchTerm}\n${page}${helpTipBottom}${choiceDescription}${error}${ansiEscapes.cursorHide}`; +} + +// the isUpKey function from the inquirer package is not used +// because it detects k and p as custom keybindings that cause +// the option selection to go up instead of writing the letters +// in the search string +function isUpKey(key: KeypressEvent): boolean { + return key.name === 'up'; +} + +// the isDownKey function from the inquirer package is not used +// because it detects j and n as custom keybindings that cause +// the option selection to go down instead of writing the letters +// in the search string +function isDownKey(key: KeypressEvent): boolean { + return key.name === 'down'; +} + +export const searchableCheckBox = createPrompt( + ( + config: SearchableCheckboxConfig, + done: (value: Array) => void, + ) => { + const { + instructions, + pageSize = 7, + loop = true, + required, + validate = () => true, + selectableOptionsSeparator, + } = config; + const theme = makeTheme( + checkboxTheme, + config.theme, + ); + const firstRender = useRef(true); + const [status, setStatus] = useState('idle'); + const prefix = usePrefix({ theme }); + const [searchTerm, setSearchTerm] = useState(''); + const [errorMsg, setError] = useState(); + + const normalizedChoices = normalizeChoices(config.choices); + const [optionState, setOptionState] = useState< + SearchableCheckboxState + >({ + options: normalizedChoices, + currentOptionState: Object.fromEntries( + normalizedChoices.map((item) => [item.name, item]), + ), + }); + + const bounds = useMemo( + () => getListBounds(optionState.options), + [optionState.options], + ); + + const [active, setActive] = useState(bounds.first); + + useEffect(() => { + let filteredItems; + if (!searchTerm) { + filteredItems = Object.values(optionState.currentOptionState); + } else { + filteredItems = Object.values(optionState.currentOptionState).filter( + (item) => + Separator.isSeparator(item) || + item.name.includes(searchTerm) || + item.checked, + ); + } + + setActive(0); + setError(undefined); + setOptionState({ + currentOptionState: optionState.currentOptionState, + options: organizeItems(filteredItems, selectableOptionsSeparator), + }); + }, [searchTerm]); + + useKeypress(async (key, rl) => { + if (isEnterKey(key)) { + const selection = optionState.options.filter(isChecked); + const isValid = await validate(selection); + if (required && !optionState.options.some(isChecked)) { + setError('At least one choice must be selected'); + } else if (isValid === true) { + setStatus('done'); + done(selection.map((choice) => choice.value)); + } else { + setError(isValid || 'You must select a valid value'); + setSearchTerm(''); + } + } else if (isUpKey(key) || isDownKey(key)) { + if ( + loop || + (isUpKey(key) && active !== bounds.first) || + (isDownKey(key) && active !== bounds.last) + ) { + const offset = isUpKey(key) ? -1 : 1; + let next = active; + do { + next = + (next + offset + optionState.options.length) % + optionState.options.length; + } while ( + optionState.options[next] && + !isSelectable(optionState.options[next]) + ); + setActive(next); + } + } else if (key.name === 'tab' && optionState.options.length > 0) { + // Avoid the message header to be printed again in the console + rl.clearLine(0); + + const currentElement = optionState.options[active]; + if ( + currentElement && + !Separator.isSeparator(currentElement) && + optionState.currentOptionState[currentElement.name] + ) { + const updatedDataMap: Record> = { + ...optionState.currentOptionState, + [currentElement.name]: toggle( + optionState.currentOptionState[currentElement.name], + ) as NormalizedChoice, + }; + + setError(undefined); + setOptionState({ + options: organizeItems( + Object.values(updatedDataMap), + selectableOptionsSeparator, + ), + currentOptionState: updatedDataMap, + }); + setSearchTerm(''); + } + } else { + setSearchTerm(rl.line); + } + }); + + let description; + const page = usePagination({ + items: optionState.options, + active, + renderItem({ item, isActive }) { + if (isActive && !Separator.isSeparator(item)) { + description = item.description; + } + + return formatRenderedItem(item, isActive, theme); + }, + pageSize, + loop, + }); + + return buildView({ + page, + theme, + prefix, + status, + pageSize, + errorMsg, + firstRender, + searchTerm, + description, + instructions, + currentOptions: optionState.options, + message: theme.style.message(config.message), + }); + }, +); diff --git a/typescript/cli/src/utils/output.ts b/typescript/cli/src/utils/output.ts new file mode 100644 index 000000000..442b8a090 --- /dev/null +++ b/typescript/cli/src/utils/output.ts @@ -0,0 +1,56 @@ +import chalk from 'chalk'; + +export enum ViolationDiffType { + None, + Expected, + Actual, +} + +type FormatterByDiffType = Record string>; + +const defaultDiffFormatter: FormatterByDiffType = { + [ViolationDiffType.Actual]: chalk.red, + [ViolationDiffType.Expected]: chalk.green, + [ViolationDiffType.None]: (text: string) => text, +}; + +/** + * Takes a yaml formatted string and highlights differences by looking at `expected` and `actual` properties. + */ +export function formatYamlViolationsOutput( + yamlString: string, + formatters: FormatterByDiffType = defaultDiffFormatter, +): string { + const lines = yamlString.split('\n'); + + let curr: ViolationDiffType = ViolationDiffType.None; + let lastDiffIndent = 0; + const highlightedLines = lines.map((line) => { + // Get how many white space/tabs we have before the property name + const match = line.match(/^(\s*)/); + const currentIndent = match ? match[0].length : 0; + + let formattedLine = line; + // if the current indentation is smaller than the previous diff one + // we just got out of a diff property and we reset the formatting + if (currentIndent < lastDiffIndent) { + curr = ViolationDiffType.None; + } + + if (line.includes('expected:')) { + lastDiffIndent = currentIndent; + curr = ViolationDiffType.Expected; + formattedLine = line.replace('expected:', 'EXPECTED:'); + } + + if (line.includes('actual:')) { + lastDiffIndent = currentIndent; + curr = ViolationDiffType.Actual; + formattedLine = line.replace('actual:', 'ACTUAL:'); + } + + return formatters[curr](formattedLine); + }); + + return highlightedLines.join('\n'); +} diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 86fb165ea..087c0bddb 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '5.1.0'; +export const VERSION = '5.6.2'; diff --git a/typescript/github-proxy/.dev.vars.example b/typescript/github-proxy/.dev.vars.example new file mode 100644 index 000000000..9cedf2571 --- /dev/null +++ b/typescript/github-proxy/.dev.vars.example @@ -0,0 +1 @@ +GITHUB_API_KEY= diff --git a/typescript/github-proxy/.gitignore b/typescript/github-proxy/.gitignore new file mode 100644 index 000000000..3b0fe33c4 --- /dev/null +++ b/typescript/github-proxy/.gitignore @@ -0,0 +1,172 @@ +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* + +# wrangler project + +.dev.vars +.wrangler/ diff --git a/typescript/github-proxy/CHANGELOG.md b/typescript/github-proxy/CHANGELOG.md new file mode 100644 index 000000000..d23057e7d --- /dev/null +++ b/typescript/github-proxy/CHANGELOG.md @@ -0,0 +1,22 @@ +# @hyperlane-xyz/github-proxy + +## 5.6.2 + +## 5.6.1 + +## 5.6.0 + +## 5.5.0 + +## 5.4.0 + +## 5.3.0 + +## 5.2.1 + +## 5.2.0 + +### Minor Changes + +- 0e2f94ba1: Add github proxy to reduce github API load +- 3113807e3: Add recursive=true query string to Github Proxy to allow nested tree search diff --git a/typescript/github-proxy/README.md b/typescript/github-proxy/README.md new file mode 100644 index 000000000..498b204ca --- /dev/null +++ b/typescript/github-proxy/README.md @@ -0,0 +1,23 @@ +# Github Proxy + +## Overview + +Github Proxy is a CloudFlare Worker that makes a Github API requests using an API key. This authenticated method allows higher limits than the non-authenticated mode. + +## Keys + +Acquire a Github api key by creating a new [fine-grained personal access token](https://github.com/settings/tokens). + +## Local Development + +Prerequisites: Copy the `.dev.vars.example` and add the Github API key. + +Development is managed by the Wrangler CLI. To start dev mode execute `yarn dev`. This will start a local server. + +## Testing + +Unit tests can be executed using `yarn test`. + +## Deployment + +Execute `yarn deploy` to deploy to production. Note that the deployment requires permissions. To deploy to a staging environment use `yarn deploy:staging`. Use `yarn deploy:key` to attach the Github key to the Worker. diff --git a/typescript/github-proxy/package.json b/typescript/github-proxy/package.json new file mode 100644 index 000000000..28d122eb4 --- /dev/null +++ b/typescript/github-proxy/package.json @@ -0,0 +1,36 @@ +{ + "name": "@hyperlane-xyz/github-proxy", + "description": "Github proxy that adds the API key to requests", + "version": "5.6.2", + "private": true, + "scripts": { + "deploy": "wrangler deploy", + "deploy:staging": "wrangler deploy --env staging", + "deploy:key": "wrangler secret put GITHUB_API_KEY", + "dev": "wrangler dev", + "start": "wrangler dev", + "test": "vitest", + "prettier": "prettier --write ./src ./test", + "cf-typegen": "wrangler types" + }, + "type": "module", + "homepage": "https://www.hyperlane.xyz", + "repository": "https://github.com/hyperlane-xyz/hyperlane-monorepo", + "keywords": [ + "Hyperlane", + "Github", + "Proxy", + "Typescript" + ], + "license": "Apache-2.0", + "devDependencies": { + "@cloudflare/vitest-pool-workers": "^0.4.5", + "@cloudflare/workers-types": "^4.20240821.1", + "@faker-js/faker": "^8.4.1", + "chai": "4.5.0", + "prettier": "^2.8.8", + "typescript": "5.3.3", + "vitest": "1.4.0", + "wrangler": "^3.74.0" + } +} diff --git a/typescript/github-proxy/src/errors.ts b/typescript/github-proxy/src/errors.ts new file mode 100644 index 000000000..7cfa31744 --- /dev/null +++ b/typescript/github-proxy/src/errors.ts @@ -0,0 +1 @@ +export const DISALLOWED_URL_MSG = 'Origin not allowed'; diff --git a/typescript/github-proxy/src/index.ts b/typescript/github-proxy/src/index.ts new file mode 100644 index 000000000..e1de8a2c5 --- /dev/null +++ b/typescript/github-proxy/src/index.ts @@ -0,0 +1,26 @@ +import { DISALLOWED_URL_MSG } from './errors.js'; + +const GITHUB_API_ALLOWLIST = [ + '/repos/hyperlane-xyz/hyperlane-registry/git/trees/main', +]; +const GITPUB_API_HOST = 'https://api.github.com'; +export default { + async fetch(request, env, _ctx): Promise { + const apiUrlPath = new URL(request.url).pathname; + const isAllowed = GITHUB_API_ALLOWLIST.includes(apiUrlPath); + if (!isAllowed) { + return new Response(DISALLOWED_URL_MSG, { status: 401 }); + } + + const apiUrl = new URL(`${GITPUB_API_HOST}${apiUrlPath}?recursive=true`); + return fetch(apiUrl, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'User-Agent': 'Hyperlane-Github-Proxy', + 'X-GitHub-Api-Version': '2022-11-28', + Authorization: `Bearer ${env.GITHUB_API_KEY}`, + }, + }); + }, +} satisfies ExportedHandler; diff --git a/typescript/github-proxy/test/index.spec.ts b/typescript/github-proxy/test/index.spec.ts new file mode 100644 index 000000000..6178c391e --- /dev/null +++ b/typescript/github-proxy/test/index.spec.ts @@ -0,0 +1,34 @@ +import { faker } from '@faker-js/faker'; +import { SELF } from 'cloudflare:test'; +import { describe, expect, it } from 'vitest'; + +import { DISALLOWED_URL_MSG } from '../src/errors.js'; + +describe('Hello World worker', () => { + it('returns empty response if pathname provided is not a valid api url', async () => { + const results = await SELF.fetch('https://example.com/favicon.ico'); + + expect(results.status).toBe(401); + expect(await results.text()).toBe(DISALLOWED_URL_MSG); + }); + + it('returns empty response if origin is not on allowlist', async () => { + const results = await SELF.fetch( + 'https://example.com/repos/custom-hyperlane-xyz/hyperlane-registry/git/trees/main?recursive=true', + ); + + expect(results.status).toBe(401); + expect(await results.text()).toBe(DISALLOWED_URL_MSG); + }); + + it('returns empty response if origin is not on allowlist (with faker 200 tests)', async () => { + for (let i = 0; i < 200; i++) { + const results = await SELF.fetch( + `https://example.com/${faker.internet.url}`, + ); + + expect(results.status).toBe(401); + expect(await results.text()).toBe(DISALLOWED_URL_MSG); + } + }); +}); diff --git a/typescript/github-proxy/test/tsconfig.json b/typescript/github-proxy/test/tsconfig.json new file mode 100644 index 000000000..509425ffe --- /dev/null +++ b/typescript/github-proxy/test/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": [ + "@cloudflare/workers-types/experimental", + "@cloudflare/vitest-pool-workers" + ] + }, + "include": ["./**/*.ts", "../src/env.d.ts"], + "exclude": [] +} diff --git a/typescript/github-proxy/tsconfig.json b/typescript/github-proxy/tsconfig.json new file mode 100644 index 000000000..b124d4ab8 --- /dev/null +++ b/typescript/github-proxy/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": [ + "@cloudflare/workers-types" + ] /* Specify type package names to be included without being referenced in a source file. */, + + "noEmit": true /* Disable emitting files from a compilation. */, + + "isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */, + "allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */ + }, + "exclude": ["test"], + "include": ["worker-configuration.d.ts", "src/**/*.ts"] +} diff --git a/typescript/github-proxy/vitest.config.ts b/typescript/github-proxy/vitest.config.ts new file mode 100644 index 000000000..931e5113e --- /dev/null +++ b/typescript/github-proxy/vitest.config.ts @@ -0,0 +1,11 @@ +import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; + +export default defineWorkersConfig({ + test: { + poolOptions: { + workers: { + wrangler: { configPath: './wrangler.toml' }, + }, + }, + }, +}); diff --git a/typescript/github-proxy/worker-configuration.d.ts b/typescript/github-proxy/worker-configuration.d.ts new file mode 100644 index 000000000..950f110d9 --- /dev/null +++ b/typescript/github-proxy/worker-configuration.d.ts @@ -0,0 +1,6 @@ +// Generated by Wrangler on Tue Sep 03 2024 19:01:13 GMT-0400 (Eastern Daylight Time) +// by running `wrangler types` + +interface Env { + GITHUB_API_KEY: string; +} diff --git a/typescript/github-proxy/wrangler.toml b/typescript/github-proxy/wrangler.toml new file mode 100644 index 000000000..adb957c5f --- /dev/null +++ b/typescript/github-proxy/wrangler.toml @@ -0,0 +1,24 @@ +#:schema node_modules/wrangler/config-schema.json +name = "github-proxy-prod" +main = "src/index.ts" +compatibility_date = "2024-08-21" +compatibility_flags = ["nodejs_compat"] +workers_dev = false +routes = [ + { pattern = "proxy.hyperlane.xyz", custom_domain = true } +] + +# CPU limit - 10ms keeps us within the free tier. As of 9/6/2024, the median CPU time is ~1ms +[limits] +cpu_ms = 10 + +# Automatically place your workloads in an optimal location to minimize latency. +# If you are running back-end logic in a Worker, running it closer to your back-end infrastructure +# rather than the end user may result in better performance. +# Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement +[placement] +mode = "smart" + +[env.staging] +name = "github-proxy-staging" +workers_dev = true \ No newline at end of file diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index f5595889a..171117f07 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,121 @@ # @hyperlane-xyz/helloworld +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a42616ff3] + - @hyperlane-xyz/sdk@5.6.2 + - @hyperlane-xyz/core@5.6.1 + +## 5.6.1 + +### Patch Changes + +- Updated dependencies [8cc0d9a4a] +- Updated dependencies [c55257cf5] +- Updated dependencies [8cc0d9a4a] + - @hyperlane-xyz/core@5.6.0 + - @hyperlane-xyz/sdk@5.6.1 + +## 5.6.0 + +### Patch Changes + +- e89f9e35d: Update registry to v4.7.0 +- Updated dependencies [46044a2e9] +- Updated dependencies [02a5b92ba] +- Updated dependencies [29341950e] +- Updated dependencies [8001bbbd6] +- Updated dependencies [32d0a67c2] +- Updated dependencies [c9085afd9] +- Updated dependencies [b1ff48bd1] +- Updated dependencies [d41aa6928] +- Updated dependencies [ec6b874b1] +- Updated dependencies [c3e9268f1] +- Updated dependencies [72c23c0d6] +- Updated dependencies [7d7bcc1a3] +- Updated dependencies [7f3e0669d] +- Updated dependencies [2317eca3c] + - @hyperlane-xyz/sdk@5.6.0 + - @hyperlane-xyz/core@5.5.0 + +## 5.5.0 + +### Patch Changes + +- Updated dependencies [92c86cca6] +- Updated dependencies [2afc484a2] +- Updated dependencies [3254472e0] +- Updated dependencies [fcfe91113] +- Updated dependencies [6176c9861] + - @hyperlane-xyz/core@5.4.1 + - @hyperlane-xyz/sdk@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [bb75eba74] +- Updated dependencies [4415ac224] +- Updated dependencies [c5c217f8e] + - @hyperlane-xyz/core@5.4.0 + - @hyperlane-xyz/sdk@5.4.0 + +## 5.3.0 + +### Minor Changes + +- 35d4503b9: Update to registry v4.3.6 + +### Patch Changes + +- Updated dependencies [eb47aaee8] +- Updated dependencies [50319d8ba] +- Updated dependencies [8de531fa4] +- Updated dependencies [fd536a79a] + - @hyperlane-xyz/sdk@5.3.0 + - @hyperlane-xyz/core@5.3.0 + +## 5.2.1 + +### Patch Changes + +- Updated dependencies [eb5afcf3e] + - @hyperlane-xyz/core@5.2.1 + - @hyperlane-xyz/sdk@5.2.1 + +## 5.2.0 + +### Minor Changes + +- 291c5fe36: Use addBufferToGasLimit from @hyperlane-xyz/utils + +### Patch Changes + +- Updated dependencies [a19e882fd] +- Updated dependencies [518a1bef9] +- Updated dependencies [203084df2] +- Updated dependencies [74a592e58] +- Updated dependencies [739af9a34] +- Updated dependencies [44588c31d] +- Updated dependencies [2bd540e0f] +- Updated dependencies [291c5fe36] +- Updated dependencies [69f17d99a] +- Updated dependencies [3ad5918da] +- Updated dependencies [9563a8beb] +- Updated dependencies [73c232b3a] +- Updated dependencies [445b6222c] +- Updated dependencies [d6de34ad5] +- Updated dependencies [2e6176f67] +- Updated dependencies [f2783c03b] +- Updated dependencies [2ffb78f5c] +- Updated dependencies [3c07ded5b] +- Updated dependencies [815542dd7] + - @hyperlane-xyz/sdk@5.2.0 + - @hyperlane-xyz/core@5.2.0 + ## 5.1.0 ### Minor Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index c93f37421..722eed273 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "5.1.0", + "version": "5.6.2", "dependencies": { - "@hyperlane-xyz/core": "5.1.0", - "@hyperlane-xyz/registry": "2.5.0", - "@hyperlane-xyz/sdk": "5.1.0", + "@hyperlane-xyz/core": "5.6.1", + "@hyperlane-xyz/registry": "4.7.0", + "@hyperlane-xyz/sdk": "5.6.2", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, @@ -18,7 +18,7 @@ "@typechain/hardhat": "^9.1.0", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", - "chai": "^4.3.6", + "chai": "4.5.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "ethereum-waffle": "^4.0.10", diff --git a/typescript/helloworld/src/app/app.ts b/typescript/helloworld/src/app/app.ts index 809cdaf7d..4cdde7761 100644 --- a/typescript/helloworld/src/app/app.ts +++ b/typescript/helloworld/src/app/app.ts @@ -9,7 +9,7 @@ import { MultiProvider, RouterApp, } from '@hyperlane-xyz/sdk'; -import { Address, rootLogger } from '@hyperlane-xyz/utils'; +import { Address, addBufferToGasLimit, rootLogger } from '@hyperlane-xyz/utils'; import { HelloWorld } from '../types/index.js'; @@ -52,12 +52,11 @@ export class HelloWorldApp extends RouterApp { message, { ...transactionOverrides, value }, ); - const gasLimit = estimated.mul(12).div(10); const quote = await sender.quoteDispatch(toDomain, message); const tx = await sender.sendHelloWorld(toDomain, message, { + gasLimit: addBufferToGasLimit(estimated), ...transactionOverrides, - gasLimit, value: value.add(quote), }); this.logger.info('Sending hello message', { diff --git a/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts b/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts index 5d9bacc23..964571cd3 100644 --- a/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts +++ b/typescript/helloworld/src/multiProtocolApp/evmAdapter.ts @@ -7,7 +7,7 @@ import { MultiProtocolProvider, ProviderType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { Address, addBufferToGasLimit } from '@hyperlane-xyz/utils'; import { HelloWorld, HelloWorld__factory } from '../types/index.js'; @@ -54,14 +54,13 @@ export class EvmHelloWorldAdapter value: BigNumber.from(value).add(quote), }, ); - const gasLimit = estimated.mul(12).div(10); const tx = await contract.populateTransaction.sendHelloWorld( toDomain, message, { + gasLimit: addBufferToGasLimit(estimated), ...transactionOverrides, - gasLimit, value: BigNumber.from(value).add(quote), }, ); diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index b4c93fc44..eee615625 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,128 @@ # @hyperlane-xyz/infra +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/sdk@5.6.2 + - @hyperlane-xyz/helloworld@5.6.2 + +## 5.6.1 + +### Patch Changes + +- @hyperlane-xyz/helloworld@5.6.1 +- @hyperlane-xyz/sdk@5.6.1 +- @hyperlane-xyz/utils@5.6.1 + +## 5.6.0 + +### Minor Changes + +- b3495b205: Updates the warpIds for Renzo's latest deployment to Sei and Taiko to be used by the Checker +- c3e9268f1: Add support for an arbitrary string in `reorgPeriod`, which is used as a block tag to get the finalized block. + +### Patch Changes + +- Updated dependencies [f1712deb7] +- Updated dependencies [46044a2e9] +- Updated dependencies [02a5b92ba] +- Updated dependencies [29341950e] +- Updated dependencies [8001bbbd6] +- Updated dependencies [32d0a67c2] +- Updated dependencies [b1ff48bd1] +- Updated dependencies [e89f9e35d] +- Updated dependencies [d41aa6928] +- Updated dependencies [c3e9268f1] +- Updated dependencies [7d7bcc1a3] +- Updated dependencies [7f3e0669d] +- Updated dependencies [2317eca3c] + - @hyperlane-xyz/utils@5.6.0 + - @hyperlane-xyz/sdk@5.6.0 + - @hyperlane-xyz/helloworld@5.6.0 + +## 5.5.0 + +### Patch Changes + +- Updated dependencies [2afc484a2] +- Updated dependencies [2afc484a2] +- Updated dependencies [3254472e0] +- Updated dependencies [fcfe91113] +- Updated dependencies [6176c9861] + - @hyperlane-xyz/sdk@5.5.0 + - @hyperlane-xyz/utils@5.5.0 + - @hyperlane-xyz/helloworld@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [4415ac224] + - @hyperlane-xyz/utils@5.4.0 + - @hyperlane-xyz/sdk@5.4.0 + - @hyperlane-xyz/helloworld@5.4.0 + +## 5.3.0 + +### Patch Changes + +- Updated dependencies [eb47aaee8] +- Updated dependencies [50319d8ba] +- Updated dependencies [35d4503b9] +- Updated dependencies [8de531fa4] +- Updated dependencies [746eeb9d9] +- Updated dependencies [fd536a79a] +- Updated dependencies [50319d8ba] + - @hyperlane-xyz/sdk@5.3.0 + - @hyperlane-xyz/helloworld@5.3.0 + - @hyperlane-xyz/utils@5.3.0 + +## 5.2.1 + +### Patch Changes + +- @hyperlane-xyz/helloworld@5.2.1 +- @hyperlane-xyz/sdk@5.2.1 +- @hyperlane-xyz/utils@5.2.1 + +## 5.2.0 + +### Minor Changes + +- 203084df2: Added sdk support for Stake weighted ISM + +### Patch Changes + +- 5a0d68bdc: replace import console module with direct console +- Updated dependencies [a19e882fd] +- Updated dependencies [d6de34ad5] +- Updated dependencies [518a1bef9] +- Updated dependencies [203084df2] +- Updated dependencies [74a592e58] +- Updated dependencies [739af9a34] +- Updated dependencies [44588c31d] +- Updated dependencies [2bd540e0f] +- Updated dependencies [291c5fe36] +- Updated dependencies [69f17d99a] +- Updated dependencies [3ad5918da] +- Updated dependencies [291c5fe36] +- Updated dependencies [9563a8beb] +- Updated dependencies [73c232b3a] +- Updated dependencies [445b6222c] +- Updated dependencies [d6de34ad5] +- Updated dependencies [2e6176f67] +- Updated dependencies [f2783c03b] +- Updated dependencies [2ffb78f5c] +- Updated dependencies [3c07ded5b] +- Updated dependencies [815542dd7] + - @hyperlane-xyz/sdk@5.2.0 + - @hyperlane-xyz/utils@5.2.0 + - @hyperlane-xyz/helloworld@5.2.0 + ## 5.1.0 ### Minor Changes diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 556d928bf..aa79f5e43 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -15,7 +15,7 @@ import { } from '../../../src/config/agent/relayer.js'; import { ALL_KEY_ROLES, Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; -import { getDomainId } from '../../registry.js'; +import { getDomainId, getWarpAddresses } from '../../registry.js'; import { environment } from './chains.js'; import { helloWorld } from './helloworld.js'; @@ -27,16 +27,18 @@ import { validatorChainConfig } from './validators.js'; import ancient8EthereumUsdcAddresses from './warp/ancient8-USDC-addresses.json'; import arbitrumTIAAddresses from './warp/arbitrum-TIA-addresses.json'; import arbitrumNeutronEclipAddresses from './warp/arbitrum-neutron-eclip-addresses.json'; +import eclipseStrideTiaAddresses from './warp/eclipse-stride-TIA-addresses.json'; +import eclipseStrideStTiaAddresses from './warp/eclipse-stride-stTIA-addresses.json'; import inevmEthereumUsdcAddresses from './warp/inevm-USDC-addresses.json'; import inevmEthereumUsdtAddresses from './warp/inevm-USDT-addresses.json'; import injectiveInevmInjAddresses from './warp/injective-inevm-addresses.json'; import mantaTIAAddresses from './warp/manta-TIA-addresses.json'; import merklyEthAddresses from './warp/merkly-eth-addresses.json'; -import renzoEzEthAddressesV1 from './warp/renzo-ezETH-addresses-v1.json'; import renzoEzEthAddressesV3 from './warp/renzo-ezETH-addresses-v3.json'; import victionEthereumEthAddresses from './warp/viction-ETH-addresses.json'; import victionEthereumUsdcAddresses from './warp/viction-USDC-addresses.json'; import victionEthereumUsdtAddresses from './warp/viction-USDT-addresses.json'; +import { WarpRouteIds } from './warp/warpIds.js'; // const releaseCandidateHelloworldMatchingList = routerMatchingList( // helloWorld[Contexts.ReleaseCandidate].addresses, @@ -55,10 +57,14 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< // Generally, we run all production validators in the Hyperlane context. [Role.Validator]: { ancient8: true, + alephzeroevm: true, + apechain: true, arbitrum: true, + arbitrumnova: true, astar: true, astarzkevm: true, avalanche: true, + b3: true, base: true, bitlayer: true, blast: true, @@ -66,6 +72,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< bsc: true, celo: true, cheesechain: true, + chiliz: true, coredao: true, cyber: true, degenchain: true, @@ -73,53 +80,77 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< eclipsemainnet: true, endurance: true, ethereum: true, + everclear: true, + fantom: true, flare: true, + flow: true, fraxtal: true, fusemainnet: true, gnosis: true, + gravity: true, + harmony: true, + immutablezkevm: true, inevm: true, injective: true, + kaia: true, kroma: true, linea: true, lisk: true, lukso: true, + lumia: true, mantapacific: true, mantle: true, merlin: true, + metall2: true, metis: true, mint: true, mode: true, molten: true, moonbeam: true, + morph: true, neutron: true, + oortmainnet: true, optimism: true, + orderly: true, osmosis: true, polygon: true, polygonzkevm: true, + polynomial: true, proofofplay: true, + rari: true, real: true, redstone: true, + rootstock: true, sanko: true, scroll: true, sei: true, shibarium: true, + snaxchain: true, solanamainnet: true, + stride: false, + superposition: true, taiko: true, tangle: true, viction: true, worldchain: true, xai: true, xlayer: true, + zeronetwork: true, zetachain: true, zircuit: true, + zksync: true, zoramainnet: true, }, [Role.Relayer]: { + alephzeroevm: true, ancient8: true, + apechain: true, arbitrum: true, + arbitrumnova: true, astar: true, astarzkevm: true, avalanche: true, + b3: true, base: true, bitlayer: true, blast: true, @@ -127,6 +158,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< bsc: true, celo: true, cheesechain: true, + chiliz: true, coredao: true, cyber: true, degenchain: true, @@ -134,54 +166,78 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< eclipsemainnet: true, endurance: true, ethereum: true, + everclear: true, + fantom: true, flare: true, + flow: true, fraxtal: true, fusemainnet: true, gnosis: true, + gravity: true, + harmony: true, + immutablezkevm: true, inevm: true, injective: true, + kaia: true, kroma: true, linea: true, lisk: true, lukso: true, + lumia: true, mantapacific: true, mantle: true, merlin: true, + metall2: true, metis: true, mint: true, mode: true, molten: true, moonbeam: true, + morph: true, // At the moment, we only relay between Neutron and Manta Pacific on the neutron context. neutron: false, + oortmainnet: true, optimism: true, + orderly: true, osmosis: true, polygon: true, polygonzkevm: true, + polynomial: true, proofofplay: true, + rari: true, real: true, redstone: true, + rootstock: true, sanko: true, scroll: true, sei: true, shibarium: true, + snaxchain: true, solanamainnet: true, + stride: true, + superposition: true, taiko: true, tangle: true, viction: true, worldchain: true, xai: true, xlayer: true, + zeronetwork: true, zetachain: true, zircuit: true, + zksync: true, zoramainnet: true, }, [Role.Scraper]: { ancient8: true, + alephzeroevm: true, + apechain: true, arbitrum: true, + arbitrumnova: true, astar: true, astarzkevm: true, avalanche: true, + b3: true, base: true, bitlayer: true, blast: true, @@ -189,51 +245,65 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< bsc: true, celo: true, cheesechain: true, + chiliz: true, coredao: true, cyber: true, degenchain: true, dogechain: true, - // Cannot scrape non-EVM chains + // Cannot scrape Sealevel chains eclipsemainnet: false, endurance: true, ethereum: true, + everclear: true, + fantom: true, flare: true, + flow: true, fraxtal: true, fusemainnet: true, gnosis: true, + gravity: true, + harmony: true, + immutablezkevm: true, inevm: true, - // Cannot scrape non-EVM chains - injective: false, + injective: true, + kaia: true, kroma: true, linea: true, lisk: true, lukso: true, + lumia: true, mantapacific: true, mantle: true, merlin: true, + metall2: true, metis: true, mint: true, mode: true, molten: true, moonbeam: true, - // Cannot scrape non-EVM chains - neutron: false, + morph: true, + neutron: true, + oortmainnet: true, optimism: true, - // Cannot scrape non-EVM chains - osmosis: false, + orderly: true, + osmosis: true, polygon: true, polygonzkevm: true, + polynomial: true, proofofplay: true, + rari: true, real: true, redstone: true, + rootstock: true, sanko: true, scroll: true, - // Out of caution around pointer contracts (https://www.docs.sei.io/dev-interoperability/pointer-contracts) not being compatible - // and the scraper not gracefully handling txs that may not exist via the eth RPC, we don't run the scraper. - sei: false, + sei: true, shibarium: true, - // Cannot scrape non-EVM chains + snaxchain: true, + // Cannot scrape Sealevel chains solanamainnet: false, + stride: true, + superposition: true, taiko: true, tangle: true, // Has RPC non-compliance that breaks scraping. @@ -241,8 +311,10 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< worldchain: true, xai: true, xlayer: true, + zeronetwork: true, zetachain: true, zircuit: true, + zksync: true, zoramainnet: true, }, }; @@ -277,7 +349,13 @@ const gasPaymentEnforcement: GasPaymentEnforcement[] = [ // warp routes that we know are certainly paying for gas. { type: GasPaymentEnforcementPolicyType.None, - matchingList: [...routerMatchingList(injectiveInevmInjAddresses)], + matchingList: [ + ...routerMatchingList(injectiveInevmInjAddresses), + // As we are still indexing the IGP on Stride, temporarily whitelist + // Stride to Eclipse messages. + ...routerMatchingList(eclipseStrideTiaAddresses), + ...routerMatchingList(eclipseStrideStTiaAddresses), + ], }, { type: GasPaymentEnforcementPolicyType.OnChainFeeQuoting, @@ -322,9 +400,24 @@ const metricAppContexts = [ matchingList: matchingList(renzoEzEthAddressesV3), }, { - // preserving old addresses in case any transactions are still in flight and need to be processed - name: 'renzo_ezeth_old', - matchingList: matchingList(renzoEzEthAddressesV1), + name: 'eclipse_usdc', + matchingList: matchingList( + getWarpAddresses(WarpRouteIds.EthereumEclipseUSDC), + ), + }, + { + name: 'eclipse_teth', + matchingList: matchingList( + getWarpAddresses(WarpRouteIds.EthereumEclipseTETH), + ), + }, + { + name: 'eclipse_wif', + matchingList: matchingList(getWarpAddresses(WarpRouteIds.EclipseSolanaWIF)), + }, + { + name: 'eclipse_sol', + matchingList: matchingList(getWarpAddresses(WarpRouteIds.EclipseSolanaSOL)), }, // Hitting max env var size limits, see https://stackoverflow.com/questions/28865473/setting-environment-variable-to-a-large-value-argument-list-too-long#answer-28865503 // { @@ -372,7 +465,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '1cb1916-20240902-171411', + tag: '45399a3-20241025-210128', }, gasPaymentEnforcement: gasPaymentEnforcement, metricAppContexts, @@ -381,7 +474,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: '1cb1916-20240902-171411', + tag: '45399a3-20241025-210128', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -391,7 +484,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '707db4a-20240828-164024', + tag: '45399a3-20241025-210128', }, resources: scraperResources, }, @@ -406,7 +499,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '78b596e-20240813-123401', + tag: 'a64af8b-20241024-120818', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. @@ -418,7 +511,7 @@ const releaseCandidate: RootAgentConfig = { validators: { docker: { repo, - tag: '0d12ff3-20240620-173353', + tag: 'a64af8b-20241024-120818', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.ReleaseCandidate), @@ -439,7 +532,7 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '707db4a-20240828-164024', + tag: 'b1ff48b-20241016-183301', }, gasPaymentEnforcement: [ { diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index fd6ae33c9..67119a8ee 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -2,6 +2,12 @@ "ancient8": { "validators": ["0xbb5842ae0e05215b53df4787a29144efb7e67551"] }, + "alephzeroevm": { + "validators": ["0xcae8fab142adc4e434bb7409e40dd932cc3851aa"] + }, + "apechain": { + "validators": ["0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221"] + }, "arbitrum": { "validators": [ "0x4d966438fe9e2b1e7124c87bbb90cb4f0f6c59a1", @@ -9,6 +15,9 @@ "0x3369e12edd52570806f126eb50be269ba5e65843" ] }, + "arbitrumnova": { + "validators": ["0xd2a5e9123308d187383c87053811a2c21bd8af1f"] + }, "astar": { "validators": ["0x4d1b2cade01ee3493f44304653d8e352c66ec3e7"] }, @@ -22,6 +31,9 @@ "0x6c754f1e9cd8287088b46a7c807303d55d728b49" ] }, + "b3": { + "validators": ["0xd77b516730a836fc41934e7d5864e72c165b934e"] + }, "base": { "validators": [ "0xb9453d675e0fa3c178a17b4ce1ad5b1a279b3af9", @@ -55,6 +67,9 @@ "cheesechain": { "validators": ["0x478fb53c6860ae8fc35235ba0d38d49b13128226"] }, + "chiliz": { + "validators": ["0x82d024f453b1a3f3f6606226f06b038da27596f3"] + }, "coredao": { "validators": ["0xbd6e158a3f5830d99d7d2bce192695bc4a148de2"] }, @@ -80,9 +95,18 @@ "0x749d6e7ad949e522c92181dc77f7bbc1c5d71506" ] }, + "everclear": { + "validators": ["0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837"] + }, + "fantom": { + "validators": ["0xa779572028e634e16f26af5dfd4fa685f619457d"] + }, "flare": { "validators": ["0xb65e52be342dba3ab2c088ceeb4290c744809134"] }, + "flow": { + "validators": ["0x3aee1090318e9c54d1d23194dcd0f2bee00ddc97"] + }, "fraxtal": { "validators": ["0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1"] }, @@ -96,6 +120,15 @@ "0xb93a72cee19402553c9dd7fed2461aebd04e2454" ] }, + "gravity": { + "validators": ["0x23d549bf757a02a6f6068e9363196ecd958c974e"] + }, + "harmony": { + "validators": ["0xd677803a67651974b1c264171b5d7ca8838db8d5"] + }, + "immutablezkevm": { + "validators": ["0xa787c2952a4d22f776ee6e87e828e6f75de24330"] + }, "inevm": { "validators": [ "0xf9e35ee88e4448a3673b4676a4e153e3584a08eb", @@ -106,6 +139,9 @@ "injective": { "validators": ["0xbfb8911b72cfb138c7ce517c57d9c691535dc517"] }, + "kaia": { + "validators": ["0x9de0b3abb221d19719882fa4d61f769fdc2be9a4"] + }, "kroma": { "validators": ["0x71b83c21342787d758199e4b8634d3a15f02dc6e"] }, @@ -118,6 +154,9 @@ "lukso": { "validators": ["0xa5e953701dcddc5b958b5defb677a829d908df6d"] }, + "lumia": { + "validators": ["0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f"] + }, "mantapacific": { "validators": [ "0x8e668c97ad76d0e28375275c41ece4972ab8a5bc", @@ -131,6 +170,9 @@ "merlin": { "validators": ["0xc1d6600cb9326ed2198cc8c4ba8d6668e8671247"] }, + "metall2": { + "validators": ["0x1b000e1e1f0a032ed382c6d69a2d58f6fe773c09"] + }, "metis": { "validators": ["0xc4a3d25107060e800a43842964546db508092260"] }, @@ -150,6 +192,9 @@ "0xcc4a78aa162482bea43313cd836ba7b560b44fc4" ] }, + "morph": { + "validators": ["0x4884535f393151ec419add872100d352f71af380"] + }, "neutron": { "validators": [ "0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0", @@ -157,6 +202,9 @@ "0x7885fae56dbcf5176657f54adbbd881dc6714132" ] }, + "oortmainnet": { + "validators": ["0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7"] + }, "optimism": { "validators": [ "0x20349eadc6c72e94ce38268b96692b1a5c20de4f", @@ -164,6 +212,9 @@ "0x779a17e035018396724a6dec8a59bda1b5adf738" ] }, + "orderly": { + "validators": ["0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6"] + }, "osmosis": { "validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"] }, @@ -181,15 +232,24 @@ "0x6a1da2e0b7ae26aaece1377c0a4dbe25b85fa3ca" ] }, + "polynomial": { + "validators": ["0xa63ad0891e921ad5947d57e05831fabb9816eca7"] + }, "proofofplay": { "validators": ["0xcda40baa71970a06e5f55e306474de5ca4e21c3b"] }, + "rari": { + "validators": ["0x989d6862e09de21337078efbd86843a3eb1133e3"] + }, "real": { "validators": ["0xaebadd4998c70b05ce8715cf0c3cb8862fe0beec"] }, "redstone": { "validators": ["0x1400b9737007f7978d8b4bbafb4a69c83f0641a7"] }, + "rootstock": { + "validators": ["0xcb8e3a72cf427feff27416d0e2ec375a052eaaee"] + }, "sanko": { "validators": ["0x795c37d5babbc44094b084b0c89ed9db9b5fae39"] }, @@ -206,9 +266,15 @@ "shibarium": { "validators": ["0xfa33391ee38597cbeef72ccde8c9e13e01e78521"] }, + "snaxchain": { + "validators": ["0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e"] + }, "solanamainnet": { "validators": ["0x28464752829b3ea59a497fca0bdff575c534c3ff"] }, + "superposition": { + "validators": ["0x5978d0e6afa9270ddb87cff43a8fa7a763a5dfc4"] + }, "taiko": { "validators": ["0xa930073c8f2d0b2f7423ea32293e0d1362e65d79"] }, @@ -227,12 +293,18 @@ "xlayer": { "validators": ["0xa2ae7c594703e988f23d97220717c513db638ea3"] }, + "zeronetwork": { + "validators": ["0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87"] + }, "zetachain": { "validators": ["0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef"] }, "zircuit": { "validators": ["0x169ec400cc758fef3df6a0d6c51fbc6cdd1015bb"] }, + "zksync": { + "validators": ["0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a"] + }, "zoramainnet": { "validators": ["0x35130945b625bb69b28aee902a3b9a76fa67125f"] } diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json index e351098ac..55638fa67 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json @@ -9,6 +9,12 @@ "0x229d4dc6a740212da746b0e35314419a24bc2a5b" ] }, + "astar": { + "validators": ["0x50792eee9574f1bcd002e2c6dddf2aa73b04e254"] + }, + "astarzkevm": { + "validators": ["0x71e0a93f84548765cafc64d787eede19eea1ab06"] + }, "avalanche": { "validators": [ "0x2c7cf6d1796e37676ba95f056ff21bf536c6c2d3", @@ -23,6 +29,9 @@ "0xed7703e06572768bb09e03d88e6b788d8800b9fb" ] }, + "bitlayer": { + "validators": ["0xec567167f07479bcd02a66c2ea66fd329cb9e22f"] + }, "blast": { "validators": ["0x5b32f226e472da6ca19abfe1a29d5d28102a2d1a"] }, @@ -43,14 +52,20 @@ "cheesechain": { "validators": ["0xa481835355309ed46540c742a1c04b58380aa7b4"] }, + "coredao": { + "validators": ["0x9767df5bcebce5ca89fdca5efbfb3b891cf30c5b"] + }, "cyber": { "validators": ["0x84976d5012da6b180462742f2edfae2f9d740c82"] }, "degenchain": { "validators": ["0xac3734bb61e7ddda947b718b930c4067f0ccde7e"] }, + "dogechain": { + "validators": ["0x1e74735379a96586ec31f0a31aa4ee86016404a9"] + }, "eclipsemainnet": { - "validators": ["0xd188fbe099e2db077f4ebc1bf39bb22798500bfb"] + "validators": ["0x283065cb17f98386c2b3656650d8b85e05862c8e"] }, "ethereum": { "validators": [ @@ -59,6 +74,12 @@ "0x87cf8a85465118aff9ec728ca157798201b1e368" ] }, + "everclear": { + "validators": ["0x1b4455316aa859c9b3d2b13868490b7c97a9da3e"] + }, + "flare": { + "validators": ["0x35c3ce34f27def61c2d878ff7c3e35fbab08e92a"] + }, "fraxtal": { "validators": ["0x8c772b730c8deb333dded14cb462e577a06283da"] }, @@ -107,6 +128,9 @@ "mode": { "validators": ["0x2f04ed30b1c27ef8e9e6acd360728d9bd5c3a9e2"] }, + "molten": { + "validators": ["0x2e433ffc514a874537ca2473af51ebba4c863409"] + }, "moonbeam": { "validators": [ "0x75e3cd4e909089ae6c9f3a42b1468b33eec84161", @@ -114,12 +138,8 @@ "0xcaa9c6e6efa35e4a8b47565f3ce98845fa638bf3" ] }, - "neutron": { - "validators": [ - "0x307a8fe091b8273c7ce3d277b161b4a2167279b1", - "0xb825c1bd020cb068f477b320f591b32e26814b5b", - "0x0a5b31090d4c3c207b9ea6708f938e328f895fce" - ] + "oortmainnet": { + "validators": ["0x83f406ff315e90ae9589fa7786bf700e7c7a06f1"] }, "optimism": { "validators": [ @@ -164,8 +184,11 @@ "sei": { "validators": ["0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e"] }, + "shibarium": { + "validators": ["0x7f52b98a0a3e1ced83cf4eced8c0fa61886e7268"] + }, "solanamainnet": { - "validators": ["0xca84b665ab3fed784baf120909c54497c14c93c8"] + "validators": ["0x7bd1536cb7505cf2ea1dc6744127d91fbe87a2ad"] }, "tangle": { "validators": ["0xd778d113975b2fe45eda424bf93c28f32a90b076"] @@ -188,5 +211,12 @@ }, "zetachain": { "validators": ["0xa13d146b47242671466e4041f5fe68d22a2ffe09"] + }, + "neutron": { + "validators": [ + "0x307a8fe091b8273c7ce3d277b161b4a2167279b1", + "0xb825c1bd020cb068f477b320f591b32e26814b5b", + "0x0a5b31090d4c3c207b9ea6708f938e328f895fce" + ] } } diff --git a/typescript/infra/config/environments/mainnet3/chains.ts b/typescript/infra/config/environments/mainnet3/chains.ts index 7af35defe..b32fdeff1 100644 --- a/typescript/infra/config/environments/mainnet3/chains.ts +++ b/typescript/infra/config/environments/mainnet3/chains.ts @@ -19,13 +19,10 @@ export const chainMetadataOverrides: ChainMap> = { }, }, polygon: { - blocks: { - confirmations: 3, - }, transactionOverrides: { // A very high max fee per gas is used as Polygon is susceptible // to large swings in gas prices. - maxFeePerGas: 550 * 10 ** 9, // 550 gwei + maxFeePerGas: 800 * 10 ** 9, // 800 gwei maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei }, }, @@ -34,21 +31,12 @@ export const chainMetadataOverrides: ChainMap> = { gasPrice: 1 * 10 ** 9, // 1 gwei }, }, - ethereum: { - blocks: { - confirmations: 3, - }, - transactionOverrides: { - maxFeePerGas: 150 * 10 ** 9, // gwei - maxPriorityFeePerGas: 5 * 10 ** 9, // gwei - }, - }, scroll: { transactionOverrides: { // Scroll doesn't use EIP 1559 and the gas price that's returned is sometimes // too low for the transaction to be included in a reasonable amount of time - // this often leads to transaction underpriced issues. - gasPrice: 2 * 10 ** 9, // 2 gwei + gasPrice: 2 * 10 ** 8, // 0.2 gwei }, }, sei: { @@ -65,6 +53,23 @@ export const chainMetadataOverrides: ChainMap> = { maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei }, }, + rootstock: { + transactionOverrides: { + gasPrice: 7 * 10 ** 7, // 0.07 gwei + // gasLimit: 6800000, // set when deploying contracts + }, + }, + // Deploy-only overrides, set when deploying contracts + // chiliz: { + // transactionOverrides: { + // maxFeePerGas: 100000 * 10 ** 9, // 100,000 gwei + // }, + // }, + // zircuit: { + // blocks: { + // confirmations: 3, + // }, + // }, }; export const getRegistry = async (useSecrets = true): Promise => diff --git a/typescript/infra/config/environments/mainnet3/core/verification.json b/typescript/infra/config/environments/mainnet3/core/verification.json index 1999c9706..029f75ad6 100644 --- a/typescript/infra/config/environments/mainnet3/core/verification.json +++ b/typescript/infra/config/environments/mainnet3/core/verification.json @@ -3447,5 +3447,1841 @@ "constructorArguments": "000000000000000000000000000000000000000000000000000000000000006d", "isProxy": false } + ], + "everclear": [ + { + "name": "ProxyAdmin", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000062ef", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa70000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7" + }, + { + "name": "PausableIsm", + "address": "0xA38D1D7F217A52A27b0e6BF50E0a9ddAD05798C0", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xCC3D1659D50461d27a2F025dDb2c9B06B584B7e1", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x3C2b535a49c6827DF0b8e94467e6922c99E3c092", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000cc3d1659d50461d27a2f025ddb2c9b06b584b7e1", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xf8344D85a1429708e0BE6724218E938087e596DF", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x8a28b3ec380cfdFF904a5D0bA615d53A4b19dc91", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xb58257cc81E47EC72fD38aE16297048de23163b4", + "constructorArguments": "0000000000000000000000008a28b3ec380cfdff904a5d0ba615d53a4b19dc910000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x8a28b3ec380cfdFF904a5D0bA615d53A4b19dc91" + }, + { + "name": "ProtocolFee", + "address": "0xC49aF4965264FA7BB6424CE37aA06773ad177224", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xC88bAD76EC7acD9fd3b9Bb264f7f5C18097c5710", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + } + ], + "oortmainnet": [ + { + "name": "PausableIsm", + "address": "0x31894E7a734540B343d67E491148EB4FC9f7A45B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x3C2b535a49c6827DF0b8e94467e6922c99E3c092", + "constructorArguments": "000000000000000000000000b163e75e2008cdc94e50278b5fcdd081761a2ee4", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", + "constructorArguments": "000000000000000000000000b163e75e2008cdc94e50278b5fcdd081761a2ee4000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000003c2b535a49c6827df0b8e94467e6922c99e3c092", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf8344D85a1429708e0BE6724218E938087e596DF", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2c61Cda929e4e2174cb10cd8e2724A9ceaD62E67", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x8AB7A6FaC052518A39628497735C855a2Beb515B", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xb4fc9B5fD57499Ef6FfF3995728a55F7A618ef86", + "constructorArguments": "0000000000000000000000008ab7a6fac052518a39628497735c855a2beb515b0000000000000000000000008d078f74df9e5a4fa8da06f2abce5d01fdf793c200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x8AB7A6FaC052518A39628497735C855a2Beb515B" + }, + { + "name": "ProtocolFee", + "address": "0x199ba45E836440f2cCe00aa60466Ae0e733D6647", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xcd9D3744512F07AE844c40E27912092d7c503565", + "constructorArguments": "000000000000000000000000b163e75e2008cdc94e50278b5fcdd081761a2ee4", + "isProxy": false + }, + { + "name": "ProxyAdmin", + "address": "0x148CF67B8A242c1360bb2C93fCe203EC4d4f9B56", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000003ca", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "constructorArguments": "000000000000000000000000cd849e612aaa138f03698c3edb42a34117bff631000000000000000000000000148cf67b8a242c1360bb2c93fce203ec4d4f9b5600000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631" + }, + { + "name": "PausableIsm", + "address": "0x989B7307d266151BE763935C856493D968b2affF", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "constructorArguments": "000000000000000000000000b129828b9eda48192d0b2db35d0e40dcf51b3594", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "000000000000000000000000b129828b9eda48192d0b2db35d0e40dcf51b3594000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000003e969ba938e6a993eecd6f65b0dd8712b07dfe59", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "ProtocolFee", + "address": "0xfdefdDc8E153d5E0463d7E193F79A3714be16021", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x6f77d5Ef273C38CC19d1d02352785F52565A1A6c", + "constructorArguments": "000000000000000000000000b129828b9eda48192d0b2db35d0e40dcf51b3594", + "isProxy": false + } + ], + "alephzeroevm": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000a1ef", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000dab56c5a1efffdd23f6bd1243e457b1575984bc6", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "constructorArguments": "0000000000000000000000002d5918c3602f17937ff982f7bb7110774d3a24ad0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD" + }, + { + "name": "ProtocolFee", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "superposition": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000d7cc", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000009c44e6b8f0db517c2c3a0478caac5349b614f912", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "constructorArguments": "0000000000000000000000004757bdd68bba8a6d901cec82e61e184ff2986918000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918" + }, + { + "name": "ProtocolFee", + "address": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x989B7307d266151BE763935C856493D968b2affF", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "rari": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000052415249", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000dab56c5a1efffdd23f6bd1243e457b1575984bc6", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "constructorArguments": "0000000000000000000000002d5918c3602f17937ff982f7bb7110774d3a24ad0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD" + }, + { + "name": "ProtocolFee", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "immutablezkevm": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000343b", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000dab56c5a1efffdd23f6bd1243e457b1575984bc6", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "constructorArguments": "0000000000000000000000002d5918c3602f17937ff982f7bb7110774d3a24ad0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD" + }, + { + "name": "ProtocolFee", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "lumia": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b4c8eb9", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000009c44e6b8f0db517c2c3a0478caac5349b614f912", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "constructorArguments": "0000000000000000000000004757bdd68bba8a6d901cec82e61e184ff2986918000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918" + }, + { + "name": "ProtocolFee", + "address": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x989B7307d266151BE763935C856493D968b2affF", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "chiliz": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000015b38", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000dab56c5a1efffdd23f6bd1243e457b1575984bc6", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x73db9c7430548f399e335f3424e8d56080e9010c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "constructorArguments": "0000000000000000000000002d5918c3602f17937ff982f7bb7110774d3a24ad0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD" + }, + { + "name": "ProtocolFee", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "rootstock": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000001e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x9c44E6b8F0dB517C2c3a0478caaC5349b614F912", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x53e912b41125d6094590a7DBEf1360d3d56EEa19", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000053e912b41125d6094590a7dbef1360d3d56eea19", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x2d5918c3602F17937Ff982F7Bb7110774D3A24AD", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x148CF67B8A242c1360bb2C93fCe203EC4d4f9B56", + "constructorArguments": "0000000000000000000000009024a3902b542c87a5c4a2b3e15d60b2f087dc3e0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E" + }, + { + "name": "ProtocolFee", + "address": "0x989B7307d266151BE763935C856493D968b2affF", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "metall2": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000006d6", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x089DdA086dCbfA0C2cCa69B45F2eB6DE7Fd71F38", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x4757Bdd68Bba8a6d901cEC82E61E184fF2986918", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xe8d5590F2e969F9d21f0132f2b596273f8a03Ef2", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000004757bdd68bba8a6d901cec82e61e184ff2986918", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x749848D7b783A328638C3ea74AcFcfb73c977CbE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "constructorArguments": "000000000000000000000000cd849e612aaa138f03698c3edb42a34117bff6310000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631" + }, + { + "name": "ProtocolFee", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "polynomial": [ + { + "name": "ProxyAdmin", + "address": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000001f48", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x02d16BC51af6BfD153d67CA61754cF912E82C4d9", + "constructorArguments": "0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e30000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b3900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3" + }, + { + "name": "PausableIsm", + "address": "0x9024A3902B542C87a5C4A2b3e15d60B2f087Dc3E", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x148CF67B8A242c1360bb2C93fCe203EC4d4f9B56", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xcd849e612Aaa138f03698C3Edb42a34117BFF631", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000148cf67b8a242c1360bb2c93fce203ec4d4f9b56", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xb129828B9EDa48192D0B2db35D0E40dCF51B3594", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xE56Da9D48E698eB70F56aeCC0BC25Ff1710EEA76", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x71388C9E25BE7b229B5d17Df7D4DB3F7DA7C962d", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "00000000000000000000000071388c9e25be7b229b5d17df7d4db3f7da7c962d0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b3900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x71388C9E25BE7b229B5d17Df7D4DB3F7DA7C962d" + }, + { + "name": "ProtocolFee", + "address": "0xfdefdDc8E153d5E0463d7E193F79A3714be16021", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x6f77d5Ef273C38CC19d1d02352785F52565A1A6c", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9", + "isProxy": false + } + ], + "flow": [ + { + "name": "ProxyAdmin", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000002eb", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "constructorArguments": "000000000000000000000000cda455dfd9c938451bfafc6ff0d497c8c0469c9600000000000000000000000060b8d195f1b2ecac26d54b95c69e6399cfd64b5300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96" + }, + { + "name": "PausableIsm", + "address": "0x14C586824E6d04F0761BF9fCa6983F7282002299", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x25d668D37f20E6f396cB5DF1DFf5A3f2F568e707", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x549F241472FccdA169E3202048aE2241231A7772", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000025d668d37f20e6f396cb5df1dff5a3f2f568e707", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xC9ab9Dc82F05eA118F266611f4c474529d43b599", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x59973ae98D340cA0577ed92502402E82E987Ba2E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xf9609bB22847e0DB5F6fB8f95b84D25A19b46ac5", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xA1Df6B70044029a2D1eDDC50EfDE2813e478140a", + "constructorArguments": "000000000000000000000000f9609bb22847e0db5f6fb8f95b84d25a19b46ac500000000000000000000000060b8d195f1b2ecac26d54b95c69e6399cfd64b5300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xf9609bB22847e0DB5F6fB8f95b84D25A19b46ac5" + }, + { + "name": "ProtocolFee", + "address": "0x3AdCBc94ab8C48EC52D06dc65Bb787fD1981E3d5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xE2B36A37bD98ba81658dC5454F2dB2F98438d140", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90", + "isProxy": false + } + ], + "zksync": [ + { + "name": "ProxyAdmin", + "address": "0xD01274DC164D32F8595bE707F221375E68cE300C", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x1B4E7fd3052Fc6d84DAB69eEf6a156C7D7909a78", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000144", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x6bD0A2214797Bc81e0b006F7B74d6221BcD8cb6E", + "constructorArguments": "0000000000000000000000001b4e7fd3052fc6d84dab69eef6a156c7d7909a78000000000000000000000000d01274dc164d32f8595be707f221375e68ce300c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x1B4E7fd3052Fc6d84DAB69eEf6a156C7D7909a78" + }, + { + "name": "DomainRoutingIsm", + "address": "0xec650696FDAE2355A928520AD7d6491c6072cf7f", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x823500D69D77A52212DC93f8836E9c08581487eE", + "constructorArguments": "0000000000000000000000006bd0a2214797bc81e0b006f7b74d6221bcd8cb6e", + "isProxy": false + }, + { + "name": "FallbackDomainRoutingHook", + "address": "0xe4e98Cc5D0318aBFD2adA8A3C6817b727063F500", + "constructorArguments": "0000000000000000000000006bd0a2214797bc81e0b006f7b74d6221bcd8cb6e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000823500d69d77a52212dc93f8836e9c08581487ee", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x37f4afe769087738f0577A77ffA24abef6fCBF99", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x95d20666eDf61b39f2706a7fc95E50C2758F800b", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xf44AdA86a1f765A938d404699B8070Dd47bD2431", + "constructorArguments": "00000000000000000000000095d20666edf61b39f2706a7fc95e50c2758f800b000000000000000000000000d01274dc164d32f8595be707f221375e68ce300c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x95d20666eDf61b39f2706a7fc95E50C2758F800b" + }, + { + "name": "ValidatorAnnounce", + "address": "0x576aF402c97bFE452Dcc203B6c3f6F4EBC92A0f5", + "constructorArguments": "0000000000000000000000006bd0a2214797bc81e0b006f7b74d6221bcd8cb6e", + "isProxy": false + } + ], + "zeronetwork": [ + { + "name": "ProxyAdmin", + "address": "0x72e2A678442Edc65f14476A0E4c94312C0469f4A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a4c1e089dCaba813c10b641d8296a972ffAd939", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000849ea", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xd7b351D2dE3495eA259DD10ab4b9300A378Afbf3", + "constructorArguments": "0000000000000000000000003a4c1e089dcaba813c10b641d8296a972ffad93900000000000000000000000072e2a678442edc65f14476a0e4c94312c0469f4a00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a4c1e089dCaba813c10b641d8296a972ffAd939" + }, + { + "name": "DomainRoutingIsm", + "address": "0x307A9dBD1df2329c3c597aF6853de60660baFFb5", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x55379421409961Ef129738c24261379ef8A547Df", + "constructorArguments": "000000000000000000000000d7b351d2de3495ea259dd10ab4b9300a378afbf3", + "isProxy": false + }, + { + "name": "FallbackDomainRoutingHook", + "address": "0x671836d35BB15E21ECc92c4936F0e3131efe12B4", + "constructorArguments": "000000000000000000000000d7b351d2de3495ea259dd10ab4b9300a378afbf3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000055379421409961ef129738c24261379ef8a547df", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xe85d65f04D1562f8571d57326d6798e4584aa254", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xDA6193892B324fdEc49209E7cB37E5eE84Cb2459", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x318FbdB17d4e743aBF3183658a4730777101B75C", + "constructorArguments": "000000000000000000000000da6193892b324fdec49209e7cb37e5ee84cb245900000000000000000000000072e2a678442edc65f14476a0e4c94312c0469f4a00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xDA6193892B324fdEc49209E7cB37E5eE84Cb2459" + }, + { + "name": "ValidatorAnnounce", + "address": "0xB2F0e411B46AbE3248dAFB5e89aDB5b8404F45DF", + "constructorArguments": "000000000000000000000000d7b351d2de3495ea259dd10ab4b9300a378afbf3", + "isProxy": false + } + ], + "gravity": [ + { + "name": "ProxyAdmin", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000659", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D" + }, + { + "name": "PausableIsm", + "address": "0x7621e04860F0bDe63311db9D5D8b589AD3458A1f", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000005090df2fbda7127c7ada41f60b79f5c55d380dd8", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xF1854214392864c628A16930E73B699f7a51b3EE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", + "constructorArguments": "000000000000000000000000f1854214392864c628a16930e73b699f7a51b3ee0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xF1854214392864c628A16930E73B699f7a51b3EE" + }, + { + "name": "ProtocolFee", + "address": "0x8C3e1794018a589c9E9226b8543105fCb6cC88C4", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + } + ], + "arbitrumnova": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000a4ba", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "apechain": [ + { + "name": "ProxyAdmin", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000008173", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa70000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7" + }, + { + "name": "PausableIsm", + "address": "0x5090dF2FBDa7127c7aDa41f60B79F5c55D380Dd8", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000886bb0f329781b98f98fdeb1ce7a8957f2d43b9f", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xF1854214392864c628A16930E73B699f7a51b3EE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x18B0688990720103dB63559a3563f7E8d0f63EDb", + "constructorArguments": "000000000000000000000000f3dff6747e7fc74b431c943961054b7bf6309d8a0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xf3dFf6747E7FC74B431C943961054B7BF6309d8a" + }, + { + "name": "ProtocolFee", + "address": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xcDA455DfD9C938451BfaFC6FF0D497c8C0469C96", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + } + ], + "harmony": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000063564c40", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "kaia": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000002019", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "b3": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000208d", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "orderly": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000123", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "snaxchain": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000890", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "morph": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000b02", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } + ], + "fantom": [ + { + "name": "ProxyAdmin", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000fa", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E" + }, + { + "name": "PausableIsm", + "address": "0x696df5e79C4f1bd5F8D587Ba8946361d9B029d4B", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6963480b05EB58f4d624B014ab92e9aD4d21df6D", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x886BB0f329781b98f98FDeb1ce7a8957F2d43B9F", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006963480b05eb58f4d624b014ab92e9ad4d21df6d", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xD0dca420feFda68537695A8D887080eeF4030AF7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x2F619Ac5122689180AeBB930ADccdae215d538a9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x145566181A18E23bB6a8A3eC6D87765542A7F754", + "constructorArguments": "000000000000000000000000ee08043cf22c80b27bf24d19999231df4a3fc256000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xEe08043cf22c80b27BF24d19999231dF4a3fC256" + }, + { + "name": "ProtocolFee", + "address": "0x13E83ac41e696856B6996263501fB3225AD5E6F5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index fbb7555c4..1c348376a 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -10,7 +10,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '72d498f-20240828-092240', + tag: '2cde782-20241029-100534', }, // We're currently using the same deployer/key funder key as mainnet2. // To minimize nonce clobbering we offset the key funder cron @@ -27,10 +27,14 @@ export const keyFunderConfig: KeyFunderConfig< // desired balance config, must be set for each chain desiredBalancePerChain: { ancient8: '0.5', + alephzeroevm: '100', + apechain: '50', arbitrum: '0.5', + arbitrumnova: '0.05', astar: '100', astarzkevm: '0.05', avalanche: '5', + b3: '0.05', base: '0.5', bitlayer: '0.002', blast: '0.2', @@ -38,47 +42,69 @@ export const keyFunderConfig: KeyFunderConfig< bsc: '5', celo: '3', cheesechain: '50', + chiliz: '200', coredao: '25', cyber: '0.05', degenchain: '100', dogechain: '100', endurance: '20', ethereum: '0.5', + everclear: '0.05', + fantom: '100', flare: '500', + flow: '5', fraxtal: '0.2', fusemainnet: '20', gnosis: '5', + gravity: '500', + harmony: '500', + immutablezkevm: '25', inevm: '3', + kaia: '250', kroma: '0.05', linea: '0.2', lisk: '0.05', lukso: '20', + lumia: '1', mantapacific: '0.2', mantle: '20', merlin: '0.002', + metall2: '0.05', metis: '3', mint: '0.05', mode: '0.2', molten: '3', moonbeam: '5', + morph: '0.05', + oortmainnet: '2000', optimism: '0.5', + orderly: '0.05', polygon: '20', polygonzkevm: '0.5', + polynomial: '0.05', proofofplay: '0.05', + rari: '0.05', real: '0.1', redstone: '0.2', + rootstock: '0.002', sanko: '2', scroll: '0.5', sei: '50', shibarium: '50', + snaxchain: '0.05', + // ignore non-evm chains + stride: '0', + superposition: '0.05', taiko: '0.2', tangle: '2', viction: '3', worldchain: '0.2', xai: '20', xlayer: '0.5', + zeronetwork: '0.05', zetachain: '20', zircuit: '0.02', + zksync: '0.05', zoramainnet: '0.2', // ignore non-evm chains injective: '0', diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index 0c78455c7..4652bedc1 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -3,8 +3,20 @@ "amount": "0.001000252", "decimals": 9 }, + "alephzeroevm": { + "amount": "40.0", + "decimals": 9 + }, + "apechain": { + "amount": "25.42069", + "decimals": 9 + }, "arbitrum": { - "amount": "0.5", + "amount": "0.032084", + "decimals": 9 + }, + "arbitrumnova": { + "amount": "0.01", "decimals": 9 }, "astar": { @@ -12,15 +24,19 @@ "decimals": 9 }, "astarzkevm": { - "amount": "0.0097", + "amount": "0.0696", "decimals": 9 }, "avalanche": { - "amount": "43.212830197", + "amount": "25.0", + "decimals": 9 + }, + "b3": { + "amount": "0.001000252", "decimals": 9 }, "base": { - "amount": "0.013311447", + "amount": "0.025135767", "decimals": 9 }, "bitlayer": { @@ -28,7 +44,7 @@ "decimals": 9 }, "blast": { - "amount": "0.5", + "amount": "0.004639802", "decimals": 9 }, "bob": { @@ -36,7 +52,7 @@ "decimals": 9 }, "bsc": { - "amount": "5.0", + "amount": "1.0", "decimals": 9 }, "celo": { @@ -47,6 +63,10 @@ "amount": "1.0", "decimals": 9 }, + "chiliz": { + "amount": "5000.0", + "decimals": 9 + }, "coredao": { "amount": "30.0", "decimals": 9 @@ -64,19 +84,31 @@ "decimals": 9 }, "eclipsemainnet": { - "amount": "0.001", - "decimals": 9 + "amount": "0.0000001", + "decimals": 1 }, "endurance": { "amount": "1.500000007", "decimals": 9 }, "ethereum": { - "amount": "2", + "amount": "9.379005838", + "decimals": 9 + }, + "everclear": { + "amount": "0.1", + "decimals": 9 + }, + "fantom": { + "amount": "9.871668079", "decimals": 9 }, "flare": { - "amount": "25.0", + "amount": "26.179282546", + "decimals": 9 + }, + "flow": { + "amount": "0.1", "decimals": 9 }, "fraxtal": { @@ -84,11 +116,23 @@ "decimals": 9 }, "fusemainnet": { - "amount": "50.0", + "amount": "20.0", "decimals": 9 }, "gnosis": { - "amount": "5.877696928", + "amount": "1.430000001", + "decimals": 9 + }, + "gravity": { + "amount": "1800.0", + "decimals": 9 + }, + "harmony": { + "amount": "100.0", + "decimals": 9 + }, + "immutablezkevm": { + "amount": "11.000000049", "decimals": 9 }, "inevm": { @@ -99,12 +143,16 @@ "amount": "700000000", "decimals": 1 }, + "kaia": { + "amount": "27.5", + "decimals": 9 + }, "kroma": { "amount": "0.001000252", "decimals": 9 }, "linea": { - "amount": "0.110844655", + "amount": "0.312253183", "decimals": 9 }, "lisk": { @@ -112,11 +160,15 @@ "decimals": 9 }, "lukso": { - "amount": "2.113054947", + "amount": "0.557453646", + "decimals": 9 + }, + "lumia": { + "amount": "1.0", "decimals": 9 }, "mantapacific": { - "amount": "0.113932514", + "amount": "0.001001418", "decimals": 9 }, "mantle": { @@ -127,8 +179,12 @@ "amount": "0.095", "decimals": 9 }, + "metall2": { + "amount": "0.001000252", + "decimals": 9 + }, "metis": { - "amount": "1.682845607", + "amount": "1.077971462", "decimals": 9 }, "mint": { @@ -136,7 +192,7 @@ "decimals": 9 }, "mode": { - "amount": "0.1", + "amount": "0.001000265", "decimals": 9 }, "molten": { @@ -147,12 +203,24 @@ "amount": "125.0", "decimals": 9 }, + "morph": { + "amount": "0.0041472", + "decimals": 9 + }, "neutron": { "amount": "0.0053", "decimals": 1 }, + "oortmainnet": { + "amount": "100.0", + "decimals": 9 + }, "optimism": { - "amount": "0.25", + "amount": "0.001001064", + "decimals": 9 + }, + "orderly": { + "amount": "0.00100025", "decimals": 9 }, "osmosis": { @@ -160,23 +228,35 @@ "decimals": 1 }, "polygon": { - "amount": "101.76455238", + "amount": "100", "decimals": 9 }, "polygonzkevm": { - "amount": "3.95", + "amount": "0.5", + "decimals": 9 + }, + "polynomial": { + "amount": "0.001000252", "decimals": 9 }, "proofofplay": { "amount": "0.01", "decimals": 9 }, + "rari": { + "amount": "0.15", + "decimals": 9 + }, "real": { - "amount": "0.24", + "amount": "0.03", "decimals": 9 }, "redstone": { - "amount": "0.0003", + "amount": "0.00010005", + "decimals": 9 + }, + "rootstock": { + "amount": "0.07", "decimals": 9 }, "sanko": { @@ -184,23 +264,35 @@ "decimals": 9 }, "scroll": { - "amount": "1.3231", + "amount": "0.041113999", "decimals": 9 }, "sei": { - "amount": "100.0", + "amount": "100.005260996", "decimals": 9 }, "shibarium": { - "amount": "54.30595191", + "amount": "61.627947201", + "decimals": 9 + }, + "snaxchain": { + "amount": "0.001000252", "decimals": 9 }, "solanamainnet": { "amount": "0.5", "decimals": 1 }, + "stride": { + "amount": "0.005", + "decimals": 1 + }, + "superposition": { + "amount": "0.01", + "decimals": 9 + }, "taiko": { - "amount": "0.050000001", + "amount": "0.11100569", "decimals": 9 }, "tangle": { @@ -212,15 +304,19 @@ "decimals": 9 }, "worldchain": { - "amount": "0.002002589", + "amount": "0.001000298", "decimals": 9 }, "xai": { - "amount": "0.23886", + "amount": "0.10081", "decimals": 9 }, "xlayer": { - "amount": "9.67", + "amount": "5.882081", + "decimals": 9 + }, + "zeronetwork": { + "amount": "0.04525", "decimals": 9 }, "zetachain": { @@ -231,8 +327,12 @@ "amount": "0.001000253", "decimals": 9 }, + "zksync": { + "amount": "0.04525", + "decimals": 9 + }, "zoramainnet": { - "amount": "0.001000252", + "amount": "0.001000255", "decimals": 9 } } diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index 39e09e07d..2b388afdd 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -1,21 +1,19 @@ -import { BigNumber, ethers } from 'ethers'; - import { ChainMap, ChainName, HookType, IgpConfig, - TOKEN_EXCHANGE_RATE_DECIMALS, - defaultMultisigConfigs, - multisigIsmVerificationCost, + getTokenExchangeRateFromValues, } from '@hyperlane-xyz/sdk'; import { exclude, objMap } from '@hyperlane-xyz/utils'; import { AllStorageGasOracleConfigs, + EXCHANGE_RATE_MARGIN_PCT, getAllStorageGasOracleConfigs, - getTokenExchangeRateFromValues, + getOverhead, } from '../../../src/config/gas-oracle.js'; +import { mustGetChainNativeToken } from '../../../src/utils/utils.js'; import { ethereumChainNames } from './chains.js'; import gasPrices from './gasPrices.json'; @@ -25,37 +23,31 @@ import rawTokenPrices from './tokenPrices.json'; const tokenPrices: ChainMap = rawTokenPrices; -const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen - -const remoteOverhead = (remote: ChainName) => - ethereumChainNames.includes(remote as any) - ? multisigIsmVerificationCost( - defaultMultisigConfigs[remote].threshold, - defaultMultisigConfigs[remote].validators.length, - ) - : FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead - -// Gets the exchange rate of the remote quoted in local tokens -function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber { - const localValue = ethers.utils.parseUnits( - tokenPrices[local], - TOKEN_EXCHANGE_RATE_DECIMALS, - ); - const remoteValue = ethers.utils.parseUnits( - tokenPrices[remote], - TOKEN_EXCHANGE_RATE_DECIMALS, - ); - - return getTokenExchangeRateFromValues(local, localValue, remote, remoteValue); +export function getOverheadWithOverrides(local: ChainName, remote: ChainName) { + let overhead = getOverhead(local, remote, ethereumChainNames); + if (remote === 'moonbeam') { + overhead *= 4; + } + return overhead; } const storageGasOracleConfig: AllStorageGasOracleConfigs = getAllStorageGasOracleConfigs( supportedChainNames, gasPrices, - getTokenExchangeRate, + (local, remote) => + getTokenExchangeRateFromValues({ + local, + remote, + tokenPrices, + exchangeRateMarginPct: EXCHANGE_RATE_MARGIN_PCT, + decimals: { + local: mustGetChainNativeToken(local).decimals, + remote: mustGetChainNativeToken(remote).decimals, + }, + }), (local) => parseFloat(tokenPrices[local]), - (local) => remoteOverhead(local), + (local, remote) => getOverheadWithOverrides(local, remote), ); export const igp: ChainMap = objMap( @@ -73,7 +65,7 @@ export const igp: ChainMap = objMap( overhead: Object.fromEntries( exclude(local, supportedChainNames).map((remote) => [ remote, - remoteOverhead(remote), + getOverheadWithOverrides(local, remote), ]), ), oracleConfig: storageGasOracleConfig[local], diff --git a/typescript/infra/config/environments/mainnet3/ism/verification.json b/typescript/infra/config/environments/mainnet3/ism/verification.json index 52d18b2c3..b291cf0e3 100644 --- a/typescript/infra/config/environments/mainnet3/ism/verification.json +++ b/typescript/infra/config/environments/mainnet3/ism/verification.json @@ -4852,5 +4852,1811 @@ "constructorArguments": "", "isProxy": false } + ], + "everclear": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xDFF18Bf286c9cDd0fC653a28616460Cf7443F8EF", + "constructorArguments": "", + "isProxy": true + } + ], + "oortmainnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xe139ceA3397ABfE745B3A4a9f6976A6519754100", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x70296bdA00742EA05A46aAE3B11b16C38134c992", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x8aEA374F73d0b1182A6cdf5Cc9143f777D03b279", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0xDE3c6d94dA0b4c066b46eDE6ECf9F10237E8389a", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x23cEB84ddEe5cfC3d98F98b7501e96fB0de8D2E3", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x8928Fe94d9145623b4359F3C36C819C7f5086E6b", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xD898F08AA84b97B7a3F6f259b81bb86FaF00E867", + "constructorArguments": "", + "isProxy": true + } + ], + "alephzeroevm": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "superposition": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "rari": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "immutablezkevm": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "lumia": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "chiliz": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "rootstock": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x70032dF7C43e5C3611705C21b1528850ac9ce88A", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xC17126bfdA167e88ea89847aCC63aaE7FB898CB0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0xb6A3aDDAc0d8bD0B38E17083F70D6B3b6e65967e", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0xe139ceA3397ABfE745B3A4a9f6976A6519754100", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x70296bdA00742EA05A46aAE3B11b16C38134c992", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x8aEA374F73d0b1182A6cdf5Cc9143f777D03b279", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xDE3c6d94dA0b4c066b46eDE6ECf9F10237E8389a", + "constructorArguments": "", + "isProxy": true + } + ], + "polynomial": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xDFF18Bf286c9cDd0fC653a28616460Cf7443F8EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3a49EcAC1031612D66fa20D6F40f214aCeAc2B4B", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xfb288565DBa8489e745Fb814584d06331809d16F", + "constructorArguments": "", + "isProxy": true + } + ], + "metall2": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "gravity": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + } + ], + "apechain": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xDFF18Bf286c9cDd0fC653a28616460Cf7443F8EF", + "constructorArguments": "", + "isProxy": true + } + ], + "arbitrumnova": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "harmony": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "kaia": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "b3": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "fantom": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "morph": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "orderly": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } + ], + "snaxchain": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7f51A658837A315134A97ff8B586d71B726B7e61", + "constructorArguments": "", + "isProxy": true + } ] } diff --git a/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json b/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json index 2eee67272..773a6e781 100644 --- a/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json +++ b/typescript/infra/config/environments/mainnet3/middleware/accounts/verification.json @@ -1138,5 +1138,676 @@ "isProxy": true, "expectedimplementation": "0xF1854214392864c628A16930E73B699f7a51b3EE" } + ], + "everclear": [ + { + "name": "InterchainAccountIsm", + "address": "0xcd9D3744512F07AE844c40E27912092d7c503565", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x58556AaeB2e3829d52EE5E711D44735412efA43B", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", + "constructorArguments": "00000000000000000000000058556aaeb2e3829d52ee5e711d44735412efa43b0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd9d3744512f07ae844c40e27912092d7c503565000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x58556AaeB2e3829d52EE5E711D44735412efA43B" + } + ], + "oortmainnet": [ + { + "name": "InterchainAccountIsm", + "address": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", + "constructorArguments": "000000000000000000000000b163e75e2008cdc94e50278b5fcdd081761a2ee4", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9", + "constructorArguments": "000000000000000000000000b163e75e2008cdc94e50278b5fcdd081761a2ee4", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "constructorArguments": "000000000000000000000000662771d29dff0d7c36bb9bb6d4241a02e77585d90000000000000000000000008d078f74df9e5a4fa8da06f2abce5d01fdf793c200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092cdbf0ccdf8e93467fa858fb986fa650a02f2a8000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x662771d29DFf0d7C36bB9BB6d4241a02e77585d9" + }, + { + "name": "InterchainAccountIsm", + "address": "0xc23BaF5Eb5848D19701BbE7f139645e6bd58a319", + "constructorArguments": "000000000000000000000000b129828b9eda48192d0b2db35d0e40dcf51b3594", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x376aD181E8cd45eAd5403F78d5A871D08c3c4D77", + "constructorArguments": "000000000000000000000000b129828b9eda48192d0b2db35d0e40dcf51b3594", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x7c58Cadcc2b60ACF794eE1843488d6f5703f76BE", + "constructorArguments": "000000000000000000000000376ad181e8cd45ead5403f78d5a871d08c3c4d77000000000000000000000000148cf67b8a242c1360bb2c93fce203ec4d4f9b5600000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c23baf5eb5848d19701bbe7f139645e6bd58a319000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x376aD181E8cd45eAd5403F78d5A871D08c3c4D77" + } + ], + "alephzeroevm": [ + { + "name": "InterchainAccountIsm", + "address": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "000000000000000000000000168dff0ad2b180f3801883fe5ae56d7e7d91d5f40000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e969ba938e6a993eecd6f65b0dd8712b07dfe59000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4" + }, + { + "name": "InterchainAccountIsm", + "address": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", + "constructorArguments": "000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed" + } + ], + "rari": [ + { + "name": "InterchainAccountIsm", + "address": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "000000000000000000000000168dff0ad2b180f3801883fe5ae56d7e7d91d5f40000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e969ba938e6a993eecd6f65b0dd8712b07dfe59000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4" + }, + { + "name": "InterchainAccountIsm", + "address": "0x99fEFc1119E86Ee0153eb887cF8E8ab2d92A16e8", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xCDeb368Db32ecCefaf7018e152DA9120565cb572", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xbB88a31E4b709b645c06825c0E0b5CAC906d97DE", + "constructorArguments": "000000000000000000000000cdeb368db32eccefaf7018e152da9120565cb5720000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000099fefc1119e86ee0153eb887cf8e8ab2d92a16e8000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xCDeb368Db32ecCefaf7018e152DA9120565cb572" + } + ], + "superposition": [ + { + "name": "InterchainAccountIsm", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x504236Da6344e5E144def5653C2b1d0fFd18cB7d", + "constructorArguments": "0000000000000000000000006fb36672365c7c797028c400a61c58c0ecc53cd2000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000168dff0ad2b180f3801883fe5ae56d7e7d91d5f4000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6Fb36672365C7c797028C400A61c58c0ECc53cD2" + }, + { + "name": "InterchainAccountIsm", + "address": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xbb0AE51BCa526cF313b6a95BfaB020794af6C394", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x83475ca5bEB2Eaa59A2FF48a0544ebaa4a32c2de", + "constructorArguments": "000000000000000000000000bb0ae51bca526cf313b6a95bfab020794af6c394000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000009eb56085ddbda60adf7d2b533afed90e38fc9666000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xbb0AE51BCa526cF313b6a95BfaB020794af6C394" + } + ], + "chiliz": [ + { + "name": "InterchainAccountIsm", + "address": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "000000000000000000000000168dff0ad2b180f3801883fe5ae56d7e7d91d5f40000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e969ba938e6a993eecd6f65b0dd8712b07dfe59000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4" + }, + { + "name": "InterchainAccountIsm", + "address": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x0D3bD9F1bcDA82bD1682b2C895a907d7aaE45849", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xbb0AE51BCa526cF313b6a95BfaB020794af6C394", + "constructorArguments": "0000000000000000000000000d3bd9f1bcda82bd1682b2c895a907d7aae458490000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000009eaac366bfd70430cfee6e70265fefff1cfc9e47000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x0D3bD9F1bcDA82bD1682b2C895a907d7aaE45849" + } + ], + "immutablezkevm": [ + { + "name": "InterchainAccountIsm", + "address": "0x3E969bA938E6A993eeCD6F65b0dd8712B07dFe59", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "000000000000000000000000168dff0ad2b180f3801883fe5ae56d7e7d91d5f40000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e969ba938e6a993eecd6f65b0dd8712b07dfe59000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x168DFF0Ad2b180F3801883Fe5Ae56d7E7d91D5f4" + }, + { + "name": "InterchainAccountIsm", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x9eb56085DdbDA60aDf7d2B533AFeD90e38fC9666", + "constructorArguments": "0000000000000000000000009eaac366bfd70430cfee6e70265fefff1cfc9e470000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ee8c0e1eebffcc451a013336386ea53e42a44451000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47" + } + ], + "lumia": [ + { + "name": "InterchainAccountIsm", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x504236Da6344e5E144def5653C2b1d0fFd18cB7d", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xC9c1A8E0d7A389ff4E3A5ab1C3F9555c50BaD325", + "constructorArguments": "000000000000000000000000504236da6344e5e144def5653c2b1d0ffd18cb7d000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d569fb1753167312ec5b78526743f2bea027e5d8000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x504236Da6344e5E144def5653C2b1d0fFd18cB7d" + }, + { + "name": "InterchainAccountIsm", + "address": "0x25EAC2007b0D40E3f0AF112FD346412321038719", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x0F9d4704E1Fb25e416042524e594F1cEac6fF597", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xfF26696DcDb6BbFD27e959b847D4f1399D5BcF64", + "constructorArguments": "0000000000000000000000000f9d4704e1fb25e416042524e594f1ceac6ff597000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025eac2007b0d40e3f0af112fd346412321038719000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x0F9d4704E1Fb25e416042524e594F1cEac6fF597" + } + ], + "rootstock": [ + { + "name": "InterchainAccountIsm", + "address": "0xD569fb1753167312ec5b78526743F2Bea027E5d8", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x504236Da6344e5E144def5653C2b1d0fFd18cB7d", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xC9c1A8E0d7A389ff4E3A5ab1C3F9555c50BaD325", + "constructorArguments": "000000000000000000000000504236da6344e5e144def5653c2b1d0ffd18cb7d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d569fb1753167312ec5b78526743f2bea027e5d8000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x504236Da6344e5E144def5653C2b1d0fFd18cB7d" + }, + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e10000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "polynomial": [ + { + "name": "InterchainAccountIsm", + "address": "0xc23BaF5Eb5848D19701BbE7f139645e6bd58a319", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x376aD181E8cd45eAd5403F78d5A871D08c3c4D77", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x7c58Cadcc2b60ACF794eE1843488d6f5703f76BE", + "constructorArguments": "000000000000000000000000376ad181e8cd45ead5403f78d5a871d08c3c4d770000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b3900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c23baf5eb5848d19701bbe7f139645e6bd58a319000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x376aD181E8cd45eAd5403F78d5A871D08c3c4D77" + }, + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "00000000000000000000000002d16bc51af6bfd153d67ca61754cf912e82c4d9", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e10000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b3900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "metall2": [ + { + "name": "InterchainAccountIsm", + "address": "0xC9c1A8E0d7A389ff4E3A5ab1C3F9555c50BaD325", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6f77d5Ef273C38CC19d1d02352785F52565A1A6c", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x5c12ADC734699C07b095fe30B8312F1A7bbaA788", + "constructorArguments": "0000000000000000000000006f77d5ef273c38cc19d1d02352785f52565a1a6c0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c9c1a8e0d7a389ff4e3a5ab1c3f9555c50bad325000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6f77d5Ef273C38CC19d1d02352785F52565A1A6c" + }, + { + "name": "InterchainAccountIsm", + "address": "0x61374178e45F65fF9D6252d017Cd580FC60B7654", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "constructorArguments": "00000000000000000000000060b8d195f1b2ecac26d54b95c69e6399cfd64b530000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061374178e45f65ff9d6252d017cd580fc60b7654000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x60B8d195f1b2EcaC26d54b95C69E6399cFD64b53" + } + ], + "flow": [ + { + "name": "InterchainAccountIsm", + "address": "0x587463AF636f527783c584F4DdbF8188e09EC213", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xDd2059c375C81638DaB52AF4145d2671C446c5e9", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x6F9eC4f83ef69b923Fa1Dc00189e591728DF0ac0", + "constructorArguments": "000000000000000000000000dd2059c375c81638dab52af4145d2671c446c5e900000000000000000000000060b8d195f1b2ecac26d54b95c69e6399cfd64b5300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000587463af636f527783c584f4ddbf8188e09ec213000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xDd2059c375C81638DaB52AF4145d2671C446c5e9" + }, + { + "name": "InterchainAccountIsm", + "address": "0xb5668713E9BA8bC96f97D691663E70b54CE90b0A", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x3E12271EbD523d0886D0D51A4FF8D8e046CF2E1D", + "constructorArguments": "000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xc5068BB6803ADbe5600DE5189fe27A4dAcE31170", + "constructorArguments": "0000000000000000000000003e12271ebd523d0886d0d51a4ff8d8e046cf2e1d00000000000000000000000060b8d195f1b2ecac26d54b95c69e6399cfd64b5300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b5668713e9ba8bc96f97d691663e70b54ce90b0a000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3E12271EbD523d0886D0D51A4FF8D8e046CF2E1D" + } + ], + "gravity": [ + { + "name": "InterchainAccountIsm", + "address": "0x783EC5e105234a570eB90f314284E5dBe53bdd90", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", + "constructorArguments": "000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000783ec5e105234a570eb90f314284e5dbe53bdd90000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed" + } + ], + "apechain": [ + { + "name": "InterchainAccountIsm", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563", + "constructorArguments": "0000000000000000000000007f50c5776722630a0024fae05fde8b47571d7b39", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x9eaaC366BFD70430cFee6E70265fefFf1CfC9E47", + "constructorArguments": "000000000000000000000000c5d6acaafbccec6d7fd7d92f4509befce641c5630000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xc5D6aCaafBCcEC6D7fD7d92F4509befce641c563" + } + ], + "arbitrumnova": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "harmony": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "kaia": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "b3": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "orderly": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "snaxchain": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "fantom": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } + ], + "morph": [ + { + "name": "InterchainAccountIsm", + "address": "0xFB9e40D811Cea562cc8a322b029eF2BDcC3ef6ed", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1", + "constructorArguments": "0000000000000000000000003a867fcffec2b790970eebdc9023e75b0a172aa7", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xeE8C0E1EeBfFCC451a013336386eA53E42a44451", + "constructorArguments": "0000000000000000000000006119b76720ccfeb3d256ec1b91218eeffd6756e1000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fb9e40d811cea562cc8a322b029ef2bdcc3ef6ed000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1" + } ] } diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index 028f5d8a4..e32d8961c 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -66,6 +66,78 @@ export const icas: Partial< > = { viction: '0x23ed65DE22ac29Ec1C16E75EddB0cE3A187357b4', inevm: '0xFDF9EDcb2243D51f5f317b9CEcA8edD2bEEE036e', + + // Jul 26, 2024 batch + // ---------------------------------------------------------- + xlayer: '0x1571c482fe9E76bbf50829912b1c746792966369', + cheesechain: '0xEe2C5320BE9bC7A1492187cfb289953b53E3ff1b', + worldchain: '0x1996DbFcFB433737fE404F58D2c32A7f5f334210', + // zircuit: '0x0d67c56E818a02ABa58cd2394b95EF26db999aA3', // already has a safe + + // Aug 5, 2024 batch + // ---------------------------------------------------------- + cyber: '0x984Fe5a45Ac4aaeC4E4655b50f776aB79c9Be19F', + degenchain: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', + kroma: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', + lisk: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', + lukso: '0xc1e20A0D78E79B94D71d4bDBC8FD0Af7c856Dd7A', + merlin: '0xCf867cEaeeE8CBe65C680c734D29d26440931D5b', + metis: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', + mint: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', + proofofplay: '0xb51e63CD0842D670a13c88B159fCFc268DA652A3', + real: '0xc761e68BF3A94326FD0D305e3ccb4cdaab2edA19', + sanko: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752', + tangle: '0xCC2aeb692197C7894E561d31ADFE8F79746f7d9F', + xai: '0x22d952d3b9F493442731a3c7660aCaD98e55C00A', + // taiko: '0x483D218D2FEe7FC7204ba15F00C7901acbF9697D', // renzo chain + + // Aug 26, 2024 batch + // ---------------------------------------------------------- + astar: '0x6b241544eBa7d89B51b72DF85a0342dAa37371Ca', + astarzkevm: '0x526c6DAee1175A1A2337E703B63593acb327Dde4', + bitlayer: '0xe6239316cA60814229E801fF0B9DD71C9CA29008', + coredao: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', + dogechain: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', + flare: '0x689b8DaBBF2f9Fd83D37427A062B30edF463e20b', + molten: '0x84802CdF47565C95d8ffd59E7c4B1cf027F5452F', + shibarium: '0x6348FAe3a8374dbAAaE84EEe5458AE4063Fe2be7', + + // Sep 9, 2024 batch + // ---------------------------------------------------------- + everclear: '0x63B2075372D6d0565F51210D0A296D3c8a773aB6', + oortmainnet: '0x7021D11F9fAe455AB2f45D72dbc2C64d116Cb657', + + // Sep 19, 2024 SAFE --> ICA v1 Migration + // ---------------------------------------------------------- + celo: '0x3fA264c58E1365f1d5963B831b864EcdD2ddD19b', + avalanche: '0x8c8695cD9905e22d84E466804ABE55408A87e595', + polygon: '0xBDD25dd5203fedE33FD631e30fEF9b9eF2598ECE', + moonbeam: '0x480e5b5De6a29F07fe8295C60A1845d36b7BfdE6', + gnosis: '0xD42125a4889A7A36F32d7D12bFa0ae52B0AD106b', + scroll: '0x2a3fe2513F4A7813683d480724AB0a3683EfF8AC', + polygonzkevm: '0x66037De438a59C966214B78c1d377c4e93a5C7D1', + ancient8: '0xA9FD5BeB556AB1859D7625B381110a257f56F98C', + redstone: '0x5DAcd2f1AafC749F2935A160865Ab1568eC23752', + mantle: '0x08C880b88335CA3e85Ebb4E461245a7e899863c9', + bob: '0xc99e58b9A4E330e2E4d09e2c94CD3c553904F588', + zetachain: '0xc876B8e63c3ff5b636d9492715BE375644CaD345', + zoramainnet: '0x84977Eb15E0ff5824a6129c789F70e88352C230b', + fusemainnet: '0xbBdb1682B2922C282b56DD716C29db5EFbdb5632', + endurance: '0x470E04D8a3b7938b385093B93CeBd8Db7A1E557C', + // sei: '0xabad187003EdeDd6C720Fc633f929EA632996567', // renzo chain + + // Oct 16, 2024 batch + // ---------------------------------------------------------- + immutablezkevm: '0x8483e1480B62cB9f0aCecEbF42469b9f4013577a', + rari: '0x1124D54E989570A798769E534eAFbE1444f40AF6', + rootstock: '0x69350aeA98c5195F2c3cC6E6A065d0d8B12F659A', + alephzeroevm: '0x004a4C2e4Cd4F5Bd564fe0A6Ab2Da56258aE576f', + chiliz: '0xb52D281aD2BA9761c16f400d755837493e2baDB7', + lumia: '0x418E10Ac9e0b84022d0636228d05bc74172e0e41', + superposition: '0x34b57ff8fBA8da0cFdA795CC0F874FfaB14B1DE9', + flow: '0xf48377f8A3ddA7AAD7C2460C81d939434c829b45', + metall2: '0x2f1b1B0Fb7652E621316460f6c3b019F61d8dC9a', + polynomial: '0xC20eFa1e5A378af9233e9b24515eb3408d43f900', } as const; export const DEPLOYER = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; diff --git a/typescript/infra/config/environments/mainnet3/safe/safeSigners.json b/typescript/infra/config/environments/mainnet3/safe/safeSigners.json index d66b992ab..fca35815f 100644 --- a/typescript/infra/config/environments/mainnet3/safe/safeSigners.json +++ b/typescript/infra/config/environments/mainnet3/safe/safeSigners.json @@ -3,7 +3,7 @@ "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba", "0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA", "0x3b7f8f68A4FD0420FeA2F42a1eFc53422f205599", - "0x88436919fAa2310d32A36D20d13E0a441D24fAc3", + "0x478be6076f31E9666123B9721D0B6631baD944AF", "0x003DDD9eEAb62013b7332Ab4CC6B10077a8ca961", "0xd00d6A31485C93c597D1d8231eeeE0ed17B9844B", "0x483fd7284A696343FEc0819DDF2cf7E06E8A06E5", diff --git a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts index 454703f59..39957bea0 100644 --- a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts +++ b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts @@ -2,10 +2,14 @@ // Placing them here instead of adjacent chains file to avoid circular dep export const mainnet3SupportedChainNames = [ 'ancient8', + 'alephzeroevm', + 'apechain', 'arbitrum', + 'arbitrumnova', 'astar', 'astarzkevm', 'avalanche', + 'b3', 'base', 'bitlayer', 'blast', @@ -13,6 +17,7 @@ export const mainnet3SupportedChainNames = [ 'bsc', 'celo', 'cheesechain', + 'chiliz', 'coredao', 'cyber', 'degenchain', @@ -20,45 +25,65 @@ export const mainnet3SupportedChainNames = [ 'eclipsemainnet', 'endurance', 'ethereum', + 'everclear', + 'fantom', 'flare', + 'flow', 'fraxtal', 'fusemainnet', 'gnosis', + 'gravity', + 'harmony', + 'immutablezkevm', 'inevm', 'injective', + 'kaia', 'kroma', 'linea', 'lisk', 'lukso', + 'lumia', 'mantapacific', 'mantle', 'merlin', + 'metall2', 'metis', 'mint', 'mode', 'molten', 'moonbeam', + 'morph', 'neutron', + 'oortmainnet', 'optimism', + 'orderly', 'osmosis', 'polygon', 'polygonzkevm', + 'polynomial', 'proofofplay', + 'rari', 'real', 'redstone', + 'rootstock', 'sanko', 'scroll', 'sei', 'shibarium', + 'snaxchain', 'solanamainnet', + 'stride', + 'superposition', 'taiko', 'tangle', 'viction', 'worldchain', 'xai', 'xlayer', + 'zeronetwork', 'zetachain', 'zircuit', + 'zksync', 'zoramainnet', ] as const; diff --git a/typescript/infra/config/environments/mainnet3/testrecipient/verification.json b/typescript/infra/config/environments/mainnet3/testrecipient/verification.json index 6003193f5..aa476dffb 100644 --- a/typescript/infra/config/environments/mainnet3/testrecipient/verification.json +++ b/typescript/infra/config/environments/mainnet3/testrecipient/verification.json @@ -94,5 +94,13 @@ "isProxy": false, "name": "TestRecipient" } + ], + "lumia": [ + { + "name": "TestRecipient", + "address": "0xf7D882A816D4845BB221Ceb03CE531d1e7645F60", + "constructorArguments": "", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index 4b29e06a6..abe764bb7 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -1,61 +1,86 @@ { - "ancient8": "2620.58", - "arbitrum": "2620.58", - "astar": "0.074541", - "astarzkevm": "2620.58", - "avalanche": "25.76", - "base": "2620.58", - "bitlayer": "62418", - "blast": "2620.58", - "bob": "2620.58", - "bsc": "551.73", - "celo": "0.481911", - "cheesechain": "0.00204939", - "coredao": "1.091", - "cyber": "2620.58", - "degenchain": "0.00398489", - "dogechain": "0.104488", - "eclipsemainnet": "2620.58", - "endurance": "2.34", - "ethereum": "2620.58", - "flare": "0.01529346", - "fraxtal": "2619.2", - "fusemainnet": "0.03214191", - "gnosis": "1.001", - "inevm": "20.44", - "injective": "20.44", - "kroma": "2620.58", - "linea": "2620.58", - "lisk": "2620.58", - "lukso": "2.54", - "mantapacific": "2620.58", - "mantle": "0.60943", - "merlin": "62351", - "metis": "35.7", - "mint": "2620.58", - "mode": "2620.58", - "molten": "0.498892", - "moonbeam": "0.169444", - "neutron": "0.404548", - "optimism": "2620.58", - "osmosis": "0.435141", - "polygon": "0.49875", - "polygonzkevm": "2620.58", - "proofofplay": "2620.58", - "real": "2620.58", - "redstone": "2620.58", - "sanko": "49.41", - "scroll": "2620.58", - "sei": "0.325976", - "shibarium": "0.418598", - "solanamainnet": "154.32", - "taiko": "2620.58", - "tangle": "0.999894", - "viction": "0.380653", - "worldchain": "2620.58", - "xai": "0.209969", - "xlayer": "37.74", - "zetachain": "0.528535", - "zircuit": "2620.58", - "zoramainnet": "2620.58" + "ancient8": "2509.23", + "alephzeroevm": "0.374106", + "apechain": "1.17", + "arbitrum": "2509.23", + "arbitrumnova": "2509.23", + "astar": "0.056948", + "astarzkevm": "2509.23", + "avalanche": "25.94", + "b3": "2509.23", + "base": "2509.23", + "bitlayer": "67372", + "blast": "2509.23", + "bob": "2509.23", + "bsc": "587.93", + "celo": "0.687006", + "cheesechain": "0.00295261", + "chiliz": "0.065082", + "coredao": "0.9041", + "cyber": "2509.23", + "degenchain": "0.00694026", + "dogechain": "0.135405", + "eclipsemainnet": "2509.23", + "endurance": "2.14", + "ethereum": "2509.23", + "everclear": "2509.23", + "fantom": "0.668043", + "flare": "0.01413977", + "flow": "0.537723", + "fraxtal": "2506.4", + "fusemainnet": "0.02708378", + "gnosis": "1", + "gravity": "0.03113181", + "harmony": "0.012995", + "immutablezkevm": "1.42", + "inevm": "19.75", + "injective": "19.75", + "kaia": "1", + "kroma": "2509.23", + "linea": "2509.23", + "lisk": "2509.23", + "lukso": "1.38", + "lumia": "1.14", + "mantapacific": "2509.23", + "mantle": "0.591459", + "merlin": "67402", + "metall2": "2509.23", + "metis": "41.48", + "mint": "2509.23", + "mode": "2509.23", + "molten": "0.202455", + "moonbeam": "0.163232", + "morph": "2509.23", + "neutron": "0.383777", + "oortmainnet": "0.109681", + "optimism": "2509.23", + "orderly": "2509.23", + "osmosis": "0.483881", + "polygon": "0.340093", + "polygonzkevm": "2509.23", + "polynomial": "2509.23", + "proofofplay": "2509.23", + "rari": "2509.23", + "real": "1", + "redstone": "2509.23", + "rootstock": "67025", + "sanko": "47.83", + "scroll": "2509.23", + "sei": "0.393025", + "shibarium": "0.383601", + "snaxchain": "2509.23", + "solanamainnet": "168.99", + "stride": "0.731952", + "superposition": "2509.23", + "taiko": "2509.23", + "tangle": "1", + "viction": "0.348004", + "worldchain": "2509.23", + "xai": "0.20891", + "xlayer": "39.32", + "zeronetwork": "2509.23", + "zetachain": "0.661744", + "zircuit": "2509.23", + "zksync": "2509.23", + "zoramainnet": "2509.23" } diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index a214d883e..c3747ac2a 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -872,5 +872,253 @@ export const validatorChainConfig = ( 'shibarium', ), }, + everclear: { + interval: 5, + reorgPeriod: getReorgPeriod('everclear'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837'], + [Contexts.ReleaseCandidate]: [''], + [Contexts.Neutron]: [], + }, + 'everclear', + ), + }, + oortmainnet: { + interval: 5, + reorgPeriod: getReorgPeriod('oortmainnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7'], + [Contexts.ReleaseCandidate]: [''], + [Contexts.Neutron]: [], + }, + 'oortmainnet', + ), + }, + + immutablezkevm: { + interval: 5, + reorgPeriod: getReorgPeriod('immutablezkevm'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xa787c2952a4d22f776ee6e87e828e6f75de24330'], + }, + 'immutablezkevm', + ), + }, + rari: { + interval: 5, + reorgPeriod: getReorgPeriod('rari'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x989d6862e09de21337078efbd86843a3eb1133e3'], + }, + 'rari', + ), + }, + rootstock: { + interval: 5, + reorgPeriod: getReorgPeriod('rootstock'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xcb8e3a72cf427feff27416d0e2ec375a052eaaee'], + }, + 'rootstock', + ), + }, + alephzeroevm: { + interval: 5, + reorgPeriod: getReorgPeriod('alephzeroevm'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xcae8fab142adc4e434bb7409e40dd932cc3851aa'], + }, + 'alephzeroevm', + ), + }, + chiliz: { + interval: 5, + reorgPeriod: getReorgPeriod('chiliz'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x82d024f453b1a3f3f6606226f06b038da27596f3'], + }, + 'chiliz', + ), + }, + lumia: { + interval: 5, + reorgPeriod: getReorgPeriod('lumia'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f'], + }, + 'lumia', + ), + }, + superposition: { + interval: 5, + reorgPeriod: getReorgPeriod('superposition'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x5978d0e6afa9270ddb87cff43a8fa7a763a5dfc4'], + }, + 'superposition', + ), + }, + + metall2: { + interval: 5, + reorgPeriod: getReorgPeriod('metall2'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x1b000e1e1f0a032ed382c6d69a2d58f6fe773c09'], + }, + 'metall2', + ), + }, + polynomial: { + interval: 5, + reorgPeriod: getReorgPeriod('polynomial'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xa63ad0891e921ad5947d57e05831fabb9816eca7'], + }, + 'polynomial', + ), + }, + flow: { + interval: 5, + reorgPeriod: getReorgPeriod('flow'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x3aee1090318e9c54d1d23194dcd0f2bee00ddc97'], + }, + 'flow', + ), + }, + + zeronetwork: { + interval: 5, + reorgPeriod: getReorgPeriod('zeronetwork'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87'], + }, + 'zeronetwork', + ), + }, + zksync: { + interval: 5, + reorgPeriod: getReorgPeriod('zksync'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a'], + }, + 'zksync', + ), + }, + + apechain: { + interval: 5, + reorgPeriod: getReorgPeriod('apechain'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221'], + }, + 'apechain', + ), + }, + arbitrumnova: { + interval: 5, + reorgPeriod: getReorgPeriod('arbitrumnova'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xd2a5e9123308d187383c87053811a2c21bd8af1f'], + }, + 'arbitrumnova', + ), + }, + b3: { + interval: 5, + reorgPeriod: getReorgPeriod('b3'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xd77b516730a836fc41934e7d5864e72c165b934e'], + }, + 'b3', + ), + }, + fantom: { + interval: 5, + reorgPeriod: getReorgPeriod('fantom'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xa779572028e634e16f26af5dfd4fa685f619457d'], + }, + 'fantom', + ), + }, + gravity: { + interval: 5, + reorgPeriod: getReorgPeriod('gravity'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x23d549bf757a02a6f6068e9363196ecd958c974e'], + }, + 'gravity', + ), + }, + harmony: { + interval: 5, + reorgPeriod: getReorgPeriod('harmony'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xd677803a67651974b1c264171b5d7ca8838db8d5'], + }, + 'harmony', + ), + }, + kaia: { + interval: 5, + reorgPeriod: getReorgPeriod('kaia'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x9de0b3abb221d19719882fa4d61f769fdc2be9a4'], + }, + 'kaia', + ), + }, + morph: { + interval: 5, + reorgPeriod: getReorgPeriod('morph'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x4884535f393151ec419add872100d352f71af380'], + }, + 'morph', + ), + }, + orderly: { + interval: 5, + reorgPeriod: getReorgPeriod('orderly'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6'], + }, + 'orderly', + ), + }, + snaxchain: { + interval: 5, + reorgPeriod: getReorgPeriod('snaxchain'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e'], + }, + 'snaxchain', + ), + }, }; }; diff --git a/typescript/infra/config/environments/mainnet3/warp/AMPHRETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/AMPHRETH-deployments.yaml new file mode 100644 index 000000000..222a321c2 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/AMPHRETH-deployments.yaml @@ -0,0 +1,28 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-18T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + arbitrum: + protocolType: ethereum + type: synthetic + hypAddress: '0x6D251aADfc6Ff69031e01eA39bE3cb5BABf8438f' + name: Amphor Restaked ETH + symbol: AMPHRETH + decimals: 18 + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0xdc89990a6fdC1C922b841f1d977835628A24Ed57' + tokenAddress: '0x5fD13359Ba15A84B76f7F87568309040176167cd' + name: Amphor Restaked ETH + symbol: AMPHRETH + decimals: 18 + zircuit: + protocolType: ethereum + type: synthetic + hypAddress: '0x7D5a79539d7B1c9aE5e54d18EEE188840f1Fe4CC' + name: Amphor Restaked ETH + symbol: AMPHRETH + decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml index bc90c54bb..26a0a5807 100644 --- a/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/EZETH-deployments.yaml @@ -9,7 +9,8 @@ data: name: Renzo Restaked ETH symbol: ezETH hypAddress: '0xC59336D8edDa9722B4f1Ec104007191Ec16f7087' - tokenAddress: '0x2416092f143378750bb29b79eD961ab195CcEea5' + tokenAddress: '0xbf5495Efe5DB9ce00f80364C8B423567e58d2110' + tokenCoinGeckoId: renzo-restaked-eth decimals: 18 bsc: protocolType: ethereum diff --git a/typescript/infra/config/environments/mainnet3/warp/STTIA-eclipsestride-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/STTIA-eclipsestride-deployments.yaml new file mode 100644 index 000000000..9938ad9f9 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/STTIA-eclipsestride-deployments.yaml @@ -0,0 +1,23 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-18T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + stride: + protocolType: cosmos + type: collateral + hypAddress: stride134axwdlam929m3mar3wv95nvkyep7mr87ravkqcpf8dfe3v0pjlqwrw6ee + tokenAddress: 'stutia' + name: Stride Staked TIA + symbol: stTIA + decimals: 6 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: 'tKUHyJ5NxhnwU94JUmzh1ekukDcHHX8mZF6fqxbMwX6' + tokenAddress: 'V5m1Cc9VK61mKL8xVYrjR7bjD2BC5VpADLa6ws3G8KM' + isSpl2022: true + name: Turbo Eth + symbol: tETH + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/TIA-eclipsestride-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/TIA-eclipsestride-deployments.yaml new file mode 100644 index 000000000..999e8ea95 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/TIA-eclipsestride-deployments.yaml @@ -0,0 +1,23 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-18T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + stride: + protocolType: cosmos + type: collateral + hypAddress: stride1pvtesu3ve7qn7ctll2x495mrqf2ysp6fws68grvcu6f7n2ajghgsh2jdj6 + tokenAddress: ibc/BF3B4F53F3694B66E13C23107C84B6485BD2B96296BB7EC680EA77BBA75B4801 + name: Celestia + symbol: TIA + decimals: 6 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: 'BpXHAiktwjx7fN6M9ST9wr6qKAsH27wZFhdHEhReJsR6' + tokenAddress: '9RryNMhAVJpAwAGjCAMKbbTFwgjapqPkzpGMfTQhEjf8' + isSpl2022: true + name: Turbo Eth + symbol: tETH + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml index 07be06027..d87b3fa0a 100644 --- a/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/ancient8-USDC-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0x8b4192B9Ad1fCa440A5808641261e5289e6de95D' tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + tokenCoinGeckoId: usd-coin name: USDC symbol: USDC decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml new file mode 100644 index 000000000..d106ac884 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/bsc-lumia-LUMIA-deployments.yaml @@ -0,0 +1,30 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between Ethereum and Binance Smart Chain and Lumia +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-18T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0xdD313D475f8A9d81CBE2eA953a357f52e10BA357' + tokenAddress: '0xd9343a049d5dbd89cd19dc6bca8c48fb3a0a42a7' + tokenCoinGeckoId: lumia + name: Lumia Token + symbol: LUMIA + decimals: 18 + bsc: + protocolType: ethereum + type: synthetic + hypAddress: '0x7F39BcdCa8E0E581c1d43aaa1cB862AA1c8C2047' + name: Lumia Token + symbol: LUMIA + decimals: 18 + lumia: + protocolType: ethereum + type: native + hypAddress: '0x6a77331cE28E47c3Cb9Fea48AB6cD1e9594ce0A9' + name: Lumia Token + symbol: LUMIA + decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts b/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts index d16bb0510..866de41f9 100644 --- a/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts +++ b/typescript/infra/config/environments/mainnet3/warp/checkWarpDeploy.ts @@ -4,7 +4,7 @@ import { environment } from '../chains.js'; export const checkWarpDeployConfig: CheckWarpDeployConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '36f7e14-20240823-160646', + tag: 'main', }, namespace: environment, cronSchedule: '0 15 * * *', // set to 3pm utc every day diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumZircuitAmphrETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumZircuitAmphrETHWarpConfig.ts new file mode 100644 index 000000000..1b81e8110 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getArbitrumEthereumZircuitAmphrETHWarpConfig.ts @@ -0,0 +1,55 @@ +import { ethers } from 'ethers'; + +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +import { tokens } from '../../../../../src/config/warp.js'; + +const arbitrumOwner = '0x008615770B588633265cB01Abd19740fAe67d0B9'; +const ethereumOwner = '0x008615770B588633265cB01Abd19740fAe67d0B9'; +const zircuitOwner = '0xD0673e7F3FB4037CA79F53d2d311D0e017d39963'; + +export const getArbitrumEthereumZircuitAmphrETHWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + const arbitrum: TokenRouterConfig = { + ...routerConfig.arbitrum, + type: TokenType.synthetic, + interchainSecurityModule: ethers.constants.AddressZero, + owner: arbitrumOwner, + ownerOverrides: { + proxyAdmin: arbitrumOwner, + }, + }; + + const ethereum: TokenRouterConfig = { + ...routerConfig.ethereum, + type: TokenType.collateral, + token: tokens.ethereum.amphrETH, + owner: ethereumOwner, + interchainSecurityModule: ethers.constants.AddressZero, + ownerOverrides: { + proxyAdmin: ethereumOwner, + }, + }; + + const zircuit: TokenRouterConfig = { + ...routerConfig.zircuit, + type: TokenType.synthetic, + interchainSecurityModule: ethers.constants.AddressZero, + owner: zircuitOwner, + ownerOverrides: { + proxyAdmin: zircuitOwner, + }, + }; + + return { + arbitrum, + ethereum, + zircuit, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.ts new file mode 100644 index 000000000..eaf935f9b --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.ts @@ -0,0 +1,29 @@ +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +export const getEclipseStrideTiaWarpConfig = async ( + _routerConfig: ChainMap, +): Promise> => { + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const eclipsemainnet: TokenRouterConfig = { + type: TokenType.synthetic, + foreignDeployment: 'BpXHAiktwjx7fN6M9ST9wr6qKAsH27wZFhdHEhReJsR6', + gas: 300_000, + }; + + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const stride: TokenRouterConfig = { + type: TokenType.collateral, + foreignDeployment: + 'stride1pvtesu3ve7qn7ctll2x495mrqf2ysp6fws68grvcu6f7n2ajghgsh2jdj6', + }; + + return { + eclipsemainnet, + stride, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.ts new file mode 100644 index 000000000..aa2cf6a10 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.ts @@ -0,0 +1,29 @@ +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +export const getEclipseStrideStTiaWarpConfig = async ( + _routerConfig: ChainMap, +): Promise> => { + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const eclipsemainnet: TokenRouterConfig = { + type: TokenType.synthetic, + foreignDeployment: 'tKUHyJ5NxhnwU94JUmzh1ekukDcHHX8mZF6fqxbMwX6', + gas: 300_000, + }; + + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const stride: TokenRouterConfig = { + type: TokenType.collateral, + foreignDeployment: + 'stride134axwdlam929m3mar3wv95nvkyep7mr87ravkqcpf8dfe3v0pjlqwrw6ee', + }; + + return { + eclipsemainnet, + stride, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumBscLumiaLUMIAWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumBscLumiaLUMIAWarpConfig.ts new file mode 100644 index 000000000..d063f9a8f --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumBscLumiaLUMIAWarpConfig.ts @@ -0,0 +1,41 @@ +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; +import { objMap } from '@hyperlane-xyz/utils'; + +// Lumia Team +const owner = '0x8bBA07Ddc72455b55530C17e6f6223EF6E156863'; + +export const getEthereumBscLUMIAWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + const ethereum = { + type: TokenType.collateral, + token: '0xD9343a049D5DBd89CD19DC6BcA8c48fB3a0a42a7', + }; + + const bsc = { + type: TokenType.synthetic, + }; + + const lumia = { + type: TokenType.native, + }; + + const configMap = { + ethereum, + bsc, + lumia, + }; + + const merged = objMap(configMap, (chain, config) => ({ + ...routerConfig[chain], + ...config, + owner, + })); + + return merged as ChainMap; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSolanaPzETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumEclipseTETHWarpConfig.ts similarity index 70% rename from typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSolanaPzETHWarpConfig.ts rename to typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumEclipseTETHWarpConfig.ts index 83b937b6e..57ed42891 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSolanaPzETHWarpConfig.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumEclipseTETHWarpConfig.ts @@ -9,13 +9,13 @@ import { import { DEPLOYER } from '../../owners.js'; -export const getEthereumSolanaPzETHWarpConfig = async ( +export const getEthereumEclipseTETHWarpConfig = async ( routerConfig: ChainMap, ): Promise> => { // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig - const solana: TokenRouterConfig = { + const eclipsemainnet: TokenRouterConfig = { type: TokenType.synthetic, - foreignDeployment: 'GiP8GwN1GsscVJvmKSD4muDEihRzZRa9mxnS1Toi64pa', + foreignDeployment: 'BJa3fPvvjKx8gRCWunoSrWBbsmieub37gsGpjp4BfTfW', gas: 300_000, }; @@ -23,12 +23,11 @@ export const getEthereumSolanaPzETHWarpConfig = async ( ...routerConfig.ethereum, type: TokenType.collateral, interchainSecurityModule: ethers.constants.AddressZero, - token: '0x8c9532a60e0e7c6bbd2b2c1303f63ace1c3e9811', - owner: DEPLOYER, + token: '0x19e099B7aEd41FA52718D780dDA74678113C0b32', }; return { - solana, + eclipsemainnet, ethereum, }; }; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumEclipseUSDCWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumEclipseUSDCWarpConfig.ts new file mode 100644 index 000000000..1d39abb12 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumEclipseUSDCWarpConfig.ts @@ -0,0 +1,34 @@ +import { ethers } from 'ethers'; + +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +import { tokens } from '../../../../../src/config/warp.js'; +import { DEPLOYER } from '../../owners.js'; + +export const getEthereumEclipseUSDCWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + // @ts-ignore - foreignDeployment configs don't conform to the TokenRouterConfig + const eclipsemainnet: TokenRouterConfig = { + type: TokenType.synthetic, + foreignDeployment: 'D6k6T3G74ij6atCtBiWBs5TbFa1hFVcrFUSGZHuV7q3Z', + gas: 300_000, + }; + + const ethereum: TokenRouterConfig = { + ...routerConfig.ethereum, + type: TokenType.collateral, + interchainSecurityModule: ethers.constants.AddressZero, + token: tokens.ethereum.USDC, + }; + + return { + eclipsemainnet, + ethereum, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts new file mode 100644 index 000000000..5bd4938d2 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.ts @@ -0,0 +1,47 @@ +import { ethers } from 'ethers'; + +import { + ChainMap, + RouterConfig, + TokenRouterConfig, + TokenType, +} from '@hyperlane-xyz/sdk'; + +import { tokens } from '../../../../../src/config/warp.js'; + +// Elixir +const owner = '0x00000000F51340906F767C6999Fe512b1275955C'; + +export const getEthereumSeiFastUSDWarpConfig = async ( + routerConfig: ChainMap, +): Promise> => { + const sei: TokenRouterConfig = { + ...routerConfig.viction, + type: TokenType.XERC20, + name: 'fastUSD', + symbol: 'fastUSD', + decimals: 18, + token: tokens.sei.fastUSD, + interchainSecurityModule: ethers.constants.AddressZero, + owner, + ownerOverrides: { + proxyAdmin: owner, + }, + }; + + const ethereum: TokenRouterConfig = { + ...routerConfig.ethereum, + type: TokenType.collateral, + token: tokens.ethereum.deUSD, + owner, + interchainSecurityModule: ethers.constants.AddressZero, + ownerOverrides: { + proxyAdmin: owner, + }, + }; + + return { + sei, + ethereum, + }; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConifg.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts similarity index 74% rename from typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConifg.ts rename to typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts index cd182e4c8..7e6e31c6f 100644 --- a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConifg.ts +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.ts @@ -1,20 +1,13 @@ import { ChainMap, IsmType, - RouterConfig, TokenRouterConfig, TokenType, buildAggregationIsmConfigs, } from '@hyperlane-xyz/sdk'; import { symmetricDifference } from '@hyperlane-xyz/utils'; -import { getRegistry } from '../../chains.js'; - -const lockbox = '0xC8140dA31E6bCa19b287cC35531c2212763C2059'; -const xERC20 = '0x2416092f143378750bb29b79eD961ab195CcEea5'; -const lockboxChain = 'ethereum'; -// over the default 100k to account for xerc20 gas + ISM overhead over the default ISM https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/49f41d9759fd515bfd89e6e22e799c41b27b4119/typescript/sdk/src/router/GasRouterDeployer.ts#L14 -const warpRouteOverheadGas = 200_000; +import { getRegistry as getMainnet3Registry } from '../../chains.js'; const chainsToDeploy = [ 'arbitrum', @@ -27,9 +20,29 @@ const chainsToDeploy = [ 'ethereum', 'fraxtal', 'zircuit', + 'taiko', + 'sei', ]; +const lockboxChain = 'ethereum'; +// over the default 100k to account for xerc20 gas + ISM overhead over the default ISM https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/49f41d9759fd515bfd89e6e22e799c41b27b4119/typescript/sdk/src/router/GasRouterDeployer.ts#L14 +const warpRouteOverheadGas = 200_000; +const lockbox = '0xC8140dA31E6bCa19b287cC35531c2212763C2059'; +const xERC20: Record<(typeof chainsToDeploy)[number], string> = { + arbitrum: '0x2416092f143378750bb29b79eD961ab195CcEea5', + optimism: '0x2416092f143378750bb29b79eD961ab195CcEea5', + base: '0x2416092f143378750bb29b79eD961ab195CcEea5', + blast: '0x2416092f143378750bb29b79eD961ab195CcEea5', + bsc: '0x2416092f143378750bb29b79eD961ab195CcEea5', + mode: '0x2416092f143378750bb29b79eD961ab195CcEea5', + linea: '0x2416092f143378750bb29b79eD961ab195CcEea5', + ethereum: '0x2416092f143378750bb29b79eD961ab195CcEea5', + fraxtal: '0x2416092f143378750bb29b79eD961ab195CcEea5', + zircuit: '0x2416092f143378750bb29b79eD961ab195CcEea5', + taiko: '0x2416092f143378750bb29b79eD961ab195CcEea5', + sei: '0x6DCfbF4729890043DFd34A93A2694E5303BA2703', // redEth +}; -const ezEthValidators = { +export const ezEthValidators = { arbitrum: { threshold: 1, validators: [ @@ -100,9 +113,23 @@ const ezEthValidators = { '0x7ac6584c068eb2a72d4db82a7b7cd5ab34044061', // luganodes ], }, + taiko: { + threshold: 1, + validators: [ + '0x2f007c82672f2bb97227d4e3f80ac481bfb40a2a', // luganodes + '0xd4F6000d8e1108bd4998215d51d5dF559BdB43a1', // Renzo + ], + }, + sei: { + threshold: 1, + validators: [ + '0x7a0f4a8672f603e0c12468551db03f3956d10910', // luganodes + '0x952df7f0cb8611573a53dd7cbf29768871d9f8b0', // Renzo + ], + }, }; -const ezEthSafes: Record = { +export const ezEthSafes: Record = { arbitrum: '0x0e60fd361fF5b90088e1782e6b21A7D177d462C5', optimism: '0x8410927C286A38883BC23721e640F31D3E3E79F8', base: '0x8410927C286A38883BC23721e640F31D3E3E79F8', @@ -113,12 +140,14 @@ const ezEthSafes: Record = { ethereum: '0xD1e6626310fD54Eceb5b9a51dA2eC329D6D4B68A', fraxtal: '0x8410927C286A38883BC23721e640F31D3E3E79F8', zircuit: '0x8410927C286A38883BC23721e640F31D3E3E79F8', + taiko: '0x8410927C286A38883BC23721e640F31D3E3E79F8', + sei: '0x0e60fd361fF5b90088e1782e6b21A7D177d462C5', }; export const getRenzoEZETHWarpConfig = async (): Promise< ChainMap > => { - const registry = await getRegistry(); + const registry = await getMainnet3Registry(); const validatorDiff = symmetricDifference( new Set(chainsToDeploy), @@ -128,6 +157,10 @@ export const getRenzoEZETHWarpConfig = async (): Promise< new Set(chainsToDeploy), new Set(Object.keys(ezEthSafes)), ); + const xERC20Diff = symmetricDifference( + new Set(chainsToDeploy), + new Set(Object.keys(xERC20)), + ); if (validatorDiff.size > 0) { throw new Error( `chainsToDeploy !== validatorConfig, diff is ${Array.from( @@ -140,6 +173,13 @@ export const getRenzoEZETHWarpConfig = async (): Promise< `chainsToDeploy !== safeDiff, diff is ${Array.from(safeDiff).join(', ')}`, ); } + if (xERC20Diff.size > 0) { + throw new Error( + `chainsToDeploy !== xERC20Diff, diff is ${Array.from(xERC20Diff).join( + ', ', + )}`, + ); + } const tokenConfig = Object.fromEntries( await Promise.all( @@ -153,7 +193,7 @@ export const getRenzoEZETHWarpConfig = async (): Promise< chain === lockboxChain ? TokenType.XERC20Lockbox : TokenType.XERC20, - token: chain === lockboxChain ? lockbox : xERC20, + token: chain === lockboxChain ? lockbox : xERC20[chain], owner: ezEthSafes[chain], gas: warpRouteOverheadGas, mailbox: (await registry.getChainAddresses(chain))!.mailbox, diff --git a/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHWarpConfig.ts b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHWarpConfig.ts new file mode 100644 index 000000000..b9e9b42bb --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/configGetters/getRenzoPZETHWarpConfig.ts @@ -0,0 +1,104 @@ +import { + ChainMap, + IsmType, + TokenRouterConfig, + TokenType, + buildAggregationIsmConfigs, +} from '@hyperlane-xyz/sdk'; +import { symmetricDifference } from '@hyperlane-xyz/utils'; + +import { getRegistry as getMainnet3Registry } from '../../chains.js'; + +import { ezEthSafes, ezEthValidators } from './getRenzoEZETHWarpConfig.js'; + +const lockbox = '0xbC5511354C4A9a50DE928F56DB01DD327c4e56d5'; +const xERC20 = '0x9cb41CD74D01ae4b4f640EC40f7A60cA1bCF83E7'; +const lockboxChain = 'ethereum'; +// over the default 100k to account for xerc20 gas + ISM overhead over the default ISM https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/49f41d9759fd515bfd89e6e22e799c41b27b4119/typescript/sdk/src/router/GasRouterDeployer.ts#L14 +const warpRouteOverheadGas = 200_000; + +const chainsToDeploy = ['ethereum', 'zircuit']; + +const pzEthValidators = { + ethereum: ezEthValidators.ethereum, + zircuit: ezEthValidators.zircuit, +}; + +const pzEthSafes: Record = { + ethereum: ezEthSafes.ethereum, + zircuit: ezEthSafes.zircuit, +}; + +export const getRenzoPZETHWarpConfig = async (): Promise< + ChainMap +> => { + const registry = await getMainnet3Registry(); + + const validatorDiff = symmetricDifference( + new Set(chainsToDeploy), + new Set(Object.keys(pzEthValidators)), + ); + const safeDiff = symmetricDifference( + new Set(chainsToDeploy), + new Set(Object.keys(pzEthSafes)), + ); + if (validatorDiff.size > 0) { + throw new Error( + `chainsToDeploy !== validatorConfig, diff is ${Array.from( + validatorDiff, + ).join(', ')}`, + ); + } + if (safeDiff.size > 0) { + throw new Error( + `chainsToDeploy !== safeDiff, diff is ${Array.from(safeDiff).join(', ')}`, + ); + } + + const tokenConfig = Object.fromEntries( + await Promise.all( + chainsToDeploy.map( + async (chain): Promise<[string, TokenRouterConfig]> => { + const ret: [string, TokenRouterConfig] = [ + chain, + { + isNft: false, + type: + chain === lockboxChain + ? TokenType.XERC20Lockbox + : TokenType.XERC20, + token: chain === lockboxChain ? lockbox : xERC20, + owner: pzEthSafes[chain], + gas: warpRouteOverheadGas, + mailbox: (await registry.getChainAddresses(chain))!.mailbox, + interchainSecurityModule: { + type: IsmType.AGGREGATION, + threshold: 2, + modules: [ + { + type: IsmType.ROUTING, + owner: pzEthSafes[chain], + domains: buildAggregationIsmConfigs( + chain, + chainsToDeploy, + pzEthValidators, + ), + }, + { + type: IsmType.FALLBACK_ROUTING, + domains: {}, + owner: pzEthSafes[chain], + }, + ], + }, + }, + ]; + + return ret; + }, + ), + ), + ); + + return tokenConfig; +}; diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml new file mode 100644 index 000000000..711e6d428 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-SOL-deployments.yaml @@ -0,0 +1,24 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-09-19T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + solanamainnet: + protocolType: sealevel + type: native + hypAddress: '8DtAGQpcMuD5sG3KdxDy49ydqXUggR1LQtebh2TECbAc' + tokenCoinGeckoId: solana + name: Solana + symbol: SOL + decimals: 9 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: 'FJu4E1BDYKVg7aTWdwATZRUvytJZ8ZZ2gQuvPfMWAz9y' + tokenAddress: 'BeRUj3h7BqkbdfFU7FBNYbodgf8GCHodzKvF9aVjNNfL' + isSpl2022: true + name: Solana + symbol: SOL + decimals: 9 diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml new file mode 100644 index 000000000..7405cb94d --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-USDC-deployments.yaml @@ -0,0 +1,35 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-09-19T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0xe1De9910fe71cC216490AC7FCF019e13a34481D7' + tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + tokenCoinGeckoId: usd-coin + name: USDC + symbol: USDC + decimals: 6 + solanamainnet: + protocolType: sealevel + type: collateral + hypAddress: '3EpVCPUgyjq2MfGeCttyey6bs5zya5wjYZ2BE6yDg6bm' + tokenAddress: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' + tokenCoinGeckoId: usd-coin + isSpl2022: false + name: USDC + symbol: USDC + decimals: 6 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: 'EqRSt9aUDMKYKhzd1DGMderr3KNp29VZH3x5P7LFTC8m' + tokenAddress: 'AKEWE7Bgh87GPp171b4cJPSSZfmZwQ3KaqYqXoKLNAEE' + isSpl2022: true + name: USDC + symbol: USDC + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml new file mode 100644 index 000000000..1b7245c91 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-WIF-deployments.yaml @@ -0,0 +1,25 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-09-19T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + solanamainnet: + protocolType: sealevel + type: collateral + hypAddress: 'CuQmsT4eSF4dYiiGUGYYQxJ7c58pUAD5ADE3BbFGzQKx' + tokenAddress: 'EKpQGSJtjMFqKZ9KQanSqYXRcF8fBopzLHYxdM65zcjm' + tokenCoinGeckoId: dogwifcoin + name: dogwifhat + symbol: WIF + decimals: 9 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: '6tBGmKNxirxBYnm9khkaTtcr2fhJ9YviCgRm1RWM8NJv' + tokenAddress: '841P4tebEgNux2jaWSjCoi9LhrVr9eHGjLc758Va3RPH' + isSpl2022: false + name: dogwifhat + symbol: WIF + decimals: 9 diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-stride-TIA-addresses.json b/typescript/infra/config/environments/mainnet3/warp/eclipse-stride-TIA-addresses.json new file mode 100644 index 000000000..67cf44d89 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-stride-TIA-addresses.json @@ -0,0 +1,8 @@ +{ + "eclipsemainnet": { + "router": "0xa0c167513f4d025217a48891973c3dbe41e10e76230033ef5d676299a18ca7f5" + }, + "stride": { + "router": "stride1pvtesu3ve7qn7ctll2x495mrqf2ysp6fws68grvcu6f7n2ajghgsh2jdj6" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-stride-stTIA-addresses.json b/typescript/infra/config/environments/mainnet3/warp/eclipse-stride-stTIA-addresses.json new file mode 100644 index 000000000..538b9fe9a --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-stride-stTIA-addresses.json @@ -0,0 +1,8 @@ +{ + "eclipsemainnet": { + "router": "0x0d258188d0761163da174da890d0c1becdee51a01dbc9e2a6bfcb342140eb509" + }, + "stride": { + "router": "stride134axwdlam929m3mar3wv95nvkyep7mr87ravkqcpf8dfe3v0pjlqwrw6ee" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/eclipse-tETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/eclipse-tETH-deployments.yaml new file mode 100644 index 000000000..1f77a34e5 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/eclipse-tETH-deployments.yaml @@ -0,0 +1,24 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-09-19T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0xc2495f3183F043627CAECD56dAaa726e3B2D9c09' + tokenAddress: '0x19e099B7aEd41FA52718D780dDA74678113C0b32' + name: Turbo Eth + symbol: tETH + decimals: 18 + eclipsemainnet: + protocolType: sealevel + type: synthetic + hypAddress: '6GM7hy6M6LjhadG1xuKXPQ3jPC1eieEszR8DforoRzUp' + tokenAddress: 'GU7NS9xCwgNPiAdJ69iusFrRfawjDDPjeMBovhV1d4kn' + isSpl2022: false + name: Turbo Eth + symbol: tETH + decimals: 9 diff --git a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml index 0d8ff7755..90f22e0df 100644 --- a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0xED56728fb977b0bBdacf65bCdD5e17Bb7e84504f' tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + tokenCoinGeckoId: usd-coin name: USDC symbol: USDC decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml index 25ec599f7..91976ddd9 100644 --- a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0xab852e67bf03E74C89aF67C4BA97dd1088D3dA19' tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7' # USDT + tokenCoinGeckoId: tether name: Tether USD symbol: USDT decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml index efabaf59d..cd39382ea 100644 --- a/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml @@ -9,6 +9,7 @@ data: protocolType: cosmos type: native hypAddress: inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k + tokenCoinGeckoId: injective-protocol name: Injective Coin symbol: INJ decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml index ae13acf3d..bf4e1770a 100644 --- a/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa tokenAddress: ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7 + tokenCoinGeckoId: celestia name: Celestia symbol: TIA decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/renzo-ezETH-addresses-v3.json b/typescript/infra/config/environments/mainnet3/warp/renzo-ezETH-addresses-v3.json index 97fd3082b..e5c7023b2 100644 --- a/typescript/infra/config/environments/mainnet3/warp/renzo-ezETH-addresses-v3.json +++ b/typescript/infra/config/environments/mainnet3/warp/renzo-ezETH-addresses-v3.json @@ -2,9 +2,6 @@ "arbitrum": { "xERC20": "0xB26bBfC6d1F469C821Ea25099017862e7368F4E8" }, - "optimism": { - "xERC20": "0xacEB607CdF59EB8022Cc0699eEF3eCF246d149e2" - }, "base": { "xERC20": "0x2552516453368e42705D791F674b312b8b87CD9e" }, @@ -14,18 +11,27 @@ "bsc": { "xERC20": "0xE00C6185a5c19219F1FFeD213b4406a254968c26" }, - "mode": { - "xERC20": "0xC59336D8edDa9722B4f1Ec104007191Ec16f7087" - }, - "linea": { - "xERC20": "0xC59336D8edDa9722B4f1Ec104007191Ec16f7087" - }, "ethereum": { "xERC20Lockbox": "0xC59336D8edDa9722B4f1Ec104007191Ec16f7087" }, "fraxtal": { "xERC20": "0x3aE8635A4D581d40a6Edfb3f2ED480f9532994F5" }, + "linea": { + "xERC20": "0xC59336D8edDa9722B4f1Ec104007191Ec16f7087" + }, + "mode": { + "xERC20": "0xC59336D8edDa9722B4f1Ec104007191Ec16f7087" + }, + "optimism": { + "xERC20": "0xacEB607CdF59EB8022Cc0699eEF3eCF246d149e2" + }, + "sei": { + "xERC20": "0xE5163F148C82a0818545d5D34e30BC1EDA870cB9" + }, + "taiko": { + "xERC20": "0x5eAFB1D4b5BDFaFE81715EeBcC7713e418C80E78" + }, "zircuit": { "xERC20": "0x2552516453368e42705D791F674b312b8b87CD9e" } diff --git a/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml new file mode 100644 index 000000000..ed9672e41 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/sei-FASTUSD-deployments.yaml @@ -0,0 +1,22 @@ +description: Hyperlane Warp Route artifacts +timestamp: '2024-10-17T14:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0x9AD81058c6C3Bf552C9014CB30E824717A0ee21b' + tokenAddress: '0x15700B564Ca08D9439C58cA5053166E8317aa138' + tokenCoinGeckoId: elixir-deusd # unique setup where we want deUSD to be deposited as collateral and we want fastUSD to be minted as a synthetic on sei + name: fastUSD + symbol: fastUSD + decimals: 18 + sei: + protocolType: ethereum + type: xERC20 + hypAddress: '0xeA895A7Ff45d8d3857A04c1E38A362f3bd9a076f' + tokenAddress: '0x37a4dD9CED2b19Cfe8FAC251cd727b5787E45269' + name: fastUSD + symbol: fastUSD + decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml index 8923facc0..c3e03b54d 100644 --- a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml @@ -9,6 +9,7 @@ data: protocolType: ethereum type: native hypAddress: '0x15b5D6B614242B118AA404528A7f3E2Ad241e4A4' + tokenCoinGeckoId: ethereum name: Ether symbol: ETH decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml index a278a93e8..8a84e6a7c 100644 --- a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0x31Dca7762930f56D81292f85E65c9D67575804fE' tokenAddress: '0x31Dca7762930f56D81292f85E65c9D67575804fE' # USDC + tokenCoinGeckoId: usd-coin name: USD Coin symbol: USDC decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml index 8ddaa1e08..a22598042 100644 --- a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml @@ -10,6 +10,7 @@ data: type: collateral hypAddress: '0x4221a16A01F61c2b38A03C52d828a7041f6AAA49' tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7' # USDT + tokenCoinGeckoId: tether name: Tether USD symbol: USDT decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/warpIds.ts b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts new file mode 100644 index 000000000..35adc72c6 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/warpIds.ts @@ -0,0 +1,23 @@ +export enum WarpRouteIds { + Ancient8EthereumUSDC = 'USDC/ancient8-ethereum', + ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismSeiTaikoZircuitEZETH = 'EZETH/arbitrum-base-blast-bsc-ethereum-fraxtal-linea-mode-optimism-sei-taiko-zircuit', + ArbitrumEthereumZircuitAMPHRETH = 'AMPHRETH/arbitrum-ethereum-zircuit', + ArbitrumNeutronEclip = 'ECLIP/arbitrum-neutron', + ArbitrumNeutronTIA = 'TIA/arbitrum-neutron', + EclipseSolanaSOL = 'SOL/eclipsemainnet-solanamainnet', + EclipseSolanaWIF = 'WIF/eclipsemainnet-solanamainnet', + EclipseStrideSTTIA = 'stTIA/eclipse-stride', + EclipseStrideTIA = 'TIA/eclipse-stride', + EthereumInevmUSDC = 'USDC/ethereum-inevm', + EthereumInevmUSDT = 'USDT/ethereum-inevm', + EthereumEclipseTETH = 'tETH/eclipsemainnet-ethereum', + EthereumEclipseUSDC = 'USDC/eclipsemainnet-ethereum-solanamainnet', + EthereumSeiFastUSD = 'FASTUSD/ethereum-sei', + EthereumVictionETH = 'ETH/ethereum-viction', + EthereumVictionUSDC = 'USDC/ethereum-viction', + EthereumVictionUSDT = 'USDT/ethereum-viction', + EthereumZircuitPZETH = 'PZETH/ethereum-zircuit', + EthereumBscLumiaLUMIA = 'LUMIA/bsc-ethereum-lumia', + InevmInjectiveINJ = 'INJ/inevm-injective', + MantapacificNeutronTIA = 'TIA/mantapacific-neutron', +} diff --git a/typescript/infra/config/environments/test/gas-oracle.ts b/typescript/infra/config/environments/test/gas-oracle.ts index cacf8f3a3..65c09d9cd 100644 --- a/typescript/infra/config/environments/test/gas-oracle.ts +++ b/typescript/infra/config/environments/test/gas-oracle.ts @@ -3,12 +3,12 @@ import { BigNumber, ethers } from 'ethers'; import { ChainMap, ChainName, + GasPriceConfig, TOKEN_EXCHANGE_RATE_DECIMALS, } from '@hyperlane-xyz/sdk'; import { AllStorageGasOracleConfigs, - GasPriceConfig, getAllStorageGasOracleConfigs, } from '../../../src/config/gas-oracle.js'; diff --git a/typescript/infra/config/environments/test/igp.ts b/typescript/infra/config/environments/test/igp.ts index e63e3de59..0463d9279 100644 --- a/typescript/infra/config/environments/test/igp.ts +++ b/typescript/infra/config/environments/test/igp.ts @@ -17,8 +17,8 @@ export const igp: ChainMap = objMap( exclude(chain, testChainNames).map((remote) => [ remote, multisigIsmVerificationCost( - multisigIsm[remote].threshold, - multisigIsm[remote].validators.length, + multisigIsm[chain].threshold, + multisigIsm[chain].validators.length, ), ]), ); diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 9b4a4bd07..17db9d2ff 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -9,9 +9,13 @@ import { RootAgentConfig, getAgentChainNamesFromConfig, } from '../../../src/config/agent/agent.js'; -import { routerMatchingList } from '../../../src/config/agent/relayer.js'; +import { + BaseRelayerConfig, + routerMatchingList, +} from '../../../src/config/agent/relayer.js'; import { ALL_KEY_ROLES, Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; +import { getDomainId } from '../../registry.js'; import { environment } from './chains.js'; import { helloWorld } from './helloworld.js'; @@ -39,58 +43,94 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig< [Role.Validator]: { alfajores: true, arbitrumsepolia: true, + // arcadiatestnet: true, basesepolia: true, + berabartio: true, bsctestnet: true, + camptestnet: true, + citreatestnet: true, connextsepolia: true, ecotestnet: true, eclipsetestnet: false, + formtestnet: true, fuji: true, holesky: true, + // hyperliquidevmtestnet: false, + odysseytestnet: true, optimismsepolia: true, - plumetestnet: true, + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // plumetestnet: true, polygonamoy: true, scrollsepolia: true, sepolia: true, - solanatestnet: true, + solanatestnet: false, + soneiumtestnet: true, + sonictestnet: true, + suavetoliman: true, superpositiontestnet: true, + unichaintestnet: true, }, [Role.Relayer]: { alfajores: true, arbitrumsepolia: true, + // arcadiatestnet: true, basesepolia: true, + berabartio: true, bsctestnet: true, + camptestnet: true, + citreatestnet: true, connextsepolia: true, ecotestnet: true, eclipsetestnet: false, + formtestnet: true, fuji: true, holesky: true, + // hyperliquidevmtestnet: false, + odysseytestnet: true, optimismsepolia: true, - plumetestnet: true, + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // plumetestnet: true, polygonamoy: true, scrollsepolia: true, sepolia: true, - solanatestnet: true, + solanatestnet: false, + soneiumtestnet: true, + sonictestnet: true, + suavetoliman: true, superpositiontestnet: true, + unichaintestnet: true, }, [Role.Scraper]: { alfajores: true, arbitrumsepolia: true, + // arcadiatestnet: true, basesepolia: true, + berabartio: true, bsctestnet: true, + camptestnet: true, + citreatestnet: true, connextsepolia: false, ecotestnet: true, // Cannot scrape non-EVM chains eclipsetestnet: false, + formtestnet: true, fuji: true, holesky: true, + // hyperliquidevmtestnet: false, + odysseytestnet: true, optimismsepolia: true, - plumetestnet: true, + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // plumetestnet: true, polygonamoy: true, scrollsepolia: true, sepolia: true, // Cannot scrape non-EVM chains solanatestnet: false, + soneiumtestnet: true, + sonictestnet: true, + suavetoliman: true, superpositiontestnet: false, + unichaintestnet: true, }, }; @@ -137,6 +177,29 @@ const scraperResources = { }, }; +const relayBlacklist: BaseRelayerConfig['blacklist'] = [ + { + // In an effort to reduce some giant retry queues that resulted + // from spam txs to the old TestRecipient before we were charging for + // gas, we blacklist the old TestRecipient address. + recipientAddress: '0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE', + }, + // Ignore load testing done by Mitosis from sepolia when they used a different Mailbox on + // arbitrumsepolia and optimismsepolia. + { + originDomain: getDomainId('sepolia'), + senderAddress: '0xb6f4a8dccac0beab1062212f4665879d9937c83c', + destinationDomain: getDomainId('arbitrumsepolia'), + recipientAddress: '0x3da95d8d0b98d7428dc2f864511e2650e34f7087', + }, + { + originDomain: getDomainId('sepolia'), + senderAddress: '0xb6f4a8dccac0beab1062212f4665879d9937c83c', + destinationDomain: getDomainId('optimismsepolia'), + recipientAddress: '0xa49942c908ec50db14652914317518661ec04904', + }, +]; + const hyperlane: RootAgentConfig = { ...contextBase, contextChainNames: hyperlaneContextAgentChainNames, @@ -146,17 +209,9 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd0ce062-20240813-215255', + tag: '463b35b-20241011-161150', }, - blacklist: [ - ...releaseCandidateHelloworldMatchingList, - { - // In an effort to reduce some giant retry queues that resulted - // from spam txs to the old TestRecipient before we were charging for - // gas, we blacklist the old TestRecipient address. - recipientAddress: '0xBC3cFeca7Df5A45d61BC60E7898E63670e1654aE', - }, - ], + blacklist: [...releaseCandidateHelloworldMatchingList, ...relayBlacklist], gasPaymentEnforcement, metricAppContexts: [ { @@ -176,7 +231,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd0ce062-20240813-215255', + tag: '463b35b-20241011-161150', }, chains: validatorChainConfig(Contexts.Hyperlane), resources: validatorResources, @@ -185,7 +240,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd0ce062-20240813-215255', + tag: '463b35b-20241011-161150', }, resources: scraperResources, }, @@ -200,9 +255,10 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '0d12ff3-20240620-173353', + tag: '5a0d68b-20240916-144115', }, whitelist: [...releaseCandidateHelloworldMatchingList], + blacklist: relayBlacklist, gasPaymentEnforcement, transactionGasLimit: 750000, resources: relayerResources, @@ -211,7 +267,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '0d12ff3-20240620-173353', + tag: '73c232b-20240912-124300', }, chains: validatorChainConfig(Contexts.ReleaseCandidate), resources: validatorResources, diff --git a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json index bd44812c5..2bc9d9762 100644 --- a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json @@ -12,6 +12,9 @@ "basesepolia": { "validators": ["0x82e3b437a2944e3ff00258c93e72cd1ba5e0e921"] }, + "berabartio": { + "validators": ["0x541dd3cb282cf869d72883557badae245b63e1fd"] + }, "bsctestnet": { "validators": [ "0x242d8a855a8c932dec51f7999ae7d1e48b10c95e", @@ -19,12 +22,21 @@ "0x1f030345963c54ff8229720dd3a711c15c554aeb" ] }, + "camptestnet": { + "validators": ["0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e"] + }, + "citreatestnet": { + "validators": ["0x60d7380a41eb95c49be18f141efd2fde5e3dba20"] + }, "connextsepolia": { "validators": ["0xffbbec8c499585d80ef69eb613db624d27e089ab"] }, "ecotestnet": { "validators": ["0xb3191420d463c2af8bd9b4a395e100ec5c05915a"] }, + "formtestnet": { + "validators": ["0x72ad7fddf16d17ff902d788441151982fa31a7bc"] + }, "fuji": { "validators": [ "0xd8154f73d04cc7f7f0c332793692e6e6f6b2402e", @@ -35,16 +47,12 @@ "holesky": { "validators": ["0x7ab28ad88bb45867137ea823af88e2cb02359c03"] }, + "odysseytestnet": { + "validators": ["0xcc0a6e2d6aa8560b45b384ced7aa049870b66ea3"] + }, "optimismsepolia": { "validators": ["0x03efe4d0632ee15685d7e8f46dea0a874304aa29"] }, - "plumetestnet": { - "validators": [ - "0xe765a214849f3ecdf00793b97d00422f2d408ea6", - "0xb59998f71efc65190a85ac5e81b66bd72a192a3b", - "0xc906470a73e6b5aad65a4ceb4acd73e3eaf80e2c" - ] - }, "polygonamoy": { "validators": ["0xf0290b06e446b320bd4e9c4a519420354d7ddccd"] }, @@ -62,10 +70,19 @@ "0xd3c75dcf15056012a4d74c483a0c6ea11d8c2b83" ] }, - "solanatestnet": { - "validators": ["0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5"] + "soneiumtestnet": { + "validators": ["0x2e2101020ccdbe76aeda1c27823b0150f43d0c63"] + }, + "sonictestnet": { + "validators": ["0x62e6591d00daec3fb658c3d19403828b4e9ddbb3"] + }, + "suavetoliman": { + "validators": ["0xf58f6e30aabba34e8dd7f79b3168507192e2cc9b"] }, "superpositiontestnet": { "validators": ["0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a"] + }, + "unichaintestnet": { + "validators": ["0x5e99961cf71918308c3b17ef21b5f515a4f86fe5"] } } diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 4d6923f3f..7406081ed 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -1,5 +1,7 @@ +import { IRegistry } from '@hyperlane-xyz/registry'; import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; +import { getRegistryForEnvironment } from '../../../src/config/chain.js'; import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; import { supportedChainNames } from './supportedChainNames.js'; @@ -16,9 +18,12 @@ export const chainMetadataOverrides: ChainMap> = { gasPrice: 8 * 10 ** 9, // 8 gwei }, }, - scrollsepolia: { - transactionOverrides: { - gasPrice: 5 * 10 ** 8, // 0.5 gwei - }, - }, }; + +export const getRegistry = async (useSecrets = true): Promise => + getRegistryForEnvironment( + environment, + supportedChainNames, + chainMetadataOverrides, + useSecrets, + ); diff --git a/typescript/infra/config/environments/testnet4/core/verification.json b/typescript/infra/config/environments/testnet4/core/verification.json index d6947e4e3..f43da291e 100644 --- a/typescript/infra/config/environments/testnet4/core/verification.json +++ b/typescript/infra/config/environments/testnet4/core/verification.json @@ -1104,5 +1104,841 @@ "isProxy": false, "name": "ValidatorAnnounce" } + ], + "berabartio": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000138d4", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000006c13643b3927c57db92c790e4e3e7ee81e13f78c", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "000000000000000000000000ae7a78916ba4c507acb2f0e474ace545ff4bf84100000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841" + }, + { + "name": "ProtocolFee", + "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "citreatestnet": [ + { + "name": "ProxyAdmin", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000013fb", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xB08d78F439e55D02C398519eef61606A5926245F", + "constructorArguments": "00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x33dB966328Ea213b0f76eF96CA368AB37779F065" + }, + { + "name": "PausableIsm", + "address": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", + "constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000783c4a0bb6663359281ad4a637d5af68f83ae213", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "constructorArguments": "00000000000000000000000004438ef7622f5412f82915f59cad4f704c61ea48000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x04438ef7622f5412f82915F59caD4f704C61eA48" + }, + { + "name": "ProtocolFee", + "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f", + "isProxy": false + } + ], + "hyperliquidevmtestnet": [ + { + "name": "ProxyAdmin", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000003e6", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "00000000000000000000000054148470292c24345fb828b003461a94444145170000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x54148470292C24345fb828B003461a9444414517" + }, + { + "name": "PausableIsm", + "address": "0x75f3E2a4f424401195A5E176246Ecc9f7e7680ff", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000001b33611fcc073ab0737011d5512ef673bff74962", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", + "constructorArguments": "00000000000000000000000066b71a4e18fbe09a6977a6520b47feddffa82a1c0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c" + }, + { + "name": "ProtocolFee", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + } + ], + "soneiumtestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000079a", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000d5eb5fa3f470ebbb93a4a58c644c87031268a04a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000e0b988062a0c6492177d64823ab95a9c256c2a5f00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" + }, + { + "name": "ProtocolFee", + "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "formtestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000020726", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000d5eb5fa3f470ebbb93a4a58c644c87031268a04a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000e0b988062a0c6492177d64823ab95a9c256c2a5f00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" + }, + { + "name": "ProtocolFee", + "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "camptestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000004f588", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000d5eb5fa3f470ebbb93a4a58c644c87031268a04a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000e0b988062a0c6492177d64823ab95a9c256c2a5f00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" + }, + { + "name": "ProtocolFee", + "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "suavetoliman": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000201188a", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xD5eB5fa3f470eBBB93a4A58C644c87031268a04A", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000d5eb5fa3f470ebbb93a4a58c644c87031268a04a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000e0b988062a0c6492177d64823ab95a9c256c2a5f00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" + }, + { + "name": "ProtocolFee", + "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "sonictestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000faa5", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000086e902d2f99bcceaa28b31747ec6dc5fd43b1be", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "constructorArguments": "000000000000000000000000628bc518ed1e0e8c6cbcd574eba0ee29e7f6943e00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E" + }, + { + "name": "ProtocolFee", + "address": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "unichaintestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000515", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000086e902d2f99bcceaa28b31747ec6dc5fd43b1be", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "constructorArguments": "000000000000000000000000628bc518ed1e0e8c6cbcd574eba0ee29e7f6943e00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E" + }, + { + "name": "ProtocolFee", + "address": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "arcadiatestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000041786f6e", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000007c5b5bda7f1d1f70a6678abb4d894612fc76498f", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xEa7e618Bee8927fBb2fA20Bc41eE8DEA51838aAD", + "constructorArguments": "000000000000000000000000c76e477437065093d353b7d56c81ff54d167b0ab00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xc76E477437065093D353b7d56c81ff54D167B0Ab" + }, + { + "name": "ProtocolFee", + "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } + ], + "odysseytestnet": [ + { + "name": "ProxyAdmin", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000de9fb", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x589C201a07c26b4725A4A829d772f24423da480B" + }, + { + "name": "PausableIsm", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "MerkleTreeHook", + "address": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000ffa913705484c9baea32ffe9945bea099a1dff72", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x7483faD0Bc297667664A43A064bA7c9911659f57", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "0000000000000000000000007483fad0bc297667664a43a064ba7c9911659f5700000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x7483faD0Bc297667664A43A064bA7c9911659f57" + }, + { + "name": "ProtocolFee", + "address": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + } ] } diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 6ec5340b4..e022357a9 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -10,7 +10,7 @@ export const keyFunderConfig: KeyFunderConfig< > = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '874a58f-20240812-172413', + tag: '463b35b-20241011-161139', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron @@ -28,22 +28,34 @@ export const keyFunderConfig: KeyFunderConfig< desiredBalancePerChain: { alfajores: '5', arbitrumsepolia: '0.1', + // arcadiatestnet: '0.1', basesepolia: '0.1', + berabartio: '0.1', bsctestnet: '5', + camptestnet: '0.1', + citreatestnet: '0.001', connextsepolia: '1', ecotestnet: '0.02', // no funding for solana eclipsetestnet: '0', + formtestnet: '0.1', fuji: '5', holesky: '5', + // hyperliquidevmtestnet: '0.1', + odysseytestnet: '0.1', optimismsepolia: '0.1', - plumetestnet: '0.2', + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // plumetestnet: '0.2', polygonamoy: '0.2', scrollsepolia: '1', sepolia: '5', // no funding for solana solanatestnet: '0', + soneiumtestnet: '0.1', + sonictestnet: '1', + suavetoliman: '0.1', superpositiontestnet: '1', + unichaintestnet: '0.1', }, desiredKathyBalancePerChain: { alfajores: '1', @@ -57,7 +69,8 @@ export const keyFunderConfig: KeyFunderConfig< fuji: '1', holesky: '0', optimismsepolia: '0', - plumetestnet: '0.05', + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // plumetestnet: '0.05', polygonamoy: '0', scrollsepolia: '1', sepolia: '1', @@ -77,7 +90,8 @@ export const keyFunderConfig: KeyFunderConfig< fuji: '1', holesky: '1', optimismsepolia: '0.05', - plumetestnet: '0.1', + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // plumetestnet: '0.1', polygonamoy: '0.1', scrollsepolia: '0.1', sepolia: '1', diff --git a/typescript/infra/config/environments/testnet4/gas-oracle.ts b/typescript/infra/config/environments/testnet4/gas-oracle.ts deleted file mode 100644 index 316400270..000000000 --- a/typescript/infra/config/environments/testnet4/gas-oracle.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { BigNumber, ethers } from 'ethers'; - -import { ChainName, TOKEN_EXCHANGE_RATE_DECIMALS } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; - -import { - AllStorageGasOracleConfigs, - getAllStorageGasOracleConfigs, - getTokenExchangeRateFromValues, -} from '../../../src/config/gas-oracle.js'; - -import { ethereumChainNames } from './chains.js'; -import { testnet4SupportedChainNames } from './supportedChainNames.js'; - -// Taken by looking at each testnet and overestimating gas prices -const gasPrices: Record< - (typeof testnet4SupportedChainNames)[number], - BigNumber -> = { - alfajores: ethers.utils.parseUnits('10', 'gwei'), - arbitrumsepolia: ethers.utils.parseUnits('0.5', 'gwei'), - basesepolia: ethers.utils.parseUnits('0.1', 'gwei'), - bsctestnet: ethers.utils.parseUnits('15', 'gwei'), - connextsepolia: ethers.utils.parseUnits('0.5', 'gwei'), - ecotestnet: ethers.utils.parseUnits('0.001', 'gwei'), - eclipsetestnet: ethers.BigNumber.from('28'), - fuji: ethers.utils.parseUnits('30', 'gwei'), - holesky: ethers.utils.parseUnits('10', 'gwei'), - optimismsepolia: ethers.utils.parseUnits('0.5', 'gwei'), - plumetestnet: ethers.utils.parseUnits('0.01', 'gwei'), - polygonamoy: ethers.utils.parseUnits('100', 'gwei'), - scrollsepolia: ethers.utils.parseUnits('0.5', 'gwei'), - sepolia: ethers.utils.parseUnits('5', 'gwei'), - solanatestnet: ethers.BigNumber.from('28'), - superpositiontestnet: ethers.utils.parseUnits('0.001', 'gwei'), -}; - -// Used to categorize rarity of testnet tokens & approximate exchange rates. -// Unashamedly borrowed from Fortnite -enum Rarity { - Common, - Rare, - Mythic, -} - -// "Value" of the testnet tokens with 10 decimals of precision. -// Imagine these as quoted in USD -const RARITY_APPROXIMATE_VALUE: Record = { - [Rarity.Common]: ethers.utils.parseUnits('0.5', TOKEN_EXCHANGE_RATE_DECIMALS), - [Rarity.Rare]: ethers.utils.parseUnits('1', TOKEN_EXCHANGE_RATE_DECIMALS), - [Rarity.Mythic]: ethers.utils.parseUnits('5', TOKEN_EXCHANGE_RATE_DECIMALS), -}; - -const chainTokenRarity: Record< - (typeof testnet4SupportedChainNames)[number], - Rarity -> = { - alfajores: Rarity.Common, - arbitrumsepolia: Rarity.Common, - basesepolia: Rarity.Common, - bsctestnet: Rarity.Rare, - connextsepolia: Rarity.Common, - ecotestnet: Rarity.Rare, - eclipsetestnet: Rarity.Common, - fuji: Rarity.Rare, - holesky: Rarity.Common, - optimismsepolia: Rarity.Common, - plumetestnet: Rarity.Common, - polygonamoy: Rarity.Rare, - scrollsepolia: Rarity.Rare, - sepolia: Rarity.Mythic, - solanatestnet: Rarity.Common, - superpositiontestnet: Rarity.Common, -}; - -// Gets the "value" of a testnet chain -function getApproximateValue(chain: ChainName): BigNumber { - const rarity = chainTokenRarity[chain as keyof typeof chainTokenRarity]; - return RARITY_APPROXIMATE_VALUE[rarity]; -} - -// Gets the exchange rate of the remote quoted in local tokens -function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber { - const localValue = getApproximateValue(local); - const remoteValue = getApproximateValue(remote); - - return getTokenExchangeRateFromValues(local, localValue, remote, remoteValue); -} - -export const storageGasOracleConfig: AllStorageGasOracleConfigs = - getAllStorageGasOracleConfigs( - ethereumChainNames, - objMap(gasPrices, (_, gasPrice) => ({ - amount: gasPrice.toString(), - decimals: 1, - })), - getTokenExchangeRate, - ); diff --git a/typescript/infra/config/environments/testnet4/gasPrices.json b/typescript/infra/config/environments/testnet4/gasPrices.json new file mode 100644 index 000000000..d3a14a854 --- /dev/null +++ b/typescript/infra/config/environments/testnet4/gasPrices.json @@ -0,0 +1,102 @@ +{ + "alfajores": { + "amount": "25.001", + "decimals": 9 + }, + "arbitrumsepolia": { + "amount": "0.11832", + "decimals": 9 + }, + "arcadiatestnet": { + "amount": "0.000000008", + "decimals": 9 + }, + "basesepolia": { + "amount": "0.418699218", + "decimals": 9 + }, + "berabartio": { + "amount": "0.005844801", + "decimals": 9 + }, + "bsctestnet": { + "amount": "5.0", + "decimals": 9 + }, + "camptestnet": { + "amount": "0.001000253", + "decimals": 9 + }, + "citreatestnet": { + "amount": "1.0", + "decimals": 9 + }, + "connextsepolia": { + "amount": "0.1", + "decimals": 9 + }, + "ecotestnet": { + "amount": "0.001000252", + "decimals": 9 + }, + "eclipsetestnet": { + "amount": "0.001", + "decimals": 9 + }, + "formtestnet": { + "amount": "0.00100005", + "decimals": 9 + }, + "fuji": { + "amount": "25.000000001", + "decimals": 9 + }, + "holesky": { + "amount": "5.827232784", + "decimals": 9 + }, + "odysseytestnet": { + "amount": "1.000000252", + "decimals": 9 + }, + "optimismsepolia": { + "amount": "0.001000268", + "decimals": 9 + }, + "polygonamoy": { + "amount": "112.53", + "decimals": 9 + }, + "scrollsepolia": { + "amount": "0.167584424", + "decimals": 9 + }, + "sepolia": { + "amount": "37.999464941", + "decimals": 9 + }, + "solanatestnet": { + "amount": "0.001", + "decimals": 9 + }, + "soneiumtestnet": { + "amount": "0.001000261", + "decimals": 9 + }, + "sonictestnet": { + "amount": "1.025001", + "decimals": 9 + }, + "suavetoliman": { + "amount": "1.0", + "decimals": 9 + }, + "superpositiontestnet": { + "amount": "0.01", + "decimals": 9 + }, + "unichaintestnet": { + "amount": "0.001000252", + "decimals": 9 + } +} diff --git a/typescript/infra/config/environments/testnet4/igp.ts b/typescript/infra/config/environments/testnet4/igp.ts index a564a3360..0cabe5b69 100644 --- a/typescript/infra/config/environments/testnet4/igp.ts +++ b/typescript/infra/config/environments/testnet4/igp.ts @@ -1,27 +1,43 @@ import { ChainMap, - ChainName, HookType, IgpConfig, - defaultMultisigConfigs, - multisigIsmVerificationCost, + getTokenExchangeRateFromValues, } from '@hyperlane-xyz/sdk'; import { Address, exclude, objMap } from '@hyperlane-xyz/utils'; -import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; +import { + AllStorageGasOracleConfigs, + EXCHANGE_RATE_MARGIN_PCT, + getAllStorageGasOracleConfigs, + getOverhead, +} from '../../../src/config/gas-oracle.js'; +import { mustGetChainNativeToken } from '../../../src/utils/utils.js'; -import { storageGasOracleConfig } from './gas-oracle.js'; +import { ethereumChainNames } from './chains.js'; +import gasPrices from './gasPrices.json'; import { owners } from './owners.js'; import { supportedChainNames } from './supportedChainNames.js'; +import rawTokenPrices from './tokenPrices.json'; + +const tokenPrices: ChainMap = rawTokenPrices; -const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen -const remoteOverhead = (remote: ChainName) => - supportedChainNames.filter(isEthereumProtocolChain).includes(remote as any) - ? multisigIsmVerificationCost( - defaultMultisigConfigs[remote].threshold, - defaultMultisigConfigs[remote].validators.length, - ) - : FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead +export const storageGasOracleConfig: AllStorageGasOracleConfigs = + getAllStorageGasOracleConfigs( + supportedChainNames, + gasPrices, + (local, remote) => + getTokenExchangeRateFromValues({ + local, + remote, + tokenPrices, + exchangeRateMarginPct: EXCHANGE_RATE_MARGIN_PCT, + decimals: { + local: mustGetChainNativeToken(local).decimals, + remote: mustGetChainNativeToken(remote).decimals, + }, + }), + ); export const igp: ChainMap = objMap( owners, @@ -35,7 +51,7 @@ export const igp: ChainMap = objMap( overhead: Object.fromEntries( exclude(chain, supportedChainNames).map((remote) => [ remote, - remoteOverhead(remote), + getOverhead(chain, remote, ethereumChainNames), ]), ), }; diff --git a/typescript/infra/config/environments/testnet4/igp/verification.json b/typescript/infra/config/environments/testnet4/igp/verification.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/typescript/infra/config/environments/testnet4/igp/verification.json @@ -0,0 +1 @@ +{} diff --git a/typescript/infra/config/environments/testnet4/ism/verification.json b/typescript/infra/config/environments/testnet4/ism/verification.json index 72cb4c65a..8fb1bf91e 100644 --- a/typescript/infra/config/environments/testnet4/ism/verification.json +++ b/typescript/infra/config/environments/testnet4/ism/verification.json @@ -103,6 +103,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x09B61c756B3b85BfE871157734e6460d66C8285b", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x5681fbf346EED083FD86b299c150c9701A65FE74", + "constructorArguments": "", + "isProxy": true } ], "arbitrumsepolia": [ @@ -147,6 +159,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xF7FF9b4918484720439928399eeF3584a5f4eEF4", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xb64A0B363326BA2b2cD474987A739A0e3A04Fd8d", + "constructorArguments": "", + "isProxy": true } ], "basesepolia": [ @@ -221,6 +245,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x2ACC0D75aA6cC2829C0ABdA5e2C782E2b4792eB0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x5E9922718b01AD4C82dCFD699ceAC0cF5F89fBBc", + "constructorArguments": "", + "isProxy": true } ], "bsctestnet": [ @@ -327,6 +363,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x9BDeB5c75D786040eB91B29D0cF3aa3E911e47C0", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xcf6C887DaE2b04CC8414D8eb5363EaE8eD964158", + "constructorArguments": "", + "isProxy": true } ], "connextsepolia": [ @@ -401,6 +449,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x385aE4e729d86Ac4983d01597c33dFAa8C35686A", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xE14B55637Cc5aF2A55024D2f281446b388D64874", + "constructorArguments": "", + "isProxy": true } ], "ecotestnet": [ @@ -475,6 +535,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x76743F08b266371dCC7840bD7A445D5A52b3a752", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x1A70D3cbC72eF4941Ee047FA1F1E0469B940EDda", + "constructorArguments": "", + "isProxy": true } ], "fuji": [ @@ -591,6 +663,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xE55d19c15a8EFD8420f6E1b922cbd8C126Bc8582", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x470e03D0E844DC7D3f11cc3f299D2C48cd14Cf3E", + "constructorArguments": "", + "isProxy": true } ], "holesky": [ @@ -665,6 +749,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x6E2B21A88b70cd3Bc455174941753512fB8a981e", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xEFfa6907D9caB3680b1Be2AA2207B0Fb8900E27a", + "constructorArguments": "", + "isProxy": true } ], "moonbasealpha": [ @@ -833,6 +929,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x5E9922718b01AD4C82dCFD699ceAC0cF5F89fBBc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x9e762493C57b1aa7acCd22f67aaD072ADC88CB1e", + "constructorArguments": "", + "isProxy": true } ], "plumetestnet": [ @@ -981,6 +1089,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x5E9922718b01AD4C82dCFD699ceAC0cF5F89fBBc", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x9e762493C57b1aa7acCd22f67aaD072ADC88CB1e", + "constructorArguments": "", + "isProxy": true } ], "scrollsepolia": [ @@ -1067,6 +1187,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x921B24e07f1Ba25f6f7B280DC025De6a135951A4", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xab731b6237B4C4E864dd713949011E32DD19A3a2", + "constructorArguments": "", + "isProxy": true } ], "sepolia": [ @@ -1173,6 +1305,18 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x1c6bD6893a3484D603035E1060ed37eaeC53873f", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x0540D3c8C7Ae44f3Eb70C4cb8eA38eF27248347C", + "constructorArguments": "", + "isProxy": true } ], "superpositiontestnet": [ @@ -1247,6 +1391,1050 @@ "constructorArguments": "", "isProxy": false, "name": "StaticMessageIdWeightedMultisigIsmFactory" + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x689C320286d74406AA9A08170DAbB87aC67FF711", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xA2425897Fe6325162313CA7F0803078b3A20759F", + "constructorArguments": "", + "isProxy": true + } + ], + "mevmdevnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + } + ], + "berabartio": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "citreatestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7", + "constructorArguments": "", + "isProxy": true + } + ], + "hyperliquidevmtestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + } + ], + "camptestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "formtestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "suavetoliman": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "soneiumtestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "sonictestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "unichaintestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "arcadiatestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true + } + ], + "odysseytestnet": [ + { + "name": "StaticMerkleRootMultisigIsmFactory", + "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootMultisigIsm", + "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdMultisigIsmFactory", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdMultisigIsm", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationIsmFactory", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationIsm", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticAggregationHookFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticAggregationHook", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "DomainRoutingIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "DomaingRoutingIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMerkleRootWeightedMultisigIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMerkleRootWeightedMultisigIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "StaticMessageIdWeightedMultisigIsmFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "StaticMessageIdWeightedMultisigIsm", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true } ] } diff --git a/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json b/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json index a37f20f74..efe1a13ea 100644 --- a/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json +++ b/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json @@ -115,5 +115,404 @@ "name": "TransparentUpgradeableProxy" } ], - "sepolia": [] + "sepolia": [], + "berabartio": [ + { + "name": "InterchainAccountIsm", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "constructorArguments": "000000000000000000000000628bc518ed1e0e8c6cbcd574eba0ee29e7f6943e00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a2cf52064c921c11adcd83588cbea08cc3bff5d8000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E" + } + ], + "citreatestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xB5fB1F5410a2c2b7deD462d018541383968cB01c", + "constructorArguments": "000000000000000000000000a3ab7e6ce24e6293bd5320a53329ef2f4de73fca000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffa913705484c9baea32ffe9945bea099a1dff72000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA" + } + ], + "hyperliquidevmtestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", + "constructorArguments": "000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72", + "constructorArguments": "000000000000000000000000a2cf52064c921c11adcd83588cbea08cc3bff5d80000000000000000000000006966b0e55883d49bfb24539356a2f8a673e0203900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c5b5bda7f1d1f70a6678abb4d894612fc76498f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8" + } + ], + "formtestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "constructorArguments": "00000000000000000000000001812d60958798695391dacf092bac4a715b171800000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d356c996277efb7f75ee8bd61b31cc781a12f54f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x01812D60958798695391dacF092BAc4a715B1718" + } + ], + "camptestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "constructorArguments": "00000000000000000000000001812d60958798695391dacf092bac4a715b171800000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d356c996277efb7f75ee8bd61b31cc781a12f54f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x01812D60958798695391dacF092BAc4a715B1718" + } + ], + "suavetoliman": [ + { + "name": "InterchainAccountIsm", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "constructorArguments": "00000000000000000000000001812d60958798695391dacf092bac4a715b171800000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d356c996277efb7f75ee8bd61b31cc781a12f54f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x01812D60958798695391dacF092BAc4a715B1718" + } + ], + "soneiumtestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "constructorArguments": "00000000000000000000000001812d60958798695391dacf092bac4a715b171800000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d356c996277efb7f75ee8bd61b31cc781a12f54f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x01812D60958798695391dacF092BAc4a715B1718" + } + ], + "connextsepolia": [ + { + "name": "InterchainAccountIsm", + "address": "0xA30b2CbC14b97aa55bBC947f4AC6c4254971aFD1", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x0FAc4476d5cE3C057141Ab4df47BFC2ceE2bB259", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xc9ab470A61571ac0c39B7E0923fbEaDdB58d98FE", + "constructorArguments": "0000000000000000000000000fac4476d5ce3c057141ab4df47bfc2cee2bb25900000000000000000000000044b764045bfdc68517e10e783e69b376cef196b200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a30b2cbc14b97aa55bbc947f4ac6c4254971afd1000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x0FAc4476d5cE3C057141Ab4df47BFC2ceE2bB259" + } + ], + "arbitrumsepolia": [ + { + "name": "InterchainAccountIsm", + "address": "0xaec6382e1e16Ee12DBEf0e7EA5ADa51217813Fc3", + "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x52Fbf023eDA2610653daD5ACA0b84356e4979669", + "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x20cC3a33C49fa13627303669edf2DcA7F1E76a50", + "constructorArguments": "00000000000000000000000052fbf023eda2610653dad5aca0b84356e4979669000000000000000000000000666a24f62f7a97ba33c151776eb3d9441a059eb800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000aec6382e1e16ee12dbef0e7ea5ada51217813fc3000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x52Fbf023eDA2610653daD5ACA0b84356e4979669" + } + ], + "superpositiontestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xd09D08a19C6609a1B51e1ca6a055861E7e7A4400", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x56c09458cC7863fff1Cc6Bcb6652Dcc3412FcA86", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3572a9d808738922194921b275B2A55414BcDA57", + "constructorArguments": "00000000000000000000000056c09458cc7863fff1cc6bcb6652dcc3412fca8600000000000000000000000044b764045bfdc68517e10e783e69b376cef196b200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d09d08a19c6609a1b51e1ca6a055861e7e7a4400000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x56c09458cC7863fff1Cc6Bcb6652Dcc3412FcA86" + } + ], + "sonictestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xc08675806BA844467E559E45E4bB59e66778bDcd", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x3ca332A585FDB9d4FF51f2FA8999eA32184D3606", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x39c85C84876479694A2470c0E8075e9d68049aFc", + "constructorArguments": "0000000000000000000000003ca332a585fdb9d4ff51f2fa8999ea32184d360600000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c08675806ba844467e559e45e4bb59e66778bdcd000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x3ca332A585FDB9d4FF51f2FA8999eA32184D3606" + } + ], + "ecotestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0x0De2F539569Fb1e2e3C1d233f7A63a18B9A17110", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x111F4f782B47881898755Bd2F67f12876893300E", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x2C6dD6768E669EDB7b53f26067C1C4534862c3de", + "constructorArguments": "000000000000000000000000111f4f782b47881898755bd2f67f12876893300e00000000000000000000000044b764045bfdc68517e10e783e69b376cef196b200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000de2f539569fb1e2e3c1d233f7a63a18b9a17110000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x111F4f782B47881898755Bd2F67f12876893300E" + } + ], + "optimismsepolia": [ + { + "name": "InterchainAccountIsm", + "address": "0xA7FA26ef3Ea88CD696779735AC9591E01146DA38", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x2188512B68A5FF76431D538a613C0e2b15C7faf4", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", + "constructorArguments": "0000000000000000000000002188512b68a5ff76431d538a613c0e2b15c7faf400000000000000000000000044b764045bfdc68517e10e783e69b376cef196b200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7fa26ef3ea88cd696779735ac9591e01146da38000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x2188512B68A5FF76431D538a613C0e2b15C7faf4" + } + ], + "arcadiatestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x5e65279Fb7293a058776e37587398fcc3E9184b1", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xBF2C366530C1269d531707154948494D3fF4AcA7", + "constructorArguments": "0000000000000000000000005e65279fb7293a058776e37587398fcc3e9184b100000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054bd02f0f20677e9846f8e9fdb1abc7315c49c38000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x5e65279Fb7293a058776e37587398fcc3E9184b1" + } + ], + "basesepolia": [ + { + "name": "InterchainAccountIsm", + "address": "0xDa5177080f7fC5d9255eB32cC64B9b4e5136A716", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x14EE2f01907707Ce8d13C4F5DBC40778b5b664e0", + "constructorArguments": "0000000000000000000000006966b0e55883d49bfb24539356a2f8a673e02039", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xd876C01aB40e8cE42Db417fBC79c726d45504dE4", + "constructorArguments": "00000000000000000000000014ee2f01907707ce8d13c4f5dbc40778b5b664e000000000000000000000000044b764045bfdc68517e10e783e69b376cef196b200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000da5177080f7fc5d9255eb32cc64b9b4e5136a716000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x14EE2f01907707Ce8d13C4F5DBC40778b5b664e0" + } + ], + "unichaintestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0x3ca332A585FDB9d4FF51f2FA8999eA32184D3606", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x4eC139a771eBdD3b0a0b67bb7E08960210882d44", + "constructorArguments": "000000000000000000000000342b5630ba1c1e4d3048e51dad208201af52692c00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ca332a585fdb9d4ff51f2fa8999ea32184d3606000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x342B5630Ba1C1e4d3048E51Dad208201aF52692c" + } + ], + "holesky": [ + { + "name": "InterchainAccountIsm", + "address": "0xb04961F492f447A8bA10f6694Bd888C7619CD2D5", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x81E1b2D45694581A22aa577A2210A3E0b1Af663D", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xD31eD5a3D26c9787Ab607B0c364B21218D0f8F7b", + "constructorArguments": "00000000000000000000000081e1b2d45694581a22aa577a2210a3e0b1af663d00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b04961f492f447a8ba10f6694bd888c7619cd2d5000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x81E1b2D45694581A22aa577A2210A3E0b1Af663D" + } + ], + "polygonamoy": [ + { + "name": "InterchainAccountIsm", + "address": "0xd876C01aB40e8cE42Db417fBC79c726d45504dE4", + "constructorArguments": "00000000000000000000000054148470292c24345fb828b003461a9444414517", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0xF61322936D80cd87B49df48F3DE24fD5c02dE9D1", + "constructorArguments": "00000000000000000000000054148470292c24345fb828b003461a9444414517", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xC60C145f1e1904f9d6483A611BF1416697CCc1FE", + "constructorArguments": "000000000000000000000000f61322936d80cd87b49df48f3de24fd5c02de9d1000000000000000000000000c2e36cd6e32e194ee11f15d9273b64461a4d49a200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d876c01ab40e8ce42db417fbc79c726d45504de4000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0xF61322936D80cd87B49df48F3DE24fD5c02dE9D1" + } + ], + "odysseytestnet": [ + { + "name": "InterchainAccountIsm", + "address": "0xBF2C366530C1269d531707154948494D3fF4AcA7", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "InterchainAccountRouter", + "address": "0x843908541D24d9F6Fa30C8Bb1c39038C947D08fC", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0xBdf49bE2201A1c4B13023F0a407196C6Adb32680", + "constructorArguments": "000000000000000000000000843908541d24d9f6fa30c8bb1c39038c947d08fc00000000000000000000000054148470292c24345fb828b003461a944441451700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000064c0c53b8b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf2c366530c1269d531707154948494d3ff4aca7000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "expectedimplementation": "0x843908541D24d9F6Fa30C8Bb1c39038C947D08fC" + } + ] } diff --git a/typescript/infra/config/environments/testnet4/supportedChainNames.ts b/typescript/infra/config/environments/testnet4/supportedChainNames.ts index 2ffecf7f2..7bd9c5b56 100644 --- a/typescript/infra/config/environments/testnet4/supportedChainNames.ts +++ b/typescript/infra/config/environments/testnet4/supportedChainNames.ts @@ -2,20 +2,33 @@ export const testnet4SupportedChainNames = [ 'alfajores', 'arbitrumsepolia', + // Disabling arcadiatestnet on Oct 29, 2024: chain reset and needs to be redeployed + // 'arcadiatestnet', 'basesepolia', + 'berabartio', 'bsctestnet', + 'camptestnet', + 'citreatestnet', 'connextsepolia', 'ecotestnet', 'eclipsetestnet', + 'formtestnet', 'fuji', 'holesky', + // 'hyperliquidevmtestnet', + 'odysseytestnet', 'optimismsepolia', - 'plumetestnet', + // Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode" + // 'plumetestnet', 'polygonamoy', 'scrollsepolia', 'sepolia', 'solanatestnet', + 'soneiumtestnet', + 'sonictestnet', + 'suavetoliman', 'superpositiontestnet', + 'unichaintestnet', ] as const; export const supportedChainNames = [...testnet4SupportedChainNames]; diff --git a/typescript/infra/config/environments/testnet4/testrecipient/verification.json b/typescript/infra/config/environments/testnet4/testrecipient/verification.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/typescript/infra/config/environments/testnet4/testrecipient/verification.json @@ -0,0 +1 @@ +{} diff --git a/typescript/infra/config/environments/testnet4/tokenPrices.json b/typescript/infra/config/environments/testnet4/tokenPrices.json new file mode 100644 index 000000000..79c1811b2 --- /dev/null +++ b/typescript/infra/config/environments/testnet4/tokenPrices.json @@ -0,0 +1,27 @@ +{ + "alfajores": "10", + "arbitrumsepolia": "10", + "arcadiatestnet": "10", + "basesepolia": "10", + "berabartio": "10", + "bsctestnet": "10", + "camptestnet": "10", + "citreatestnet": "10", + "connextsepolia": "10", + "ecotestnet": "10", + "eclipsetestnet": "10", + "formtestnet": "10", + "fuji": "10", + "holesky": "10", + "odysseytestnet": "10", + "optimismsepolia": "10", + "polygonamoy": "10", + "scrollsepolia": "10", + "sepolia": "10", + "solanatestnet": "10", + "soneiumtestnet": "10", + "sonictestnet": "10", + "suavetoliman": "10", + "superpositiontestnet": "10", + "unichaintestnet": "10" +} diff --git a/typescript/infra/config/environments/testnet4/validators.ts b/typescript/infra/config/environments/testnet4/validators.ts index e65968e60..5db953450 100644 --- a/typescript/infra/config/environments/testnet4/validators.ts +++ b/typescript/infra/config/environments/testnet4/validators.ts @@ -173,72 +173,174 @@ export const validatorChainConfig = ( 'sepolia', ), }, - plumetestnet: { + superpositiontestnet: { + interval: 1, + reorgPeriod: getReorgPeriod('superpositiontestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'superpositiontestnet', + ), + }, + optimismsepolia: { interval: 5, - reorgPeriod: getReorgPeriod('plumetestnet'), + reorgPeriod: getReorgPeriod('optimismsepolia'), validators: validatorsConfig( { - [Contexts.Hyperlane]: [ - '0xe765a214849f3ecdf00793b97d00422f2d408ea6', - '0xb59998f71efc65190a85ac5e81b66bd72a192a3b', - '0xc906470a73e6b5aad65a4ceb4acd73e3eaf80e2c', - ], - [Contexts.ReleaseCandidate]: [ - '0xe6e6aeecbf7755cdbc50c2683df9f2d100f6399d', - '0x27946c13a475233a3b1eb47f0bd0f7cdec3a3983', - '0x2596413213368475c96ddfb1ae26666d22093a8b', - ], + [Contexts.Hyperlane]: ['0x03efe4d0632ee15685d7e8f46dea0a874304aa29'], + [Contexts.ReleaseCandidate]: [], [Contexts.Neutron]: [], }, - 'plumetestnet', + 'optimismsepolia', ), }, - solanatestnet: { - interval: 1, - reorgPeriod: getReorgPeriod('solanatestnet'), + polygonamoy: { + interval: 5, + reorgPeriod: getReorgPeriod('polygonamoy'), validators: validatorsConfig( { - [Contexts.Hyperlane]: ['0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5'], + [Contexts.Hyperlane]: ['0xf0290b06e446b320bd4e9c4a519420354d7ddccd'], [Contexts.ReleaseCandidate]: [], [Contexts.Neutron]: [], }, - 'solanatestnet', + 'polygonamoy', ), }, - superpositiontestnet: { - interval: 1, - reorgPeriod: getReorgPeriod('superpositiontestnet'), + // hyperliquidevmtestnet: { + // interval: 5, + // reorgPeriod: getReorgPeriod('hyperliquidevmtestnet'), + // validators: validatorsConfig( + // { + // [Contexts.Hyperlane]: ['0xea673a92a23ca319b9d85cc16b248645cd5158da'], + // [Contexts.ReleaseCandidate]: [], + // [Contexts.Neutron]: [], + // }, + // 'hyperliquidevmtestnet', + // ), + // }, + berabartio: { + interval: 5, + reorgPeriod: getReorgPeriod('berabartio'), validators: validatorsConfig( { - [Contexts.Hyperlane]: ['0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a'], + [Contexts.Hyperlane]: ['0x541dd3cb282cf869d72883557badae245b63e1fd'], [Contexts.ReleaseCandidate]: [], [Contexts.Neutron]: [], }, - 'superpositiontestnet', + 'berabartio', ), }, - optimismsepolia: { + citreatestnet: { interval: 5, - reorgPeriod: getReorgPeriod('optimismsepolia'), + reorgPeriod: getReorgPeriod('citreatestnet'), validators: validatorsConfig( { - [Contexts.Hyperlane]: ['0x03efe4d0632ee15685d7e8f46dea0a874304aa29'], + [Contexts.Hyperlane]: ['0x60d7380a41eb95c49be18f141efd2fde5e3dba20'], [Contexts.ReleaseCandidate]: [], [Contexts.Neutron]: [], }, - 'optimismsepolia', + 'citreatestnet', ), }, - polygonamoy: { + camptestnet: { interval: 5, - reorgPeriod: getReorgPeriod('polygonamoy'), + reorgPeriod: getReorgPeriod('camptestnet'), validators: validatorsConfig( { - [Contexts.Hyperlane]: ['0xf0290b06e446b320bd4e9c4a519420354d7ddccd'], + [Contexts.Hyperlane]: ['0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e'], [Contexts.ReleaseCandidate]: [], [Contexts.Neutron]: [], }, - 'polygonamoy', + 'camptestnet', + ), + }, + formtestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('formtestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x72ad7fddf16d17ff902d788441151982fa31a7bc'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'formtestnet', + ), + }, + soneiumtestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('soneiumtestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x2e2101020ccdbe76aeda1c27823b0150f43d0c63'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'soneiumtestnet', + ), + }, + suavetoliman: { + interval: 5, + reorgPeriod: getReorgPeriod('suavetoliman'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xf58f6e30aabba34e8dd7f79b3168507192e2cc9b'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'suavetoliman', + ), + }, + + unichaintestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('unichaintestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x5e99961cf71918308c3b17ef21b5f515a4f86fe5'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'unichaintestnet', + ), + }, + sonictestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('sonictestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x62e6591d00daec3fb658c3d19403828b4e9ddbb3'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'sonictestnet', + ), + }, + arcadiatestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('arcadiatestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x7ce5973d3f22971546efb86f5a0417c1248e92f5'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'arcadiatestnet', + ), + }, + + odysseytestnet: { + interval: 5, + reorgPeriod: getReorgPeriod('odysseytestnet'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xcc0a6e2d6aa8560b45b384ced7aa049870b66ea3'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'odysseytestnet', ), }, }; diff --git a/typescript/infra/config/environments/utils.ts b/typescript/infra/config/environments/utils.ts index a9286077b..48830ad61 100644 --- a/typescript/infra/config/environments/utils.ts +++ b/typescript/infra/config/environments/utils.ts @@ -21,34 +21,48 @@ export const s3BucketName = ( ) => `${context}-${environment}-${chainName}-validator-${index}`; /** - * - * @param addresses Validator addresses, provided in order of deployment priority - * only the first `count` addresses will be used - * @param context - * @param environment - * @param chain - * @param count Number of validators to use - * @returns + * Creates a validator base config for a single chain + * @param environment The environment name + * @param context The context + * @param chain The chain name + * @param addresses Validator addresses for the chain + * @returns Array of ValidatorBaseConfig for the chain */ -export const validatorBaseConfigsFn = ( +const createChainValidatorBaseConfigs = ( environment: string, context: Contexts, -): (( - addresses: Record, chain: ChainName, -) => ValidatorBaseConfig[]) => { - return (addresses, chain) => { - return addresses[context].map((address, index) => { - const bucketName = s3BucketName(context, environment, chain, index); - return { - name: bucketName, - address, - checkpointSyncer: { - type: CheckpointSyncerType.S3, - bucket: bucketName, - region: s3BucketRegion, - }, - }; - }); - }; + addresses: string[] = [], +): ValidatorBaseConfig[] => { + return addresses.map((address, index) => { + const bucketName = s3BucketName(context, environment, chain, index); + return { + name: bucketName, + address, + checkpointSyncer: { + type: CheckpointSyncerType.S3, + bucket: bucketName, + region: s3BucketRegion, + }, + }; + }); }; + +/** + * Creates validator base configs for a given context and environment + * @param environment The environment name + * @param context The context + * @returns Function to generate ValidatorBaseConfig[] for a specific chain + */ +export const validatorBaseConfigsFn = + (environment: string, context: Contexts) => + ( + addresses: Partial>, + chain: ChainName, + ): ValidatorBaseConfig[] => + createChainValidatorBaseConfigs( + environment, + context, + chain, + addresses[context], + ); diff --git a/typescript/infra/config/registry.ts b/typescript/infra/config/registry.ts index 50d6dec3d..4b72c9f5b 100644 --- a/typescript/infra/config/registry.ts +++ b/typescript/infra/config/registry.ts @@ -5,6 +5,7 @@ import { ChainAddresses, MergedRegistry, PartialRegistry, + warpConfigToWarpAddresses, } from '@hyperlane-xyz/registry'; import { FileSystemRegistry } from '@hyperlane-xyz/registry/fs'; import { @@ -77,7 +78,7 @@ export function getDomainId(chainName: ChainName): number { return resolveDomainId(chain); } -export function getReorgPeriod(chainName: ChainName): number { +export function getReorgPeriod(chainName: ChainName): string | number { const chain = getChain(chainName); return resolveReorgPeriod(chain); } @@ -90,6 +91,19 @@ export function getChainAddresses(): ChainMap { return getRegistry().getAddresses(); } +export function getWarpAddresses(warpRouteId: string) { + const registry = getRegistry(); + const warpRouteConfig = registry.getWarpRoute(warpRouteId); + + if (!warpRouteConfig) { + throw new Error( + `Warp route config for ${warpRouteId} not found in registry`, + ); + } + + return warpConfigToWarpAddresses(warpRouteConfig); +} + export function getEnvChains(env: DeployEnvironment): ChainName[] { if (env === 'mainnet3') return mainnet3Chains; if (env === 'testnet4') return testnet4Chains; diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index b5ae6f8a0..4e1246ec5 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -9,32 +9,23 @@ import { getHyperlaneCore } from '../scripts/core-utils.js'; import { EnvironmentConfig } from '../src/config/environment.js'; import { getAncient8EthereumUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getAncient8EthereumUSDCWarpConfig.js'; +import { getArbitrumEthereumZircuitAmphrETHWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumEthereumZircuitAmphrETHWarpConfig.js'; import { getArbitrumNeutronEclipWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronEclipWarpConfig.js'; import { getArbitrumNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getArbitrumNeutronTiaWarpConfig.js'; +import { getEclipseStrideTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseStrideSTTIAWarpConfig.js'; +import { getEclipseStrideStTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getEclipseStrideTIAWarpConfig.js'; +import { getEthereumBscLUMIAWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumBscLumiaLUMIAWarpConfig.js'; import { getEthereumInevmUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDCWarpConfig.js'; import { getEthereumInevmUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumInevmUSDTWarpConfig.js'; -import { getEthereumSolanaPzETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumSolanaPzETHWarpConfig.js'; +import { getEthereumSeiFastUSDWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumSeiFastUSDWarpConfig.js'; import { getEthereumVictionETHWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionETHWarpConfig.js'; import { getEthereumVictionUSDCWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDCWarpConfig.js'; import { getEthereumVictionUSDTWarpConfig } from './environments/mainnet3/warp/configGetters/getEthereumVictionUSDTWarpConfig.js'; import { getInevmInjectiveINJWarpConfig } from './environments/mainnet3/warp/configGetters/getInevmInjectiveINJWarpConfig.js'; import { getMantapacificNeutronTiaWarpConfig } from './environments/mainnet3/warp/configGetters/getMantapacificNeutronTiaWarpConfig.js'; -import { getRenzoEZETHWarpConfig } from './environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConifg.js'; - -export enum WarpRouteIds { - Ancient8EthereumUSDC = 'USDC/ancient8-ethereum', - ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismZircuitEZETH = 'EZETH/arbitrum-base-blast-bsc-ethereum-fraxtal-linea-mode-optimism-zircuit', - ArbitrumNeutronEclip = 'ECLIP/arbitrum-neutron', - ArbitrumNeutronTIA = 'TIA/arbitrum-neutron', - EthereumInevmUSDC = 'USDC/ethereum-inevm', - EthereumInevmUSDT = 'USDT/ethereum-inevm', - EthereumSolanaPzETH = 'pzETH/ethereum-solana', - EthereumVictionETH = 'ETH/ethereum-viction', - EthereumVictionUSDC = 'USDC/ethereum-viction', - EthereumVictionUSDT = 'USDT/ethereum-viction', - InevmInjectiveINJ = 'INJ/inevm-injective', - MantapacificNeutronTIA = 'TIA/mantapacific-neutron', -} +import { getRenzoEZETHWarpConfig } from './environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.js'; +import { getRenzoPZETHWarpConfig } from './environments/mainnet3/warp/configGetters/getRenzoPZETHWarpConfig.js'; +import { WarpRouteIds } from './environments/mainnet3/warp/warpIds.js'; type WarpConfigGetterWithConfig = ( routerConfig: ChainMap, @@ -47,18 +38,24 @@ export const warpConfigGetterMap: Record< WarpConfigGetterWithConfig | WarpConfigGetterWithoutConfig > = { [WarpRouteIds.Ancient8EthereumUSDC]: getAncient8EthereumUSDCWarpConfig, + [WarpRouteIds.ArbitrumEthereumZircuitAMPHRETH]: + getArbitrumEthereumZircuitAmphrETHWarpConfig, [WarpRouteIds.EthereumInevmUSDC]: getEthereumInevmUSDCWarpConfig, [WarpRouteIds.EthereumInevmUSDT]: getEthereumInevmUSDTWarpConfig, [WarpRouteIds.ArbitrumNeutronEclip]: getArbitrumNeutronEclipWarpConfig, [WarpRouteIds.ArbitrumNeutronTIA]: getArbitrumNeutronTiaWarpConfig, - [WarpRouteIds.ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismZircuitEZETH]: + [WarpRouteIds.ArbitrumBaseBlastBscEthereumFraxtalLineaModeOptimismSeiTaikoZircuitEZETH]: getRenzoEZETHWarpConfig, [WarpRouteIds.InevmInjectiveINJ]: getInevmInjectiveINJWarpConfig, + [WarpRouteIds.EthereumSeiFastUSD]: getEthereumSeiFastUSDWarpConfig, [WarpRouteIds.EthereumVictionETH]: getEthereumVictionETHWarpConfig, [WarpRouteIds.EthereumVictionUSDC]: getEthereumVictionUSDCWarpConfig, [WarpRouteIds.EthereumVictionUSDT]: getEthereumVictionUSDTWarpConfig, + [WarpRouteIds.EthereumZircuitPZETH]: getRenzoPZETHWarpConfig, + [WarpRouteIds.EthereumBscLumiaLUMIA]: getEthereumBscLUMIAWarpConfig, [WarpRouteIds.MantapacificNeutronTIA]: getMantapacificNeutronTiaWarpConfig, - [WarpRouteIds.EthereumSolanaPzETH]: getEthereumSolanaPzETHWarpConfig, + [WarpRouteIds.EclipseStrideTIA]: getEclipseStrideTiaWarpConfig, + [WarpRouteIds.EclipseStrideSTTIA]: getEclipseStrideStTiaWarpConfig, }; export async function getWarpConfig( @@ -71,7 +68,11 @@ export async function getWarpConfig( const warpConfigGetter = warpConfigGetterMap[warpRouteId]; if (!warpConfigGetter) { - throw new Error(`Unknown warp route: ${warpRouteId}`); + throw new Error( + `Unknown warp route: ${warpRouteId}, must be one of: ${Object.keys( + warpConfigGetterMap, + ).join(', ')}`, + ); } if (warpConfigGetter.length === 1) { diff --git a/typescript/infra/helm/warp-routes/templates/_helpers.tpl b/typescript/infra/helm/warp-routes/templates/_helpers.tpl index 17a9174d5..52bc42a86 100644 --- a/typescript/infra/helm/warp-routes/templates/_helpers.tpl +++ b/typescript/infra/helm/warp-routes/templates/_helpers.tpl @@ -53,6 +53,13 @@ app.kubernetes.io/name: {{ include "hyperlane.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} +{{/* +The name of the ClusterSecretStore +*/}} +{{- define "hyperlane.cluster-secret-store.name" -}} +{{- default "external-secrets-gcp-cluster-secret-store" .Values.externalSecrets.clusterSecretStore }} +{{- end }} + {{/* The warp-routes container */}} @@ -70,4 +77,9 @@ The warp-routes container - "10000" - -f - {{ .Values.configFilePath }} + - -e + - {{ .Values.environment}} + envFrom: + - secretRef: + name: {{ include "hyperlane.fullname" . }}-secret {{- end }} diff --git a/typescript/infra/helm/warp-routes/templates/env-var-external-secret.yaml b/typescript/infra/helm/warp-routes/templates/env-var-external-secret.yaml new file mode 100644 index 000000000..e7a74b714 --- /dev/null +++ b/typescript/infra/helm/warp-routes/templates/env-var-external-secret.yaml @@ -0,0 +1,44 @@ +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "hyperlane.fullname" . }}-external-secret + labels: + {{- include "hyperlane.labels" . | nindent 4 }} +spec: + secretStoreRef: + name: {{ include "hyperlane.cluster-secret-store.name" . }} + kind: ClusterSecretStore + refreshInterval: "1h" + # The secret that will be created + target: + name: {{ include "hyperlane.fullname" . }}-secret + template: + type: Opaque + metadata: + labels: + {{- include "hyperlane.labels" . | nindent 10 }} + annotations: + update-on-redeploy: "{{ now }}" + data: + GCP_SECRET_OVERRIDES_ENABLED: "true" +{{/* + * For each network, create an environment variable with the RPC endpoint. + * The templating of external-secrets will use the data section below to know how + * to replace the correct value in the created secret. + */}} + {{- range .Values.hyperlane.chains }} + GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINTS_{{ . | upper }}: {{ printf "'{{ .%s_rpcs | toString }}'" . }} + {{- end }} + data: + - secretKey: deployer_key + remoteRef: + key: {{ printf "hyperlane-%s-key-deployer" .Values.hyperlane.runEnv }} +{{/* + * For each network, load the secret in GCP secret manager with the form: environment-rpc-endpoint-network, + * and associate it with the secret key networkname_rpc. + */}} + {{- range .Values.hyperlane.chains }} + - secretKey: {{ printf "%s_rpcs" . }} + remoteRef: + key: {{ printf "%s-rpc-endpoints-%s" $.Values.hyperlane.runEnv . }} + {{- end }} diff --git a/typescript/infra/helm/warp-routes/values.yaml b/typescript/infra/helm/warp-routes/values.yaml index c5d7c35e7..a7a09d817 100644 --- a/typescript/infra/helm/warp-routes/values.yaml +++ b/typescript/infra/helm/warp-routes/values.yaml @@ -4,5 +4,8 @@ image: hyperlane: runEnv: mainnet3 context: hyperlane + chains: [] nameOverride: '' fullnameOverride: '' +externalSecrets: + clusterSecretStore: diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 03db2be61..c5fb41ac2 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/infra", "description": "Infrastructure utilities for the Hyperlane Network", - "version": "5.1.0", + "version": "5.6.2", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "5.1.0", - "@hyperlane-xyz/registry": "2.5.0", - "@hyperlane-xyz/sdk": "5.1.0", - "@hyperlane-xyz/utils": "5.1.0", + "@hyperlane-xyz/helloworld": "5.6.2", + "@hyperlane-xyz/registry": "4.10.0", + "@hyperlane-xyz/sdk": "5.6.2", + "@hyperlane-xyz/utils": "5.6.2", "@inquirer/prompts": "^5.3.8", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "1.3.0", @@ -30,7 +30,7 @@ "json-stable-stringify": "^1.1.1", "prom-client": "^14.0.1", "prompts": "^2.4.2", - "yaml": "^2.4.5", + "yaml": "2.4.5", "yargs": "^17.7.2" }, "devDependencies": { @@ -43,7 +43,7 @@ "@types/prompts": "^2.0.14", "@types/sinon-chai": "^3.2.12", "@types/yargs": "^17.0.24", - "chai": "^4.3.6", + "chai": "4.5.0", "ethereum-waffle": "^4.0.10", "ethers": "^5.7.2", "hardhat": "^2.22.2", diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index cec21c424..2d657b6f3 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -497,17 +497,6 @@ export function getAddresses(environment: DeployEnvironment, module: Modules) { } } -export function getWarpAddresses(warpRouteId: string) { - const registry = getRegistry(); - const warpRouteConfig = registry.getWarpRoute(warpRouteId); - - if (!warpRouteConfig) { - throw new Error(`Warp route config for ${warpRouteId} not found`); - } - - return warpConfigToWarpAddresses(warpRouteConfig); -} - export function writeAddresses( environment: DeployEnvironment, module: Modules, @@ -528,7 +517,7 @@ export function writeAddresses( } export function getAgentConfigDirectory() { - return path.join('../../', 'rust', 'config'); + return path.join('../../', 'rust', 'main', 'config'); } export function getAgentConfigJsonPath(environment: AgentEnvironment) { diff --git a/typescript/infra/scripts/agents/deploy-agents.ts b/typescript/infra/scripts/agents/deploy-agents.ts index 98981b0b5..44443dd84 100644 --- a/typescript/infra/scripts/agents/deploy-agents.ts +++ b/typescript/infra/scripts/agents/deploy-agents.ts @@ -1,9 +1,52 @@ +import { confirm } from '@inquirer/prompts'; +import chalk from 'chalk'; +import { execSync } from 'child_process'; + import { createAgentKeysIfNotExists } from '../../src/agents/key-utils.js'; import { HelmCommand } from '../../src/utils/helm.js'; import { getConfigsBasedOnArgs } from '../core-utils.js'; import { AgentCli } from './utils.js'; +async function fetchLatestMain() { + try { + console.log( + chalk.grey.italic('Fetching latest changes from origin/main...'), + ); + execSync('git fetch origin main', { stdio: 'inherit' }); + console.log(chalk.grey.italic('Fetch completed successfully.')); + } catch (error) { + console.error(chalk.red('Error fetching from origin/main:', error)); + process.exit(1); + } +} + +async function getCommitsBehindMain(): Promise { + // Fetch latest changes before checking if current branch is up-to-date + await fetchLatestMain(); + + try { + console.log( + chalk.grey.italic( + 'Checking if current branch is up-to-date with origin/main...', + ), + ); + const execResult = execSync( + 'git rev-list --left-right --count origin/main...HEAD', + ); + + // The output of the git command is something like: + // $ git rev-list --left-right --count origin/main...HEAD + // 0 2 + // We only care about the first number, which is the number of commits behind origin/main. + const [behindCount] = execResult.toString().trim().split('\t'); + return parseInt(behindCount); + } catch (error) { + console.error(chalk.red('Error checking git status:', error)); + process.exit(1); + } +} + async function main() { // Note the create-keys script should be ran prior to running this script. // At the moment, `runAgentHelmCommand` has the side effect of creating keys / users @@ -15,6 +58,29 @@ async function main() { const { agentConfig } = await getConfigsBasedOnArgs(); await createAgentKeysIfNotExists(agentConfig); + // Check if current branch is up-to-date with the main branch + const commitsBehind = await getCommitsBehindMain(); + + // If the current branch is not up-to-date with origin/main, prompt the user to continue + if (commitsBehind > 0) { + const shouldContinue = await confirm({ + message: chalk.yellow.bold( + `Warning: Current branch is ${commitsBehind} commit${ + commitsBehind === 1 ? '' : 's' + } behind origin/main. Are you sure you want to continue?`, + ), + default: false, + }); + if (!shouldContinue) { + console.log(chalk.red.bold('Exiting...')); + process.exit(1); + } + } else { + console.log( + chalk.green.bold('Current branch is up-to-date with origin/main.'), + ); + } + await new AgentCli().runHelmCommand(HelmCommand.InstallOrUpgrade); } diff --git a/typescript/infra/scripts/agents/update-agent-config.ts b/typescript/infra/scripts/agents/update-agent-config.ts index 551be6cda..7044bc436 100644 --- a/typescript/infra/scripts/agents/update-agent-config.ts +++ b/typescript/infra/scripts/agents/update-agent-config.ts @@ -1,24 +1,38 @@ +// eslint-disable-next-line +import fs from 'fs'; + import { ChainAddresses } from '@hyperlane-xyz/registry'; import { + AgentConfig, ChainMap, ChainTechnicalStack, + CoreFactories, + HyperlaneContracts, HyperlaneCore, HyperlaneDeploymentArtifacts, MultiProvider, buildAgentConfig, + getCosmosChainGasPrice, } from '@hyperlane-xyz/sdk'; -import { ProtocolType, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; +import { + ProtocolType, + objFilter, + objMap, + objMerge, + promiseObjAll, +} from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { DeployEnvironment, envNameToAgentEnv, } from '../../src/config/environment.js'; -import { getCosmosChainGasPrice } from '../../src/config/gas-oracle.js'; import { chainIsProtocol, filterRemoteDomainMetadata, - writeMergedJSONAtPath, + isEthereumProtocolChain, + readJSONAtPath, + writeJsonAtPath, } from '../../src/utils/utils.js'; import { Modules, @@ -50,41 +64,53 @@ export async function writeAgentConfig( const addressesForEnv = filterRemoteDomainMetadata(addressesMap); const core = HyperlaneCore.fromAddressesMap(addressesForEnv, multiProvider); + const evmContractsMap = objFilter( + core.contractsMap, + (chain, _): _ is HyperlaneContracts => + isEthereumProtocolChain(chain), + ); + // Write agent config indexing from the deployed Mailbox which stores the block number at deployment const startBlocks = await promiseObjAll( - objMap(addressesForEnv, async (chain: string, _) => { - const { index, technicalStack } = multiProvider.getChainMetadata(chain); - const indexFrom = index?.from; + objMap( + evmContractsMap, + async (chain: string, contracts: HyperlaneContracts) => { + const { index, technicalStack } = multiProvider.getChainMetadata(chain); + const indexFrom = index?.from; - // Arbitrum Nitro chains record the L1 block number they were deployed at, - // not the L2 block number. - // See: https://docs.arbitrum.io/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time#ethereum-block-numbers-within-arbitrum - if (technicalStack === ChainTechnicalStack.ArbitrumNitro && !indexFrom) { - // Should never get here because registry should enforce this, but we're being defensive. - throw new Error( - `index.from is not set for Arbitrum Nitro chain ${chain}`, - ); - } + // Arbitrum Nitro chains record the L1 block number they were deployed at, + // not the L2 block number. + // See: https://docs.arbitrum.io/build-decentralized-apps/arbitrum-vs-ethereum/block-numbers-and-time#ethereum-block-numbers-within-arbitrum + if ( + technicalStack === ChainTechnicalStack.ArbitrumNitro && + !indexFrom + ) { + // Should never get here because registry should enforce this, but we're being defensive. + throw new Error( + `index.from is not set for Arbitrum Nitro chain ${chain}`, + ); + } - // If the index.from is specified in the chain metadata, use that. - if (indexFrom) { - return indexFrom; - } + // If the index.from is specified in the chain metadata, use that. + if (indexFrom) { + return indexFrom; + } - const mailbox = core.getContracts(chain).mailbox; - try { - const deployedBlock = await mailbox.deployedBlock(); - return deployedBlock.toNumber(); - } catch (err) { - console.error( - 'Failed to get deployed block, defaulting to 0. Chain:', - chain, - 'Error:', - err, - ); - return 0; - } - }), + const mailbox = contracts.mailbox; + try { + const deployedBlock = await mailbox.deployedBlock(); + return deployedBlock.toNumber(); + } catch (err) { + console.error( + 'Failed to get deployed block, defaulting to 0. Chain:', + chain, + 'Error:', + err, + ); + return undefined; + } + }, + ), ); // Get gas prices for Cosmos chains. @@ -99,7 +125,7 @@ export async function writeAgentConfig( .map(async (chain) => [ chain, { - gasPrice: await getCosmosChainGasPrice(chain), + gasPrice: await getCosmosChainGasPrice(chain, multiProvider), }, ]), ), @@ -118,10 +144,18 @@ export async function writeAgentConfig( additionalConfig, ); - writeMergedJSONAtPath( - getAgentConfigJsonPath(envNameToAgentEnv[environment]), - agentConfig, - ); + const filepath = getAgentConfigJsonPath(envNameToAgentEnv[environment]); + if (fs.existsSync(filepath)) { + const currentAgentConfig: AgentConfig = readJSONAtPath(filepath); + // Remove transactionOverrides from each chain in the agent config + // To ensure all overrides are configured in infra code or the registry, and not in JSON + for (const chainConfig of Object.values(currentAgentConfig.chains)) { + delete chainConfig.transactionOverrides; + } + writeJsonAtPath(filepath, objMerge(currentAgentConfig, agentConfig)); + } else { + writeJsonAtPath(filepath, agentConfig); + } } main() diff --git a/typescript/infra/scripts/announce-validators.ts b/typescript/infra/scripts/announce-validators.ts index 0d6e47fd1..a4691fe75 100644 --- a/typescript/infra/scripts/announce-validators.ts +++ b/typescript/infra/scripts/announce-validators.ts @@ -1,9 +1,9 @@ -import { assert } from 'console'; import { ethers } from 'ethers'; import { readFileSync } from 'fs'; import * as path from 'path'; import { ChainName } from '@hyperlane-xyz/sdk'; +import { assert } from '@hyperlane-xyz/utils'; import { getChains } from '../config/registry.js'; import { InfraS3Validator } from '../src/agents/aws/validator.js'; @@ -118,7 +118,10 @@ async function main() { const location = announcement.value.storage_location; const announcedLocations = await validatorAnnounce.getAnnouncedStorageLocations([address]); - assert(announcedLocations.length == 1); + assert( + announcedLocations.length == 1, + `Expected 1 announced location, got ${announcedLocations.length}`, + ); const announced = announcedLocations[0].includes(location); if (!announced) { const signature = ethers.utils.joinSignature(announcement.signature); diff --git a/typescript/infra/scripts/check-validator-announce.ts b/typescript/infra/scripts/check-validator-announce.ts deleted file mode 100644 index 28e29ce74..000000000 --- a/typescript/infra/scripts/check-validator-announce.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { defaultMultisigConfigs } from '@hyperlane-xyz/sdk'; -import { eqAddress } from '@hyperlane-xyz/utils'; - -import { isEthereumProtocolChain } from '../src/utils/utils.js'; - -import { getArgs, withChains } from './agent-utils.js'; -import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; - -async function main() { - const { environment, chains } = await withChains(getArgs()).argv; - const config = getEnvironmentConfig(environment); - const { core } = await getHyperlaneCore(environment); - - const targetNetworks = ( - chains && chains.length > 0 ? chains : config.supportedChainNames - ).filter(isEthereumProtocolChain); - - const results = await Promise.all( - targetNetworks.map(async (chain) => { - const validatorAnnounce = core.getContracts(chain).validatorAnnounce; - const announcedValidators = - await validatorAnnounce.getAnnouncedValidators(); - - const validators = defaultMultisigConfigs[chain].validators || []; - const missingValidators = validators.filter( - (validator) => - !announcedValidators.some((x) => eqAddress(x, validator)), - ); - - return { - chain, - status: - missingValidators.length === 0 ? '✅' : missingValidators.join(', '), - }; - }), - ); - - console.table(results); -} - -main().catch(console.error); diff --git a/typescript/infra/scripts/check/check-deploy.ts b/typescript/infra/scripts/check/check-deploy.ts index 19943286e..2121d2582 100644 --- a/typescript/infra/scripts/check/check-deploy.ts +++ b/typescript/infra/scripts/check/check-deploy.ts @@ -10,7 +10,7 @@ async function main() { context, environment, asDeployer, - chain, + chains, fork, govern, warpRouteId, @@ -22,7 +22,7 @@ async function main() { environment, asDeployer, warpRouteId, - chain, + chains, fork, govern, ); @@ -32,13 +32,8 @@ async function main() { if (govern) { await governor.govern(false, fork); } - } else if (chain) { - await governor.checkChain(chain); - if (govern) { - await governor.govern(true, chain); - } } else { - await governor.check(); + await governor.check(chains); if (govern) { await governor.govern(); } diff --git a/typescript/infra/scripts/check/check-owner-ica.ts b/typescript/infra/scripts/check/check-owner-ica.ts new file mode 100644 index 000000000..15d053628 --- /dev/null +++ b/typescript/infra/scripts/check/check-owner-ica.ts @@ -0,0 +1,63 @@ +import { AccountConfig, InterchainAccount } from '@hyperlane-xyz/sdk'; +import { Address, eqAddress } from '@hyperlane-xyz/utils'; + +import { icas } from '../../config/environments/mainnet3/owners.js'; +import { getArgs as getEnvArgs } from '../agent-utils.js'; +import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; + +function getArgs() { + return getEnvArgs().option('ownerChain', { + type: 'string', + description: 'Origin chain where the Safe owner lives', + default: 'ethereum', + }).argv; +} + +async function main() { + const { environment, ownerChain } = await getArgs(); + const config = getEnvironmentConfig(environment); + const multiProvider = await config.getMultiProvider(); + + const owner = config.owners[ownerChain].ownerOverrides?._safeAddress; + if (!owner) { + console.error(`No Safe owner found for ${ownerChain}`); + process.exit(1); + } + + console.log(`Safe owner on ${ownerChain}: ${owner}`); + + const { chainAddresses } = await getHyperlaneCore(environment, multiProvider); + const ica = InterchainAccount.fromAddressesMap(chainAddresses, multiProvider); + + const ownerConfig: AccountConfig = { + origin: ownerChain, + owner: owner, + }; + + const mismatchedResults: Record< + string, + { Expected: Address; Actual: Address } + > = {}; + for (const [chain, expectedAddress] of Object.entries(icas)) { + const actualAccount = await ica.getAccount(chain, ownerConfig); + if (!eqAddress(expectedAddress, actualAccount)) { + mismatchedResults[chain] = { + Expected: expectedAddress, + Actual: actualAccount, + }; + } + } + + if (Object.keys(mismatchedResults).length > 0) { + console.error('\nMismatched ICAs found:'); + console.table(mismatchedResults); + process.exit(1); + } else { + console.log('✅ All ICAs match the expected addresses.'); + } +} + +main().catch((err) => { + console.error('Error:', err); + process.exit(1); +}); diff --git a/typescript/infra/scripts/check-rpc-urls.ts b/typescript/infra/scripts/check/check-rpc-urls.ts similarity index 85% rename from typescript/infra/scripts/check-rpc-urls.ts rename to typescript/infra/scripts/check/check-rpc-urls.ts index 307e4515c..7382a3007 100644 --- a/typescript/infra/scripts/check-rpc-urls.ts +++ b/typescript/infra/scripts/check/check-rpc-urls.ts @@ -2,10 +2,9 @@ import { ethers } from 'ethers'; import { rootLogger } from '@hyperlane-xyz/utils'; -import { getSecretRpcEndpoints } from '../src/agents/index.js'; - -import { getArgs } from './agent-utils.js'; -import { getEnvironmentConfig } from './core-utils.js'; +import { getSecretRpcEndpoints } from '../../src/agents/index.js'; +import { getArgs } from '../agent-utils.js'; +import { getEnvironmentConfig } from '../core-utils.js'; async function main() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/check/check-utils.ts b/typescript/infra/scripts/check/check-utils.ts index 2b2d4d22f..5b6537588 100644 --- a/typescript/infra/scripts/check/check-utils.ts +++ b/typescript/infra/scripts/check/check-utils.ts @@ -22,6 +22,7 @@ import { eqAddress, objFilter } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { DEPLOYER } from '../../config/environments/mainnet3/owners.js'; +import { getWarpAddresses } from '../../config/registry.js'; import { getWarpConfig } from '../../config/warp.js'; import { DeployEnvironment } from '../../src/config/environment.js'; import { HyperlaneAppGovernor } from '../../src/govern/HyperlaneAppGovernor.js'; @@ -35,9 +36,8 @@ import { logViolationDetails } from '../../src/utils/violation.js'; import { Modules, getArgs as getRootArgs, - getWarpAddresses, withAsDeployer, - withChain, + withChains, withContext, withFork, withGovern, @@ -50,7 +50,7 @@ import { getHelloWorldApp } from '../helloworld/utils.js'; export function getCheckBaseArgs() { return withAsDeployer( - withGovern(withChain(withFork(withContext(getRootArgs())))), + withGovern(withChains(withFork(withContext(getRootArgs())))), ); } @@ -68,7 +68,7 @@ export async function getGovernor( environment: DeployEnvironment, asDeployer: boolean, warpRouteId?: string, - chain?: string, + chains?: string[], fork?: string, govern?: boolean, ) { @@ -217,20 +217,24 @@ export async function getGovernor( multiProvider, ); - // log error and return if foreign deployment chain is specifically checked - if ( - (chain && foreignDeployments[chain]) || - (fork && foreignDeployments[fork]) - ) { + // log error and return if requesting check on foreign deployment + const nonEvmChains = chains + ? chains.filter((c) => foreignDeployments[c]) + : fork && foreignDeployments[fork] + ? [fork] + : []; + + if (nonEvmChains.length > 0) { + const chainList = nonEvmChains.join(', '); console.log( - `${ - chain ?? fork - } is non evm and it not compatible with warp checker tooling`, + `${chainList} ${ + nonEvmChains.length > 1 ? 'are' : 'is' + } non-EVM and not compatible with warp checker tooling`, ); throw Error( - `${ - chain ?? fork - } is non evm and it not compatible with warp checker tooling`, + `${chainList} ${ + nonEvmChains.length > 1 ? 'are' : 'is' + } non-EVM and not compatible with warp checker tooling`, ); } diff --git a/typescript/infra/scripts/check/check-validator-announce.ts b/typescript/infra/scripts/check/check-validator-announce.ts new file mode 100644 index 000000000..fec2f2e07 --- /dev/null +++ b/typescript/infra/scripts/check/check-validator-announce.ts @@ -0,0 +1,123 @@ +import { ChainMap, defaultMultisigConfigs } from '@hyperlane-xyz/sdk'; +import { eqAddress } from '@hyperlane-xyz/utils'; + +import { isEthereumProtocolChain } from '../../src/utils/utils.js'; +import { getArgs, withChains } from '../agent-utils.js'; +import { getEnvironmentConfig, getHyperlaneCore } from '../core-utils.js'; + +const minimumValidatorCount = 3; + +const getMinimumThreshold = (validatorCount: number): number => + Math.floor(validatorCount / 2) + 1; + +const thresholdOK = 'threshold OK'; +const totalOK = 'total OK'; + +enum CheckResult { + OK = '✅', + WARNING = '🚨', +} + +async function main() { + const { environment, chains } = await withChains(getArgs()).argv; + const config = getEnvironmentConfig(environment); + const { core } = await getHyperlaneCore(environment); + + const targetNetworks = ( + chains && chains.length > 0 ? chains : config.supportedChainNames + ).filter(isEthereumProtocolChain); + + const chainsWithUnannouncedValidators: ChainMap = {}; + + const results = await Promise.all( + targetNetworks.map(async (chain) => { + const validatorAnnounce = core.getContracts(chain).validatorAnnounce; + const announcedValidators = + await validatorAnnounce.getAnnouncedValidators(); + + const validators = defaultMultisigConfigs[chain].validators || []; + const unannouncedValidators = validators.filter( + (validator) => + !announcedValidators.some((x) => eqAddress(x, validator)), + ); + + if (unannouncedValidators.length > 0) { + chainsWithUnannouncedValidators[chain] = unannouncedValidators; + } + + const validatorCount = validators.length; + const unannouncedValidatorCount = unannouncedValidators.length; + + const threshold = defaultMultisigConfigs[chain].threshold; + const minimumThreshold = getMinimumThreshold(validatorCount); + + return { + chain, + threshold, + [thresholdOK]: + threshold < minimumThreshold || threshold > validatorCount + ? CheckResult.WARNING + : CheckResult.OK, + total: validatorCount, + [totalOK]: + validatorCount < minimumValidatorCount + ? CheckResult.WARNING + : CheckResult.OK, + unannounced: + unannouncedValidatorCount > 0 ? unannouncedValidatorCount : '', + }; + }), + ); + + console.table(results); + + const invalidThresholdChains = results + .filter((r) => r[thresholdOK] === CheckResult.WARNING) + .map((r) => r.chain); + + const lowValidatorCountChains = results + .filter((r) => r[totalOK] === CheckResult.WARNING) + .map((r) => ({ + chain: r.chain, + neededValidators: minimumValidatorCount - r.total, + })); + + if (invalidThresholdChains.length > 0) { + console.log('\n⚠️ Chains with invalid thresholds:'); + invalidThresholdChains.forEach((chain) => { + const validatorCount = defaultMultisigConfigs[chain].validators.length; + const minimumThreshold = getMinimumThreshold(validatorCount); + console.log( + ` - ${chain}:`, + `threshold should be ${minimumThreshold} ≤ t ≤ ${validatorCount}`, + ); + }); + } else { + console.log('\n✅ Thresholds look good!'); + } + + if (lowValidatorCountChains.length > 0) { + console.log('\n⚠️ Chains with low validator counts:'); + lowValidatorCountChains.forEach((c) => { + console.log( + ` - ${c.chain}: needs ${c.neededValidators} more validator${ + c.neededValidators === 1 ? '' : 's' + }`, + ); + }); + } else { + console.log('\n✅ Validator counts look good!'); + } + + const unnanouncedChains = Object.keys(chainsWithUnannouncedValidators); + if (unnanouncedChains.length > 0) { + console.log('\n⚠️ Chains with unannounced validators:'); + unnanouncedChains.forEach((chain) => { + console.log(` - ${chain}: ${chainsWithUnannouncedValidators[chain]}`); + }); + } else { + console.log('\n✅ All validators announced!'); + } +} + +main().catch(console.error); diff --git a/typescript/infra/scripts/check/check-warp-deploy.ts b/typescript/infra/scripts/check/check-warp-deploy.ts index 8082a6347..aa51c7016 100644 --- a/typescript/infra/scripts/check/check-warp-deploy.ts +++ b/typescript/infra/scripts/check/check-warp-deploy.ts @@ -1,7 +1,7 @@ import chalk from 'chalk'; import { Gauge, Registry } from 'prom-client'; -import { WarpRouteIds } from '../../config/warp.js'; +import { warpConfigGetterMap } from '../../config/warp.js'; import { submitMetrics } from '../../src/utils/metrics.js'; import { Modules } from '../agent-utils.js'; @@ -13,7 +13,7 @@ import { } from './check-utils.js'; async function main() { - const { environment, asDeployer, chain, fork, context, pushMetrics } = + const { environment, asDeployer, chains, fork, context, pushMetrics } = await getCheckWarpDeployArgs().argv; const metricsRegister = new Registry(); @@ -25,7 +25,7 @@ async function main() { const failedWarpRoutesChecks: string[] = []; // TODO: consider retrying this if check throws an error - for (const warpRouteId of Object.values(WarpRouteIds)) { + for (const warpRouteId of Object.keys(warpConfigGetterMap)) { console.log(`\nChecking warp route ${warpRouteId}...`); const warpModule = Modules.WARP; @@ -36,7 +36,7 @@ async function main() { environment, asDeployer, warpRouteId, - chain, + chains, fork, ); diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index af41da2f3..2c8d3ff90 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -27,7 +27,7 @@ import { Contexts } from '../config/contexts.js'; import { core as coreConfig } from '../config/environments/mainnet3/core.js'; import { getEnvAddresses } from '../config/registry.js'; import { getWarpConfig } from '../config/warp.js'; -import { deployWithArtifacts } from '../src/deployment/deploy.js'; +import { DeployCache, deployWithArtifacts } from '../src/deployment/deploy.js'; import { TestQuerySenderDeployer } from '../src/deployment/testcontracts/testquerysender.js'; import { extractBuildArtifact, @@ -71,6 +71,10 @@ async function main() { ).argv; const envConfig = getEnvironmentConfig(environment); + // TODO: remove once zksync PR is merged into main + delete envConfig.core.zksync; + delete envConfig.core.zeronetwork; + let multiProvider = await envConfig.getMultiProvider( context, Role.Deployer, @@ -251,7 +255,7 @@ async function main() { const verification = path.join(modulePath, 'verification.json'); - const cache = { + const cache: DeployCache = { verification, read: environment !== 'test', write: !fork, @@ -290,6 +294,9 @@ async function main() { // Use chains if provided, otherwise deploy to all chains // If fork is provided, deploy to fork only targetNetworks: chains && chains.length > 0 ? chains : !fork ? [] : [fork], + module, + multiProvider, + concurrentDeploy, }); } diff --git a/typescript/infra/scripts/funding/deploy-key-funder.ts b/typescript/infra/scripts/funding/deploy-key-funder.ts index 87ac67d7c..265a7e7d2 100644 --- a/typescript/infra/scripts/funding/deploy-key-funder.ts +++ b/typescript/infra/scripts/funding/deploy-key-funder.ts @@ -1,12 +1,11 @@ import { Contexts } from '../../config/contexts.js'; -import { environment } from '../../config/environments/mainnet3/chains.js'; import { KeyFunderHelmManager } from '../../src/funding/key-funder.js'; import { HelmCommand } from '../../src/utils/helm.js'; import { assertCorrectKubeContext } from '../agent-utils.js'; import { getConfigsBasedOnArgs } from '../core-utils.js'; async function main() { - const { agentConfig, envConfig, context } = await getConfigsBasedOnArgs(); + const { agentConfig, envConfig, environment } = await getConfigsBasedOnArgs(); if (agentConfig.context != Contexts.Hyperlane) throw new Error( `Invalid context ${agentConfig.context}, must be ${Contexts.Hyperlane}`, diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index b27bf6904..5eebaab1e 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -1,9 +1,11 @@ import { EthBridger, getL2Network } from '@arbitrum/sdk'; import { CrossChainMessenger } from '@eth-optimism/sdk'; +import { Connection, PublicKey } from '@solana/web3.js'; import { BigNumber, ethers } from 'ethers'; import { Gauge, Registry } from 'prom-client'; import { format } from 'util'; +import { eclipsemainnet } from '@hyperlane-xyz/registry'; import { ChainMap, ChainName, @@ -14,6 +16,7 @@ import { Address, objFilter, objMap, rootLogger } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; import { getEnvAddresses } from '../../config/registry.js'; +import { getSecretRpcEndpoints } from '../../src/agents/index.js'; import { KeyAsAddress, fetchLocalKeyAddresses, @@ -72,7 +75,7 @@ const constMetricLabels = { const metricsRegister = new Registry(); const walletBalanceGauge = new Gauge({ - // Mirror the rust/ethers-prometheus `wallet_balance` gauge metric. + // Mirror the rust/main/ethers-prometheus `wallet_balance` gauge metric. name: 'hyperlane_wallet_balance', help: 'Current balance of eth and other tokens in the `tokens` map for the wallet addresses in the `wallets` set', registers: [metricsRegister], @@ -96,6 +99,58 @@ const MIN_DELTA_DENOMINATOR = ethers.BigNumber.from(10); const RC_FUNDING_DISCOUNT_NUMERATOR = ethers.BigNumber.from(2); const RC_FUNDING_DISCOUNT_DENOMINATOR = ethers.BigNumber.from(10); +interface SealevelAccount { + pubkey: PublicKey; + walletName: string; +} + +const sealevelAccountsToTrack: ChainMap = { + solanamainnet: [ + { + // WIF warp route ATA payer + pubkey: new PublicKey('R5oMfxcbjx4ZYK1B2Aic1weqwt2tQsRzFEGe5WJfAxh'), + walletName: 'WIF/eclipsemainnet-solanamainnet/ata-payer', + }, + { + // USDC warp route ATA payer + pubkey: new PublicKey('A1XtL9mAzkNEpBPinrCpDRrPqVAFjgaxDk4ATFVoQVyc'), + walletName: 'USDC/eclipsemainnet-ethereum-solanamainnet/ata-payer', + }, + ], + eclipsemainnet: [ + { + // WIF warp route ATA payer + pubkey: new PublicKey('HCQAfDd5ytAEidzR9g7CipjEGv2ZrSSZq1UY34oDFv8h'), + walletName: 'WIF/eclipsemainnet-solanamainnet/ata-payer', + }, + { + // USDC warp route ATA payer + pubkey: new PublicKey('7arS1h8nwVVmmTVWSsu9rQ4WjLBN8iAi4DvHi8gWjBNC'), + walletName: 'USDC/eclipsemainnet-ethereum-solanamainnet/ata-payer', + }, + { + // tETH warp route ATA payer + pubkey: new PublicKey('Hyy4jryRxgZm5pvuSx29fXxJ9J55SuDtXiCo89kmNuz5'), + walletName: 'tETH/eclipsemainnet-ethereum/ata-payer', + }, + { + // SOL warp route ATA payer + pubkey: new PublicKey('CijxTbPs9JZxTUfo8Hmz2imxzHtKnDFD3kZP3RPy34uJ'), + walletName: 'SOL/eclipsemainnet-solanamainnet/ata-payer', + }, + { + // stTIA warp route ATA payer + pubkey: new PublicKey('Bg3bAM3gEhdam5mbPqkiMi3mLZkoAieakMRdMHo6mbcn'), + walletName: 'stTIA/eclipsemainnet-stride/ata-payer', + }, + { + // TIA warp route ATA payer + pubkey: new PublicKey('AZs4Rw6H6YwJBKoHBCfChCitHnHvQcVGgrJwGh4bKmAf'), + walletName: 'TIA/eclipsemainnet-stride/ata-payer', + }, + ], +}; + // Funds key addresses for multiple contexts from the deployer key of the context // specified via the `--context` flag. // The --contexts-and-roles flag is used to specify the contexts and the key roles @@ -466,6 +521,13 @@ class ContextFunder { false, ); + if ( + this.environment === 'mainnet3' && + this.context === Contexts.Hyperlane + ) { + await this.updateSolanaWalletBalanceGauge(); + } + return failureOccurred; } @@ -812,6 +874,54 @@ class ContextFunder { ), ); } + + private async updateSolanaWalletBalanceGauge() { + for (const chain of Object.keys(sealevelAccountsToTrack) as ChainName[]) { + await this.updateSealevelWalletBalanceAccounts( + chain, + sealevelAccountsToTrack[chain], + ); + } + } + + private async updateSealevelWalletBalanceAccounts( + chain: ChainName, + accounts: SealevelAccount[], + ) { + const rpcUrls = await getSecretRpcEndpoints(this.environment, chain); + const provider = new Connection(rpcUrls[0], 'confirmed'); + + for (const { pubkey, walletName } of accounts) { + logger.info( + { + chain, + pubkey: pubkey.toString(), + walletName, + }, + 'Fetching sealevel wallet balance', + ); + const balance = await provider.getBalance(pubkey); + logger.info( + { + balance, + chain, + pubkey: pubkey.toString(), + walletName, + }, + 'Retrieved sealevel chain wallet balance', + ); + walletBalanceGauge + .labels({ + chain, + wallet_address: pubkey.toString(), + wallet_name: walletName, + token_symbol: 'Native', + token_name: 'Native', + ...constMetricLabels, + }) + .set(balance / 1e9); + } + } } async function getAddressInfo( diff --git a/typescript/infra/scripts/generate-renzo-warp-route-config.ts b/typescript/infra/scripts/generate-renzo-ezeth-warp-route-config.ts similarity index 85% rename from typescript/infra/scripts/generate-renzo-warp-route-config.ts rename to typescript/infra/scripts/generate-renzo-ezeth-warp-route-config.ts index 5cb917d1b..c8175b0e4 100644 --- a/typescript/infra/scripts/generate-renzo-warp-route-config.ts +++ b/typescript/infra/scripts/generate-renzo-ezeth-warp-route-config.ts @@ -3,7 +3,7 @@ import { stringify as yamlStringify } from 'yaml'; import { WarpRouteDeployConfigSchema } from '@hyperlane-xyz/sdk'; -import { getRenzoEZETHWarpConfig } from '../config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConifg.js'; +import { getRenzoEZETHWarpConfig } from '../config/environments/mainnet3/warp/configGetters/getRenzoEZETHWarpConfig.js'; async function main() { const tokenConfig = await getRenzoEZETHWarpConfig(); @@ -15,7 +15,7 @@ async function main() { } writeFileSync( - 'renzo-warp-route-config.yaml', + 'renzo-ezeth-warp-route-config.yaml', yamlStringify(parsed.data, null, 2), ); } diff --git a/typescript/infra/scripts/generate-renzo-pzeth-warp-route-config.ts b/typescript/infra/scripts/generate-renzo-pzeth-warp-route-config.ts new file mode 100644 index 000000000..b65cca5f0 --- /dev/null +++ b/typescript/infra/scripts/generate-renzo-pzeth-warp-route-config.ts @@ -0,0 +1,23 @@ +import { writeFileSync } from 'fs'; +import { stringify as yamlStringify } from 'yaml'; + +import { WarpRouteDeployConfigSchema } from '@hyperlane-xyz/sdk'; + +import { getRenzoPZETHWarpConfig } from '../config/environments/mainnet3/warp/configGetters/getRenzoPZETHWarpConfig.js'; + +async function main() { + const tokenConfig = await getRenzoPZETHWarpConfig(); + const parsed = WarpRouteDeployConfigSchema.safeParse(tokenConfig); + + if (!parsed.success) { + console.dir(parsed.error.format(), { depth: null }); + return; + } + + writeFileSync( + 'renzo-pzeth-warp-route-config.yaml', + yamlStringify(parsed.data, null, 2), + ); +} + +main().catch(console.error).then(console.log); diff --git a/typescript/infra/scripts/get-owner-ica.ts b/typescript/infra/scripts/get-owner-ica.ts index e4ea61a7b..91c7ec9a8 100644 --- a/typescript/infra/scripts/get-owner-ica.ts +++ b/typescript/infra/scripts/get-owner-ica.ts @@ -1,11 +1,13 @@ import { AccountConfig, InterchainAccount } from '@hyperlane-xyz/sdk'; -import { Address, assert, eqAddress } from '@hyperlane-xyz/utils'; +import { Address, eqAddress } from '@hyperlane-xyz/utils'; -import { getArgs as getEnvArgs, withChainsRequired } from './agent-utils.js'; +import { isEthereumProtocolChain } from '../src/utils/utils.js'; + +import { getArgs as getEnvArgs, withChains } from './agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; function getArgs() { - return withChainsRequired(getEnvArgs()) + return withChains(getEnvArgs()) .option('ownerChain', { type: 'string', description: 'Origin chain where the governing owner lives', @@ -51,20 +53,47 @@ async function main() { owner: originOwner, }; + const getOwnerIcaChains = ( + chains?.length ? chains : config.supportedChainNames + ).filter(isEthereumProtocolChain); + const results: Record = {}; - for (const chain of chains) { - const account = await ica.getAccount(chain, ownerConfig); - results[chain] = { ICA: account }; + const settledResults = await Promise.allSettled( + getOwnerIcaChains.map(async (chain) => { + try { + const account = await ica.getAccount(chain, ownerConfig); + const result: { ICA: Address; Deployed?: string } = { ICA: account }; + + if (deploy) { + const deployedAccount = await ica.deployAccount(chain, ownerConfig); + result.Deployed = eqAddress(account, deployedAccount) ? '✅' : '❌'; + if (result.Deployed === '❌') { + console.warn( + `Mismatch between account and deployed account for ${chain}`, + ); + } + } - if (deploy) { - const deployedAccount = await ica.deployAccount(chain, ownerConfig); - assert( - eqAddress(account, deployedAccount), - 'Fatal mismatch between account and deployed account', - ); - results[chain].Deployed = '✅'; + return { chain, result }; + } catch (error) { + console.error(`Error processing chain ${chain}:`, error); + return { chain, error }; + } + }), + ); + + settledResults.forEach((settledResult) => { + if (settledResult.status === 'fulfilled') { + const { chain, result, error } = settledResult.value; + if (error || !result) { + console.error(`Failed to process ${chain}:`, error); + } else { + results[chain] = result; + } + } else { + console.error(`Promise rejected:`, settledResult.reason); } - } + }); console.table(results); } diff --git a/typescript/infra/scripts/get-typical-remote-gas-amounts.ts b/typescript/infra/scripts/get-typical-remote-gas-amounts.ts new file mode 100644 index 000000000..5f190a72d --- /dev/null +++ b/typescript/infra/scripts/get-typical-remote-gas-amounts.ts @@ -0,0 +1,49 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { stringifyObject } from '@hyperlane-xyz/utils'; + +import { getOverheadWithOverrides } from '../config/environments/mainnet3/igp.js'; +import { getTypicalRemoteGasAmount } from '../src/config/gas-oracle.js'; + +import { getArgs } from './agent-utils.js'; +import { getEnvironmentConfig } from './core-utils.js'; + +// This script exists to print the typical local -> remote gas amounts for a given environment. +// This is useful for Jake to use in his own models for assessing message costs. + +async function main() { + const args = await getArgs().argv; + + if (args.environment !== 'mainnet3') { + throw new Error('This script only supports the mainnet3 environment'); + } + + const environmentConfig = getEnvironmentConfig(args.environment); + + // Local -> Remote -> Amount of gas. + // Local is important because depending on the validator threshold, the cost + // to verify changes. Remote is important because the cost to execute the + // message can change depending on the chain (e.g. alt VMs, or some exceptions like Moonbeam + // that has non-standard EVM gas usage). + const amounts: ChainMap> = {}; + + for (const local of environmentConfig.supportedChainNames) { + for (const remote of environmentConfig.supportedChainNames) { + if (local === remote) { + continue; + } + amounts[local] = amounts[local] || {}; + amounts[local][remote] = getTypicalRemoteGasAmount( + local, + remote, + getOverheadWithOverrides, + ); + } + } + + console.log(stringifyObject(amounts, 'json', 2)); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/typescript/infra/scripts/list-validator-checkpoint-indices.ts b/typescript/infra/scripts/list-validator-checkpoint-indices.ts index f3cfc6c1c..957719725 100644 --- a/typescript/infra/scripts/list-validator-checkpoint-indices.ts +++ b/typescript/infra/scripts/list-validator-checkpoint-indices.ts @@ -39,11 +39,12 @@ async function main() { chain, identifier, index, + validator, }; }, ); - console.table(indices, ['chain', 'index', 'identifier']); + console.table(indices, ['chain', 'index', 'identifier', 'validator']); } main().catch(console.error); diff --git a/typescript/infra/scripts/print-balances.ts b/typescript/infra/scripts/print-balances.ts index f46ca4634..b528be05d 100644 --- a/typescript/infra/scripts/print-balances.ts +++ b/typescript/infra/scripts/print-balances.ts @@ -39,42 +39,68 @@ async function main() { const balancesObject = await Promise.all( chainsToCheck.map(async (chain) => { - const provider = multiProvider.getProvider(chain); - const { decimals, symbol } = await multiProvider.getNativeToken(chain); - const roleBalances = await Promise.all( - roles.map(async (role) => { - // Fetch key - const keys = await envConfig.getKeys(context, role as Role); - await Promise.all(Object.values(keys).map((key) => key.fetch())); + try { + const provider = multiProvider.getProvider(chain); + const { decimals, symbol } = await multiProvider.getNativeToken(chain); + const roleBalances = await Promise.all( + roles.map(async (role) => { + try { + let address: string | undefined; - // Default to known deployer/relayer addresses if not found - let address = keys[chain]?.address; - if (!address) { - if (role === Role.Deployer) { - address = - environment === 'mainnet3' ? MainnetDeployer : TestnetDeployer; - } else if (role === Role.Relayer) { - address = - environment === 'mainnet3' ? MainnetRelayer : TestnetRelayer; - } - } + if ( + role === Role.Deployer && + (environment === 'mainnet3' || environment === 'testnet4') + ) { + address = + environment === 'mainnet3' + ? MainnetDeployer + : TestnetDeployer; + } else if ( + role === Role.Relayer && + (environment === 'mainnet3' || environment === 'testnet4') + ) { + address = + environment === 'mainnet3' ? MainnetRelayer : TestnetRelayer; + } else { + // Fetch key only if role is not deployer/relayer or env is not mainnet/testnet + const keys = await envConfig.getKeys(context, role as Role); + await Promise.all( + Object.values(keys).map((key) => key.fetch()), + ); + address = keys[chain]?.address; + } - // Fetch balance - if (address) { - const balance = await provider.getBalance(address); - const formattedBalance = formatUnits(balance, decimals); - return Number(formattedBalance).toFixed(3); - } - return null; - }), - ); - return { - chain, - symbol, - ...Object.fromEntries( - roles.map((role, index) => [role, roleBalances[index]]), - ), - }; + // Fetch balance + if (address) { + const balance = await provider.getBalance(address); + const formattedBalance = formatUnits(balance, decimals); + return Number(formattedBalance).toFixed(3); + } + return null; + } catch (error) { + console.error( + `Error fetching balance for ${role} on ${chain}:`, + error, + ); + return null; + } + }), + ); + return { + chain, + symbol, + ...Object.fromEntries( + roles.map((role, index) => [role, roleBalances[index]]), + ), + }; + } catch (error) { + console.error(`Error processing chain ${chain}:`, error); + return { + chain, + symbol: 'ERROR', + ...Object.fromEntries(roles.map((role) => [role, null])), + }; + } }), ); diff --git a/typescript/infra/scripts/print-gas-prices.ts b/typescript/infra/scripts/print-gas-prices.ts index 4f94dd26a..39eca69ee 100644 --- a/typescript/infra/scripts/print-gas-prices.ts +++ b/typescript/infra/scripts/print-gas-prices.ts @@ -1,28 +1,52 @@ import { Provider } from '@ethersproject/providers'; import { ethers } from 'ethers'; -import { ChainMap, MultiProtocolProvider } from '@hyperlane-xyz/sdk'; +import { + ChainMap, + GasPriceConfig, + MultiProtocolProvider, + getCosmosChainGasPrice, +} from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; // Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')` // to avoid circular dependencies. import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js'; +import mainnet3GasPrices from '../config/environments/mainnet3/gasPrices.json' assert { type: 'json' }; import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; -import { - GasPriceConfig, - getCosmosChainGasPrice, -} from '../src/config/gas-oracle.js'; +import { getRegistry as getTestnet4Registry } from '../config/environments/testnet4/chains.js'; +import testnet4GasPrices from '../config/environments/testnet4/gasPrices.json' assert { type: 'json' }; +import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/supportedChainNames.js'; + +import { getArgs } from './agent-utils.js'; async function main() { - const registry = await getMainnet3Registry(); + const { environment } = await getArgs().argv; + const { registry, supportedChainNames, gasPrices } = + environment === 'mainnet3' + ? { + registry: await getMainnet3Registry(), + supportedChainNames: mainnet3SupportedChainNames, + gasPrices: mainnet3GasPrices, + } + : { + registry: await getTestnet4Registry(), + supportedChainNames: testnet4SupportedChainNames, + gasPrices: testnet4GasPrices, + }; + const chainMetadata = await registry.getMetadata(); const mpp = new MultiProtocolProvider(chainMetadata); const prices: ChainMap = Object.fromEntries( await Promise.all( - mainnet3SupportedChainNames.map(async (chain) => [ + supportedChainNames.map(async (chain) => [ chain, - await getGasPrice(mpp, chain), + await getGasPrice( + mpp, + chain, + gasPrices[chain as keyof typeof gasPrices], + ), ]), ), ); @@ -33,6 +57,7 @@ async function main() { async function getGasPrice( mpp: MultiProtocolProvider, chain: string, + currentGasPrice?: GasPriceConfig, ): Promise { const protocolType = mpp.getProtocol(chain); switch (protocolType) { @@ -45,19 +70,21 @@ async function getGasPrice( }; } case ProtocolType.Cosmos: { - const { amount } = await getCosmosChainGasPrice(chain); - + const { amount } = await getCosmosChainGasPrice(chain, mpp); return { amount, decimals: 1, }; } case ProtocolType.Sealevel: + // Return the gas price from the config if it exists, otherwise return some default // TODO get a reasonable value - return { - amount: '0.001', - decimals: 9, - }; + return ( + currentGasPrice ?? { + amount: 'PLEASE SET A GAS PRICE FOR SEALEVEL', + decimals: 1, + } + ); default: throw new Error(`Unsupported protocol type: ${protocolType}`); } diff --git a/typescript/infra/scripts/print-mailbox-owner.ts b/typescript/infra/scripts/print-mailbox-owner.ts new file mode 100644 index 000000000..88b04598d --- /dev/null +++ b/typescript/infra/scripts/print-mailbox-owner.ts @@ -0,0 +1,139 @@ +import { ethers } from 'ethers'; + +import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { + ChainMap, + CoreFactories, + HyperlaneContracts, + HyperlaneCore, +} from '@hyperlane-xyz/sdk'; +import { + Address, + eqAddressEvm, + objFilter, + objMap, + promiseObjAll, +} from '@hyperlane-xyz/utils'; + +import { Contexts } from '../config/contexts.js'; +import { DeployEnvironment } from '../src/config/environment.js'; +import { Role } from '../src/roles.js'; +import { + filterRemoteDomainMetadata, + isEthereumProtocolChain, +} from '../src/utils/utils.js'; + +import { + Modules, + getAddresses, + getArgs, + withAgentRoles, + withChains, + withContext, +} from './agent-utils.js'; +import { getEnvironmentConfig } from './core-utils.js'; + +const DEPLOYERS: Record = { + mainnet3: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', + testnet4: '0xfaD1C94469700833717Fa8a3017278BC1cA8031C', + test: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', +}; + +export enum Owner { + ICA = 'ICA', + SAFE = 'SAFE', + DEPLOYER = 'DEPLOYER KEY', + UNKNOWN = 'UNKNOWN', +} + +async function main() { + const { + context = Contexts.Hyperlane, + environment, + chains, + } = await withContext(withChains(withAgentRoles(getArgs()))).argv; + + const envConfig = getEnvironmentConfig(environment); + const chainsToCheck = ( + chains?.length ? chains : envConfig.supportedChainNames + ).filter(isEthereumProtocolChain); + + const multiProvider = await envConfig.getMultiProvider( + context, + Role.Deployer, + true, + chainsToCheck, + ); + + // Get the addresses for the environment + const addressesMap = getAddresses( + environment, + Modules.CORE, + ) as ChainMap; + + const addressesForEnv = filterRemoteDomainMetadata(addressesMap); + const core = HyperlaneCore.fromAddressesMap(addressesForEnv, multiProvider); + + const evmContractsMap = objFilter( + core.contractsMap, + (chain, _): _ is HyperlaneContracts => + isEthereumProtocolChain(chain), + ); + + const deployer = DEPLOYERS[environment]; + + const mailboxOwners = await promiseObjAll( + objMap( + evmContractsMap, + async (chain: string, contracts: HyperlaneContracts) => { + // get possible owners from config + const ownerConfig = envConfig.owners[chain]; + const safeAddress = + ownerConfig.ownerOverrides?._safeAddress ?? + ethers.constants.AddressZero; + const icaAddress = + ownerConfig.ownerOverrides?._icaAddress ?? + ethers.constants.AddressZero; + + // get actual onchain owner + const ownerAddress = await contracts.mailbox.owner(); + + // determine owner type + let ownerType = Owner.UNKNOWN; + if (eqAddressEvm(ownerAddress, deployer)) { + ownerType = Owner.DEPLOYER; + } else if (eqAddressEvm(ownerAddress, safeAddress)) { + ownerType = Owner.SAFE; + } else if (eqAddressEvm(ownerAddress, icaAddress)) { + ownerType = Owner.ICA; + } + + return { + owner: ownerAddress, + type: ownerType, + }; + }, + ), + ); + + console.table(mailboxOwners); + + const totalChains = Object.keys(mailboxOwners).length; + console.log(`\nTotal chains: ${totalChains}`); + + console.table( + Object.values(Owner).map((ownerType) => ({ + 'Owner Type': ownerType, + 'Chain Count': Object.values(mailboxOwners).filter( + ({ type }) => type === ownerType, + ).length, + })), + ); +} + +main() + .then() + .catch((e) => { + console.error(e); + process.exit(1); + }); diff --git a/typescript/infra/scripts/print-multisig-ism-config.ts b/typescript/infra/scripts/print-multisig-ism-config.ts deleted file mode 100644 index 61ff67fa3..000000000 --- a/typescript/infra/scripts/print-multisig-ism-config.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { IsmType } from '@hyperlane-xyz/sdk'; - -import { multisigIsms } from '../config/multisigIsm.js'; -import { getChains } from '../config/registry.js'; - -import { getArgs, withContext } from './agent-utils.js'; - -// This script exists to print the default multisig ISM validator sets for a given environment -// so they can easily be copied into the Sealevel tooling. :'( - -async function main() { - const args = await withContext(getArgs()) - .describe('local', 'local chain') - .choices('local', getChains()) - .demandOption('local').argv; - - const config = multisigIsms( - args.environment, - args.local, - IsmType.MESSAGE_ID_MULTISIG, - args.context, - ); - - console.log(JSON.stringify(config, null, 2)); -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/typescript/infra/scripts/print-token-prices.ts b/typescript/infra/scripts/print-token-prices.ts index d2d2a941c..730faff11 100644 --- a/typescript/infra/scripts/print-token-prices.ts +++ b/typescript/infra/scripts/print-token-prices.ts @@ -1,22 +1,46 @@ +import chalk from 'chalk'; + import { ChainMetadata } from '@hyperlane-xyz/sdk'; import { objMap, pick } from '@hyperlane-xyz/utils'; -// Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')` +// Intentionally circumvent `{mainnet3,testnet4}/index.ts` and `getEnvironmentConfig({'mainnet3','testnet4'})` // to avoid circular dependencies. import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js'; -import { mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; +import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; +import { getRegistry as getTestnet4Registry } from '../config/environments/testnet4/chains.js'; +import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/supportedChainNames.js'; + +import { getArgs } from './agent-utils.js'; const CURRENCY = 'usd'; +const DEFAULT_PRICE = { + mainnet3: '1', + testnet4: '10', + test: '100', +}; + async function main() { - const registry = await getMainnet3Registry(); + const { environment } = await getArgs().argv; + + const { registry, supportedChainNames } = + environment === 'mainnet3' + ? { + registry: await getMainnet3Registry(), + supportedChainNames: mainnet3SupportedChainNames, + } + : { + registry: await getTestnet4Registry(), + supportedChainNames: testnet4SupportedChainNames, + }; + const chainMetadata = await registry.getMetadata(); const metadata = pick( chainMetadata as Record< - (typeof mainnet3SupportedChainNames)[number], + (typeof supportedChainNames)[number], ChainMetadata >, - [...mainnet3SupportedChainNames], + [...supportedChainNames], ); const ids = objMap( @@ -34,15 +58,26 @@ async function main() { const prices = objMap(ids, (_, id) => { const idData = idPrices[id]; + if (!idData) { - throw new Error( - `No data for ${id}, did you set gasCurrencyCoinGeckoId in the metadata?`, + console.warn( + chalk.yellow( + `No data for ${id}, using ${DEFAULT_PRICE[environment]} as a default`, + ), ); + return DEFAULT_PRICE[environment]; } + const price = idData[CURRENCY]; if (!price) { - throw new Error(`No ${CURRENCY} price for ${id}`); + console.warn( + chalk.yellow( + `No ${CURRENCY} price for ${id}, using ${DEFAULT_PRICE[environment]} as a default`, + ), + ); + return DEFAULT_PRICE[environment]; } + return price.toString(); }); diff --git a/typescript/infra/scripts/print-chain-metadatas.ts b/typescript/infra/scripts/sealevel-helpers/print-chain-metadatas.ts similarity index 87% rename from typescript/infra/scripts/print-chain-metadatas.ts rename to typescript/infra/scripts/sealevel-helpers/print-chain-metadatas.ts index ff8f771fe..49473e5dd 100644 --- a/typescript/infra/scripts/print-chain-metadatas.ts +++ b/typescript/infra/scripts/sealevel-helpers/print-chain-metadatas.ts @@ -1,7 +1,7 @@ import { pick } from '@hyperlane-xyz/utils'; -import { getArgs } from './agent-utils.js'; -import { getEnvironmentConfig } from './core-utils.js'; +import { getArgs } from '../agent-utils.js'; +import { getEnvironmentConfig } from '../core-utils.js'; // This script exists to print the chain metadata configs for a given environment // so they can easily be copied into the Sealevel tooling. :'( diff --git a/typescript/infra/scripts/sealevel-helpers/print-multisig-ism-config.ts b/typescript/infra/scripts/sealevel-helpers/print-multisig-ism-config.ts new file mode 100644 index 000000000..69646ff01 --- /dev/null +++ b/typescript/infra/scripts/sealevel-helpers/print-multisig-ism-config.ts @@ -0,0 +1,50 @@ +import { IsmType } from '@hyperlane-xyz/sdk'; + +import { multisigIsms } from '../../config/multisigIsm.js'; +import { getChains } from '../../config/registry.js'; +import { getArgs, withContext } from '../agent-utils.js'; + +// This script exists to print the default multisig ISM validator sets for a given environment +// so they can easily be copied into the Sealevel tooling. :'( + +async function main() { + const args = await withContext(getArgs()) + .describe('local', 'local chain') + .choices('local', getChains()) + .demandOption('local').argv; + + const config = multisigIsms( + args.environment, + args.local, + IsmType.MESSAGE_ID_MULTISIG, + args.context, + ); + + // Cap any thresholds to 4 due to the Sealevel transaction size limit. + // Any higher than 4 at the moment will cause warp route synthetic deliveries to fail. + // Example message Solana -> Eclipse that mints a synthetic: + // https://explorer.eclipse.xyz/tx/3wcMvqZZjQon9o8nD49e3ci16AUJopZRLAfsAfs16ZrxgoNLoboNvrbV1hQHbnN3KXrWSqHmKnmM28mUvh5Un5Hd/inspect. + // At the time, the Solana threshold was 3. Taking the max tx size of 1232 and the tx's size 1121, + // we can find the number of additional signatures to be: floor((1232 - 1121)/65) = floor(1.707) = 1. + // So the total number of signatures is 3 + 1 = 4. + + const MAX_THRESHOLD = 4; + + for (const chain of Object.keys(config)) { + if (config[chain].threshold > MAX_THRESHOLD) { + console.warn( + `Threshold for ${chain} is ${config[chain].threshold}. Capping to ${MAX_THRESHOLD}.`, + ); + config[chain].threshold = MAX_THRESHOLD; + } + } + + console.warn; + + console.log(JSON.stringify(config, null, 2)); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts b/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts index 009323ad6..95a749ace 100644 --- a/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts +++ b/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts @@ -1,8 +1,9 @@ import yargs from 'yargs'; +import { Contexts } from '../../config/contexts.js'; import { HelmCommand } from '../../src/utils/helm.js'; import { WarpRouteMonitorHelmManager } from '../../src/warp/helm.js'; -import { assertCorrectKubeContext } from '../agent-utils.js'; +import { assertCorrectKubeContext, getAgentConfig } from '../agent-utils.js'; import { getEnvironmentConfig } from '../core-utils.js'; async function main() { @@ -16,9 +17,15 @@ async function main() { .string('filePath') .parse(); - await assertCorrectKubeContext(getEnvironmentConfig('mainnet3')); + const environment = 'mainnet3'; + await assertCorrectKubeContext(getEnvironmentConfig(environment)); + const agentConfig = getAgentConfig(Contexts.Hyperlane, environment); - const helmManager = new WarpRouteMonitorHelmManager(filePath, 'mainnet3'); + const helmManager = new WarpRouteMonitorHelmManager( + filePath, + environment, + agentConfig.environmentChainNames, + ); await helmManager.runHelmCommand(HelmCommand.InstallOrUpgrade); } diff --git a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts index 478dfc232..2d588a89c 100644 --- a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts @@ -1,7 +1,6 @@ import { SystemProgram } from '@solana/web3.js'; import { ethers } from 'ethers'; import { Gauge, Registry } from 'prom-client'; -import yargs from 'yargs'; import { HypXERC20Lockbox__factory, @@ -10,13 +9,18 @@ import { IXERC20__factory, } from '@hyperlane-xyz/core'; import { ERC20__factory } from '@hyperlane-xyz/core'; +import { createWarpRouteConfigId } from '@hyperlane-xyz/registry'; import { ChainMap, + ChainMetadata, ChainName, + CoinGeckoTokenPriceGetter, CosmNativeTokenAdapter, CwNativeTokenAdapter, MultiProtocolProvider, SealevelHypCollateralAdapter, + SealevelHypNativeAdapter, + SealevelHypSyntheticAdapter, TokenType, WarpRouteConfig, WarpRouteConfigSchema, @@ -28,40 +32,71 @@ import { rootLogger, } from '@hyperlane-xyz/utils'; -import { getChainMetadata } from '../../config/registry.js'; import { startMetricsServer } from '../../src/utils/metrics.js'; import { readYaml } from '../../src/utils/utils.js'; +import { getArgs } from '../agent-utils.js'; +import { getEnvironmentConfig } from '../core-utils.js'; const logger = rootLogger.child({ module: 'warp-balance-monitor' }); const metricsRegister = new Registry(); + +interface WarpRouteMetrics { + chain_name: ChainName; + token_address: string; + token_name: string; + wallet_address: string; + token_type: TokenType; + warp_route_id: string; + related_chain_names: string; +} + +type WarpRouteMetricLabels = keyof WarpRouteMetrics; + +const warpRouteMetricLabels: WarpRouteMetricLabels[] = [ + 'chain_name', + 'token_address', + 'token_name', + 'wallet_address', + 'token_type', + 'warp_route_id', + 'related_chain_names', +]; + const warpRouteTokenBalance = new Gauge({ name: 'hyperlane_warp_route_token_balance', help: 'HypERC20 token balance of a Warp Route', registers: [metricsRegister], - labelNames: [ - 'chain_name', - 'token_address', - 'token_name', - 'wallet_address', - 'token_type', - ], + labelNames: warpRouteMetricLabels, +}); + +const warpRouteCollateralValue = new Gauge({ + name: 'hyperlane_warp_route_collateral_value', + help: 'Total value of collateral held in a HypERC20Collateral or HypNative contract of a Warp Route', + registers: [metricsRegister], + labelNames: warpRouteMetricLabels, }); const xERC20LimitsGauge = new Gauge({ name: 'hyperlane_xerc20_limits', help: 'Current minting and burning limits of xERC20 tokens', registers: [metricsRegister], - labelNames: ['chain_name', 'limit_type'], + labelNames: ['chain_name', 'limit_type', 'token_name'], }); interface xERC20Limit { + tokenName: string; mint: number; burn: number; mintMax: number; burnMax: number; } +interface WarpRouteInfo { + balance: number; + valueUSD?: number; +} + export function readWarpRouteConfig(filePath: string) { const config = readYaml(filePath); if (!config) throw new Error(`No warp config found at ${filePath}`); @@ -76,7 +111,7 @@ export function readWarpRouteConfig(filePath: string) { } async function main(): Promise { - const { checkFrequency, filePath } = await yargs(process.argv.slice(2)) + const { checkFrequency, filePath, environment } = await getArgs() .describe('checkFrequency', 'frequency to check balances in ms') .demandOption('checkFrequency') .alias('v', 'checkFrequency') // v as in Greek letter nu @@ -95,18 +130,11 @@ async function main(): Promise { const tokenConfig: WarpRouteConfig = readWarpRouteConfig(filePath).data.config; - // TODO: eventually support token balance checks for xERC20 token type also - if ( - Object.values(tokenConfig).some( - (token) => - token.type === TokenType.XERC20 || - token.type === TokenType.XERC20Lockbox, - ) - ) { - await checkXERC20Limits(checkFrequency, tokenConfig); - } else { - await checkTokenBalances(checkFrequency, tokenConfig); - } + const envConfig = getEnvironmentConfig(environment); + const registry = await envConfig.getRegistry(); + const chainMetadata = await registry.getMetadata(); + + await checkWarpRouteMetrics(checkFrequency, tokenConfig, chainMetadata); return true; } @@ -115,7 +143,8 @@ async function main(): Promise { async function checkBalance( tokenConfig: WarpRouteConfig, multiProtocolProvider: MultiProtocolProvider, -): Promise> { + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise> { const output = objMap( tokenConfig, async (chain: ChainName, token: WarpRouteConfig[ChainName]) => { @@ -125,13 +154,38 @@ async function checkBalance( case ProtocolType.Ethereum: { const provider = multiProtocolProvider.getEthersV5Provider(chain); const nativeBalance = await provider.getBalance(token.hypAddress); - return parseFloat( - ethers.utils.formatUnits(nativeBalance, token.decimals), + + return getNativeTokenWarpInfo( + nativeBalance, + token.decimals, + tokenPriceGetter, + chain, + ); + } + case ProtocolType.Sealevel: { + const adapter = new SealevelHypNativeAdapter( + chain, + multiProtocolProvider, + { + token: token.tokenAddress, + warpRouter: token.hypAddress, + // Mailbox only required for transfers, using system as placeholder + mailbox: SystemProgram.programId.toBase58(), + }, + // Not used for native tokens, but required for the adapter + token?.isSpl2022 ?? false, + ); + const balance = ethers.BigNumber.from( + await adapter.getBalance(token.hypAddress), + ); + + return getNativeTokenWarpInfo( + balance, + token.decimals, + tokenPriceGetter, + chain, ); } - case ProtocolType.Sealevel: - // TODO - solana native - return 0; case ProtocolType.Cosmos: { if (!token.ibcDenom) throw new Error('IBC denom missing for native token'); @@ -142,8 +196,12 @@ async function checkBalance( { ibcDenom: token.ibcDenom }, ); const tokenBalance = await adapter.getBalance(token.hypAddress); - return parseFloat( - ethers.utils.formatUnits(tokenBalance, token.decimals), + + return getNativeTokenWarpInfo( + tokenBalance, + token.decimals, + tokenPriceGetter, + chain, ); } } @@ -163,13 +221,16 @@ async function checkBalance( token.hypAddress, ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } case ProtocolType.Sealevel: { if (!token.tokenAddress) - throw new Error('Token address missing for synthetic token'); + throw new Error('Token address missing for collateral token'); const adapter = new SealevelHypCollateralAdapter( chain, multiProtocolProvider, @@ -184,8 +245,12 @@ async function checkBalance( const collateralBalance = ethers.BigNumber.from( await adapter.getBalance(token.hypAddress), ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } case ProtocolType.Cosmos: { @@ -202,8 +267,12 @@ async function checkBalance( const collateralBalance = ethers.BigNumber.from( await adapter.getBalance(token.hypAddress), ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), + + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, ); } } @@ -218,21 +287,102 @@ async function checkBalance( provider, ); const syntheticBalance = await tokenContract.totalSupply(); - return parseFloat( - ethers.utils.formatUnits(syntheticBalance, token.decimals), + return { + balance: parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ), + }; + } + case ProtocolType.Sealevel: { + if (!token.tokenAddress) + throw new Error('Token address missing for synthetic token'); + const adapter = new SealevelHypSyntheticAdapter( + chain, + multiProtocolProvider, + { + token: token.tokenAddress, + warpRouter: token.hypAddress, + // Mailbox only required for transfers, using system as placeholder + mailbox: SystemProgram.programId.toBase58(), + }, + token?.isSpl2022 ?? false, + ); + const syntheticBalance = ethers.BigNumber.from( + await adapter.getTotalSupply(), ); + return { + balance: parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ), + }; } - case ProtocolType.Sealevel: - // TODO - solana native - return 0; case ProtocolType.Cosmos: - // TODO - cosmos native - return 0; + // TODO - cosmos synthetic + return { balance: 0 }; } break; } + case TokenType.XERC20: { + switch (token.protocolType) { + case ProtocolType.Ethereum: { + const provider = multiProtocolProvider.getEthersV5Provider(chain); + const hypXERC20 = HypXERC20__factory.connect( + token.hypAddress, + provider, + ); + const xerc20Address = await hypXERC20.wrappedToken(); + const xerc20 = IXERC20__factory.connect(xerc20Address, provider); + const syntheticBalance = await xerc20.totalSupply(); + + return { + balance: parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ), + }; + } + default: + throw new Error( + `Unsupported protocol type ${token.protocolType} for token type ${token.type}`, + ); + } + } + case TokenType.XERC20Lockbox: { + switch (token.protocolType) { + case ProtocolType.Ethereum: { + if (!token.tokenAddress) + throw new Error( + 'Token address missing for xERC20Lockbox token', + ); + const provider = multiProtocolProvider.getEthersV5Provider(chain); + const hypXERC20Lockbox = HypXERC20Lockbox__factory.connect( + token.hypAddress, + provider, + ); + const xerc20LockboxAddress = await hypXERC20Lockbox.lockbox(); + const tokenContract = ERC20__factory.connect( + token.tokenAddress, + provider, + ); + + const collateralBalance = await tokenContract.balanceOf( + xerc20LockboxAddress, + ); + + return getCollateralTokenWarpInfo( + collateralBalance, + token.decimals, + tokenPriceGetter, + token.tokenCoinGeckoId, + ); + } + default: + throw new Error( + `Unsupported protocol type ${token.protocolType} for token type ${token.type}`, + ); + } + } } - return 0; + return { balance: 0 }; }, ); @@ -241,66 +391,94 @@ async function checkBalance( export function updateTokenBalanceMetrics( tokenConfig: WarpRouteConfig, - balances: ChainMap, + balances: ChainMap, ) { objMap(tokenConfig, (chain: ChainName, token: WarpRouteConfig[ChainName]) => { - warpRouteTokenBalance - .labels({ - chain_name: chain, - token_address: token.tokenAddress ?? ethers.constants.AddressZero, - token_name: token.name, - wallet_address: token.hypAddress, - token_type: token.type, - }) - .set(balances[chain]); + const metrics: WarpRouteMetrics = { + chain_name: chain, + token_address: token.tokenAddress ?? ethers.constants.AddressZero, + token_name: token.name, + wallet_address: token.hypAddress, + token_type: token.type, + warp_route_id: createWarpRouteConfigId( + token.symbol, + Object.keys(tokenConfig) as ChainName[], + ), + related_chain_names: Object.keys(tokenConfig) + .filter((chainName) => chainName !== chain) + .sort() + .join(','), + }; + + warpRouteTokenBalance.labels(metrics).set(balances[chain].balance); + if (balances[chain].valueUSD) { + warpRouteCollateralValue + .labels(metrics) + .set(balances[chain].valueUSD as number); + logger.debug('Collateral value updated for chain', { + chain, + related_chain_names: metrics.related_chain_names, + warp_route_id: metrics.warp_route_id, + token: metrics.token_name, + value: balances[chain].valueUSD, + }); + } logger.debug('Wallet balance updated for chain', { chain, - token: token.name, - balance: balances[chain], + related_chain_names: metrics.related_chain_names, + warp_route_id: metrics.warp_route_id, + token: metrics.token_name, + balance: balances[chain].balance, }); }); } -export function updateXERC20LimitsMetrics(xERC20Limits: ChainMap) { - objMap(xERC20Limits, (chain: ChainName, limit: xERC20Limit) => { - xERC20LimitsGauge - .labels({ - chain_name: chain, - limit_type: 'mint', - }) - .set(limit.mint); - xERC20LimitsGauge - .labels({ - chain_name: chain, - limit_type: 'burn', - }) - .set(limit.burn); - xERC20LimitsGauge - .labels({ - chain_name: chain, - limit_type: 'mintMax', - }) - .set(limit.mintMax); - xERC20LimitsGauge - .labels({ - chain_name: chain, - limit_type: 'burnMax', - }) - .set(limit.burnMax); - logger.info('xERC20 limits updated for chain', { - chain, - mint: limit.mint, - burn: limit.burn, - mintMax: limit.mintMax, - burnMax: limit.burnMax, - }); +export function updateXERC20LimitsMetrics( + xERC20Limits: ChainMap, +) { + objMap(xERC20Limits, (chain: ChainName, limits: xERC20Limit | undefined) => { + if (limits) { + xERC20LimitsGauge + .labels({ + chain_name: chain, + limit_type: 'mint', + token_name: limits.tokenName, + }) + .set(limits.mint); + xERC20LimitsGauge + .labels({ + chain_name: chain, + limit_type: 'burn', + token_name: limits.tokenName, + }) + .set(limits.burn); + xERC20LimitsGauge + .labels({ + chain_name: chain, + limit_type: 'mintMax', + token_name: limits.tokenName, + }) + .set(limits.mintMax); + xERC20LimitsGauge + .labels({ + chain_name: chain, + limit_type: 'burnMax', + token_name: limits.tokenName, + }) + .set(limits.burnMax); + logger.info('xERC20 limits updated for chain', { + chain, + limits, + }); + } }); } async function getXERC20Limits( tokenConfig: WarpRouteConfig, -): Promise> { - const multiProtocolProvider = new MultiProtocolProvider(getChainMetadata()); + chainMetadata: ChainMap, +): Promise> { + const multiProtocolProvider = new MultiProtocolProvider(chainMetadata); const output = objMap( tokenConfig, @@ -317,7 +495,12 @@ async function getXERC20Limits( ); const xerc20Address = await lockbox.xERC20(); const xerc20 = IXERC20__factory.connect(xerc20Address, provider); - return getXERC20Limit(routerAddress, xerc20, token.decimals); + return getXERC20Limit( + routerAddress, + xerc20, + token.decimals, + token.name, + ); } case TokenType.XERC20: { const provider = multiProtocolProvider.getEthersV5Provider(chain); @@ -328,19 +511,24 @@ async function getXERC20Limits( ); const xerc20Address = await hypXERC20.wrappedToken(); const xerc20 = IXERC20__factory.connect(xerc20Address, provider); - return getXERC20Limit(routerAddress, xerc20, token.decimals); + return getXERC20Limit( + routerAddress, + xerc20, + token.decimals, + token.name, + ); } + default: + logger.info( + `Unsupported token type ${token.type} for xERC20 limits check on protocol type ${token.protocolType}`, + ); + + return undefined; } - break; } + default: + throw new Error(`Unsupported protocol type ${token.protocolType}`); } - return { - chain: chain, - mint: 0, - mintMax: 0, - burn: 0, - burnMax: 0, - }; }, ); @@ -351,12 +539,14 @@ const getXERC20Limit = async ( routerAddress: string, xerc20: IXERC20, decimals: number, + tokenName: string, ): Promise => { const mintCurrent = await xerc20.mintingCurrentLimitOf(routerAddress); const mintMax = await xerc20.mintingMaxLimitOf(routerAddress); const burnCurrent = await xerc20.burningCurrentLimitOf(routerAddress); const burnMax = await xerc20.burningMaxLimitOf(routerAddress); return { + tokenName, mint: parseFloat(ethers.utils.formatUnits(mintCurrent, decimals)), mintMax: parseFloat(ethers.utils.formatUnits(mintMax, decimals)), burn: parseFloat(ethers.utils.formatUnits(burnCurrent, decimals)), @@ -364,36 +554,121 @@ const getXERC20Limit = async ( }; }; -async function checkXERC20Limits( - checkFrequency: number, - tokenConfig: WarpRouteConfig, -) { - setInterval(async () => { - try { - const xERC20Limits = await getXERC20Limits(tokenConfig); - logger.info('xERC20 Limits:', xERC20Limits); - updateXERC20LimitsMetrics(xERC20Limits); - } catch (e) { - logger.error('Error checking balances', e); - } - }, checkFrequency); +async function getTokenPriceByChain( + chain: ChainName, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + try { + return await tokenPriceGetter.getTokenPrice(chain); + } catch (e) { + logger.warn('Error getting token price', e); + return undefined; + } } -async function checkTokenBalances( +async function getNativeTokenValue( + chain: ChainName, + balanceFloat: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + const price = await getTokenPriceByChain(chain, tokenPriceGetter); + logger.debug(`${chain} native token price ${price}`); + if (!price) return undefined; + return balanceFloat * price; +} + +async function getNativeTokenWarpInfo( + balance: ethers.BigNumber | bigint, + decimal: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, + chain: ChainName, +): Promise { + const balanceFloat = parseFloat(ethers.utils.formatUnits(balance, decimal)); + const value = await getNativeTokenValue( + chain, + balanceFloat, + tokenPriceGetter, + ); + return { balance: balanceFloat, valueUSD: value }; +} + +async function getCollateralTokenPrice( + tokenCoinGeckoId: string | undefined, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + if (!tokenCoinGeckoId) return undefined; + const prices = await tokenPriceGetter.getTokenPriceByIds([tokenCoinGeckoId]); + if (!prices) return undefined; + return prices[0]; +} + +async function getCollateralTokenValue( + tokenCoinGeckoId: string | undefined, + balanceFloat: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, +): Promise { + const price = await getCollateralTokenPrice( + tokenCoinGeckoId, + tokenPriceGetter, + ); + logger.debug(`${tokenCoinGeckoId} token price ${price}`); + if (!price) return undefined; + return balanceFloat * price; +} + +async function getCollateralTokenWarpInfo( + balance: ethers.BigNumber | bigint, + decimal: number, + tokenPriceGetter: CoinGeckoTokenPriceGetter, + tokenCoinGeckoId?: string, +): Promise { + const balanceFloat = parseFloat(ethers.utils.formatUnits(balance, decimal)); + const value = await getCollateralTokenValue( + tokenCoinGeckoId, + balanceFloat, + tokenPriceGetter, + ); + return { balance: balanceFloat, valueUSD: value }; +} + +async function checkWarpRouteMetrics( checkFrequency: number, tokenConfig: WarpRouteConfig, + chainMetadata: ChainMap, ) { - logger.info('Starting Warp Route balance monitor'); - const multiProtocolProvider = new MultiProtocolProvider(getChainMetadata()); + const tokenPriceGetter = + CoinGeckoTokenPriceGetter.withDefaultCoinGecko(chainMetadata); setInterval(async () => { try { - logger.debug('Checking balances'); - const balances = await checkBalance(tokenConfig, multiProtocolProvider); + const multiProtocolProvider = new MultiProtocolProvider(chainMetadata); + const balances = await checkBalance( + tokenConfig, + multiProtocolProvider, + tokenPriceGetter, + ); + logger.info('Token Balances:', balances); updateTokenBalanceMetrics(tokenConfig, balances); } catch (e) { logger.error('Error checking balances', e); } + + // only check xERC20 limits if there are xERC20 tokens in the config + if ( + Object.keys(tokenConfig).some( + (chain) => + tokenConfig[chain].type === TokenType.XERC20 || + tokenConfig[chain].type === TokenType.XERC20Lockbox, + ) + ) { + try { + const xERC20Limits = await getXERC20Limits(tokenConfig, chainMetadata); + logger.info('xERC20 Limits:', xERC20Limits); + updateXERC20LimitsMetrics(xERC20Limits); + } catch (e) { + logger.error('Error checking xERC20 limits', e); + } + } }, checkFrequency); } diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 533e1407a..3f3f6469a 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -35,7 +35,7 @@ import { AgentGCPKey } from './gcp.js'; const HELM_CHART_PATH = join( getInfraPath(), - '/../../rust/helm/hyperlane-agent/', + '/../../rust/main/helm/hyperlane-agent/', ); if (!fs.existsSync(HELM_CHART_PATH + 'Chart.yaml')) @@ -164,6 +164,19 @@ export class RelayerHelmManager extends OmniscientAgentHelmManager { signer: signers[name], })); + if (!values.tolerations) { + values.tolerations = []; + } + + // Relayer pods should only be scheduled on nodes with the component label set to relayer. + // NoSchedule was chosen so that some daemonsets (like the prometheus node exporter) would not be evicted. + values.tolerations.push({ + key: 'component', + operator: 'Equal', + value: 'relayer', + effect: 'NoSchedule', + }); + return values; } } diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index 69ec265d7..987a05f0e 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -30,15 +30,16 @@ export type DeepPartial = T extends object } : T; -// See rust/helm/values.yaml for the full list of options and their defaults. +// See rust/main/helm/values.yaml for the full list of options and their defaults. // This is the root object in the values file. export interface HelmRootAgentValues { image: HelmImageValues; hyperlane: HelmHyperlaneValues; nameOverride?: string; + tolerations?: KubernetesToleration[]; } -// See rust/helm/values.yaml for the full list of options and their defaults. +// See rust/main/helm/values.yaml for the full list of options and their defaults. // This is at `.hyperlane` in the values file. interface HelmHyperlaneValues { runEnv: DeployEnvironment; @@ -54,7 +55,7 @@ interface HelmHyperlaneValues { scraper?: HelmScraperValues; } -// See rust/helm/values.yaml for the full list of options and their defaults. +// See rust/main/helm/values.yaml for the full list of options and their defaults. // This is at `.hyperlane.chains` in the values file. export interface HelmAgentChainOverride extends DeepPartial { @@ -132,6 +133,13 @@ export interface KubernetesComputeResources { memory: string; } +export interface KubernetesToleration { + key: string; + operator: string; + value: string; + effect: string; +} + export class RootAgentConfigHelper implements AgentContextConfig { readonly rawConfig: RootAgentConfig; diff --git a/typescript/infra/src/config/agent/relayer.ts b/typescript/infra/src/config/agent/relayer.ts index f9d4bac73..cd7e4a46d 100644 --- a/typescript/infra/src/config/agent/relayer.ts +++ b/typescript/infra/src/config/agent/relayer.ts @@ -51,14 +51,14 @@ export interface BaseRelayerConfig { // Full relayer-specific agent config for a single chain export type RelayerConfig = Omit; -// See rust/helm/values.yaml for the full list of options and their defaults. +// See rust/main/helm/values.yaml for the full list of options and their defaults. // This is at `.hyperlane.relayer` in the values file. export interface HelmRelayerValues extends HelmStatefulSetValues { aws: boolean; config?: RelayerConfig; } -// See rust/helm/values.yaml for the full list of options and their defaults. +// See rust/main/helm/values.yaml for the full list of options and their defaults. // This is at `.hyperlane.relayerChains` in the values file. export interface HelmRelayerChainValues { name: string; @@ -169,16 +169,26 @@ export class RelayerConfigHelper extends AgentConfigHelper { }), ); - return allSanctionedAddresses.flat().filter((address) => { - if (!isValidAddressEvm(address)) { - this.logger.debug( - { address }, - 'Invalid sanctioned address, throwing out', - ); - return false; - } - return true; - }); + const sanctionedEthereumAdresses = allSanctionedAddresses + .flat() + .filter((address) => { + if (!isValidAddressEvm(address)) { + this.logger.debug( + { address }, + 'Invalid sanctioned address, throwing out', + ); + return false; + } + return true; + }); + + const radiantExploiter = [ + '0xA0e768A68ba1BFffb9F4366dfC8D9195EE7217d1', + '0x0629b1048298AE9deff0F4100A31967Fb3f98962', + '0x97a05beCc2e7891D07F382457Cd5d57FD242e4e8', + ]; + + return [...sanctionedEthereumAdresses, ...radiantExploiter]; } // Returns whether the relayer requires AWS credentials diff --git a/typescript/infra/src/config/agent/validator.ts b/typescript/infra/src/config/agent/validator.ts index 05293abfa..e18220017 100644 --- a/typescript/infra/src/config/agent/validator.ts +++ b/typescript/infra/src/config/agent/validator.ts @@ -26,8 +26,8 @@ export type ValidatorBaseChainConfigMap = ChainMap; export interface ValidatorBaseChainConfig { // How frequently to check for new checkpoints interval: number; - // The reorg_period in blocks; overrides chain metadata - reorgPeriod: number; + // The reorg_period in blocks or block tag; overrides chain metadata + reorgPeriod: string | number; // Individual validator agents validators: Array; } diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index acc232c5a..622ad18b3 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -1,57 +1,48 @@ +import chalk from 'chalk'; import { BigNumber, ethers } from 'ethers'; import { - AgentCosmosGasPrice, ChainMap, ChainName, - StorageGasOracleConfig as DestinationOracleConfig, + GasPriceConfig, + StorageGasOracleConfig, TOKEN_EXCHANGE_RATE_SCALE, - getCosmosRegistryChain, + defaultMultisigConfigs, + multisigIsmVerificationCost, } from '@hyperlane-xyz/sdk'; -import { ProtocolType, convertDecimals } from '@hyperlane-xyz/utils'; -import { getChain } from '../../config/registry.js'; -import { - isEthereumProtocolChain, - mustGetChainNativeToken, -} from '../utils/utils.js'; - -// Gas data to configure on a single local chain. Includes DestinationOracleConfig -// for each remote chain. -export type StorageGasOracleConfig = ChainMap; +import { isEthereumProtocolChain } from '../utils/utils.js'; -// StorageGasOracleConfigs for each local chain -export type AllStorageGasOracleConfigs = ChainMap; - -// A configuration for a gas price. -// Some chains, e.g. Neutron, have gas prices that are -// not integers and and are still quoted in the "wei" version -// of the token. Therefore it's possible for the amount to be a -// float (e.g. "0.0053") and for decimals to be 1. This is why -// we intentionally don't deal with BigNumber here. -export interface GasPriceConfig { - amount: string; - decimals: number; -} +// gas oracle configs for each chain, which includes +// a map for each chain's remote chains +export type AllStorageGasOracleConfigs = ChainMap< + ChainMap +>; // Overcharge by 50% to account for market making risk -const EXCHANGE_RATE_MARGIN_PCT = 50; +export const EXCHANGE_RATE_MARGIN_PCT = 50; + +// Arbitrarily chosen as a typical amount of gas used in a message's handle function. +// Used for determining typical gas costs for a message. +export const TYPICAL_HANDLE_GAS_USAGE = 50_000; -// Gets the StorageGasOracleConfig for a particular local chain. +// Gets the StorageGasOracleConfig for each remote chain for a particular local chain. // Accommodates small non-integer gas prices by scaling up the gas price // and scaling down the exchange rate by the same factor. -function getLocalStorageGasOracleConfig( +function getLocalStorageGasOracleConfigOverride( local: ChainName, remotes: ChainName[], gasPrices: ChainMap, getTokenExchangeRate: (local: ChainName, remote: ChainName) => BigNumber, getTokenUsdPrice?: (chain: ChainName) => number, - remoteOverhead?: (remote: ChainName) => number, -): StorageGasOracleConfig { + getOverhead?: (local: ChainName, remote: ChainName) => number, +): ChainMap { return remotes.reduce((agg, remote) => { let exchangeRate = getTokenExchangeRate(local, remote); if (!gasPrices[remote]) { - throw new Error(`No gas price found for chain ${remote}`); + // Will run into this case when adding new chains + console.warn(chalk.yellow(`No gas price set for ${remote}`)); + return agg; } // First parse as a number, so we have floating point precision. @@ -93,8 +84,12 @@ function getLocalStorageGasOracleConfig( // If we have access to these, let's use the USD prices to apply some minimum // typical USD payment heuristics. - if (getTokenUsdPrice && remoteOverhead) { - const typicalRemoteGasAmount = remoteOverhead(remote) + 50_000; + if (getTokenUsdPrice && getOverhead) { + const typicalRemoteGasAmount = getTypicalRemoteGasAmount( + local, + remote, + getOverhead, + ); const typicalIgpQuoteUsd = getUsdQuote( local, gasPriceBn, @@ -125,6 +120,14 @@ function getLocalStorageGasOracleConfig( }, {}); } +export function getTypicalRemoteGasAmount( + local: ChainName, + remote: ChainName, + getOverhead: (local: ChainName, remote: ChainName) => number, +): number { + return getOverhead(local, remote) + TYPICAL_HANDLE_GAS_USAGE; +} + function getMinUsdCost(local: ChainName, remote: ChainName): number { // By default, min cost is 20 cents let minUsdCost = 0.2; @@ -180,78 +183,43 @@ function getUsdQuote( return quoteUsd; } -// Gets the StorageGasOracleConfig for each local chain +// cosmwasm warp route somewhat arbitrarily chosen +const FOREIGN_DEFAULT_OVERHEAD = 600_000; + +// Overhead for interchain messaging +export function getOverhead( + local: ChainName, + remote: ChainName, + ethereumChainNames: ChainName[], +): number { + return ethereumChainNames.includes(remote as any) + ? multisigIsmVerificationCost( + defaultMultisigConfigs[local].threshold, + defaultMultisigConfigs[local].validators.length, + ) + : FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead +} + +// Gets the map of remote gas oracle configs for each local chain export function getAllStorageGasOracleConfigs( chainNames: ChainName[], gasPrices: ChainMap, getTokenExchangeRate: (local: ChainName, remote: ChainName) => BigNumber, getTokenUsdPrice?: (chain: ChainName) => number, - remoteOverhead?: (remote: ChainName) => number, + getOverhead?: (local: ChainName, remote: ChainName) => number, ): AllStorageGasOracleConfigs { return chainNames.filter(isEthereumProtocolChain).reduce((agg, local) => { const remotes = chainNames.filter((chain) => local !== chain); return { ...agg, - [local]: getLocalStorageGasOracleConfig( + [local]: getLocalStorageGasOracleConfigOverride( local, remotes, gasPrices, getTokenExchangeRate, getTokenUsdPrice, - remoteOverhead, + getOverhead, ), }; }, {}) as AllStorageGasOracleConfigs; } - -export function getTokenExchangeRateFromValues( - local: ChainName, - localValue: BigNumber, - remote: ChainName, - remoteValue: BigNumber, -): BigNumber { - // This does not yet account for decimals! - let exchangeRate = remoteValue.mul(TOKEN_EXCHANGE_RATE_SCALE).div(localValue); - // Apply the premium - exchangeRate = exchangeRate.mul(100 + EXCHANGE_RATE_MARGIN_PCT).div(100); - - return BigNumber.from( - convertDecimals( - mustGetChainNativeToken(remote).decimals, - mustGetChainNativeToken(local).decimals, - exchangeRate.toString(), - ), - ); -} - -export async function getCosmosChainGasPrice( - chain: ChainName, -): Promise { - const metadata = getChain(chain); - if (!metadata) { - throw new Error(`No metadata found for Cosmos chain ${chain}`); - } - if (metadata.protocol !== ProtocolType.Cosmos) { - throw new Error(`Chain ${chain} is not a Cosmos chain`); - } - - const cosmosRegistryChain = await getCosmosRegistryChain(chain); - - const nativeToken = mustGetChainNativeToken(chain); - - const fee = cosmosRegistryChain.fees?.fee_tokens.find( - (fee: { denom: string }) => { - return ( - fee.denom === nativeToken.denom || fee.denom === `u${nativeToken.denom}` - ); - }, - ); - if (!fee || fee.average_gas_price === undefined) { - throw new Error(`No gas price found for Cosmos chain ${chain}`); - } - - return { - denom: fee.denom, - amount: fee.average_gas_price.toString(), - }; -} diff --git a/typescript/infra/src/config/warp.ts b/typescript/infra/src/config/warp.ts index cf9d125d8..d66aed2fc 100644 --- a/typescript/infra/src/config/warp.ts +++ b/typescript/infra/src/config/warp.ts @@ -6,5 +6,10 @@ export const tokens: ChainMap> = { ethereum: { USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', USDT: '0xdac17f958d2ee523a2206206994597c13d831ec7', + deUSD: '0x15700B564Ca08D9439C58cA5053166E8317aa138', + amphrETH: '0x5fD13359Ba15A84B76f7F87568309040176167cd', + }, + sei: { + fastUSD: '0x37a4dD9CED2b19Cfe8FAC251cd727b5787E45269', }, }; diff --git a/typescript/infra/src/deployment/deploy.ts b/typescript/infra/src/deployment/deploy.ts index 398820f8e..b50203343 100644 --- a/typescript/infra/src/deployment/deploy.ts +++ b/typescript/infra/src/deployment/deploy.ts @@ -1,10 +1,20 @@ +import chalk from 'chalk'; + import { ChainMap, ChainName, + HyperlaneContractsMap, HyperlaneDeployer, + HyperlaneFactories, + MultiProvider, serializeContractsMap, } from '@hyperlane-xyz/sdk'; -import { objFilter, objMerge } from '@hyperlane-xyz/utils'; +import { + ProtocolType, + objFilter, + objMerge, + runWithTimeout, +} from '@hyperlane-xyz/utils'; import { Modules, @@ -14,22 +24,47 @@ import { import { DeployEnvironment } from '../config/environment.js'; import { readJSONAtPath, writeJsonAtPath } from '../utils/utils.js'; +enum DeployStatus { + EMPTY = '🫥', + SUCCESS = '✅', + INFLIGHT = '⏳', + FAILURE = '❌', +} + +const deployStatus: ChainMap = {}; + +const standardDeployModules = [ + Modules.PROXY_FACTORY, + Modules.CORE, + Modules.TEST_RECIPIENT, + Modules.INTERCHAIN_GAS_PAYMASTER, + Modules.HOOK, +]; + +export interface DeployCache { + verification: string; + read: boolean; + write: boolean; + environment: DeployEnvironment; + module: Modules; +} + export async function deployWithArtifacts({ configMap, deployer, cache, targetNetworks, + module, + multiProvider, + concurrentDeploy, }: { configMap: ChainMap; deployer: HyperlaneDeployer; - cache: { - verification: string; - read: boolean; - write: boolean; - environment: DeployEnvironment; - module: Modules; - }; + cache: DeployCache; targetNetworks: ChainName[]; + module: Modules; + multiProvider: MultiProvider; + concurrentDeploy: boolean; }) { if (cache.read) { const addressesMap = getAddresses(cache.environment, cache.module); @@ -37,17 +72,58 @@ export async function deployWithArtifacts({ } // Filter the config map to only deploy the target networks - let targetConfigMap = configMap; - if (targetNetworks.length > 0) { - targetConfigMap = objFilter(configMap, (chain, _): _ is Config => - targetNetworks.includes(chain), - ); - } + const targetConfigMap = + targetNetworks.length > 0 + ? objFilter(configMap, (chain, _): _ is Config => + targetNetworks.includes(chain), + ) + : configMap; + // Run post-deploy steps const handleExit = async () => { - console.log('Running post-deploy steps'); - await postDeploy(deployer, cache); - console.log('Post-deploy completed'); + console.info(chalk.gray.italic('Running post-deploy steps')); + await runWithTimeout(5000, () => postDeploy(deployer, cache)) + .then(() => console.info('Post-deploy completed')) + .catch((error) => { + console.error( + chalk.red('Post-deploy steps timed out or failed'), + error, + ); + }); + + if (Object.keys(deployStatus).length > 0) { + const statusTable = Object.entries(deployStatus).map( + ([chain, status]) => ({ chain, status: status ?? DeployStatus.EMPTY }), + ); + console.table(statusTable); + + const failedChainNames = Object.entries(deployStatus) + .filter(([_, status]) => status === DeployStatus.FAILURE) + .map(([chain, _]) => chain); + + // If there are failed chains, exit with a non-zero status + if (failedChainNames.length > 0) { + console.error( + chalk.red.bold( + `\nFailed to deploy on ${failedChainNames.length} chain${ + failedChainNames.length === 1 ? '' : 's' + }:\n${failedChainNames.join(' ')}`, + ), + ); + process.exit(1); + } else { + const numTotalChains = Object.keys(targetConfigMap).length; + console.info( + chalk.green.bold( + `Successfully deployed contracts on ${numTotalChains} chain${ + numTotalChains === 1 ? '' : 's' + }`, + ), + ); + } + } + + process.exit(0); }; // Handle Ctrl+C @@ -56,33 +132,108 @@ export async function deployWithArtifacts({ // deployments exceeding the timeout are still written process.on('beforeExit', handleExit); - // Deploy the contracts - try { - await deployer.deploy(targetConfigMap); - } catch (e: any) { - if (e?.message.includes('Timed out')) { - console.warn('Contract deployment exceeding configured timeout', e); - } else { - console.error('Contract deployment failed', e); + // Standard deploy modules are the ones that can be deployed with the + // abstract HyperlaneDeployer's deploy function because they don't require any special logic + if (standardDeployModules.includes(module)) { + await baseDeploy( + targetConfigMap, + deployer, + multiProvider, + concurrentDeploy, + ); + } else { + try { + await deployer.deploy(targetConfigMap); + } catch (error: any) { + if (error?.message.includes('Timed out')) { + console.warn( + chalk.yellow('Contract deployment exceeding configured timeout'), + error, + ); + } else { + console.error(chalk.red('Contract deployment failed'), error); + } + } + } +} + +async function baseDeploy< + Config extends object, + Factories extends HyperlaneFactories, +>( + configMap: ChainMap, + deployer: HyperlaneDeployer, + multiProvider: MultiProvider, + concurrentDeploy: boolean, +): Promise> { + const configChains = Object.keys(configMap); + const ethereumConfigChains = configChains.filter( + (chain) => + multiProvider.getChainMetadata(chain).protocol === ProtocolType.Ethereum, + ); + + const targetChains = multiProvider.intersect( + ethereumConfigChains, + true, + ).intersection; + + console.info(`Start deploy to ${targetChains}`); + + const deployChain = async (chain: ChainName) => { + const signerAddress = await multiProvider.getSignerAddress(chain); + console.info( + chalk.gray.italic(`Deploying to ${chain} from ${signerAddress}`), + ); + + return runWithTimeout(deployer.chainTimeoutMs, async () => { + deployStatus[chain] = DeployStatus.INFLIGHT; + const contracts = await deployer.deployContracts(chain, configMap[chain]); + deployer.deployedContracts[chain] = { + ...deployer.deployedContracts[chain], + ...contracts, + }; + }) + .then(() => { + deployStatus[chain] = DeployStatus.SUCCESS; + const inFlightChains = Object.entries(deployStatus) + .filter(([_, status]) => status === DeployStatus.INFLIGHT) + .map(([chain, _]) => chain); + const numInFlight = inFlightChains.length; + console.info( + chalk.green.bold(`Successfully deployed contracts on ${chain}`), + chalk.blue.italic( + numInFlight === 0 + ? '\nAll chains deployed' + : `\n${numInFlight} chain${ + numInFlight === 1 ? '' : 's' + } still in-flight: ${inFlightChains.join(', ')}`, + ), + ); + }) + .catch((error) => { + deployStatus[chain] = DeployStatus.FAILURE; + console.error( + chalk.red.bold(`Deployment failed on ${chain}. ${error}`), + ); + }); + }; + + if (concurrentDeploy) { + await Promise.allSettled(targetChains.map(deployChain)); + } else { + for (const chain of targetChains) { + await deployChain(chain); } } - // Call the post-deploy hook to write artifacts - await postDeploy(deployer, cache); + return deployer.deployedContracts; } -export async function postDeploy( +async function postDeploy( deployer: HyperlaneDeployer, - cache: { - verification: string; - read: boolean; - write: boolean; - environment: DeployEnvironment; - module: Modules; - }, + cache: DeployCache, ) { if (cache.write) { - // TODO: dedupe deployedContracts with cachedAddresses const deployedAddresses = serializeContractsMap(deployer.deployedContracts); const cachedAddresses = deployer.cachedAddresses; const addresses = objMerge(deployedAddresses, cachedAddresses); @@ -94,7 +245,9 @@ export async function postDeploy( try { savedVerification = readJSONAtPath(cache.verification); } catch (e) { - console.error('Failed to load cached verification inputs'); + console.error( + chalk.red('Failed to load cached verification inputs. Error: ', e), + ); } // merge with existing cache of verification inputs diff --git a/typescript/infra/src/funding/key-funder.ts b/typescript/infra/src/funding/key-funder.ts index 8ecbea981..ad63128ec 100644 --- a/typescript/infra/src/funding/key-funder.ts +++ b/typescript/infra/src/funding/key-funder.ts @@ -6,8 +6,8 @@ import { getEnvironmentConfig } from '../../scripts/core-utils.js'; import { AgentContextConfig } from '../config/agent/agent.js'; import { DeployEnvironment, EnvironmentConfig } from '../config/environment.js'; import { KeyFunderConfig } from '../config/funding.js'; -import { HelmCommand, HelmManager, helmifyValues } from '../utils/helm.js'; -import { execCmd, getInfraPath } from '../utils/utils.js'; +import { HelmManager } from '../utils/helm.js'; +import { getInfraPath } from '../utils/utils.js'; export class KeyFunderHelmManager extends HelmManager { readonly helmReleaseName: string = 'key-funder'; diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index 64e4d0655..893a25c71 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import { BigNumber } from 'ethers'; import prompts from 'prompts'; @@ -25,6 +26,8 @@ import { retryAsync, } from '@hyperlane-xyz/utils'; +import { getSafeAndService, updateSafeOwner } from '../utils/safe.js'; + import { ManualMultiSend, MultiSend, @@ -41,12 +44,15 @@ export enum SubmissionType { export type AnnotatedCallData = CallData & { submissionType?: SubmissionType; description: string; + expandedDescription?: string; + icaTargetChain?: ChainName; }; export type InferredCall = { type: SubmissionType; chain: ChainName; call: AnnotatedCallData; + icaTargetChain?: ChainName; }; export abstract class HyperlaneAppGovernor< @@ -70,8 +76,8 @@ export abstract class HyperlaneAppGovernor< } } - async check() { - await this.checker.check(); + async check(chainsToCheck?: ChainName[]) { + await this.checker.check(chainsToCheck); } async checkChain(chain: ChainName) { @@ -99,62 +105,114 @@ export abstract class HyperlaneAppGovernor< } protected async sendCalls(chain: ChainName, requestConfirmation: boolean) { - const calls = this.calls[chain]; + const calls = this.calls[chain] || []; console.log(`\nFound ${calls.length} transactions for ${chain}`); const filterCalls = (submissionType: SubmissionType) => calls.filter((call) => call.submissionType == submissionType); const summarizeCalls = async ( submissionType: SubmissionType, - calls: AnnotatedCallData[], + callsForSubmissionType: AnnotatedCallData[], ): Promise => { - if (calls.length > 0) { - console.log( - `> ${calls.length} calls will be submitted via ${SubmissionType[submissionType]}`, - ); - calls.map((c) => - console.log( - `> > ${c.description} (to: ${c.to} data: ${c.data}) value: ${c.value}`, - ), - ); - if (!requestConfirmation) return true; + if (!callsForSubmissionType || callsForSubmissionType.length === 0) { + return false; + } - const { value: confirmed } = await prompts({ - type: 'confirm', - name: 'value', - message: 'Can you confirm?', - initial: false, - }); + console.log( + `${SubmissionType[submissionType]} calls: ${callsForSubmissionType.length}`, + ); + callsForSubmissionType.map( + ({ icaTargetChain, description, expandedDescription, ...call }) => { + // Print a blank line to separate calls + console.log(''); + + // Print the ICA call header if it exists + if (icaTargetChain) { + console.log( + chalk.bold( + `> INTERCHAIN ACCOUNT CALL: ${chain} -> ${icaTargetChain}`, + ), + ); + } - return !!confirmed; - } - return false; + // Print the call details + console.log(chalk.bold(`> ${description.trimEnd()}`)); + if (expandedDescription) { + console.info(chalk.gray(`${expandedDescription.trimEnd()}`)); + } + + console.info(chalk.gray(`to: ${call.to}`)); + console.info(chalk.gray(`data: ${call.data}`)); + console.info(chalk.gray(`value: ${call.value}`)); + }, + ); + if (!requestConfirmation) return true; + + const { value: confirmed } = await prompts({ + type: 'confirm', + name: 'value', + message: 'Can you confirm?', + initial: false, + }); + + return !!confirmed; }; const sendCallsForType = async ( submissionType: SubmissionType, multiSend: MultiSend, ) => { - const calls = filterCalls(submissionType); - if (calls.length > 0) { - const confirmed = await summarizeCalls(submissionType, calls); + const callsForSubmissionType = []; + const filteredCalls = filterCalls(submissionType); + + // If calls are being submitted via a safe, we need to check for any safe owner changes first + if (submissionType === SubmissionType.SAFE) { + try { + const { safeSdk } = await getSafeAndService( + chain, + this.checker.multiProvider, + (multiSend as SafeMultiSend).safeAddress, + ); + const updateOwnerCalls = await updateSafeOwner(safeSdk); + callsForSubmissionType.push(...updateOwnerCalls); + } catch (error) { + // Catch but don't throw because we want to try submitting any remaining calls + console.error(`Error updating safe owner: ${error}`); + } + } + + // Add the filtered calls to the calls for submission type + callsForSubmissionType.push(...filteredCalls); + + if (callsForSubmissionType.length > 0) { + this.printSeparator(); + const confirmed = await summarizeCalls( + submissionType, + callsForSubmissionType, + ); if (confirmed) { - console.log( - `Submitting calls on ${chain} via ${SubmissionType[submissionType]}`, + console.info( + chalk.italic( + `Submitting calls on ${chain} via ${SubmissionType[submissionType]}`, + ), ); try { await multiSend.sendTransactions( - calls.map((call) => ({ + callsForSubmissionType.map((call) => ({ to: call.to, data: call.data, value: call.value, })), ); } catch (error) { - console.error(`Error submitting calls on ${chain}: ${error}`); + console.error( + chalk.red(`Error submitting calls on ${chain}: ${error}`), + ); } } else { - console.log( - `Skipping submission of calls on ${chain} via ${SubmissionType[submissionType]}`, + console.info( + chalk.italic( + `Skipping submission of calls on ${chain} via ${SubmissionType[submissionType]}`, + ), ); } } @@ -167,9 +225,7 @@ export abstract class HyperlaneAppGovernor< const safeOwner = this.checker.configMap[chain].ownerOverrides?._safeAddress; - if (!safeOwner) { - console.warn(`No Safe owner found for chain ${chain}`); - } else { + if (safeOwner) { await retryAsync( () => sendCallsForType( @@ -181,6 +237,14 @@ export abstract class HyperlaneAppGovernor< } await sendCallsForType(SubmissionType.MANUAL, new ManualMultiSend(chain)); + + this.printSeparator(); + } + + private printSeparator() { + console.log( + `-------------------------------------------------------------------------------------------------------------------`, + ); } protected pushCall(chain: ChainName, call: AnnotatedCallData) { @@ -216,103 +280,167 @@ export abstract class HyperlaneAppGovernor< protected async inferCallSubmissionTypes() { const newCalls: ChainMap = {}; - const pushNewCall = (inferredCall: InferredCall) => { newCalls[inferredCall.chain] = newCalls[inferredCall.chain] || []; newCalls[inferredCall.chain].push({ submissionType: inferredCall.type, + icaTargetChain: inferredCall.icaTargetChain, ...inferredCall.call, }); }; - for (const chain of Object.keys(this.calls)) { - try { - for (const call of this.calls[chain]) { - const inferredCall = await this.inferCallSubmissionType(chain, call); - pushNewCall(inferredCall); + const results: ChainMap = {}; + await Promise.all( + Object.keys(this.calls).map(async (chain) => { + try { + results[chain] = await Promise.all( + this.calls[chain].map((call) => + this.inferCallSubmissionType(chain, call), + ), + ); + } catch (error) { + console.error( + chalk.red( + `Error inferring call submission types for chain ${chain}: ${error}`, + ), + ); + results[chain] = []; } - } catch (error) { - console.error( - `Error inferring call submission types for chain ${chain}: ${error}`, - ); - } - } + }), + ); + + Object.entries(results).forEach(([_, inferredCalls]) => { + inferredCalls.forEach(pushNewCall); + }); this.calls = newCalls; } + /** + * Infers the submission type for a call that may be encoded for an Interchain Account (ICA). + * + * This function performs the following steps: + * 1. Checks if an ICA exists. If not, defaults to manual submission. + * 2. Retrieves the owner of the target contract. + * 3. Verifies if the owner is the ICA router. If not, defaults to manual submission. + * 4. Fetches the ICA configuration to determine the origin chain. + * 5. Prepares the call for remote execution via the ICA if all conditions are met. + * + * @param chain The chain where the call is to be executed + * @param call The call data to be executed + * @returns An InferredCall object with the appropriate submission type and details + */ protected async inferICAEncodedSubmissionType( chain: ChainName, call: AnnotatedCallData, ): Promise { const multiProvider = this.checker.multiProvider; const signer = multiProvider.getSigner(chain); - if (this.interchainAccount) { - const ownableAddress = call.to; - const ownable = Ownable__factory.connect(ownableAddress, signer); - const account = Ownable__factory.connect(await ownable.owner(), signer); - const localOwner = await account.owner(); - if (eqAddress(localOwner, this.interchainAccount.routerAddress(chain))) { - const accountConfig = await this.interchainAccount.getAccountConfig( - chain, - account.address, - ); - const origin = this.interchainAccount.multiProvider.getChainName( - accountConfig.origin, - ); - console.log( - `Inferred call for ICA remote owner ${bytes32ToAddress( - accountConfig.owner, - )} on ${origin}`, - ); - const callRemote = await this.interchainAccount.getCallRemote({ - chain: origin, - destination: chain, - innerCalls: [ - { - to: call.to, - data: call.data, - value: call.value?.toString() || '0', - }, - ], - config: accountConfig, - }); - if (!callRemote.to || !callRemote.data) { - return { - type: SubmissionType.MANUAL, - chain, - call, - }; - } - const encodedCall: AnnotatedCallData = { - to: callRemote.to, - data: callRemote.data, - value: callRemote.value, - description: `${call.description} - interchain account call from ${origin} to ${chain}`, - }; - const { type: subType } = await this.inferCallSubmissionType( - origin, - encodedCall, - (chain: ChainName, submitterAddress: Address) => { - // Require the submitter to be the owner of the ICA on the origin chain. - return ( - chain === origin && - eqAddress(bytes32ToAddress(accountConfig.owner), submitterAddress) - ); - }, - true, // Flag this as an ICA call + + // If there is no ICA, default to manual submission + if (!this.interchainAccount) { + return { + type: SubmissionType.MANUAL, + chain, + call, + }; + } + + // Get the account's owner + const ownableAddress = call.to; + const ownable = Ownable__factory.connect(ownableAddress, signer); + const account = Ownable__factory.connect(await ownable.owner(), signer); + const localOwner = await account.owner(); + + // If the account's owner is not the ICA router, default to manual submission + if (!eqAddress(localOwner, this.interchainAccount.routerAddress(chain))) { + console.info( + chalk.gray( + `Account's owner ${localOwner} is not ICA router. Defaulting to manual submission.`, + ), + ); + return { + type: SubmissionType.MANUAL, + chain, + call, + }; + } + + // Get the account's config + const accountConfig = await this.interchainAccount.getAccountConfig( + chain, + account.address, + ); + const origin = this.interchainAccount.multiProvider.getChainName( + accountConfig.origin, + ); + console.info( + chalk.gray( + `Inferred call for ICA remote owner ${bytes32ToAddress( + accountConfig.owner, + )} on ${origin} to ${chain}`, + ), + ); + + // Get the encoded call to the remote ICA + const callRemote = await this.interchainAccount.getCallRemote({ + chain: origin, + destination: chain, + innerCalls: [ + { + to: call.to, + data: call.data, + value: call.value?.toString() || '0', + }, + ], + config: accountConfig, + }); + + // If the call to the remote ICA is not valid, default to manual submission + if (!callRemote.to || !callRemote.data) { + return { + type: SubmissionType.MANUAL, + chain, + call, + }; + } + + // If the call to the remote ICA is valid, infer the submission type + const { description, expandedDescription } = call; + const encodedCall: AnnotatedCallData = { + to: callRemote.to, + data: callRemote.data, + value: callRemote.value, + description, + expandedDescription, + }; + + // Try to infer the submission type for the ICA call + const { type: subType } = await this.inferCallSubmissionType( + origin, + encodedCall, + (chain: ChainName, submitterAddress: Address) => { + // Require the submitter to be the owner of the ICA on the origin chain. + return ( + chain === origin && + eqAddress(bytes32ToAddress(accountConfig.owner), submitterAddress) ); - if (subType !== SubmissionType.MANUAL) { - return { - type: subType, - chain: origin, - call: encodedCall, - }; - } - } else { - console.log(`Account's owner ${localOwner} is not ICA router`); - } + }, + true, // Flag this as an ICA call + ); + + // If returned submission type is not MANUAL + // we'll return the inferred call with the ICA target chain + if (subType !== SubmissionType.MANUAL) { + return { + type: subType, + chain: origin, + call: encodedCall, + icaTargetChain: chain, + }; } + + // Else, default to manual submission return { type: SubmissionType.MANUAL, chain, @@ -320,6 +448,21 @@ export abstract class HyperlaneAppGovernor< }; } + /** + * Infers the submission type for a call. + * + * This function performs the following steps: + * 1. Checks if the transaction will succeed with the SIGNER. + * 2. Checks if the transaction will succeed with a SAFE. + * 3. If not already an ICA call, tries to infer an ICA call. + * 4. If the transaction will not succeed with SIGNER, SAFE, or ICA, defaults to MANUAL submission. + * + * @param chain The chain where the call is to be executed + * @param call The call data to be executed + * @param additionalTxSuccessCriteria An optional function to check additional success criteria for the transaction + * @param isICACall Flag to indicate if the call is already an ICA call + * @returns An InferredCall object with the appropriate submission type and details + */ protected async inferCallSubmissionType( chain: ChainName, call: AnnotatedCallData, @@ -333,115 +476,120 @@ export abstract class HyperlaneAppGovernor< const signer = multiProvider.getSigner(chain); const signerAddress = await signer.getAddress(); - const transactionSucceedsFromSender = async ( + // Check if the transaction will succeed with a given submitter address + const checkTransactionSuccess = async ( chain: ChainName, submitterAddress: Address, ): Promise => { - // The submitter needs to have enough balance to pay for the call. - // Surface a warning if the submitter's balance is insufficient, as this - // can result in fooling the tooling into thinking otherwise valid submission - // types are invalid. + // Check if the transaction has a value and if the submitter has enough balance if (call.value !== undefined) { - const submitterBalance = await multiProvider - .getProvider(chain) - .getBalance(submitterAddress); - if (submitterBalance.lt(call.value)) { - console.warn( - `Submitter ${submitterAddress} has an insufficient balance for the call and is likely to fail. Balance:`, - submitterBalance, - 'Balance required:', - call.value, - ); - } + await this.checkSubmitterBalance(chain, submitterAddress, call.value); } + // Check if the transaction has additional success criteria + if ( + additionalTxSuccessCriteria && + !additionalTxSuccessCriteria(chain, submitterAddress) + ) { + return false; + } + + // Check if the transaction will succeed with the signer try { - if ( - additionalTxSuccessCriteria && - !additionalTxSuccessCriteria(chain, submitterAddress) - ) { - return false; - } - // Will throw if the transaction fails await multiProvider.estimateGas(chain, call, submitterAddress); return true; - } catch (e) {} // eslint-disable-line no-empty - return false; + } catch (e) { + return false; + } }; - if (await transactionSucceedsFromSender(chain, signerAddress)) { - return { - type: SubmissionType.SIGNER, - chain, - call, - }; + // Check if the transaction will succeed with the SIGNER + if (await checkTransactionSuccess(chain, signerAddress)) { + return { type: SubmissionType.SIGNER, chain, call }; } - // 2. Check if the call will succeed via Gnosis Safe. + // Check if the transaction will succeed with a SAFE const safeAddress = this.checker.configMap[chain].ownerOverrides?._safeAddress; - if (typeof safeAddress === 'string') { - // 2a. Confirm that the signer is a Safe owner or delegate. - // This should implicitly check whether or not the owner is a gnosis - // safe. - if (!this.canPropose[chain].has(safeAddress)) { - try { - const canPropose = await canProposeSafeTransactions( - signerAddress, - chain, - multiProvider, - safeAddress, - ); - this.canPropose[chain].set(safeAddress, canPropose); - } catch (error) { - // if we hit this error, it's likely a custom safe chain - // so let's fallback to a manual submission - if ( - error instanceof Error && - (error.message.includes('Invalid MultiSend contract address') || - error.message.includes( - 'Invalid MultiSendCallOnly contract address', - )) - ) { - console.warn(`${error.message}: Setting submission type to MANUAL`); - return { - type: SubmissionType.MANUAL, - chain, - call, - }; - } else { - console.error( - `Failed to determine if signer can propose safe transactions: ${error}`, - ); - } - } - } - - // 2b. Check if calling from the owner/safeAddress will succeed. + // Check if the safe can propose transactions + const canProposeSafe = await this.checkSafeProposalEligibility( + chain, + signerAddress, + safeAddress, + ); if ( - this.canPropose[chain].get(safeAddress) && - (await transactionSucceedsFromSender(chain, safeAddress)) + canProposeSafe && + (await checkTransactionSuccess(chain, safeAddress)) ) { - return { - type: SubmissionType.SAFE, - chain, - call, - }; + // If the transaction will succeed with the safe, return the inferred call + return { type: SubmissionType.SAFE, chain, call }; } } - // Only try ICA encoding if this isn't already an ICA call + // If we're not already an ICA call, try to infer an ICA call if (!isICACall) { return this.inferICAEncodedSubmissionType(chain, call); } - // If it is an ICA call and we've reached this point, default to manual submission - return { - type: SubmissionType.MANUAL, - chain, - call, - }; + // If the transaction will not succeed with SIGNER, SAFE or ICA, default to MANUAL submission + return { type: SubmissionType.MANUAL, chain, call }; + } + + private async checkSubmitterBalance( + chain: ChainName, + submitterAddress: Address, + requiredValue: BigNumber, + ): Promise { + const submitterBalance = await this.checker.multiProvider + .getProvider(chain) + .getBalance(submitterAddress); + if (submitterBalance.lt(requiredValue)) { + console.warn( + chalk.yellow( + `Submitter ${submitterAddress} has an insufficient balance for the call and is likely to fail. Balance: ${submitterBalance}, Balance required: ${requiredValue}`, + ), + ); + } + } + + private async checkSafeProposalEligibility( + chain: ChainName, + signerAddress: Address, + safeAddress: string, + ): Promise { + if (!this.canPropose[chain].has(safeAddress)) { + try { + const canPropose = await canProposeSafeTransactions( + signerAddress, + chain, + this.checker.multiProvider, + safeAddress, + ); + this.canPropose[chain].set(safeAddress, canPropose); + } catch (error) { + if ( + error instanceof Error && + (error.message.includes('Invalid MultiSend contract address') || + error.message.includes( + 'Invalid MultiSendCallOnly contract address', + )) + ) { + console.warn( + chalk.yellow(`${error.message}: Setting submission type to MANUAL`), + ); + return false; + } else { + console.error( + chalk.red( + `Failed to determine if signer can propose safe transactions on ${chain}. Setting submission type to MANUAL. Error: ${error}`, + ), + ); + return false; + } + } + } + return this.canPropose[chain].get(safeAddress) || false; } handleOwnerViolation(violation: OwnerViolation) { diff --git a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts index 61152058a..f4b9c7455 100644 --- a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts @@ -1,3 +1,4 @@ +import chalk from 'chalk'; import { BigNumber } from 'ethers'; import { @@ -76,14 +77,18 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor< return this.handleMailboxViolation(violation as MailboxViolation); } case CoreViolationType.ValidatorAnnounce: { - console.warn('Ignoring ValidatorAnnounce violation'); + console.warn(chalk.yellow('Ignoring ValidatorAnnounce violation')); return undefined; } case ViolationType.ProxyAdmin: { return this.handleProxyAdminViolation(violation as ProxyAdminViolation); } default: - throw new Error(`Unsupported violation type ${violation.type}`); + throw new Error( + `Unsupported violation type ${violation.type}: ${JSON.stringify( + violation, + )}`, + ); } } } diff --git a/typescript/infra/src/govern/HyperlaneHaasGovernor.ts b/typescript/infra/src/govern/HyperlaneHaasGovernor.ts index e1fca5731..921649c5b 100644 --- a/typescript/infra/src/govern/HyperlaneHaasGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneHaasGovernor.ts @@ -70,9 +70,9 @@ export class HyperlaneHaasGovernor extends HyperlaneAppGovernor< } } - async check() { - await this.icaChecker.check(); - await this.coreChecker.check(); + async check(chainsToCheck?: ChainName[]) { + await this.icaChecker.check(chainsToCheck); + await this.coreChecker.check(chainsToCheck); } async checkChain(chain: ChainName) { diff --git a/typescript/infra/src/govern/HyperlaneIgpGovernor.ts b/typescript/infra/src/govern/HyperlaneIgpGovernor.ts index 592301d9b..30528d230 100644 --- a/typescript/infra/src/govern/HyperlaneIgpGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneIgpGovernor.ts @@ -29,7 +29,11 @@ export class HyperlaneIgpGovernor extends HyperlaneAppGovernor< return super.handleOwnerViolation(violation as OwnerViolation); } default: - throw new Error(`Unsupported violation type ${violation.type}`); + throw new Error( + `Unsupported violation type ${violation.type}: ${JSON.stringify( + violation, + )}`, + ); } } diff --git a/typescript/infra/src/govern/ProxiedRouterGovernor.ts b/typescript/infra/src/govern/ProxiedRouterGovernor.ts index 6a5dfe655..d07c55395 100644 --- a/typescript/infra/src/govern/ProxiedRouterGovernor.ts +++ b/typescript/infra/src/govern/ProxiedRouterGovernor.ts @@ -12,6 +12,7 @@ import { RouterViolationType, ViolationType, } from '@hyperlane-xyz/sdk'; +import { stringifyObject } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from './HyperlaneAppGovernor.js'; @@ -54,19 +55,30 @@ export class ProxiedRouterGovernor< } protected handleEnrolledRouterViolation(violation: RouterViolation) { - const remoteDomain = this.checker.multiProvider.getDomainId( - violation.remoteChain, - ); + const expectedDomains: number[] = []; + const expectedAddresses: string[] = []; + + for (const [remoteChain, routerDiff] of Object.entries( + violation.routerDiff, + )) { + const remoteDomain = this.checker.multiProvider.getDomainId(remoteChain); + expectedDomains.push(remoteDomain); + expectedAddresses.push(routerDiff.expected); + } + return { chain: violation.chain, call: { to: violation.contract.address, data: violation.contract.interface.encodeFunctionData( - 'enrollRemoteRouter', - [remoteDomain, violation.expected], + 'enrollRemoteRouters', + [expectedDomains, expectedAddresses], ), value: BigNumber.from(0), - description: `Enroll router for remote chain ${violation.remoteChain} (${remoteDomain}) ${violation.expected} in ${violation.contract.address}`, + description: `Updating routers in ${violation.contract.address} for ${expectedDomains.length} remote chains`, + expandedDescription: `Updating routers for chains ${Object.keys( + violation.routerDiff, + ).join(', ')}:\n${stringifyObject(violation.routerDiff)}`, }, }; } diff --git a/typescript/infra/src/govern/multisend.ts b/typescript/infra/src/govern/multisend.ts index 26c066ff8..0b3792424 100644 --- a/typescript/infra/src/govern/multisend.ts +++ b/typescript/infra/src/govern/multisend.ts @@ -1,9 +1,15 @@ import SafeApiKit from '@safe-global/api-kit'; import Safe from '@safe-global/protocol-kit'; import { SafeTransaction } from '@safe-global/safe-core-sdk-types'; +import chalk from 'chalk'; import { ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; -import { Address, CallData, eqAddress } from '@hyperlane-xyz/utils'; +import { + Address, + CallData, + addBufferToGasLimit, + eqAddress, +} from '@hyperlane-xyz/utils'; import { createSafeTransaction, @@ -28,10 +34,10 @@ export class SignerMultiSend extends MultiSend { for (const call of calls) { const estimate = await this.multiProvider.estimateGas(this.chain, call); const receipt = await this.multiProvider.sendTransaction(this.chain, { - gasLimit: estimate.mul(11).div(10), // 10% buffer + gasLimit: addBufferToGasLimit(estimate), ...call, }); - console.log(`confirmed tx ${receipt.transactionHash}`); + console.log(chalk.green(`Confirmed tx ${receipt.transactionHash}`)); } } } @@ -69,8 +75,10 @@ export class SafeMultiSend extends MultiSend { // If the multiSend address is the same as the safe address, we need to // propose the transactions individually. See: gnosisSafe.js in the SDK. if (eqAddress(safeSdk.getMultiSendAddress(), this.safeAddress)) { - console.log( - `MultiSend contract not deployed on ${this.chain}. Proposing transactions individually.`, + console.info( + chalk.gray( + `MultiSend contract not deployed on ${this.chain}. Proposing transactions individually.`, + ), ); await this.proposeIndividualTransactions(calls, safeSdk, safeService); } else { @@ -110,6 +118,7 @@ export class SafeMultiSend extends MultiSend { safeService, this.safeAddress, safeTransactionData, + true, ); await this.proposeSafeTransaction(safeSdk, safeService, safeTransaction); } diff --git a/typescript/infra/src/utils/gcloud.ts b/typescript/infra/src/utils/gcloud.ts index 56d913b7c..3fc157b4c 100644 --- a/typescript/infra/src/utils/gcloud.ts +++ b/typescript/infra/src/utils/gcloud.ts @@ -35,6 +35,7 @@ export async function fetchGCPSecret( ); output = envVarOverride; } else { + debugLog(`Fetching GCP secret with name ${secretName}`); output = await fetchLatestGCPSecret(secretName); } @@ -74,8 +75,10 @@ function tryGCPSecretFromEnvVariable(gcpSecretName: string) { process.env.GCP_SECRET_OVERRIDES_ENABLED && process.env.GCP_SECRET_OVERRIDES_ENABLED.length > 0; if (!overridingEnabled) { + debugLog('GCP secret overrides disabled'); return undefined; } + debugLog('GCP secret overrides enabled'); const overrideEnvVarName = `GCP_SECRET_OVERRIDE_${gcpSecretName .replaceAll('-', '_') .toUpperCase()}`; diff --git a/typescript/infra/src/utils/safe.ts b/typescript/infra/src/utils/safe.ts index 512727493..fc03e9366 100644 --- a/typescript/infra/src/utils/safe.ts +++ b/typescript/infra/src/utils/safe.ts @@ -5,7 +5,8 @@ import { MetaTransactionData, SafeTransaction, } from '@safe-global/safe-core-sdk-types'; -import { ethers } from 'ethers'; +import chalk from 'chalk'; +import { BigNumber, ethers } from 'ethers'; import { ChainNameOrId, @@ -13,7 +14,10 @@ import { getSafe, getSafeService, } from '@hyperlane-xyz/sdk'; -import { Address, CallData } from '@hyperlane-xyz/utils'; +import { Address, CallData, eqAddress } from '@hyperlane-xyz/utils'; + +import safeSigners from '../../config/environments/mainnet3/safe/safeSigners.json' assert { type: 'json' }; +import { AnnotatedCallData } from '../govern/HyperlaneAppGovernor.js'; export async function getSafeAndService( chain: ChainNameOrId, @@ -42,10 +46,12 @@ export async function createSafeTransaction( safeService: SafeApiKit.default, safeAddress: Address, safeTransactionData: MetaTransactionData[], + onlyCalls?: boolean, ): Promise { const nextNonce = await safeService.getNextNonce(safeAddress); return safeSdk.createTransaction({ safeTransactionData, + onlyCalls, options: { nonce: nextNonce }, }); } @@ -70,7 +76,9 @@ export async function proposeSafeTransaction( senderSignature: senderSignature.data, }); - console.log(`Proposed transaction on ${chain} with hash ${safeTxHash}`); + console.log( + chalk.green(`Proposed transaction on ${chain} with hash ${safeTxHash}`), + ); } export async function deleteAllPendingSafeTxs( @@ -89,7 +97,9 @@ export async function deleteAllPendingSafeTxs( }); if (!pendingTxsResponse.ok) { - console.error(`Failed to fetch pending transactions for ${safeAddress}`); + console.error( + chalk.red(`Failed to fetch pending transactions for ${safeAddress}`), + ); return; } @@ -124,7 +134,9 @@ export async function deleteSafeTx( }); if (!txDetailsResponse.ok) { - console.error(`Failed to fetch transaction details for ${safeTxHash}`); + console.error( + chalk.red(`Failed to fetch transaction details for ${safeTxHash}`), + ); return; } @@ -132,7 +144,7 @@ export async function deleteSafeTx( const proposer = txDetails.proposer; if (!proposer) { - console.error(`No proposer found for transaction ${safeTxHash}`); + console.error(chalk.red(`No proposer found for transaction ${safeTxHash}`)); return; } @@ -140,7 +152,9 @@ export async function deleteSafeTx( const signerAddress = await signer.getAddress(); if (proposer !== signerAddress) { console.log( - `Skipping deletion of transaction ${safeTxHash} proposed by ${proposer}`, + chalk.italic( + `Skipping deletion of transaction ${safeTxHash} proposed by ${proposer}`, + ), ); return; } @@ -190,18 +204,71 @@ export async function deleteSafeTx( }); if (res.status === 204) { - console.log(`Successfully deleted transaction ${safeTxHash} on ${chain}`); + console.log( + chalk.green( + `Successfully deleted transaction ${safeTxHash} on ${chain}`, + ), + ); return; } const errorBody = await res.text(); console.error( - `Failed to delete transaction ${safeTxHash} on ${chain}: Status ${res.status} ${res.statusText}. Response body: ${errorBody}`, + chalk.red( + `Failed to delete transaction ${safeTxHash} on ${chain}: Status ${res.status} ${res.statusText}. Response body: ${errorBody}`, + ), ); } catch (error) { console.error( - `Failed to delete transaction ${safeTxHash} on ${chain}:`, + chalk.red(`Failed to delete transaction ${safeTxHash} on ${chain}:`), error, ); } } + +export async function updateSafeOwner( + safeSdk: Safe.default, +): Promise { + const threshold = await safeSdk.getThreshold(); + const owners = await safeSdk.getOwners(); + const newOwners = safeSigners.signers; + const ownersToRemove = owners.filter( + (owner) => !newOwners.some((newOwner) => eqAddress(owner, newOwner)), + ); + const ownersToAdd = newOwners.filter( + (newOwner) => !owners.some((owner) => eqAddress(newOwner, owner)), + ); + + console.log(chalk.magentaBright('Owners to remove:', ownersToRemove)); + console.log(chalk.magentaBright('Owners to add:', ownersToAdd)); + + const transactions: AnnotatedCallData[] = []; + + for (const ownerToRemove of ownersToRemove) { + const { data: removeTxData } = await safeSdk.createRemoveOwnerTx({ + ownerAddress: ownerToRemove, + threshold, + }); + transactions.push({ + to: removeTxData.to, + data: removeTxData.data, + value: BigNumber.from(removeTxData.value), + description: `Remove safe owner ${ownerToRemove}`, + }); + } + + for (const ownerToAdd of ownersToAdd) { + const { data: addTxData } = await safeSdk.createAddOwnerTx({ + ownerAddress: ownerToAdd, + threshold, + }); + transactions.push({ + to: addTxData.to, + data: addTxData.data, + value: BigNumber.from(addTxData.value), + description: `Add safe owner ${ownerToAdd}`, + }); + } + + return transactions; +} diff --git a/typescript/infra/src/warp/helm.ts b/typescript/infra/src/warp/helm.ts index 779a53584..bbb98a6d5 100644 --- a/typescript/infra/src/warp/helm.ts +++ b/typescript/infra/src/warp/helm.ts @@ -13,6 +13,7 @@ export class WarpRouteMonitorHelmManager extends HelmManager { constructor( readonly configFilePath: string, readonly runEnv: DeployEnvironment, + readonly environmentChainNames: string[], ) { super(); } @@ -26,10 +27,14 @@ export class WarpRouteMonitorHelmManager extends HelmManager { return { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '38ff1c4-20240823-093934', + tag: '8e2f616-20241025-163752', }, configFilePath: pathRelativeToMonorepoRoot, fullnameOverride: this.helmReleaseName, + environment: this.runEnv, + hyperlane: { + chains: this.environmentChainNames, + }, }; } diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 72fdc1a2a..a37619fdf 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,140 @@ # @hyperlane-xyz/sdk +## 5.6.2 + +### Patch Changes + +- 5fd4267e7: Supported non-32 byte non-EVM recipients when sending warps from Sealevel +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] +- Updated dependencies [a42616ff3] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/core@5.6.1 + +## 5.6.1 + +### Patch Changes + +- Updated dependencies [8cc0d9a4a] +- Updated dependencies [c55257cf5] +- Updated dependencies [8cc0d9a4a] + - @hyperlane-xyz/core@5.6.0 + - @hyperlane-xyz/utils@5.6.1 + +## 5.6.0 + +### Minor Changes + +- 46044a2e9: Deploy to odysseytestnet +- 02a5b92ba: Enroll new validators. Add tx overrides when deploying ICA accounts. Core checker now surfaces owner violations for defaultHook and requiredHook. App checker temporarily ignores bytecode mismatch violations. +- 29341950e: Adds new `core check` command to compare local configuration and on chain deployments. Adds memoization to the EvmHookReader to avoid repeating configuration derivation +- 8001bbbd6: Add override to some transactions to fix warp apply +- 32d0a67c2: Adds the warp check command to compare warp routes config files with on chain warp route deployments +- b1ff48bd1: Add rebasing yield route support into CLI/SDK +- d41aa6928: Add `EthJsonRpcBlockParameterTag` enum for validating reorgPeriod +- c3e9268f1: Add support for an arbitrary string in `reorgPeriod`, which is used as a block tag to get the finalized block. +- 7d7bcc1a3: Add deployments for mainnets: flow, metall2, polynomial + +### Patch Changes + +- 7f3e0669d: Fix filtering non-evm addresses in appFromAddressesMapHelper +- 2317eca3c: Set transaction overrides and add 10% gas limit buffer when sending message through HyperlaneCore. +- Updated dependencies [f1712deb7] +- Updated dependencies [29341950e] +- Updated dependencies [c9085afd9] +- Updated dependencies [ec6b874b1] +- Updated dependencies [72c23c0d6] + - @hyperlane-xyz/utils@5.6.0 + - @hyperlane-xyz/core@5.5.0 + +## 5.5.0 + +### Minor Changes + +- 2afc484a2: Break out BlockExplorerSchema and export separately + Migrate RPC + Explorer health tests back to SDK from registry +- 3254472e0: Add deployments for chains: immutablezkevm, rari, rootstock, alephzeroevm, chiliz, lumia, and superposition +- 6176c9861: Add opstack, polygoncdk, polkadotsubstrate and zksync to ChainTechnicalStack enum + +### Patch Changes + +- fcfe91113: Reuse SDK transaction typings in tx submitters +- Updated dependencies [92c86cca6] +- Updated dependencies [2afc484a2] + - @hyperlane-xyz/core@5.4.1 + - @hyperlane-xyz/utils@5.5.0 + +## 5.4.0 + +### Minor Changes + +- 4415ac224: Add Gnosis safe transaction builder to warp apply + +### Patch Changes + +- Updated dependencies [bb75eba74] +- Updated dependencies [4415ac224] +- Updated dependencies [c5c217f8e] + - @hyperlane-xyz/core@5.4.0 + - @hyperlane-xyz/utils@5.4.0 + +## 5.3.0 + +### Patch Changes + +- eb47aaee8: Use collateral account for sealevel native warp route balance +- 50319d8ba: Make HyperlaneDeployer.chainTimeoutMs public. + Remove HyperlaneDeployer.startingBlockNumbers as it's not used by any deployer. + Update HyperlaneDeployer.deploy for better logging and error handling. +- 8de531fa4: fix: warn on submodule metadata builder failures +- fd536a79a: Include priority fee instruction with SVM warp transfers +- Updated dependencies [746eeb9d9] +- Updated dependencies [50319d8ba] + - @hyperlane-xyz/utils@5.3.0 + - @hyperlane-xyz/core@5.3.0 + +## 5.2.1 + +### Patch Changes + +- Updated dependencies [eb5afcf3e] + - @hyperlane-xyz/core@5.2.1 + - @hyperlane-xyz/utils@5.2.1 + +## 5.2.0 + +### Minor Changes + +- a19e882fd: Improve Router Checker/Governor tooling to support enrolling multiple routers for missing domains +- 203084df2: Added sdk support for Stake weighted ISM +- 74a592e58: Adds OwnerCollateral to token mapping which will output the correct standard to the warp deploy artifact. +- 739af9a34: Support providing multiple chains for checking in HyperlaneAppChecker +- 44588c31d: Enroll new validators for cyber degenchain kroma lisk lukso merlin metis mint proofofplay real sanko tangle xai taiko +- 291c5fe36: Use addBufferToGasLimit from @hyperlane-xyz/utils +- 69f17d99a: Fix to correctly infer the default set of multisend addresses for a given chain, and update to latest safe-deployments patch release +- 9563a8beb: Sorted cwNative funds by denom in transfer tx +- 73c232b3a: Deploy to oortmainnet +- 445b6222c: ArbL2ToL1Ism handles value via the executeTransaction branch +- d6de34ad5: Sort values in EvmModuleDeployer.deployStaticAddressSet +- 2e6176f67: Deploy to everclear mainnet +- f2783c03b: Add ChainSubmissionStrategySchema +- 3c07ded5b: Add Safe submit functionality to warp apply + +### Patch Changes + +- 518a1bef9: add 10% gas bump to initialize call in EvmModuleDeployer +- 2bd540e0f: Estimate and add 10% gas bump for ICA initialization and enrollment +- 3ad5918da: Support DefaultFallbackRoutingIsm in metadata builder +- 2ffb78f5c: Improved check for mailbox initialization +- 815542dd7: Fix arg validation for Sealevel HypNative adapters + Allow extra properties in ChainMetadata objects +- Updated dependencies [d6de34ad5] +- Updated dependencies [203084df2] +- Updated dependencies [291c5fe36] +- Updated dependencies [445b6222c] + - @hyperlane-xyz/utils@5.2.0 + - @hyperlane-xyz/core@5.2.0 + ## 5.1.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 8c612aade..133a57927 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,17 +1,17 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "5.1.0", + "version": "5.6.2", "dependencies": { "@arbitrum/sdk": "^4.0.0", "@aws-sdk/client-s3": "^3.74.0", "@cosmjs/cosmwasm-stargate": "^0.32.4", "@cosmjs/stargate": "^0.32.4", - "@hyperlane-xyz/core": "5.1.0", - "@hyperlane-xyz/utils": "5.1.0", + "@hyperlane-xyz/core": "5.6.1", + "@hyperlane-xyz/utils": "5.6.2", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", - "@safe-global/safe-deployments": "1.37.3", + "@safe-global/safe-deployments": "1.37.8", "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.0", "@types/coingecko-api": "^1.0.10", @@ -33,7 +33,7 @@ "@types/sinon": "^17.0.1", "@types/sinon-chai": "^3.2.12", "@types/ws": "^8.5.5", - "chai": "^4.3.6", + "chai": "4.5.0", "dotenv": "^10.0.0", "eslint": "^8.57.0", "ethereum-waffle": "^4.0.10", @@ -44,7 +44,7 @@ "ts-node": "^10.8.0", "tsx": "^4.7.1", "typescript": "5.3.3", - "yaml": "^2.4.1" + "yaml": "2.4.5" }, "type": "module", "exports": { diff --git a/typescript/sdk/src/consts/concurrency.ts b/typescript/sdk/src/consts/concurrency.ts index 62e6c801c..d9cc8fa7c 100644 --- a/typescript/sdk/src/consts/concurrency.ts +++ b/typescript/sdk/src/consts/concurrency.ts @@ -1 +1 @@ -export const DEFAULT_CONTRACT_READ_CONCURRENCY = 20; +export const DEFAULT_CONTRACT_READ_CONCURRENCY = 1; diff --git a/typescript/sdk/src/consts/multisigIsm.test.ts b/typescript/sdk/src/consts/multisigIsm.test.ts index 58b84c7cb..53f93dc16 100644 --- a/typescript/sdk/src/consts/multisigIsm.test.ts +++ b/typescript/sdk/src/consts/multisigIsm.test.ts @@ -1,5 +1,7 @@ import { expect } from 'chai'; +import { isAddress } from '@hyperlane-xyz/utils'; + import { defaultMultisigConfigs } from './multisigIsm.js'; describe('MultisigIsm', () => { @@ -13,5 +15,25 @@ describe('MultisigIsm', () => { ); } }); + + it('has a valid number of validators for each threshold', async () => { + for (const [chain, config] of Object.entries(defaultMultisigConfigs)) { + expect(config.validators.length).to.be.greaterThanOrEqual( + config.threshold, + `Number of validators for ${chain} is less than the threshold, expected at least ${config.threshold}, got ${config.validators.length}`, + ); + } + }); + + it('has valid EVM addresses for each validator', async () => { + for (const [chain, config] of Object.entries(defaultMultisigConfigs)) { + for (const validator of config.validators) { + expect(isAddress(validator)).to.equal( + true, + `Validator address ${validator} for ${chain} is not a valid EVM address`, + ); + } + } + }); }); }); diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index a519f08d8..27478ed9e 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -3,6 +3,15 @@ import { ChainMap } from '../types.js'; // TODO: consider migrating these to the registry too export const defaultMultisigConfigs: ChainMap = { + alephzeroevm: { + threshold: 2, + validators: [ + '0xcae8fab142adc4e434bb7409e40dd932cc3851aa', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + alfajores: { threshold: 2, validators: [ @@ -21,6 +30,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + apechain: { + threshold: 1, + validators: ['0x773d7fe6ffb1ba4de814c28044ff9a2d83a48221'], + }, + arbitrum: { threshold: 3, validators: [ @@ -32,19 +46,37 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + arbitrumnova: { + threshold: 1, + validators: ['0xd2a5e9123308d187383c87053811a2c21bd8af1f'], + }, + arbitrumsepolia: { threshold: 1, validators: ['0x09fabfbca0b8bf042e2a1161ee5010d147b0f603'], }, - astar: { + arcadiatestnet: { threshold: 1, - validators: ['0x4d1b2cade01ee3493f44304653d8e352c66ec3e7'], + validators: ['0x7ce5973d3f22971546efb86f5a0417c1248e92f5'], + }, + + astar: { + threshold: 2, + validators: [ + '0x4d1b2cade01ee3493f44304653d8e352c66ec3e7', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], }, astarzkevm: { - threshold: 1, - validators: ['0x89ecdd6caf138934bf3a2fb7b323984d72fd66de'], + threshold: 2, + validators: [ + '0x89ecdd6caf138934bf3a2fb7b323984d72fd66de', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], }, avalanche: { @@ -56,6 +88,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + b3: { + threshold: 1, + validators: ['0xd77b516730a836fc41934e7d5864e72c165b934e'], + }, + base: { threshold: 3, validators: [ @@ -72,9 +109,18 @@ export const defaultMultisigConfigs: ChainMap = { validators: ['0x82e3b437a2944e3ff00258c93e72cd1ba5e0e921'], }, - bitlayer: { + berabartio: { threshold: 1, - validators: ['0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f'], + validators: ['0x541dd3cb282cf869d72883557badae245b63e1fd'], + }, + + bitlayer: { + threshold: 2, + validators: [ + '0x1d9b0f4ea80dbfc71cb7d64d8005eccf7c41e75f', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], }, blast: { @@ -114,6 +160,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + camptestnet: { + threshold: 1, + validators: ['0x238f40f055a7ff697ea6dbff3ae943c9eae7a38e'], + }, + celo: { threshold: 3, validators: [ @@ -142,14 +193,32 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + chiliz: { + threshold: 2, + validators: [ + '0x82d024f453b1a3f3f6606226f06b038da27596f3', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + + citreatestnet: { + threshold: 1, + validators: ['0x60d7380a41eb95c49be18f141efd2fde5e3dba20'], + }, + connextsepolia: { threshold: 1, validators: ['0xffbbec8c499585d80ef69eb613db624d27e089ab'], }, coredao: { - threshold: 1, - validators: ['0xbd6e158a3f5830d99d7d2bce192695bc4a148de2'], + threshold: 2, + validators: [ + '0xbd6e158a3f5830d99d7d2bce192695bc4a148de2', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], }, cyber: { @@ -171,13 +240,22 @@ export const defaultMultisigConfigs: ChainMap = { }, dogechain: { - threshold: 1, - validators: ['0xe43f742c37858746e6d7e458bc591180d0cba440'], + threshold: 2, + validators: [ + '0xe43f742c37858746e6d7e458bc591180d0cba440', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], }, eclipsemainnet: { - threshold: 1, - validators: ['0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0'], + threshold: 3, + validators: [ + '0xebb52d7eaa3ff7a5a6260bfe5111ce52d57401d0', + '0x3571223e745dc0fcbdefa164c9b826b90c0d2dac', // luganodes + '0xea83086a62617a7228ce4206fae2ea8b0ab23513', // imperator + '0x4d4629f5bfeabe66edc7a78da26ef5273c266f97', // eclipse + ], }, eclipsetestnet: { @@ -212,9 +290,41 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + everclear: { + threshold: 2, + validators: [ + '0xeff20ae3d5ab90abb11e882cfce4b92ea6c74837', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0xD79DFbF56ee2268f061cc613027a44A880f61Ba2', // everclear + ], + }, + + fantom: { + threshold: 1, + validators: ['0xa779572028e634e16f26af5dfd4fa685f619457d'], + }, + flare: { + threshold: 2, + validators: [ + '0xb65e52be342dba3ab2c088ceeb4290c744809134', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + + flow: { + threshold: 2, + validators: [ + '0x3aee1090318e9c54d1d23194dcd0f2bee00ddc97', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + + formtestnet: { threshold: 1, - validators: ['0xb65e52be342dba3ab2c088ceeb4290c744809134'], + validators: ['0x72ad7fddf16d17ff902d788441151982fa31a7bc'], }, fraxtal: { @@ -222,6 +332,7 @@ export const defaultMultisigConfigs: ChainMap = { validators: [ '0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1', '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x25b3a88f7cfd3c9f7d7e32b295673a16a6ddbd91', // luganodes ], }, @@ -253,11 +364,35 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + gravity: { + threshold: 1, + validators: ['0x23d549bf757a02a6f6068e9363196ecd958c974e'], + }, + + harmony: { + threshold: 1, + validators: ['0xd677803a67651974b1c264171b5d7ca8838db8d5'], + }, + holesky: { threshold: 1, validators: ['0x7ab28ad88bb45867137ea823af88e2cb02359c03'], // TODO }, + hyperliquidevmtestnet: { + threshold: 1, + validators: ['0xea673a92a23ca319b9d85cc16b248645cd5158da'], + }, + + immutablezkevm: { + threshold: 2, + validators: [ + '0xa787c2952a4d22f776ee6e87e828e6f75de24330', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + inevm: { threshold: 2, validators: [ @@ -276,6 +411,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + kaia: { + threshold: 1, + validators: ['0x9de0b3abb221d19719882fa4d61f769fdc2be9a4'], + }, + kroma: { threshold: 2, validators: [ @@ -308,6 +448,16 @@ export const defaultMultisigConfigs: ChainMap = { validators: [ '0xa5e953701dcddc5b958b5defb677a829d908df6d', '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x101cE77261245140A0871f9407d6233C8230Ec47', // blockhunters + ], + }, + + lumia: { + threshold: 2, + validators: [ + '0x9e283254ed2cd2c80f007348c2822fc8e5c2fa5f', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis ], }, @@ -342,6 +492,15 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + metall2: { + threshold: 2, + validators: [ + '0x1b000e1e1f0a032ed382c6d69a2d58f6fe773c09', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + metis: { threshold: 2, validators: [ @@ -361,16 +520,22 @@ export const defaultMultisigConfigs: ChainMap = { }, mode: { - threshold: 2, + threshold: 3, validators: [ '0x7eb2e1920a4166c19d6884c1cec3d2cf356fc9b7', '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + '0x7e29608c6e5792bbf9128599ca309be0728af7b4', // renzo + '0x101cE77261245140A0871f9407d6233C8230Ec47', // blockhunters ], }, molten: { - threshold: 1, - validators: ['0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f'], + threshold: 2, + validators: [ + '0xad5aa33f0d67f6fa258abbe75458ea4908f1dc9f', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], }, moonbeam: { @@ -383,6 +548,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + morph: { + threshold: 1, + validators: ['0x4884535f393151ec419add872100d352f71af380'], + }, + neutron: { threshold: 4, validators: [ @@ -396,6 +566,20 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + odysseytestnet: { + threshold: 1, + validators: ['0xcc0a6e2d6aa8560b45b384ced7aa049870b66ea3'], + }, + + oortmainnet: { + threshold: 2, + validators: [ + '0x9b7ff56cd9aa69006f73f1c5b8c63390c706a5d7', + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + '0x032dE4f94676bF9314331e7D83E8Db4aC74c9E21', // oort + ], + }, + optimism: { threshold: 3, validators: [ @@ -412,6 +596,11 @@ export const defaultMultisigConfigs: ChainMap = { validators: ['0x03efe4d0632ee15685d7e8f46dea0a874304aa29'], }, + orderly: { + threshold: 1, + validators: ['0xec3dc91f9fa2ad35edf5842aa764d5573b778bb6'], + }, + osmosis: { threshold: 1, validators: ['0xea483af11c19fa41b16c31d1534c2a486a92bcac'], @@ -446,6 +635,15 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + polynomial: { + threshold: 2, + validators: [ + '0xa63ad0891e921ad5947d57e05831fabb9816eca7', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + proofofplay: { threshold: 2, validators: [ @@ -455,6 +653,15 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + rari: { + threshold: 2, + validators: [ + '0x989d6862e09de21337078efbd86843a3eb1133e3', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + real: { threshold: 2, validators: [ @@ -469,6 +676,16 @@ export const defaultMultisigConfigs: ChainMap = { validators: [ '0x1400b9737007f7978d8b4bbafb4a69c83f0641a7', '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + + rootstock: { + threshold: 2, + validators: [ + '0xcb8e3a72cf427feff27416d0e2ec375a052eaaee', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis ], }, @@ -501,10 +718,12 @@ export const defaultMultisigConfigs: ChainMap = { }, sei: { - threshold: 2, + threshold: 3, validators: [ '0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5', '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x101cE77261245140A0871f9407d6233C8230Ec47', // blockhunters + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis ], }, @@ -518,8 +737,17 @@ export const defaultMultisigConfigs: ChainMap = { }, shibarium: { + threshold: 2, + validators: [ + '0xfa33391ee38597cbeef72ccde8c9e13e01e78521', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + + snaxchain: { threshold: 1, - validators: ['0xfa33391ee38597cbeef72ccde8c9e13e01e78521'], + validators: ['0x2c25829ae32a772d2a49f6c4b34f8b01fd03ef9e'], }, solanadevnet: { @@ -532,8 +760,14 @@ export const defaultMultisigConfigs: ChainMap = { }, solanamainnet: { - threshold: 1, - validators: ['0x28464752829b3ea59a497fca0bdff575c534c3ff'], + threshold: 3, + validators: [ + '0x28464752829b3ea59a497fca0bdff575c534c3ff', + '0x2b7514a2f77bd86bbf093fe6bb67d8611f51c659', // luganodes + '0xd90ea26ff731d967c5ea660851f7d63cb04ab820', // dsrv + '0x38c7a4ca1273ead2e867d096adbcdd0e2acb21d8', // everstake + '0xcb6bcbd0de155072a7ff486d9d7286b0f71dcc2d', // eclipse + ], }, solanatestnet: { @@ -541,6 +775,16 @@ export const defaultMultisigConfigs: ChainMap = { validators: ['0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5'], }, + soneiumtestnet: { + threshold: 1, + validators: ['0x2e2101020ccdbe76aeda1c27823b0150f43d0c63'], + }, + + sonictestnet: { + threshold: 1, + validators: ['0x62e6591d00daec3fb658c3d19403828b4e9ddbb3'], + }, + stride: { threshold: 6, validators: [ @@ -557,6 +801,20 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + suavetoliman: { + threshold: 1, + validators: ['0xf58f6e30aabba34e8dd7f79b3168507192e2cc9b'], + }, + + superposition: { + threshold: 2, + validators: [ + '0x5978d0e6afa9270ddb87cff43a8fa7a763a5dfc4', + '0xCF0211faFBb91FD9D06D7E306B30032DC3A1934f', // merkly + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + ], + }, + superpositiontestnet: { threshold: 1, validators: ['0x1d3168504b23b73cdf9c27f13bb0a595d7f1a96a'], @@ -581,6 +839,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + unichaintestnet: { + threshold: 1, + validators: ['0x5e99961cf71918308c3b17ef21b5f515a4f86fe5'], + }, + viction: { threshold: 2, validators: [ @@ -617,11 +880,18 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + zeronetwork: { + threshold: 1, + validators: ['0x1bd9e3f8a90ea1a13b0f2838a1858046368aad87'], + }, + zetachain: { - threshold: 2, + threshold: 3, validators: [ '0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef', '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + '0x101cE77261245140A0871f9407d6233C8230Ec47', // blockhunters + '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis ], }, @@ -635,6 +905,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + zksync: { + threshold: 1, + validators: ['0xadd1d39ce7a687e32255ac457cf99a6d8c5b5d1a'], + }, + zoramainnet: { threshold: 3, validators: [ diff --git a/typescript/sdk/src/contracts/contracts.ts b/typescript/sdk/src/contracts/contracts.ts index 90b754e8c..aadeba085 100644 --- a/typescript/sdk/src/contracts/contracts.ts +++ b/typescript/sdk/src/contracts/contracts.ts @@ -1,10 +1,11 @@ -import { Contract, ethers } from 'ethers'; +import { Contract } from 'ethers'; -import { Ownable } from '@hyperlane-xyz/core'; +import { Ownable, Ownable__factory } from '@hyperlane-xyz/core'; import { Address, ProtocolType, ValueOf, + eqAddress, hexOrBase58ToHex, objFilter, objMap, @@ -12,8 +13,10 @@ import { promiseObjAll, } from '@hyperlane-xyz/utils'; +import { OwnableConfig } from '../deploy/types.js'; import { ChainMetadataManager } from '../metadata/ChainMetadataManager.js'; import { MultiProvider } from '../providers/MultiProvider.js'; +import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; import { ChainMap, Connection } from '../types.js'; import { @@ -235,31 +238,15 @@ export function appFromAddressesMapHelper( contractsMap: HyperlaneContractsMap; multiProvider: MultiProvider; } { - // Hack to accommodate non-Ethereum artifacts, while still retaining their - // presence in the addressesMap so that they are included in the list of chains - // on the MultiProvider (needed for getting metadata). A non-Ethereum-style address - // from another execution environment will cause Ethers to throw if we try to attach - // it, so we just replace it with the zero address. - const addressesMapWithEthereumizedAddresses = objMap( + // Filter out non-Ethereum chains from the addressesMap + const ethereumAddressesMap = objFilter( addressesMap, - (chain, addresses) => { - const metadata = multiProvider.getChainMetadata(chain); - if (metadata.protocol === ProtocolType.Ethereum) { - return addresses; - } - return objMap( - addresses, - (_key, _address) => ethers.constants.AddressZero, - ); - }, + (chain, _): _ is HyperlaneAddresses => + multiProvider.getProtocol(chain) === ProtocolType.Ethereum, ); - // Attaches contracts for each chain for which we have a complete set of - // addresses - const contractsMap = attachContractsMap( - addressesMapWithEthereumizedAddresses, - factories, - ); + // Attaches contracts for each Ethereum chain for which we have a complete set of addresses + const contractsMap = attachContractsMap(ethereumAddressesMap, factories); // Filters out providers for chains for which we don't have a complete set // of addresses @@ -270,6 +257,32 @@ export function appFromAddressesMapHelper( return { contractsMap: filteredContractsMap, - multiProvider: multiProvider, + multiProvider, }; } + +export function transferOwnershipTransactions( + chainId: number, + contract: Address, + actual: OwnableConfig, + expected: OwnableConfig, + label?: string, +): AnnotatedEV5Transaction[] { + if (eqAddress(actual.owner, expected.owner)) { + return []; + } + + return [ + { + chainId, + annotation: `Transferring ownership of ${label ?? contract} from ${ + actual.owner + } to ${expected.owner}`, + to: contract, + data: Ownable__factory.createInterface().encodeFunctionData( + 'transferOwnership', + [expected.owner], + ), + }, + ]; +} diff --git a/typescript/sdk/src/core/AbstractHyperlaneModule.ts b/typescript/sdk/src/core/AbstractHyperlaneModule.ts index d4eadabae..b4a42ec30 100644 --- a/typescript/sdk/src/core/AbstractHyperlaneModule.ts +++ b/typescript/sdk/src/core/AbstractHyperlaneModule.ts @@ -1,17 +1,8 @@ import { Logger } from 'pino'; -import { Ownable__factory } from '@hyperlane-xyz/core'; -import { - Address, - Annotated, - ProtocolType, - eqAddress, -} from '@hyperlane-xyz/utils'; +import { Annotated, ProtocolType } from '@hyperlane-xyz/utils'; -import { - AnnotatedEV5Transaction, - ProtocolTypedTransaction, -} from '../providers/ProviderType.js'; +import { ProtocolTypedTransaction } from '../providers/ProviderType.js'; import { ChainNameOrId } from '../types.js'; export type HyperlaneModuleParams< @@ -43,40 +34,6 @@ export abstract class HyperlaneModule< config: TConfig, ): Promise['transaction'][]>>; - /** - * Transfers ownership of a contract to a new owner. - * - * @param actualOwner - The current owner of the contract. - * @param expectedOwner - The expected new owner of the contract. - * @param deployedAddress - The address of the deployed contract. - * @param chainId - The chain ID of the network the contract is deployed on. - * @returns An array of annotated EV5 transactions that need to be executed to update the owner. - */ - static createTransferOwnershipTx(params: { - actualOwner: Address; - expectedOwner: Address; - deployedAddress: Address; - chainId: number; - }): AnnotatedEV5Transaction[] { - const { actualOwner, expectedOwner, deployedAddress, chainId } = params; - const updateTransactions: AnnotatedEV5Transaction[] = []; - if (eqAddress(actualOwner, expectedOwner)) { - return []; - } - - updateTransactions.push({ - annotation: `Transferring ownership of ${deployedAddress} from current owner ${actualOwner} to new owner ${expectedOwner}`, - chainId, - to: deployedAddress, - data: Ownable__factory.createInterface().encodeFunctionData( - 'transferOwnership(address)', - [expectedOwner], - ), - }); - - return updateTransactions; - } - // /* // Types and static methods can be challenging. Ensure each implementation includes a static create function. // Currently, include TConfig to maintain the structure for ISM/Hook configurations. diff --git a/typescript/sdk/src/core/EvmCoreModule.hardhat-test.ts b/typescript/sdk/src/core/EvmCoreModule.hardhat-test.ts index 509e443d1..13f81f92e 100644 --- a/typescript/sdk/src/core/EvmCoreModule.hardhat-test.ts +++ b/typescript/sdk/src/core/EvmCoreModule.hardhat-test.ts @@ -10,13 +10,14 @@ import { TimelockController__factory, ValidatorAnnounce__factory, } from '@hyperlane-xyz/core'; -import { normalizeConfig, objMap } from '@hyperlane-xyz/utils'; +import { objMap } from '@hyperlane-xyz/utils'; import { TestChainName } from '../consts/testChains.js'; import { IsmConfig, IsmType } from '../ism/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; import { randomAddress, testCoreConfig } from '../test/testUtils.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmCoreModule } from './EvmCoreModule.js'; import { CoreConfig } from './types.js'; @@ -104,10 +105,12 @@ describe('EvmCoreModule', async () => { }); it('should deploy ISM factories', () => { - // Each ISM factory - const deployedContracts = evmCoreModule.serialize(); + // Each ISM factory is a contract that is deployed by the core module + // Ignore IGP because it's not part of the default config + const { interchainGasPaymaster: _, ...coreContracts } = + evmCoreModule.serialize(); - objMap(deployedContracts as any, (_, address) => { + objMap(coreContracts as any, (_, address) => { expect(address).to.exist; expect(address).to.not.equal(constants.AddressZero); }); diff --git a/typescript/sdk/src/core/EvmCoreModule.ts b/typescript/sdk/src/core/EvmCoreModule.ts index 1ab4ef649..1be81d98f 100644 --- a/typescript/sdk/src/core/EvmCoreModule.ts +++ b/typescript/sdk/src/core/EvmCoreModule.ts @@ -10,8 +10,12 @@ import { import { attachContractsMap, serializeContractsMap, + transferOwnershipTransactions, } from '../contracts/contracts.js'; -import { HyperlaneAddresses } from '../contracts/types.js'; +import { + HyperlaneAddresses, + HyperlaneContractsMap, +} from '../contracts/types.js'; import { DeployedCoreAddresses } from '../core/schemas.js'; import { CoreConfig } from '../core/types.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; @@ -20,6 +24,7 @@ import { proxyFactoryFactories, } from '../deploy/contracts.js'; import { ContractVerifier } from '../deploy/verify/ContractVerifier.js'; +import { HookFactories } from '../hook/contracts.js'; import { EvmIsmModule } from '../ism/EvmIsmModule.js'; import { DerivedIsmConfig } from '../ism/EvmIsmReader.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; @@ -35,6 +40,7 @@ import { import { EvmCoreReader } from './EvmCoreReader.js'; import { EvmIcaModule } from './EvmIcaModule.js'; import { HyperlaneCoreDeployer } from './HyperlaneCoreDeployer.js'; +import { CoreFactories } from './contracts.js'; import { CoreConfigSchema } from './schemas.js'; export class EvmCoreModule extends HyperlaneModule< @@ -196,12 +202,13 @@ export class EvmCoreModule extends HyperlaneModule< actualConfig: CoreConfig, expectedConfig: CoreConfig, ): AnnotatedEV5Transaction[] { - return EvmCoreModule.createTransferOwnershipTx({ - actualOwner: actualConfig.owner, - expectedOwner: expectedConfig.owner, - deployedAddress: this.args.addresses.mailbox, - chainId: this.domainId, - }); + return transferOwnershipTransactions( + this.domainId, + this.args.addresses.mailbox, + actualConfig, + expectedConfig, + 'Mailbox', + ); } /** @@ -315,9 +322,20 @@ export class EvmCoreModule extends HyperlaneModule< ) ).address; + // Obtain addresses of every contract created by the deployer + // and extract only the merkleTreeHook and interchainGasPaymaster + const serializedContracts = serializeContractsMap( + coreDeployer.deployedContracts as HyperlaneContractsMap< + CoreFactories & HookFactories + >, + ); + const { merkleTreeHook, interchainGasPaymaster } = + serializedContracts[chainName]; + // Set Core & extra addresses return { ...ismFactoryFactories, + proxyAdmin, mailbox: mailbox.address, interchainAccountRouter, @@ -325,6 +343,8 @@ export class EvmCoreModule extends HyperlaneModule< validatorAnnounce, timelockController, testRecipient, + merkleTreeHook, + interchainGasPaymaster, }; } diff --git a/typescript/sdk/src/core/HyperlaneCore.ts b/typescript/sdk/src/core/HyperlaneCore.ts index 16391b2a8..8b1425d10 100644 --- a/typescript/sdk/src/core/HyperlaneCore.ts +++ b/typescript/sdk/src/core/HyperlaneCore.ts @@ -12,6 +12,7 @@ import { Address, AddressBytes32, ProtocolType, + addBufferToGasLimit, addressToBytes32, bytes32ToAddress, isZeroishAddress, @@ -25,7 +26,10 @@ import { import { HyperlaneApp } from '../app/HyperlaneApp.js'; import { appFromAddressesMapHelper } from '../contracts/contracts.js'; -import { HyperlaneAddressesMap } from '../contracts/types.js'; +import { + HyperlaneAddressesMap, + HyperlaneContracts, +} from '../contracts/types.js'; import { OwnableConfig } from '../deploy/types.js'; import { DerivedHookConfig, EvmHookReader } from '../hook/EvmHookReader.js'; import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js'; @@ -38,6 +42,10 @@ import { CoreFactories, coreFactories } from './contracts.js'; import { DispatchEvent } from './events.js'; import { DispatchedMessage } from './types.js'; +// If no metadata is provided, ensure we provide a default of 0x0001. +// We set to 0x0001 instead of 0x0 to ensure it does not break on zksync. +const DEFAULT_METADATA = '0x0001'; + export class HyperlaneCore extends HyperlaneApp { static fromAddressesMap( addressesMap: HyperlaneAddressesMap, @@ -54,9 +62,16 @@ export class HyperlaneCore extends HyperlaneApp { getRouterConfig = ( owners: Address | ChainMap, ): ChainMap => { + // filter for EVM chains + const evmContractsMap = objFilter( + this.contractsMap, + (chainName, _): _ is HyperlaneContracts => + this.multiProvider.getProtocol(chainName) === ProtocolType.Ethereum, + ); + // get config const config = objMap( - this.contractsMap, + evmContractsMap, (chain, contracts): RouterConfig => ({ mailbox: contracts.mailbox.address, owner: typeof owners === 'string' ? owners : owners[chain].owner, @@ -64,12 +79,8 @@ export class HyperlaneCore extends HyperlaneApp { typeof owners === 'string' ? undefined : owners[chain].ownerOverrides, }), ); - // filter for EVM chains - return objFilter( - config, - (chainName, _): _ is RouterConfig => - this.multiProvider.getProtocol(chainName) === ProtocolType.Ethereum, - ); + + return config; }; quoteGasPayment = ( @@ -87,7 +98,7 @@ export class HyperlaneCore extends HyperlaneApp { destinationId, recipient, body, - metadata || '0x', + metadata || DEFAULT_METADATA, hook || ethers.constants.AddressZero, ); }; @@ -147,17 +158,31 @@ export class HyperlaneCore extends HyperlaneApp { metadata, hook, ); + + const dispatchParams = [ + destinationDomain, + recipientBytes32, + body, + metadata || DEFAULT_METADATA, + hook || ethers.constants.AddressZero, + ] as const; + + const estimateGas = await mailbox.estimateGas[ + 'dispatch(uint32,bytes32,bytes,bytes,address)' + ](...dispatchParams, { value: quote }); + const dispatchTx = await this.multiProvider.handleTx( origin, mailbox['dispatch(uint32,bytes32,bytes,bytes,address)']( - destinationDomain, - recipientBytes32, - body, - metadata || '0x', - hook || ethers.constants.AddressZero, - { value: quote }, + ...dispatchParams, + { + ...this.multiProvider.getTransactionOverrides(origin), + value: quote, + gasLimit: addBufferToGasLimit(estimateGas), + }, ), ); + return { dispatchTx, message: this.getDispatchedMessages(dispatchTx)[0], @@ -235,11 +260,14 @@ export class HyperlaneCore extends HyperlaneApp { ismMetadata: string, ): Promise { const destinationChain = this.getDestination(message); + const txOverrides = + this.multiProvider.getTransactionOverrides(destinationChain); return this.multiProvider.handleTx( destinationChain, this.getContracts(destinationChain).mailbox.process( ismMetadata, message.message, + { ...txOverrides }, ), ); } diff --git a/typescript/sdk/src/core/HyperlaneCoreChecker.ts b/typescript/sdk/src/core/HyperlaneCoreChecker.ts index 5fc7f014d..82a26c405 100644 --- a/typescript/sdk/src/core/HyperlaneCoreChecker.ts +++ b/typescript/sdk/src/core/HyperlaneCoreChecker.ts @@ -1,10 +1,12 @@ import { ethers, utils as ethersUtils } from 'ethers'; -import { assert, eqAddress } from '@hyperlane-xyz/utils'; +import { Ownable__factory } from '@hyperlane-xyz/core'; +import { assert, eqAddress, rootLogger } from '@hyperlane-xyz/utils'; import { BytecodeHash } from '../consts/bytecode.js'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js'; import { proxyImplementation } from '../deploy/proxy.js'; +import { OwnerViolation, ViolationType } from '../deploy/types.js'; import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { collectValidators, moduleMatchesConfig } from '../ism/utils.js'; @@ -36,6 +38,12 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker< async checkChain(chain: ChainName): Promise { const config = this.configMap[chain]; + + if (!config) { + rootLogger.warn(`No config for chain ${chain}`); + return; + } + // skip chains that are configured to be removed if (config.remove) { return; @@ -60,6 +68,31 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker< return this.checkOwnership(chain, config.owner, config.ownerOverrides); } + async checkHook( + chain: ChainName, + hookName: string, + hookAddress: string, + expectedHookOwner: string, + ): Promise { + const hook = Ownable__factory.connect( + hookAddress, + this.multiProvider.getProvider(chain), + ); + const hookOwner = await hook.owner(); + + if (!eqAddress(hookOwner, expectedHookOwner)) { + const violation: OwnerViolation = { + type: ViolationType.Owner, + chain, + name: hookName, + actual: hookOwner, + expected: expectedHookOwner, + contract: hook, + }; + this.addViolation(violation); + } + } + async checkMailbox(chain: ChainName): Promise { const contracts = this.app.getContracts(chain); const mailbox = contracts.mailbox; @@ -71,9 +104,27 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker< )} for ${chain}`, ); - const actualIsmAddress = await mailbox.defaultIsm(); - const config = this.configMap[chain]; + const expectedHookOwner = this.getOwner( + config.owner, + 'fallbackRoutingHook', + config.ownerOverrides, + ); + + await this.checkHook( + chain, + 'defaultHook', + await mailbox.defaultHook(), + expectedHookOwner, + ); + await this.checkHook( + chain, + 'requiredHook', + await mailbox.requiredHook(), + expectedHookOwner, + ); + + const actualIsmAddress = await mailbox.defaultIsm(); const matches = await moduleMatchesConfig( chain, actualIsmAddress, diff --git a/typescript/sdk/src/core/HyperlaneRelayer.ts b/typescript/sdk/src/core/HyperlaneRelayer.ts index 27341a773..cb0ba570e 100644 --- a/typescript/sdk/src/core/HyperlaneRelayer.ts +++ b/typescript/sdk/src/core/HyperlaneRelayer.ts @@ -75,12 +75,18 @@ export class HyperlaneRelayer { async getHookConfig( chain: ChainName, hook: Address, + messageContext?: DispatchedMessage, ): Promise { let config: DerivedHookConfig | undefined; if (this.cache?.hook[chain]?.[hook]) { config = this.cache.hook[chain][hook] as DerivedHookConfig | undefined; } else { - const evmHookReader = new EvmHookReader(this.multiProvider, chain); + const evmHookReader = new EvmHookReader( + this.multiProvider, + chain, + undefined, + messageContext, + ); config = await evmHookReader.deriveHookConfig(hook); } @@ -98,12 +104,18 @@ export class HyperlaneRelayer { async getIsmConfig( chain: ChainName, ism: Address, + messageContext?: DispatchedMessage, ): Promise { let config: DerivedIsmConfig | undefined; if (this.cache?.ism[chain]?.[ism]) { config = this.cache.ism[chain][ism] as DerivedIsmConfig | undefined; } else { - const evmIsmReader = new EvmIsmReader(this.multiProvider, chain); + const evmIsmReader = new EvmIsmReader( + this.multiProvider, + chain, + undefined, + messageContext, + ); config = await evmIsmReader.deriveIsmConfig(ism); } @@ -124,7 +136,7 @@ export class HyperlaneRelayer { ): Promise { const originChain = this.core.getOrigin(message); const hook = await this.core.getSenderHookAddress(message); - return this.getHookConfig(originChain, hook); + return this.getHookConfig(originChain, hook, message); } async getRecipientIsmConfig( @@ -132,7 +144,7 @@ export class HyperlaneRelayer { ): Promise { const destinationChain = this.core.getDestination(message); const ism = await this.core.getRecipientIsmAddress(message); - return this.getIsmConfig(destinationChain, ism); + return this.getIsmConfig(destinationChain, ism, message); } async relayMessage( diff --git a/typescript/sdk/src/core/schemas.ts b/typescript/sdk/src/core/schemas.ts index 79b08bf24..569bb2ee0 100644 --- a/typescript/sdk/src/core/schemas.ts +++ b/typescript/sdk/src/core/schemas.ts @@ -19,6 +19,8 @@ export const DeployedCoreAddressesSchema = ProxyFactoryFactoriesSchema.extend({ timelockController: z.string().optional(), interchainAccountRouter: z.string(), interchainAccountIsm: z.string(), + merkleTreeHook: z.string().optional(), + interchainGasPaymaster: z.string().optional(), }); export type DeployedCoreAddresses = z.infer; diff --git a/typescript/sdk/src/deploy/EvmModuleDeployer.ts b/typescript/sdk/src/deploy/EvmModuleDeployer.ts deleted file mode 100644 index 2d742c9ea..000000000 --- a/typescript/sdk/src/deploy/EvmModuleDeployer.ts +++ /dev/null @@ -1,312 +0,0 @@ -import { ethers } from 'ethers'; -import { Logger } from 'pino'; - -import { - StaticAddressSetFactory, - StaticThresholdAddressSetFactory, - TransparentUpgradeableProxy__factory, -} from '@hyperlane-xyz/core'; -import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArtifact.js'; -import { Address, rootLogger } from '@hyperlane-xyz/utils'; - -import { HyperlaneContracts, HyperlaneFactories } from '../contracts/types.js'; -import { MultiProvider } from '../providers/MultiProvider.js'; -import { ChainMap, ChainName } from '../types.js'; - -import { isProxy, proxyConstructorArgs } from './proxy.js'; -import { ContractVerifier } from './verify/ContractVerifier.js'; -import { - ContractVerificationInput, - ExplorerLicenseType, -} from './verify/types.js'; -import { getContractVerificationInput } from './verify/utils.js'; - -export class EvmModuleDeployer { - public verificationInputs: ChainMap = {}; - - constructor( - protected readonly multiProvider: MultiProvider, - protected readonly factories: Factories, - protected readonly logger = rootLogger.child({ - module: 'EvmModuleDeployer', - }), - protected readonly contractVerifier?: ContractVerifier, - ) { - this.contractVerifier ??= new ContractVerifier( - multiProvider, - {}, - coreBuildArtifact, - ExplorerLicenseType.MIT, - ); - } - - // Deploys a contract from a factory - public async deployContractFromFactory({ - chain, - factory, - contractName, - constructorArgs, - initializeArgs, - implementationAddress, - }: { - chain: ChainName; - factory: F; - contractName: string; - constructorArgs: Parameters; - initializeArgs?: Parameters>['initialize']>; - implementationAddress?: Address; - }): Promise> { - this.logger.info( - `Deploying ${contractName} on ${chain} with constructor args (${constructorArgs.join( - ', ', - )})...`, - ); - const contract = await this.multiProvider.handleDeploy( - chain, - factory, - constructorArgs, - ); - - if (initializeArgs) { - this.logger.debug(`Initialize ${contractName} on ${chain}`); - // Estimate gas for the initialize transaction - const estimatedGas = await contract.estimateGas.initialize( - ...initializeArgs, - ); - - // deploy with 10% buffer on gas limit - const overrides = this.multiProvider.getTransactionOverrides(chain); - const initTx = await contract.initialize(...initializeArgs, { - gasLimit: estimatedGas.add(estimatedGas.div(10)), - ...overrides, - }); - - await this.multiProvider.handleTx(chain, initTx); - } - - const verificationInput = getContractVerificationInput({ - name: contractName, - contract, - bytecode: factory.bytecode, - expectedimplementation: implementationAddress, - }); - this.addVerificationArtifacts({ chain, artifacts: [verificationInput] }); - - // try verifying contract - try { - await this.contractVerifier?.verifyContract(chain, verificationInput); - } catch (error) { - // log error but keep deploying, can also verify post-deployment if needed - this.logger.debug(`Error verifying contract: ${error}`); - } - - return contract; - } - - /** - * Deploys a contract with a specified name. - * - * This function is capable of deploying any contract type defined within the `Factories` type to a specified chain. - * - * @param {ChainName} chain - The name of the chain on which the contract is to be deployed. - * @param {K} contractKey - The key identifying the factory to use for deployment. - * @param {string} contractName - The name of the contract to deploy. This must match the contract source code. - * @param {Parameters} constructorArgs - Arguments for the contract's constructor. - * @param {Parameters>['initialize']>?} initializeArgs - Optional arguments for the contract's initialization function. - * @returns {Promise[K]>} A promise that resolves to the deployed contract instance. - */ - public async deployContractWithName({ - chain, - contractKey, - contractName, - constructorArgs, - initializeArgs, - }: { - chain: ChainName; - contractKey: K; - contractName: string; - constructorArgs: Parameters; - initializeArgs?: Parameters< - Awaited>['initialize'] - >; - }): Promise[K]> { - const contract = await this.deployContractFromFactory({ - chain, - factory: this.factories[contractKey], - contractName, - constructorArgs, - initializeArgs, - }); - return contract; - } - - // Deploys a contract with the same name as the contract key - public async deployContract({ - chain, - contractKey, - constructorArgs, - initializeArgs, - }: { - chain: ChainName; - contractKey: K; - constructorArgs: Parameters; - initializeArgs?: Parameters< - Awaited>['initialize'] - >; - }): Promise[K]> { - return this.deployContractWithName({ - chain, - contractKey, - contractName: contractKey.toString(), - constructorArgs, - initializeArgs, - }); - } - - // Deploys the Implementation and Proxy for a given contract - public async deployProxiedContract({ - chain, - contractKey, - contractName, - proxyAdmin, - constructorArgs, - initializeArgs, - }: { - chain: ChainName; - contractKey: K; - contractName: string; - proxyAdmin: string; - constructorArgs: Parameters; - initializeArgs?: Parameters[K]['initialize']>; - }): Promise[K]> { - // Try to initialize the implementation even though it may not be necessary - const implementation = await this.deployContractWithName({ - chain, - contractKey, - contractName, - constructorArgs, - initializeArgs, - }); - - // Initialize the proxy the same way - return this.deployProxy({ - chain, - implementation, - proxyAdmin, - initializeArgs, - }); - } - - // Deploys a proxy for a given implementation contract - protected async deployProxy({ - chain, - implementation, - proxyAdmin, - initializeArgs, - }: { - chain: ChainName; - implementation: C; - proxyAdmin: string; - initializeArgs?: Parameters; - }): Promise { - const isProxied = await isProxy( - this.multiProvider.getProvider(chain), - implementation.address, - ); - if (isProxied) { - // if the implementation is already a proxy, do not deploy a new proxy - return implementation; - } - - const constructorArgs = proxyConstructorArgs( - implementation, - proxyAdmin, - initializeArgs, - ); - const proxy = await this.deployContractFromFactory({ - chain, - factory: new TransparentUpgradeableProxy__factory(), - contractName: 'TransparentUpgradeableProxy', - constructorArgs, - implementationAddress: implementation.address, - }); - - return implementation.attach(proxy.address) as C; - } - - // Adds verification artifacts to the verificationInputs map - protected addVerificationArtifacts({ - chain, - artifacts, - }: { - chain: ChainName; - artifacts: ContractVerificationInput[]; - }): void { - this.verificationInputs[chain] = this.verificationInputs[chain] || []; - artifacts.forEach((artifact) => { - this.verificationInputs[chain].push(artifact); - }); - } - - // Static deploy function used by Hook and ISM modules. - public static async deployStaticAddressSet({ - chain, - factory, - values, - logger, - threshold = values.length, - multiProvider, - }: { - chain: ChainName; - factory: StaticThresholdAddressSetFactory | StaticAddressSetFactory; - values: Address[]; - logger: Logger; - threshold?: number; - multiProvider: MultiProvider; - }): Promise
{ - const address = await factory['getAddress(address[],uint8)']( - values, - threshold, - ); - const code = await multiProvider.getProvider(chain).getCode(address); - if (code === '0x') { - logger.debug( - `Deploying new ${threshold} of ${values.length} address set to ${chain}`, - ); - const overrides = multiProvider.getTransactionOverrides(chain); - - // estimate gas - const estimatedGas = await factory.estimateGas['deploy(address[],uint8)']( - values, - threshold, - overrides, - ); - - // add 10% buffer - const hash = await factory['deploy(address[],uint8)'](values, threshold, { - ...overrides, - gasLimit: estimatedGas.add(estimatedGas.div(10)), // 10% buffer - }); - - await multiProvider.handleTx(chain, hash); - } else { - logger.debug( - `Recovered ${threshold} of ${values.length} address set on ${chain}: ${address}`, - ); - } - - // TODO: figure out how to get the constructor arguments for manual deploy TXs - // const verificationInput = buildVerificationInput( - // NAME, - // ADDRESS, - // CONSTRUCTOR_ARGS, - // ); - // await this.deployer.verifyContract( - // this.chainName, - // verificationInput, - // logger, - // ); - - return address; - } -} diff --git a/typescript/sdk/src/deploy/HyperlaneAppChecker.ts b/typescript/sdk/src/deploy/HyperlaneAppChecker.ts index 753e2c0ea..55f288f08 100644 --- a/typescript/sdk/src/deploy/HyperlaneAppChecker.ts +++ b/typescript/sdk/src/deploy/HyperlaneAppChecker.ts @@ -12,6 +12,7 @@ import { eqAddress, objMap, promiseObjAll, + rootLogger, } from '@hyperlane-xyz/utils'; import { HyperlaneApp } from '../app/HyperlaneApp.js'; @@ -35,47 +36,57 @@ export abstract class HyperlaneAppChecker< App extends HyperlaneApp, Config, > { - readonly multiProvider: MultiProvider; - readonly app: App; - readonly configMap: ChainMap; - readonly violations: CheckerViolation[]; + readonly violations: CheckerViolation[] = []; constructor( - multiProvider: MultiProvider, - app: App, - configMap: ChainMap, - ) { - this.multiProvider = multiProvider; - this.app = app; - this.violations = []; - this.configMap = configMap; - } + readonly multiProvider: MultiProvider, + readonly app: App, + readonly configMap: ChainMap, + ) {} abstract checkChain(chain: ChainName): Promise; - async check(): Promise { - Object.keys(this.configMap) - .filter( - (chain) => - this.multiProvider.getChainMetadata(chain).protocol === - ProtocolType.Ethereum && !this.app.chains().includes(chain), - ) - .forEach((chain: string) => + async check(chainsToCheck?: ChainName[]): Promise { + // Get all EVM chains from config + const evmChains = Object.keys(this.configMap).filter( + (chain) => + this.multiProvider.getChainMetadata(chain).protocol === + ProtocolType.Ethereum, + ); + + // Mark any EVM chains that are not deployed + const appChains = this.app.chains(); + for (const chain of evmChains) { + if (!appChains.includes(chain)) { this.addViolation({ type: ViolationType.NotDeployed, chain, expected: '', actual: '', - }), - ); + }); + } + } + // Finally, check the chains that were explicitly requested + // If no chains were requested, check all app chains + const chains = + !chainsToCheck || chainsToCheck.length === 0 ? appChains : chainsToCheck; return Promise.all( - // this.app.chains() will only return Ethereum chains that can be interacted with. - this.app.chains().map((chain) => this.checkChain(chain)), + chains + .filter( + (chain) => + this.multiProvider.getChainMetadata(chain).protocol === + ProtocolType.Ethereum, + ) + .map((chain) => this.checkChain(chain)), ); } addViolation(violation: CheckerViolation): void { + if (violation.type === ViolationType.BytecodeMismatch) { + rootLogger.warn({ violation }, `Found bytecode mismatch. Ignoring...`); + return; + } this.violations.push(violation); } @@ -133,7 +144,7 @@ export abstract class HyperlaneAppChecker< type: ViolationType.Owner, actual: actualProxyAdminOwner, expected: expectedOwner, - contract, + contract: actualProxyAdminContract, }; this.addViolation(violation); } @@ -202,7 +213,7 @@ export abstract class HyperlaneAppChecker< return bytecode.substring(0, bytecode.length - 90); } - private getOwner( + protected getOwner( owner: Address, contractName: string, ownableOverrides?: Record, diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index c771337a5..c6cd2048c 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -15,6 +15,7 @@ import { buildArtifact as coreBuildArtifact } from '@hyperlane-xyz/core/buildArt import { Address, ProtocolType, + addBufferToGasLimit, eqAddress, isZeroishAddress, rootLogger, @@ -72,10 +73,11 @@ export abstract class HyperlaneDeployer< public verificationInputs: ChainMap = {}; public cachedAddresses: HyperlaneAddressesMap = {}; public deployedContracts: HyperlaneContractsMap = {}; - public startingBlockNumbers: ChainMap = {}; + + protected cachingEnabled = true; protected logger: Logger; - protected chainTimeoutMs: number; + chainTimeoutMs: number; constructor( protected readonly multiProvider: MultiProvider, @@ -86,7 +88,6 @@ export abstract class HyperlaneDeployer< ) { this.logger = options?.logger ?? rootLogger.child({ module: 'deployer' }); this.chainTimeoutMs = options?.chainTimeoutMs ?? 15 * 60 * 1000; // 15 minute timeout per chain - this.options.ismFactory?.setDeployer(this); if (Object.keys(icaAddresses).length > 0) { this.options.icaApp = InterchainAccount.fromAddressesMap( icaAddresses, @@ -137,40 +138,41 @@ export abstract class HyperlaneDeployer< this.logger.debug(`Start deploy to ${targetChains}`); - const deployPromises = []; - for (const chain of targetChains) { + const failedChains: ChainName[] = []; + const deployChain = async (chain: ChainName) => { const signerUrl = await this.multiProvider.tryGetExplorerAddressUrl( chain, ); const signerAddress = await this.multiProvider.getSignerAddress(chain); const fromString = signerUrl || signerAddress; this.logger.info(`Deploying to ${chain} from ${fromString}`); - this.startingBlockNumbers[chain] = await this.multiProvider - .getProvider(chain) - .getBlockNumber(); - const deployPromise = runWithTimeout(this.chainTimeoutMs, async () => { + return runWithTimeout(this.chainTimeoutMs, async () => { const contracts = await this.deployContracts(chain, configMap[chain]); this.addDeployedContracts(chain, contracts); - this.logger.info(`Successfully deployed contracts on ${chain}`); - }); - if (this.options.concurrentDeploy) { - deployPromises.push(deployPromise); - } else { - await deployPromise; + }) + .then(() => { + this.logger.info(`Successfully deployed contracts on ${chain}`); + }) + .catch((error) => { + failedChains.push(chain); + this.logger.error(`Deployment failed on ${chain}. Error: ${error}`); + throw error; + }); + }; + + if (this.options.concurrentDeploy) { + await Promise.allSettled(targetChains.map(deployChain)); + } else { + for (const chain of targetChains) { + await deployChain(chain); } } - // Await all deploy promises. If concurrent deploy is not enabled, this will be a no-op. - const deployResults = await Promise.allSettled(deployPromises); - for (const [i, result] of deployResults.entries()) { - if (result.status === 'rejected') { - this.logger.error( - { chain: targetChains[i], error: result.reason }, - 'Deployment failed', - ); - throw result.reason; - } + if (failedChains.length > 0) { + throw new Error( + `Deployment failed on chains: ${failedChains.join(', ')}`, + ); } return this.deployedContracts; @@ -373,7 +375,7 @@ export abstract class HyperlaneDeployer< shouldRecover = true, implementationAddress?: Address, ): Promise> { - if (shouldRecover) { + if (this.cachingEnabled && shouldRecover) { const cachedContract = this.readCache(chain, factory, contractName); if (cachedContract) { if (this.recoverVerificationInputs) { @@ -421,10 +423,10 @@ export abstract class HyperlaneDeployer< ...initializeArgs, ); - // deploy with 10% buffer on gas limit + // deploy with buffer on gas limit const overrides = this.multiProvider.getTransactionOverrides(chain); const initTx = await contract.initialize(...initializeArgs, { - gasLimit: estimatedGas.add(estimatedGas.div(10)), + gasLimit: addBufferToGasLimit(estimatedGas), ...overrides, }); const receipt = await this.multiProvider.handleTx(chain, initTx); diff --git a/typescript/sdk/src/gas/token-prices.ts b/typescript/sdk/src/gas/token-prices.ts index 26d17cd8b..d60949630 100644 --- a/typescript/sdk/src/gas/token-prices.ts +++ b/typescript/sdk/src/gas/token-prices.ts @@ -23,23 +23,23 @@ type TokenPriceCacheEntry = { }; class TokenPriceCache { - protected cache: Map; + protected cache: Map; protected freshSeconds: number; protected evictionSeconds: number; constructor(freshSeconds = 60, evictionSeconds = 3 * 60 * 60) { - this.cache = new Map(); + this.cache = new Map(); this.freshSeconds = freshSeconds; this.evictionSeconds = evictionSeconds; } - put(chain: ChainName, price: number): void { + put(id: string, price: number): void { const now = new Date(); - this.cache.set(chain, { timestamp: now, price }); + this.cache.set(id, { timestamp: now, price }); } - isFresh(chain: ChainName): boolean { - const entry = this.cache.get(chain); + isFresh(id: string): boolean { + const entry = this.cache.get(id); if (!entry) return false; const expiryTime = new Date( @@ -49,17 +49,17 @@ class TokenPriceCache { return now < expiryTime; } - fetch(chain: ChainName): number { - const entry = this.cache.get(chain); + fetch(id: string): number { + const entry = this.cache.get(id); if (!entry) { - throw new Error(`no entry found for ${chain} in token price cache`); + throw new Error(`no entry found for ${id} in token price cache`); } const evictionTime = new Date( entry.timestamp.getTime() + 1000 * this.evictionSeconds, ); const now = new Date(); if (now > evictionTime) { - throw new Error(`evicted entry found for ${chain} in token price cache`); + throw new Error(`evicted entry found for ${id} in token price cache`); } return entry.price; } @@ -97,20 +97,30 @@ export class CoinGeckoTokenPriceGetter implements TokenPriceGetter { ); } - async getTokenPrice(chain: ChainName): Promise { - const [price] = await this.getTokenPrices([chain]); + async getTokenPrice( + chain: ChainName, + currency: string = 'usd', + ): Promise { + const [price] = await this.getTokenPrices([chain], currency); return price; } async getTokenExchangeRate( base: ChainName, quote: ChainName, + currency: string = 'usd', ): Promise { - const [basePrice, quotePrice] = await this.getTokenPrices([base, quote]); + const [basePrice, quotePrice] = await this.getTokenPrices( + [base, quote], + currency, + ); return basePrice / quotePrice; } - private async getTokenPrices(chains: ChainName[]): Promise { + private async getTokenPrices( + chains: ChainName[], + currency: string = 'usd', + ): Promise { const isMainnet = chains.map((c) => !this.metadata[c].isTestnet); const allMainnets = isMainnet.every((v) => v === true); const allTestnets = isMainnet.every((v) => v === false); @@ -125,32 +135,43 @@ export class CoinGeckoTokenPriceGetter implements TokenPriceGetter { ); } - const toQuery = chains.filter((c) => !this.cache.isFresh(c)); + const ids = chains.map( + (chain) => this.metadata[chain].gasCurrencyCoinGeckoId || chain, + ); + + await this.getTokenPriceByIds(ids, currency); + return chains.map((chain) => + this.cache.fetch(this.metadata[chain].gasCurrencyCoinGeckoId || chain), + ); + } + + public async getTokenPriceByIds( + ids: string[], + currency: string = 'usd', + ): Promise { + const toQuery = ids.filter((id) => !this.cache.isFresh(id)); + await sleep(this.sleepMsBetweenRequests); + if (toQuery.length > 0) { + let response: any; try { - await this.queryTokenPrices(toQuery); + response = await this.coinGecko.simple.price({ + ids: toQuery, + vs_currencies: [currency], + }); + + if (response.success === true) { + const prices = toQuery.map((id) => response.data[id][currency]); + toQuery.map((id, i) => this.cache.put(id, prices[i])); + } else { + rootLogger.warn('Failed to query token prices', response.message); + return undefined; + } } catch (e) { - rootLogger.warn('Failed to query token prices', e); + rootLogger.warn('Error when querying token prices', e); + return undefined; } } - return chains.map((chain) => this.cache.fetch(chain)); - } - - private async queryTokenPrices(chains: ChainName[]): Promise { - const currency = 'usd'; - // The CoinGecko API expects, in some cases, IDs that do not match - // ChainNames. - const ids = chains.map( - (chain) => this.metadata[chain].gasCurrencyCoinGeckoId || chain, - ); - // Coingecko rate limits, so we are adding this sleep - await sleep(this.sleepMsBetweenRequests); - const response = await this.coinGecko.simple.price({ - ids, - vs_currencies: [currency], - }); - const prices = ids.map((id) => response.data[id][currency]); - // Update the cache with the newly fetched prices - chains.map((chain, i) => this.cache.put(chain, prices[i])); + return ids.map((id) => this.cache.fetch(id)); } } diff --git a/typescript/sdk/src/gas/utils.ts b/typescript/sdk/src/gas/utils.ts new file mode 100644 index 000000000..3a658d9b6 --- /dev/null +++ b/typescript/sdk/src/gas/utils.ts @@ -0,0 +1,251 @@ +import { Provider } from '@ethersproject/providers'; +import { BigNumber, ethers } from 'ethers'; + +import { ProtocolType, convertDecimals, objMap } from '@hyperlane-xyz/utils'; + +import { + TOKEN_EXCHANGE_RATE_DECIMALS, + TOKEN_EXCHANGE_RATE_SCALE, +} from '../consts/igp.js'; +import { ChainMetadataManager } from '../metadata/ChainMetadataManager.js'; +import { AgentCosmosGasPrice } from '../metadata/agentConfig.js'; +import { ChainMetadata } from '../metadata/chainMetadataTypes.js'; +import { MultiProtocolProvider } from '../providers/MultiProtocolProvider.js'; +import { ChainMap, ChainName } from '../types.js'; +import { getCosmosRegistryChain } from '../utils/cosmos.js'; + +import { StorageGasOracleConfig } from './oracle/types.js'; + +export interface GasPriceConfig { + amount: string; + decimals: number; +} + +export interface NativeTokenPriceConfig { + price: string; + decimals: number; +} + +export interface ChainGasOracleParams { + gasPrice: GasPriceConfig; + nativeToken: NativeTokenPriceConfig; +} + +export async function getGasPrice( + mpp: MultiProtocolProvider, + chain: string, +): Promise { + const protocolType = mpp.getProtocol(chain); + switch (protocolType) { + case ProtocolType.Ethereum: { + const provider = mpp.getProvider(chain); + const gasPrice = await (provider.provider as Provider).getGasPrice(); + return { + amount: ethers.utils.formatUnits(gasPrice, 'gwei'), + decimals: 9, + }; + } + case ProtocolType.Cosmos: { + const { amount } = await getCosmosChainGasPrice(chain, mpp); + return { + amount, + decimals: 1, + }; + } + case ProtocolType.Sealevel: + // TODO get a reasonable value + return { + amount: '0.001', + decimals: 9, + }; + default: + throw new Error(`Unsupported protocol type: ${protocolType}`); + } +} + +// Gets the gas price for a Cosmos chain +export async function getCosmosChainGasPrice( + chain: ChainName, + chainMetadataManager: ChainMetadataManager, +): Promise { + const metadata = chainMetadataManager.getChainMetadata(chain); + if (!metadata) { + throw new Error(`No metadata found for Cosmos chain ${chain}`); + } + if (metadata.protocol !== ProtocolType.Cosmos) { + throw new Error(`Chain ${chain} is not a Cosmos chain`); + } + + const cosmosRegistryChain = await getCosmosRegistryChain(chain); + const nativeToken = metadata.nativeToken; + if (!nativeToken) { + throw new Error(`No native token found for Cosmos chain ${chain}`); + } + if (!nativeToken.denom) { + throw new Error(`No denom found for native token on Cosmos chain ${chain}`); + } + + const fee = cosmosRegistryChain.fees?.fee_tokens.find( + (fee: { denom: string }) => { + return ( + fee.denom === nativeToken.denom || fee.denom === `u${nativeToken.denom}` + ); + }, + ); + if (!fee || fee.average_gas_price === undefined) { + throw new Error(`No gas price found for Cosmos chain ${chain}`); + } + + return { + denom: fee.denom, + amount: fee.average_gas_price.toString(), + }; +} + +// Gets the exchange rate of the remote quoted in local tokens +export function getTokenExchangeRateFromValues({ + local, + remote, + tokenPrices, + exchangeRateMarginPct, + decimals, +}: { + local: ChainName; + remote: ChainName; + tokenPrices: ChainMap; + exchangeRateMarginPct: number; + decimals: { local: number; remote: number }; +}): BigNumber { + // Workaround for chicken-egg dependency problem. + // We need to provide some default value here to satisfy the config on initial load, + // whilst knowing that it will get overwritten when a script actually gets run. + const defaultValue = '1'; + const localValue = ethers.utils.parseUnits( + tokenPrices[local] ?? defaultValue, + TOKEN_EXCHANGE_RATE_DECIMALS, + ); + const remoteValue = ethers.utils.parseUnits( + tokenPrices[remote] ?? defaultValue, + TOKEN_EXCHANGE_RATE_DECIMALS, + ); + + // This does not yet account for decimals! + let exchangeRate = remoteValue.mul(TOKEN_EXCHANGE_RATE_SCALE).div(localValue); + // Apply the premium + exchangeRate = exchangeRate.mul(100 + exchangeRateMarginPct).div(100); + + return BigNumber.from( + convertDecimals(decimals.remote, decimals.local, exchangeRate.toString()), + ); +} + +// Gets the StorageGasOracleConfig for each remote chain for a particular local chain. +// Accommodates small non-integer gas prices by scaling up the gas price +// and scaling down the exchange rate by the same factor. +export function getLocalStorageGasOracleConfig({ + local, + gasOracleParams, + exchangeRateMarginPct, +}: { + local: ChainName; + gasOracleParams: ChainMap; + exchangeRateMarginPct: number; +}): ChainMap { + const remotes = Object.keys(gasOracleParams).filter( + (remote) => remote !== local, + ); + const tokenPrices: ChainMap = objMap( + gasOracleParams, + (chain) => gasOracleParams[chain].nativeToken.price, + ); + const localDecimals = gasOracleParams[local].nativeToken.decimals; + return remotes.reduce((agg, remote) => { + const remoteDecimals = gasOracleParams[remote].nativeToken.decimals; + let exchangeRate = getTokenExchangeRateFromValues({ + local, + remote, + tokenPrices, + exchangeRateMarginPct, + decimals: { local: localDecimals, remote: remoteDecimals }, + }); + + // First parse as a number, so we have floating point precision. + // Recall it's possible to have gas prices that are not integers, even + // after converting to the "wei" version of the token. + let gasPrice = + parseFloat(gasOracleParams[remote].gasPrice.amount) * + Math.pow(10, gasOracleParams[remote].gasPrice.decimals); + if (isNaN(gasPrice)) { + throw new Error( + `Invalid gas price for chain ${remote}: ${gasOracleParams[remote].gasPrice.amount}`, + ); + } + + // We have very little precision and ultimately need an integer value for + // the gas price that will be set on-chain. We scale up the gas price and + // scale down the exchange rate by the same factor. + if (gasPrice < 10 && gasPrice % 1 !== 0) { + // Scale up the gas price by 1e4 + const gasPriceScalingFactor = 1e4; + + // Check that there's no significant underflow when applying + // this to the exchange rate: + const adjustedExchangeRate = exchangeRate.div(gasPriceScalingFactor); + const recoveredExchangeRate = adjustedExchangeRate.mul( + gasPriceScalingFactor, + ); + if (recoveredExchangeRate.mul(100).div(exchangeRate).lt(99)) { + throw new Error('Too much underflow when downscaling exchange rate'); + } + + // Apply the scaling factor + exchangeRate = adjustedExchangeRate; + gasPrice *= gasPriceScalingFactor; + } + + // Our integer gas price. + const gasPriceBn = BigNumber.from(Math.ceil(gasPrice)); + + return { + ...agg, + [remote]: { + tokenExchangeRate: exchangeRate.toString(), + gasPrice: gasPriceBn.toString(), + }, + }; + }, {} as ChainMap); +} + +const COINGECKO_PRICE_API = 'https://api.coingecko.com/api/v3/simple/price'; + +export async function getCoingeckoTokenPrices( + chainMetadata: ChainMap, + currency = 'usd', +): Promise> { + const ids = objMap( + chainMetadata, + (_, metadata) => metadata.gasCurrencyCoinGeckoId ?? metadata.name, + ); + + const resp = await fetch( + `${COINGECKO_PRICE_API}?ids=${Object.entries(ids).join( + ',', + )}&vs_currencies=${currency}`, + ); + + const idPrices = await resp.json(); + + const prices = objMap(ids, (chain, id) => { + const idData = idPrices[id]; + if (!idData) { + return undefined; + } + const price = idData[currency]; + if (!price) { + return undefined; + } + return price.toString(); + }); + + return prices; +} diff --git a/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts b/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts index 82abd5b44..aa4c9d559 100644 --- a/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts +++ b/typescript/sdk/src/hook/EvmHookModule.hardhat-test.ts @@ -3,13 +3,7 @@ import { expect } from 'chai'; import { Signer } from 'ethers'; import hre from 'hardhat'; -import { - Address, - assert, - deepEquals, - eqAddress, - normalizeConfig, -} from '@hyperlane-xyz/utils'; +import { Address, assert, deepEquals, eqAddress } from '@hyperlane-xyz/utils'; import { TestChainName, testChains } from '../consts/testChains.js'; import { HyperlaneAddresses, HyperlaneContracts } from '../contracts/types.js'; @@ -20,6 +14,7 @@ import { ProxyFactoryFactories } from '../deploy/contracts.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { randomAddress, randomInt } from '../test/testUtils.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmHookModule } from './EvmHookModule.js'; import { @@ -180,7 +175,7 @@ describe('EvmHookModule', async () => { let factoryContracts: HyperlaneContracts; let exampleRoutingConfig: DomainRoutingHookConfig | FallbackRoutingHookConfig; - beforeEach(async () => { + before(async () => { [signer, funder] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); diff --git a/typescript/sdk/src/hook/EvmHookModule.ts b/typescript/sdk/src/hook/EvmHookModule.ts index ef8185140..b09099464 100644 --- a/typescript/sdk/src/hook/EvmHookModule.ts +++ b/typescript/sdk/src/hook/EvmHookModule.ts @@ -30,7 +30,6 @@ import { addressToBytes32, deepEquals, eqAddress, - normalizeConfig, rootLogger, } from '@hyperlane-xyz/utils'; @@ -41,16 +40,17 @@ import { HyperlaneModuleParams, } from '../core/AbstractHyperlaneModule.js'; import { CoreAddresses } from '../core/contracts.js'; -import { EvmModuleDeployer } from '../deploy/EvmModuleDeployer.js'; +import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer.js'; import { ProxyFactoryFactories } from '../deploy/contracts.js'; import { ContractVerifier } from '../deploy/verify/ContractVerifier.js'; -import { IgpFactories, igpFactories } from '../gas/contracts.js'; import { IgpConfig } from '../gas/types.js'; import { EvmIsmModule } from '../ism/EvmIsmModule.js'; +import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { ArbL2ToL1IsmConfig, IsmType, OpStackIsmConfig } from '../ism/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; -import { ChainNameOrId } from '../types.js'; +import { ChainName, ChainNameOrId } from '../types.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmHookReader } from './EvmHookReader.js'; import { DeployedHook, HookFactories, hookFactories } from './contracts.js'; @@ -75,6 +75,14 @@ type HookModuleAddresses = { proxyAdmin: Address; }; +class HookDeployer extends HyperlaneDeployer<{}, HookFactories> { + protected cachingEnabled = false; + + deployContracts(_chain: ChainName, _config: {}): Promise { + throw new Error('Method not implemented.'); + } +} + export class EvmHookModule extends HyperlaneModule< ProtocolType.Ethereum, HookConfig, @@ -82,7 +90,9 @@ export class EvmHookModule extends HyperlaneModule< > { protected readonly logger = rootLogger.child({ module: 'EvmHookModule' }); protected readonly reader: EvmHookReader; - protected readonly deployer: EvmModuleDeployer; + // "ISM" Factory has aggregation hook factories too + protected readonly hookFactory: HyperlaneIsmFactory; + protected readonly deployer: HookDeployer; // Adding these to reduce how often we need to grab from MultiProvider. public readonly chain: string; @@ -105,15 +115,11 @@ export class EvmHookModule extends HyperlaneModule< super(params); this.reader = new EvmHookReader(multiProvider, this.args.chain); - this.deployer = new EvmModuleDeployer( + this.hookFactory = HyperlaneIsmFactory.fromAddressesMap( + { [this.args.chain]: params.addresses }, multiProvider, - { - ...hookFactories, - ...igpFactories, - }, - this.logger, - contractVerifier, ); + this.deployer = new HookDeployer(multiProvider, hookFactories); this.chain = this.multiProvider.getChainName(this.args.chain); this.domainId = this.multiProvider.getDomainId(this.chain); @@ -625,11 +631,9 @@ export class EvmHookModule extends HyperlaneModule< switch (config.type) { case HookType.MERKLE_TREE: - return this.deployer.deployContract({ - chain: this.chain, - contractKey: HookType.MERKLE_TREE, - constructorArgs: [this.args.addresses.mailbox], - }); + return this.deployer.deployContract(this.chain, HookType.MERKLE_TREE, [ + this.args.addresses.mailbox, + ]); case HookType.INTERCHAIN_GAS_PAYMASTER: return this.deployIgpHook({ config }); case HookType.AGGREGATION: @@ -657,16 +661,13 @@ export class EvmHookModule extends HyperlaneModule< config: ProtocolFeeHookConfig; }): Promise { this.logger.debug('Deploying ProtocolFeeHook...'); - return this.deployer.deployContract({ - chain: this.chain, - contractKey: HookType.PROTOCOL_FEE, - constructorArgs: [ - config.maxProtocolFee, - config.protocolFee, - config.beneficiary, - config.owner, - ], - }); + const deployer = new HookDeployer(this.multiProvider, hookFactories); + return deployer.deployContract(this.chain, HookType.PROTOCOL_FEE, [ + config.maxProtocolFee, + config.protocolFee, + config.beneficiary, + config.owner, + ]); } protected async deployPausableHook({ @@ -675,11 +676,12 @@ export class EvmHookModule extends HyperlaneModule< config: PausableHookConfig; }): Promise { this.logger.debug('Deploying PausableHook...'); - const hook = await this.deployer.deployContract({ - chain: this.chain, - contractKey: HookType.PAUSABLE, - constructorArgs: [], - }); + const deployer = new HookDeployer(this.multiProvider, hookFactories); + const hook = await deployer.deployContract( + this.chain, + HookType.PAUSABLE, + [], + ); // transfer ownership await this.multiProvider.handleTx( @@ -715,13 +717,12 @@ export class EvmHookModule extends HyperlaneModule< this.args.addresses.staticAggregationHookFactory, signer, ); - const address = await EvmModuleDeployer.deployStaticAddressSet({ - chain: this.chain, + const address = await this.hookFactory.deployStaticAddressSet( + this.chain, factory, - values: aggregatedHooks, - logger: this.logger, - multiProvider: this.multiProvider, - }); + aggregatedHooks, + this.logger, + ); // return aggregation hook return StaticAggregationHook__factory.connect(address, signer); @@ -773,16 +774,12 @@ export class EvmHookModule extends HyperlaneModule< ); // deploy opstack hook - const hook = await this.deployer.deployContract({ - chain, - contractKey: HookType.OP_STACK, - constructorArgs: [ - mailbox, - this.multiProvider.getDomainId(config.destinationChain), - addressToBytes32(opstackIsm.address), - config.nativeBridge, - ], - }); + const hook = await this.deployer.deployContract(chain, HookType.OP_STACK, [ + mailbox, + this.multiProvider.getDomainId(config.destinationChain), + addressToBytes32(opstackIsm.address), + config.nativeBridge, + ]); // set authorized hook on opstack ism const authorizedHook = await opstackIsm.authorizedHook(); @@ -865,18 +862,20 @@ export class EvmHookModule extends HyperlaneModule< this.multiProvider.getSignerOrProvider(config.destinationChain), ); + const childHook = await this.deploy({ config: config.childHook }); + // deploy arbL1ToL1 hook - const hook = await this.deployer.deployContract({ + const hook = await this.deployer.deployContract( chain, - contractKey: HookType.ARB_L2_TO_L1, - constructorArgs: [ + HookType.ARB_L2_TO_L1, + [ mailbox, this.multiProvider.getDomainId(config.destinationChain), addressToBytes32(arbL2ToL1IsmAddress), config.arbSys, - BigNumber.from(200_000), // 2x estimate of executeTransaction call overhead + childHook.address, ], - }); + ); // set authorized hook on arbL2ToL1 ism const authorizedHook = await arbL2ToL1Ism.authorizedHook(); if (authorizedHook === addressToBytes32(hook.address)) { @@ -928,22 +927,18 @@ export class EvmHookModule extends HyperlaneModule< // deploy fallback hook const fallbackHook = await this.deploy({ config: config.fallback }); // deploy routing hook with fallback - routingHook = await this.deployer.deployContract({ - chain: this.chain, - contractKey: HookType.FALLBACK_ROUTING, - constructorArgs: [ - this.args.addresses.mailbox, - deployerAddress, - fallbackHook.address, - ], - }); + routingHook = await this.deployer.deployContract( + this.chain, + HookType.FALLBACK_ROUTING, + [this.args.addresses.mailbox, deployerAddress, fallbackHook.address], + ); } else { // deploy routing hook - routingHook = await this.deployer.deployContract({ - chain: this.chain, - contractKey: HookType.ROUTING, - constructorArgs: [this.args.addresses.mailbox, deployerAddress], - }); + routingHook = await this.deployer.deployContract( + this.chain, + HookType.ROUTING, + [this.args.addresses.mailbox, deployerAddress], + ); } // compute the hooks that need to be set @@ -1002,14 +997,14 @@ export class EvmHookModule extends HyperlaneModule< ); // Deploy the InterchainGasPaymaster - const igp = await this.deployer.deployProxiedContract({ - chain: this.chain, - contractKey: HookType.INTERCHAIN_GAS_PAYMASTER, - contractName: HookType.INTERCHAIN_GAS_PAYMASTER, - proxyAdmin: this.args.addresses.proxyAdmin, - constructorArgs: [], - initializeArgs: [deployerAddress, config.beneficiary], - }); + const igp = await this.deployer.deployProxiedContract( + this.chain, + HookType.INTERCHAIN_GAS_PAYMASTER, + HookType.INTERCHAIN_GAS_PAYMASTER, + this.args.addresses.proxyAdmin, + [], + [deployerAddress, config.beneficiary], + ); // Obtain the transactions to set the gas params for each remote const configureTxs = await this.updateIgpRemoteGasParams({ @@ -1038,11 +1033,12 @@ export class EvmHookModule extends HyperlaneModule< config: IgpConfig; }): Promise { // Deploy the StorageGasOracle, by default msg.sender is the owner - const gasOracle = await this.deployer.deployContract({ - chain: this.chain, - contractKey: 'storageGasOracle', - constructorArgs: [], - }); + const gasOracle = await this.deployer.deployContractFromFactory( + this.chain, + new StorageGasOracle__factory(), + 'storageGasOracle', + [], + ); // Obtain the transactions to set the gas params for each remote const configureTxs = await this.updateStorageGasOracle({ diff --git a/typescript/sdk/src/hook/EvmHookReader.ts b/typescript/sdk/src/hook/EvmHookReader.ts index f54c4cf50..f84eecc8a 100644 --- a/typescript/sdk/src/hook/EvmHookReader.ts +++ b/typescript/sdk/src/hook/EvmHookReader.ts @@ -26,6 +26,7 @@ import { } from '@hyperlane-xyz/utils'; import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js'; +import { DispatchedMessage } from '../core/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { ChainNameOrId } from '../types.js'; import { HyperlaneReader } from '../utils/HyperlaneReader.js'; @@ -83,6 +84,12 @@ export interface HookReader { export class EvmHookReader extends HyperlaneReader implements HookReader { protected readonly logger = rootLogger.child({ module: 'EvmHookReader' }); + /** + * HookConfig cache for already retrieved configs. Useful to avoid recomputing configs + * when they have already been retrieved in previous calls where `deriveHookConfig` was called by + * the specific hook methods. + */ + private _cache: Map = new Map(); constructor( protected readonly multiProvider: MultiProvider, @@ -90,13 +97,29 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { protected readonly concurrency: number = multiProvider.tryGetRpcConcurrency( chain, ) ?? DEFAULT_CONTRACT_READ_CONCURRENCY, + protected readonly messageContext?: DispatchedMessage, ) { super(multiProvider, chain); } async deriveHookConfig(address: Address): Promise { + this.logger.debug('Deriving HookConfig:', { address }); + + const cachedValue = this._cache.get(address); + if (cachedValue) { + this.logger.debug( + `Cache hit for HookConfig on chain ${this.chain} at: ${address}`, + ); + return cachedValue; + } + + this.logger.debug( + `Cache miss for HookConfig on chain ${this.chain} at: ${address}`, + ); + let onchainHookType: OnchainHookType | undefined = undefined; let derivedHookConfig: DerivedHookConfig; + try { const hook = IPostDispatchHook__factory.connect(address, this.provider); this.logger.debug('Deriving HookConfig:', { address }); @@ -168,10 +191,14 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { const hook = MerkleTreeHook__factory.connect(address, this.provider); this.assertHookType(await hook.hookType(), OnchainHookType.MERKLE_TREE); - return { + const config: WithAddress = { address, type: HookType.MERKLE_TREE, }; + + this._cache.set(address, config); + + return config; } async deriveAggregationConfig( @@ -187,11 +214,15 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { (hook) => this.deriveHookConfig(hook), ); - return { + const config: WithAddress = { address, type: HookType.AGGREGATION, hooks: hookConfigs, }; + + this._cache.set(address, config); + + return config; } async deriveIgpConfig(address: Address): Promise> { @@ -259,7 +290,7 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { oracleKey = resolvedOracleKeys[0]; } - return { + const config: WithAddress = { owner, address, type: HookType.INTERCHAIN_GAS_PAYMASTER, @@ -268,6 +299,10 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { overhead, oracleConfig, }; + + this._cache.set(address, config); + + return config; } async deriveProtocolFeeConfig( @@ -281,7 +316,7 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { const protocolFee = await hook.protocolFee(); const beneficiary = await hook.beneficiary(); - return { + const config: WithAddress = { owner, address, type: HookType.PROTOCOL_FEE, @@ -289,6 +324,10 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { protocolFee: protocolFee.toString(), beneficiary, }; + + this._cache.set(address, config); + + return config; } async deriveOpStackConfig( @@ -303,13 +342,17 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { const destinationChainName = this.multiProvider.getChainName(destinationDomain); - return { + const config: WithAddress = { owner, address, type: HookType.OP_STACK, nativeBridge: messengerContract, destinationChain: destinationChainName, }; + + this._cache.set(address, config); + + return config; } async deriveArbL2ToL1Config( @@ -321,29 +364,42 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { const destinationDomain = await hook.destinationDomain(); const destinationChainName = this.multiProvider.getChainName(destinationDomain); - return { + + const childHookAddress = await hook.childHook(); + const childHookConfig = await this.deriveHookConfig(childHookAddress); + const config: WithAddress = { address, type: HookType.ARB_L2_TO_L1, destinationChain: destinationChainName, arbSys, + childHook: childHookConfig, }; + + this._cache.set(address, config); + + return config; } async deriveDomainRoutingConfig( address: Address, ): Promise> { const hook = DomainRoutingHook__factory.connect(address, this.provider); + this.assertHookType(await hook.hookType(), OnchainHookType.ROUTING); const owner = await hook.owner(); const domainHooks = await this.fetchDomainHooks(hook); - return { + const config: WithAddress = { owner, address, type: HookType.ROUTING, domains: domainHooks, }; + + this._cache.set(address, config); + + return config; } async deriveFallbackRoutingConfig( @@ -353,6 +409,7 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { address, this.provider, ); + this.assertHookType( await hook.hookType(), OnchainHookType.FALLBACK_ROUTING, @@ -364,19 +421,25 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { const fallbackHook = await hook.fallbackHook(); const fallbackHookConfig = await this.deriveHookConfig(fallbackHook); - return { + const config: WithAddress = { owner, address, type: HookType.FALLBACK_ROUTING, domains: domainHooks, fallback: fallbackHookConfig, }; + + this._cache.set(address, config); + + return config; } private async fetchDomainHooks( hook: DomainRoutingHook | FallbackDomainRoutingHook, ): Promise { - const domainIds = this.multiProvider.getKnownDomainIds(); + const domainIds = this.messageContext + ? [this.messageContext.parsed.destination] + : this.multiProvider.getKnownDomainIds(); const domainHooks: RoutingHookConfig['domains'] = {}; await concurrentMap(this.concurrency, domainIds, async (domainId) => { @@ -406,12 +469,16 @@ export class EvmHookReader extends HyperlaneReader implements HookReader { const owner = await hook.owner(); const paused = await hook.paused(); - return { + const config: WithAddress = { owner, address, paused, type: HookType.PAUSABLE, }; + + this._cache.set(address, config); + + return config; } assertHookType( diff --git a/typescript/sdk/src/hook/schemas.ts b/typescript/sdk/src/hook/schemas.ts index 1111098e8..16bd01b27 100644 --- a/typescript/sdk/src/hook/schemas.ts +++ b/typescript/sdk/src/hook/schemas.ts @@ -46,6 +46,7 @@ export const ArbL2ToL1HookSchema = z.object({ 'address of the bridge contract on L1, optional only needed for non @arbitrum/sdk chains', ), destinationChain: z.string(), + childHook: z.lazy((): z.ZodSchema => HookConfigSchema), }); export const IgpSchema = OwnableSchema.extend({ diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 3270b4973..efadabc44 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -27,10 +27,10 @@ export { testSealevelChain, } from './consts/testChains.js'; export { + attachAndConnectContracts, attachContracts, attachContractsMap, attachContractsMapAndGetForeignDeployments, - attachAndConnectContracts, connectContracts, connectContractsMap, filterAddressesMap, @@ -69,8 +69,8 @@ export { export { MultiProtocolCore } from './core/MultiProtocolCore.js'; export { CoreConfigSchema, - DeployedCoreAddressesSchema, DeployedCoreAddresses, + DeployedCoreAddressesSchema, } from './core/schemas.js'; export { TestCoreApp } from './core/TestCoreApp.js'; export { TestCoreDeployer } from './core/TestCoreDeployer.js'; @@ -195,6 +195,8 @@ export { } from './metadata/ChainMetadataManager.js'; export { BlockExplorer, + BlockExplorerSchema, + EthJsonRpcBlockParameterTag, ChainMetadata, ChainMetadataSchema, ChainMetadataSchemaObject, @@ -208,6 +210,8 @@ export { getDomainId, getReorgPeriod, isValidChainMetadata, + mergeChainMetadata, + mergeChainMetadataMap, } from './metadata/chainMetadataTypes.js'; export { ZChainName, ZHash } from './metadata/customZodTypes.js'; export { @@ -254,6 +258,7 @@ export { InterchainQueryConfig, InterchainQueryDeployer, } from './middleware/query/InterchainQueryDeployer.js'; +export { isBlockExplorerHealthy } from './providers/explorerHealthTest.js'; export { MultiProtocolProvider, MultiProtocolProviderOptions, @@ -302,7 +307,12 @@ export { ViemTransaction, ViemTransactionReceipt, } from './providers/ProviderType.js'; -export { ProviderRetryOptions } from './providers/SmartProvider/types.js'; +export { + isCosmJsProviderHealthy, + isEthersV5ProviderHealthy, + isRpcHealthy, + isSolanaWeb3ProviderHealthy, +} from './providers/rpcHealthTest.js'; export { HyperlaneEtherscanProvider } from './providers/SmartProvider/HyperlaneEtherscanProvider.js'; export { HyperlaneJsonRpcProvider } from './providers/SmartProvider/HyperlaneJsonRpcProvider.js'; export { @@ -313,14 +323,10 @@ export { } from './providers/SmartProvider/ProviderMethods.js'; export { HyperlaneSmartProvider } from './providers/SmartProvider/SmartProvider.js'; export { - PopulatedTransactionSchema, - PopulatedTransactionsSchema, -} from './providers/transactions/schemas.js'; -export { - CallData, - PopulatedTransaction, - PopulatedTransactions, -} from './providers/transactions/types.js'; + ProviderRetryOptions, + SmartProviderOptions, +} from './providers/SmartProvider/types.js'; +export { CallData } from './providers/transactions/types.js'; export { SubmitterMetadataSchema } from './providers/transactions/submitter/schemas.js'; export { TxSubmitterInterface } from './providers/transactions/submitter/TxSubmitterInterface.js'; @@ -337,15 +343,16 @@ export { } from './providers/transactions/submitter/ethersV5/types.js'; export { - SubmissionStrategySchema, ChainSubmissionStrategySchema, + SubmissionStrategySchema, } from './providers/transactions/submitter/builder/schemas.js'; export { TxSubmitterBuilder } from './providers/transactions/submitter/builder/TxSubmitterBuilder.js'; export { - SubmissionStrategy, ChainSubmissionStrategy, + SubmissionStrategy, } from './providers/transactions/submitter/builder/types.js'; +export { EV5GnosisSafeTxBuilder } from './providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.js'; export { EV5GnosisSafeTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.js'; export { EV5ImpersonatedAccountTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.js'; export { EV5JsonRpcTxSubmitter } from './providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js'; @@ -386,11 +393,12 @@ export { MailboxClientConfig, ProxiedFactories, ProxiedRouterConfig, + RemoteRouters, + DestinationGas, RouterAddress, RouterConfig, RouterViolation, RouterViolationType, - RemoteRouters, proxiedFactories, } from './router/types.js'; export { @@ -469,6 +477,7 @@ export { TOKEN_TYPE_TO_STANDARD, TokenStandard, } from './token/TokenStandard.js'; +export { TokenRouterConfig, WarpRouteDeployConfig } from './token/types.js'; export { ChainMap, ChainName, ChainNameOrId, Connection } from './types.js'; export { getCosmosRegistryChain } from './utils/cosmos.js'; export { filterByChains } from './utils/filter.js'; @@ -480,9 +489,8 @@ export { setFork, stopImpersonatingAccount, } from './utils/fork.js'; +export { multisigIsmVerificationCost, normalizeConfig } from './utils/ism.js'; export { MultiGeneric } from './utils/MultiGeneric.js'; -export { TokenRouterConfig, WarpRouteDeployConfig } from './token/types.js'; -export { multisigIsmVerificationCost } from './utils/ism.js'; export { SealevelAccountDataWrapper, SealevelInstructionWrapper, @@ -506,9 +514,11 @@ export { NativeConfig, TokenRouterConfigSchema, WarpRouteDeployConfigSchema, + WarpRouteDeployConfigSchemaErrors, isCollateralConfig, isNativeConfig, isSyntheticConfig, + isSyntheticRebaseConfig, isTokenMetadata, } from './token/schemas.js'; export { isCompliant } from './utils/schemas.js'; @@ -522,11 +532,21 @@ export { } from './utils/gnosisSafe.js'; export { EvmCoreModule } from './core/EvmCoreModule.js'; -export { EvmIsmModule } from './ism/EvmIsmModule.js'; -export { EvmERC20WarpModule } from './token/EvmERC20WarpModule.js'; +export { proxyAdmin } from './deploy/proxy.js'; export { - ProxyFactoryFactoriesSchema, ProxyFactoryFactoriesAddresses, + ProxyFactoryFactoriesSchema, } from './deploy/schemas.js'; +export { EvmIsmModule } from './ism/EvmIsmModule.js'; export { AnnotatedEV5Transaction } from './providers/ProviderType.js'; -export { proxyAdmin } from './deploy/proxy.js'; +export { EvmERC20WarpModule } from './token/EvmERC20WarpModule.js'; +export { + GasPriceConfig, + NativeTokenPriceConfig, + ChainGasOracleParams, + getCoingeckoTokenPrices, + getCosmosChainGasPrice, + getGasPrice, + getLocalStorageGasOracleConfig, + getTokenExchangeRateFromValues, +} from './gas/utils.js'; diff --git a/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts b/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts index 3c079d138..fc03107cc 100644 --- a/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts +++ b/typescript/sdk/src/ism/EvmIsmModule.hardhat-test.ts @@ -4,7 +4,7 @@ import { expect } from 'chai'; import { Signer } from 'ethers'; import hre from 'hardhat'; -import { Address, eqAddress, normalizeConfig } from '@hyperlane-xyz/utils'; +import { Address, eqAddress } from '@hyperlane-xyz/utils'; import { TestChainName, testChains } from '../consts/testChains.js'; import { HyperlaneAddresses, HyperlaneContracts } from '../contracts/types.js'; @@ -13,6 +13,7 @@ import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDe import { ProxyFactoryFactories } from '../deploy/contracts.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { randomAddress, randomInt } from '../test/testUtils.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmIsmModule } from './EvmIsmModule.js'; import { HyperlaneIsmFactory } from './HyperlaneIsmFactory.js'; @@ -36,19 +37,27 @@ const randomMultisigIsmConfig = (m: number, n: number): MultisigIsmConfig => { }; }; +const ModuleTypes = [ + ModuleType.AGGREGATION, + ModuleType.MERKLE_ROOT_MULTISIG, + ModuleType.ROUTING, + ModuleType.NULL, +]; + +const NonNestedModuleTypes = [ModuleType.MERKLE_ROOT_MULTISIG, ModuleType.NULL]; + function randomModuleType(): ModuleType { - const choices = [ - ModuleType.AGGREGATION, - ModuleType.MERKLE_ROOT_MULTISIG, - ModuleType.ROUTING, - ModuleType.NULL, - ]; - return choices[randomInt(choices.length)]; + return ModuleTypes[randomInt(ModuleTypes.length)]; +} + +function randomNonNestedModuleType(): ModuleType { + return NonNestedModuleTypes[randomInt(NonNestedModuleTypes.length)]; } -const randomIsmConfig = (depth = 0, maxDepth = 2): IsmConfig => { +const randomIsmConfig = (depth = 0, maxDepth = 2) => { const moduleType = - depth == maxDepth ? ModuleType.MERKLE_ROOT_MULTISIG : randomModuleType(); + depth === maxDepth ? randomNonNestedModuleType() : randomModuleType(); + switch (moduleType) { case ModuleType.MERKLE_ROOT_MULTISIG: { const n = randomInt(5, 1); @@ -65,10 +74,21 @@ const randomIsmConfig = (depth = 0, maxDepth = 2): IsmConfig => { return config; } case ModuleType.AGGREGATION: { - const n = randomInt(5, 1); - const modules = new Array(n) - .fill(0) - .map(() => randomIsmConfig(depth + 1)); + const n = randomInt(2, 1); + const moduleTypes = new Set(); + const modules = new Array(n).fill(0).map(() => { + let moduleConfig; + let moduleType; + + // Ensure that we do not add the same module type more than once per level + do { + moduleConfig = randomIsmConfig(depth + 1, maxDepth); + moduleType = moduleConfig.type; + } while (moduleTypes.has(moduleType)); + + moduleTypes.add(moduleType); + return moduleConfig; + }); const config: AggregationIsmConfig = { type: IsmType.AGGREGATION, threshold: randomInt(n, 1), @@ -98,7 +118,7 @@ describe('EvmIsmModule', async () => { let factoryAddresses: HyperlaneAddresses; let factoryContracts: HyperlaneContracts; - beforeEach(async () => { + before(async () => { const [signer, funder] = await hre.ethers.getSigners(); fundingAccount = funder; multiProvider = MultiProvider.createTestMultiProvider({ signer }); @@ -125,6 +145,12 @@ describe('EvmIsmModule', async () => { mailboxAddress = ( await new TestCoreDeployer(multiProvider, legacyIsmFactory).deployApp() ).getContracts(chain).mailbox.address; + }); + + beforeEach(async () => { + // Reset the MultiProvider for each test + const [signer] = await hre.ethers.getSigners(); + multiProvider = MultiProvider.createTestMultiProvider({ signer }); // example routing config exampleRoutingConfig = { @@ -170,8 +196,10 @@ describe('EvmIsmModule', async () => { // expect that the ISM matches the config after all tests afterEach(async () => { - const normalizedDerivedConfig = normalizeConfig(await testIsm.read()); + const derivedConfiig = await testIsm.read(); + const normalizedDerivedConfig = normalizeConfig(derivedConfiig); const normalizedConfig = normalizeConfig(testConfig); + assert.deepStrictEqual(normalizedDerivedConfig, normalizedConfig); }); @@ -341,6 +369,74 @@ describe('EvmIsmModule', async () => { .true; }); + it(`reordering validators in an existing ${type} should not trigger a redeployment`, async () => { + // create a new ISM + const routerConfig = { + type: IsmType.ROUTING, + owner: (await multiProvider.getSignerAddress(chain)).toLowerCase(), + domains: { + test1: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + ], + threshold: 2, + }, + test2: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + ], + threshold: 2, + }, + }, + }; + + const { ism, initialIsmAddress } = await createIsm( + routerConfig as RoutingIsmConfig, + ); + + const updatedRouterConfig = { + type: IsmType.ROUTING, + owner: (await multiProvider.getSignerAddress(chain)).toLowerCase(), + domains: { + test1: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + ], + threshold: 2, + }, + test2: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + ], + threshold: 2, + }, + }, + }; + + // expect 0 updates + await expectTxsAndUpdate( + ism, + updatedRouterConfig as RoutingIsmConfig, + 0, + ); + + // expect the ISM address to be the same + expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be + .true; + }); + it(`update owner in an existing ${type} not owned by deployer`, async () => { // ISM owner is not the deployer exampleRoutingConfig.owner = randomAddress(); @@ -429,5 +525,99 @@ describe('EvmIsmModule', async () => { .true; }); } + + it(`reordering modules in an existing staticAggregationIsm should not trigger a redeployment`, async () => { + // create a new ISM + const config: AggregationIsmConfig = { + type: IsmType.AGGREGATION, + modules: [ + { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + ], + threshold: 2, + }, + { + type: IsmType.ROUTING, + owner: (await multiProvider.getSignerAddress(chain)).toLowerCase(), + domains: { + test1: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + ], + threshold: 2, + }, + test2: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + ], + threshold: 2, + }, + }, + }, + ], + threshold: 2, + }; + + const { ism, initialIsmAddress } = await createIsm( + config as AggregationIsmConfig, + ); + + const updatedConfig: AggregationIsmConfig = { + type: IsmType.AGGREGATION, + modules: [ + { + type: IsmType.ROUTING, + owner: (await multiProvider.getSignerAddress(chain)).toLowerCase(), + domains: { + test2: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + ], + threshold: 2, + }, + test1: { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + ], + threshold: 2, + }, + }, + }, + { + type: IsmType.MERKLE_ROOT_MULTISIG, + validators: [ + '0x5FbDB2315678afecb367f032d93F642f64180aa3', + '0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2', + '0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db', + ], + threshold: 2, + }, + ], + threshold: 2, + }; + + // expect 0 updates + await expectTxsAndUpdate(ism, updatedConfig, 0); + + // expect the ISM address to be the same + expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be + .true; + }); }); }); diff --git a/typescript/sdk/src/ism/EvmIsmModule.ts b/typescript/sdk/src/ism/EvmIsmModule.ts index 4b7db71c7..bba38aa57 100644 --- a/typescript/sdk/src/ism/EvmIsmModule.ts +++ b/typescript/sdk/src/ism/EvmIsmModule.ts @@ -1,62 +1,38 @@ import { ethers } from 'ethers'; import { Logger } from 'pino'; -import { - ArbL2ToL1Ism__factory, - DefaultFallbackRoutingIsm__factory, - DomainRoutingIsm, - DomainRoutingIsmFactory__factory, - DomainRoutingIsm__factory, - IAggregationIsm, - IAggregationIsm__factory, - IInterchainSecurityModule__factory, - IMultisigIsm, - IMultisigIsm__factory, - IRoutingIsm, - OPStackIsm__factory, - Ownable__factory, - PausableIsm__factory, - TestIsm__factory, - TrustedRelayerIsm__factory, -} from '@hyperlane-xyz/core'; +import { DomainRoutingIsm__factory } from '@hyperlane-xyz/core'; import { Address, Domain, ProtocolType, assert, deepEquals, - eqAddress, - normalizeConfig, - objFilter, + intersection, rootLogger, } from '@hyperlane-xyz/utils'; -import { attachAndConnectContracts } from '../contracts/contracts.js'; -import { HyperlaneAddresses, HyperlaneContracts } from '../contracts/types.js'; +import { transferOwnershipTransactions } from '../contracts/contracts.js'; +import { HyperlaneAddresses } from '../contracts/types.js'; import { HyperlaneModule, HyperlaneModuleParams, } from '../core/AbstractHyperlaneModule.js'; -import { EvmModuleDeployer } from '../deploy/EvmModuleDeployer.js'; -import { - ProxyFactoryFactories, - proxyFactoryFactories, -} from '../deploy/contracts.js'; +import { ProxyFactoryFactories } from '../deploy/contracts.js'; import { ContractVerifier } from '../deploy/verify/ContractVerifier.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; import { ChainName, ChainNameOrId } from '../types.js'; -import { findMatchingLogEvents } from '../utils/logUtils.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmIsmReader } from './EvmIsmReader.js'; +import { HyperlaneIsmFactory } from './HyperlaneIsmFactory.js'; import { IsmConfigSchema } from './schemas.js'; import { - AggregationIsmConfig, DeployedIsm, IsmConfig, IsmType, MUTABLE_ISM_TYPE, - MultisigIsmConfig, RoutingIsmConfig, } from './types.js'; import { calculateDomainRoutingDelta } from './utils.js'; @@ -73,8 +49,8 @@ export class EvmIsmModule extends HyperlaneModule< > { protected readonly logger = rootLogger.child({ module: 'EvmIsmModule' }); protected readonly reader: EvmIsmReader; - protected readonly deployer: EvmModuleDeployer; - protected readonly factories: HyperlaneContracts; + protected readonly ismFactory: HyperlaneIsmFactory; + protected readonly mailbox: Address; // Adding these to reduce how often we need to grab from MultiProvider. public readonly chain: ChainName; @@ -94,33 +70,14 @@ export class EvmIsmModule extends HyperlaneModule< super(params); this.reader = new EvmIsmReader(multiProvider, params.chain); - this.deployer = new EvmModuleDeployer( - this.multiProvider, - {}, - this.logger, - contractVerifier, - ); - this.factories = attachAndConnectContracts( - { - staticMerkleRootMultisigIsmFactory: - params.addresses.staticMerkleRootMultisigIsmFactory, - staticMessageIdMultisigIsmFactory: - params.addresses.staticMessageIdMultisigIsmFactory, - staticAggregationIsmFactory: - params.addresses.staticAggregationIsmFactory, - staticAggregationHookFactory: - params.addresses.staticAggregationHookFactory, - domainRoutingIsmFactory: params.addresses.domainRoutingIsmFactory, - staticMerkleRootWeightedMultisigIsmFactory: - params.addresses.staticMerkleRootWeightedMultisigIsmFactory, - staticMessageIdWeightedMultisigIsmFactory: - params.addresses.staticMessageIdWeightedMultisigIsmFactory, - }, - proxyFactoryFactories, - multiProvider.getSigner(params.chain), + this.ismFactory = HyperlaneIsmFactory.fromAddressesMap( + { [params.chain]: params.addresses }, + multiProvider, ); + this.mailbox = params.addresses.mailbox; + this.chain = this.multiProvider.getChainName(this.args.chain); this.domainId = this.multiProvider.getDomainId(this.chain); } @@ -210,24 +167,14 @@ export class EvmIsmModule extends HyperlaneModule< } // Lastly, check if the resolved owner is different from the current owner - const provider = this.multiProvider.getProvider(this.chain); - const owner = await Ownable__factory.connect( - this.args.addresses.deployedIsm, - provider, - ).owner(); - - // Return an ownership transfer transaction if required - if (!eqAddress(targetConfig.owner, owner)) { - updateTxs.push({ - annotation: 'Transferring ownership of ownable ISM...', - chainId: this.domainId, - to: this.args.addresses.deployedIsm, - data: Ownable__factory.createInterface().encodeFunctionData( - 'transferOwnership(address)', - [targetConfig.owner], - ), - }); - } + updateTxs.push( + ...transferOwnershipTransactions( + this.domainId, + this.args.addresses.deployedIsm, + currentConfig, + targetConfig, + ), + ); return updateTxs; } @@ -277,30 +224,24 @@ export class EvmIsmModule extends HyperlaneModule< target: RoutingIsmConfig; logger: Logger; }): Promise { - const routingIsmInterface = DomainRoutingIsm__factory.createInterface(); - const updateTxs = []; - - // filter out domains which are not part of the multiprovider - current = { - ...current, - domains: this.filterRoutingIsmDomains({ - config: current, - }).availableDomains, - }; - target = { - ...target, - domains: this.filterRoutingIsmDomains({ - config: target, - }).availableDomains, - }; + const contract = DomainRoutingIsm__factory.connect( + this.args.addresses.deployedIsm, + this.multiProvider.getProvider(this.chain), + ); + + const updateTxs: AnnotatedEV5Transaction[] = []; + + const knownChains = new Set(this.multiProvider.getKnownChainNames()); const { domainsToEnroll, domainsToUnenroll } = calculateDomainRoutingDelta( current, target, ); + const knownEnrolls = intersection(knownChains, new Set(domainsToEnroll)); + // Enroll domains - for (const origin of domainsToEnroll) { + for (const origin of knownEnrolls) { logger.debug( `Reconfiguring preexisting routing ISM for origin ${origin}...`, ); @@ -309,27 +250,27 @@ export class EvmIsmModule extends HyperlaneModule< }); const domainId = this.multiProvider.getDomainId(origin); + const tx = await contract.populateTransaction.set(domainId, ism.address); updateTxs.push({ annotation: `Setting new ISM for origin ${origin}...`, + ...tx, chainId: this.domainId, - to: this.args.addresses.deployedIsm, - data: routingIsmInterface.encodeFunctionData('set(uint32,address)', [ - domainId, - ism.address, - ]), }); } + const knownUnenrolls = intersection( + knownChains, + new Set(domainsToUnenroll), + ); + // Unenroll domains - for (const origin of domainsToUnenroll) { + for (const origin of knownUnenrolls) { const domainId = this.multiProvider.getDomainId(origin); + const tx = await contract.populateTransaction.remove(domainId); updateTxs.push({ annotation: `Unenrolling originDomain ${domainId} from preexisting routing ISM at ${this.args.addresses.deployedIsm}...`, + ...tx, chainId: this.domainId, - to: this.args.addresses.deployedIsm, - data: routingIsmInterface.encodeFunctionData('remove(uint32)', [ - domainId, - ]), }); } @@ -343,274 +284,10 @@ export class EvmIsmModule extends HyperlaneModule< }): Promise { config = IsmConfigSchema.parse(config); - // If it's an address ISM, just return a base ISM - if (typeof config === 'string') { - // TODO: https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3773 - // we can remove the ts-ignore once we have a proper type for address ISMs - // @ts-ignore - return IInterchainSecurityModule__factory.connect( - config, - this.multiProvider.getSignerOrProvider(this.args.chain), - ); - } - - const ismType = config.type; - const logger = rootLogger.child({ chainName: this.chain, ismType }); - - logger.debug(`Deploying ${ismType} to ${this.args.chain}`); - - switch (ismType) { - case IsmType.MESSAGE_ID_MULTISIG: - case IsmType.MERKLE_ROOT_MULTISIG: - return this.deployMultisigIsm({ - config, - logger, - }); - - case IsmType.ROUTING: - case IsmType.FALLBACK_ROUTING: - return this.deployRoutingIsm({ - config, - logger, - }); - - case IsmType.AGGREGATION: - return this.deployAggregationIsm({ - config, - logger, - }); - - case IsmType.OP_STACK: - return this.deployer.deployContractFromFactory({ - chain: this.chain, - factory: new OPStackIsm__factory(), - contractName: IsmType.OP_STACK, - constructorArgs: [config.nativeBridge], - }); - - case IsmType.ARB_L2_TO_L1: - return this.deployer.deployContractFromFactory({ - chain: this.chain, - factory: new ArbL2ToL1Ism__factory(), - contractName: IsmType.ARB_L2_TO_L1, - constructorArgs: [config.bridge], - }); - - case IsmType.PAUSABLE: - return this.deployer.deployContractFromFactory({ - chain: this.chain, - factory: new PausableIsm__factory(), - contractName: IsmType.PAUSABLE, - constructorArgs: [config.owner], - }); - - case IsmType.TRUSTED_RELAYER: - assert( - this.args.addresses.mailbox, - `Mailbox address is required for deploying ${ismType}`, - ); - return this.deployer.deployContractFromFactory({ - chain: this.chain, - factory: new TrustedRelayerIsm__factory(), - contractName: IsmType.TRUSTED_RELAYER, - constructorArgs: [this.args.addresses.mailbox, config.relayer], - }); - - case IsmType.TEST_ISM: - return this.deployer.deployContractFromFactory({ - chain: this.chain, - factory: new TestIsm__factory(), - contractName: IsmType.TEST_ISM, - constructorArgs: [], - }); - - default: - throw new Error(`Unsupported ISM type ${ismType}`); - } - } - - protected async deployMultisigIsm({ - config, - logger, - }: { - config: MultisigIsmConfig; - logger: Logger; - }): Promise { - const signer = this.multiProvider.getSigner(this.chain); - const factoryName = - config.type === IsmType.MERKLE_ROOT_MULTISIG - ? 'staticMerkleRootMultisigIsmFactory' - : 'staticMessageIdMultisigIsmFactory'; - - const address = await EvmModuleDeployer.deployStaticAddressSet({ - chain: this.chain, - factory: this.factories[factoryName], - values: config.validators, - logger, - threshold: config.threshold, - multiProvider: this.multiProvider, - }); - - return IMultisigIsm__factory.connect(address, signer); - } - - protected async deployRoutingIsm({ - config, - logger, - }: { - config: RoutingIsmConfig; - logger: Logger; - }): Promise { - // filter out domains which are not part of the multiprovider - const { availableDomains, availableDomainIds } = - this.filterRoutingIsmDomains({ - config, - }); - config = { - ...config, - domains: availableDomains, - }; - - // deploy the submodules first - const submoduleAddresses: Address[] = []; - for (const origin of Object.keys(config.domains)) { - const { address } = await this.deploy({ - config: config.domains[origin], - }); - submoduleAddresses.push(address); - } - - if (config.type === IsmType.FALLBACK_ROUTING) { - // deploy the fallback routing ISM - logger.debug('Deploying fallback routing ISM ...'); - const ism = await this.multiProvider.handleDeploy( - this.chain, - new DefaultFallbackRoutingIsm__factory(), - [this.args.addresses.mailbox], - ); - - // initialize the fallback routing ISM - logger.debug('Initializing fallback routing ISM ...'); - const tx = await ism['initialize(address,uint32[],address[])']( - config.owner, - availableDomainIds, - submoduleAddresses, - ); - - await this.multiProvider.handleTx(this.chain, tx); - // return the fallback routing ISM - return ism; - } - - // then deploy the domain routing ISM - logger.debug('Deploying domain routing ISM ...'); - return this.deployDomainRoutingIsm({ - owner: config.owner, - domainIds: availableDomainIds, - submoduleAddresses, - }); - } - - protected async deployDomainRoutingIsm({ - owner, - domainIds, - submoduleAddresses, - }: { - owner: string; - domainIds: number[]; - submoduleAddresses: string[]; - }): Promise { - const overrides = this.multiProvider.getTransactionOverrides( - this.args.chain, - ); - - const signer = this.multiProvider.getSigner(this.args.chain); - const domainRoutingIsmFactory = DomainRoutingIsmFactory__factory.connect( - this.args.addresses.domainRoutingIsmFactory, - signer, - ); - - // estimate gas - const estimatedGas = await domainRoutingIsmFactory.estimateGas.deploy( - owner, - domainIds, - submoduleAddresses, - overrides, - ); - - // deploying new domain routing ISM, add 10% buffer - const tx = await domainRoutingIsmFactory.deploy( - owner, - domainIds, - submoduleAddresses, - { - ...overrides, - gasLimit: estimatedGas.add(estimatedGas.div(10)), // 10% buffer - }, - ); - - const receipt = await this.multiProvider.handleTx(this.args.chain, tx); - const dispatchLogs = findMatchingLogEvents( - receipt.logs, - domainRoutingIsmFactory.interface, - 'ModuleDeployed', - ); - - if (dispatchLogs.length === 0) { - throw new Error('No ModuleDeployed event found'); - } - - const moduleAddress = dispatchLogs[0].args['module']; - return DomainRoutingIsm__factory.connect(moduleAddress, signer); - } - - protected async deployAggregationIsm({ - config, - logger, - }: { - config: AggregationIsmConfig; - logger: Logger; - }): Promise { - const addresses: Address[] = []; - // Needs to be deployed sequentially because Ethers will throw `Error: replacement fee too low` - for (const module of config.modules) { - const submodule = await this.deploy({ config: module }); - addresses.push(submodule.address); - } - - const factoryName = 'staticAggregationIsmFactory'; - const address = await EvmModuleDeployer.deployStaticAddressSet({ - chain: this.chain, - factory: this.factories[factoryName], - values: addresses, - logger: logger, - threshold: config.threshold, - multiProvider: this.multiProvider, + return this.ismFactory.deploy({ + destination: this.chain, + config, + mailbox: this.mailbox, }); - - const signer = this.multiProvider.getSigner(this.args.chain); - return IAggregationIsm__factory.connect(address, signer); - } - - // filtering out domains which are not part of the multiprovider - private filterRoutingIsmDomains({ config }: { config: RoutingIsmConfig }) { - const availableDomainIds: number[] = []; - const availableDomains = objFilter( - config.domains, - (domain, _): _ is IsmConfig => { - const domainId = this.multiProvider.tryGetDomainId(domain); - if (domainId === null) { - this.logger.warn( - `Domain ${domain} doesn't have chain metadata provided, skipping ...`, - ); - return false; - } - - availableDomainIds.push(domainId); - return true; - }, - ); - - return { availableDomains, availableDomainIds }; } } diff --git a/typescript/sdk/src/ism/EvmIsmReader.ts b/typescript/sdk/src/ism/EvmIsmReader.ts index c252a94a9..06493cb45 100644 --- a/typescript/sdk/src/ism/EvmIsmReader.ts +++ b/typescript/sdk/src/ism/EvmIsmReader.ts @@ -1,4 +1,4 @@ -import { ethers } from 'ethers'; +import { BigNumber, ethers } from 'ethers'; import { ArbL2ToL1Ism__factory, @@ -21,6 +21,7 @@ import { } from '@hyperlane-xyz/utils'; import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js'; +import { DispatchedMessage } from '../core/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { ChainNameOrId } from '../types.js'; import { HyperlaneReader } from '../utils/HyperlaneReader.js'; @@ -66,6 +67,7 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { protected readonly concurrency: number = multiProvider.tryGetRpcConcurrency( chain, ) ?? DEFAULT_CONTRACT_READ_CONCURRENCY, + protected readonly messageContext?: DispatchedMessage, ) { super(multiProvider, chain); } @@ -112,7 +114,7 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { throw new Error(`Unknown ISM ModuleType: ${moduleType}`); } } catch (e: any) { - const errorMessage = `Failed to derive ISM module type ${moduleType} (${address}):\n\t${e}`; + const errorMessage = `Failed to derive ISM module type ${moduleType} on ${this.chain} (${address}) :\n\t${e}`; this.logger.debug(errorMessage); throw new Error(errorMessage); } finally { @@ -129,11 +131,14 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { address, this.provider, ); + const owner = await ism.owner(); this.assertModuleType(await ism.moduleType(), ModuleType.ROUTING); + const domainIds = this.messageContext + ? [BigNumber.from(this.messageContext.parsed.origin)] + : await ism.domains(); const domains: RoutingIsmConfig['domains'] = {}; - const domainIds = await ism.domains(); await concurrentMap(this.concurrency, domainIds, async (domainId) => { const chainName = this.multiProvider.tryGetChainName(domainId.toNumber()); @@ -143,7 +148,9 @@ export class EvmIsmReader extends HyperlaneReader implements IsmReader { ); return; } - const module = await ism.module(domainId); + const module = this.messageContext + ? await ism.route(this.messageContext.message) + : await ism.module(domainId); domains[chainName] = await this.deriveIsmConfig(module); }); diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts index 017d539f7..894eaa278 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts @@ -6,9 +6,10 @@ import { DomainRoutingIsm, TrustedRelayerIsm } from '@hyperlane-xyz/core'; import { Address, randomElement, randomInt } from '@hyperlane-xyz/utils'; import { TestChainName, testChains } from '../consts/testChains.js'; -import { TestCoreApp } from '../core/TestCoreApp.js'; +import { HyperlaneContractsMap } from '../contracts/types.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; +import { ProxyFactoryFactories } from '../deploy/contracts.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { randomAddress } from '../test/testUtils.js'; @@ -132,29 +133,41 @@ export const randomIsmConfig = ( }; describe('HyperlaneIsmFactory', async () => { + let ismFactoryDeployer: HyperlaneProxyFactoryDeployer; let ismFactory: HyperlaneIsmFactory; - let coreApp: TestCoreApp; let multiProvider: MultiProvider; - let ismFactoryDeployer: HyperlaneProxyFactoryDeployer; let exampleRoutingConfig: RoutingIsmConfig; - let mailboxAddress: Address, newMailboxAddress: Address; + let mailboxAddress: Address; + let newMailboxAddress: Address; + let contractsMap: HyperlaneContractsMap = {}; + const chain = TestChainName.test1; - beforeEach(async () => { + before(async () => { const [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); + ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); - ismFactory = new HyperlaneIsmFactory( - await ismFactoryDeployer.deploy(multiProvider.mapKnownChains(() => ({}))), - multiProvider, + contractsMap = await ismFactoryDeployer.deploy( + multiProvider.mapKnownChains(() => ({})), ); - let coreDeployer = new TestCoreDeployer(multiProvider, ismFactory); - coreApp = await coreDeployer.deployApp(); - mailboxAddress = coreApp.getContracts(chain).mailbox.address; + ismFactory = new HyperlaneIsmFactory(contractsMap, multiProvider); + + mailboxAddress = ( + await new TestCoreDeployer(multiProvider, ismFactory).deployApp() + ).getContracts(chain).mailbox.address; - coreDeployer = new TestCoreDeployer(multiProvider, ismFactory); - coreApp = await coreDeployer.deployApp(); - newMailboxAddress = coreApp.getContracts(chain).mailbox.address; + newMailboxAddress = ( + await new TestCoreDeployer(multiProvider, ismFactory).deployApp() + ).getContracts(chain).mailbox.address; + }); + + beforeEach(async () => { + const [signer] = await hre.ethers.getSigners(); + multiProvider = MultiProvider.createTestMultiProvider({ signer }); + + ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); + ismFactory = new HyperlaneIsmFactory(contractsMap, multiProvider); exampleRoutingConfig = { type: IsmType.ROUTING, diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts index 15aae7b50..645fee8aa 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts @@ -2,6 +2,7 @@ import { ethers } from 'ethers'; import { Logger } from 'pino'; import { + ArbL2ToL1Ism__factory, DefaultFallbackRoutingIsm, DefaultFallbackRoutingIsm__factory, DomainRoutingIsm, @@ -24,6 +25,7 @@ import { import { Address, Domain, + addBufferToGasLimit, assert, eqAddress, objFilter, @@ -32,7 +34,10 @@ import { import { HyperlaneApp } from '../app/HyperlaneApp.js'; import { appFromAddressesMapHelper } from '../contracts/contracts.js'; -import { HyperlaneAddressesMap } from '../contracts/types.js'; +import { + HyperlaneAddressesMap, + HyperlaneContractsMap, +} from '../contracts/types.js'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer.js'; import { ProxyFactoryFactories, @@ -54,15 +59,39 @@ import { } from './types.js'; import { routingModuleDelta } from './utils.js'; +const ismFactories = { + [IsmType.PAUSABLE]: new PausableIsm__factory(), + [IsmType.TRUSTED_RELAYER]: new TrustedRelayerIsm__factory(), + [IsmType.TEST_ISM]: new TestIsm__factory(), + [IsmType.OP_STACK]: new OPStackIsm__factory(), + [IsmType.ARB_L2_TO_L1]: new ArbL2ToL1Ism__factory(), +}; + +class IsmDeployer extends HyperlaneDeployer<{}, typeof ismFactories> { + protected readonly cachingEnabled = false; + + deployContracts(_chain: ChainName, _config: any): Promise { + throw new Error('Method not implemented.'); + } +} + export class HyperlaneIsmFactory extends HyperlaneApp { // The shape of this object is `ChainMap
`, // although `any` is use here because that type breaks a lot of signatures. // TODO: fix this in the next refactoring public deployedIsms: ChainMap = {}; + protected readonly deployer: IsmDeployer; - protected deployer?: HyperlaneDeployer; - setDeployer(deployer: HyperlaneDeployer): void { - this.deployer = deployer; + constructor( + contractsMap: HyperlaneContractsMap, + public readonly multiProvider: MultiProvider, + ) { + super( + contractsMap, + multiProvider, + rootLogger.child({ module: 'ismFactoryApp' }), + ); + this.deployer = new IsmDeployer(multiProvider, ismFactories); } static fromAddressesMap( @@ -74,11 +103,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { proxyFactoryFactories, multiProvider, ); - return new HyperlaneIsmFactory( - helper.contractsMap, - multiProvider, - rootLogger.child({ module: 'ismFactoryApp' }), - ); + return new HyperlaneIsmFactory(helper.contractsMap, multiProvider); } async deploy(params: { @@ -141,56 +166,39 @@ export class HyperlaneIsmFactory extends HyperlaneApp { }); break; case IsmType.OP_STACK: - assert( - this.deployer, - `HyperlaneDeployer must be set to deploy ${ismType}`, - ); - contract = await this.deployer.deployContractFromFactory( - destination, - new OPStackIsm__factory(), - IsmType.OP_STACK, - [config.nativeBridge], - ); + contract = await this.deployer.deployContract(destination, ismType, [ + config.nativeBridge, + ]); break; case IsmType.PAUSABLE: - assert( - this.deployer, - `HyperlaneDeployer must be set to deploy ${ismType}`, - ); - contract = await this.deployer.deployContractFromFactory( + contract = await this.deployer.deployContract( destination, - new PausableIsm__factory(), IsmType.PAUSABLE, [config.owner], ); - await this.deployer.transferOwnershipOfContracts(destination, config, { - [IsmType.PAUSABLE]: contract, - }); break; case IsmType.TRUSTED_RELAYER: - assert( - this.deployer, - `HyperlaneDeployer must be set to deploy ${ismType}`, - ); assert(mailbox, `Mailbox address is required for deploying ${ismType}`); - contract = await this.deployer.deployContractFromFactory( + contract = await this.deployer.deployContract( destination, - new TrustedRelayerIsm__factory(), IsmType.TRUSTED_RELAYER, [mailbox, config.relayer], ); break; case IsmType.TEST_ISM: - if (!this.deployer) { - throw new Error(`HyperlaneDeployer must be set to deploy ${ismType}`); - } - contract = await this.deployer.deployContractFromFactory( + contract = await this.deployer.deployContract( destination, - new TestIsm__factory(), IsmType.TEST_ISM, [], ); break; + case IsmType.ARB_L2_TO_L1: + contract = await this.deployer.deployContract( + destination, + IsmType.ARB_L2_TO_L1, + [config.bridge], + ); + break; default: throw new Error(`Unsupported ISM type ${ismType}`); } @@ -400,14 +408,14 @@ export class HyperlaneIsmFactory extends HyperlaneApp { submoduleAddresses, overrides, ); - // add 10% buffer + // add gas buffer const tx = await domainRoutingIsmFactory.deploy( owner, safeConfigDomains, submoduleAddresses, { + gasLimit: addBufferToGasLimit(estimatedGas), ...overrides, - gasLimit: estimatedGas.add(estimatedGas.div(10)), // 10% buffer }, ); // TODO: Should verify contract here @@ -496,10 +504,10 @@ export class HyperlaneIsmFactory extends HyperlaneApp { threshold, overrides, ); - // add 10% buffer + // add gas buffer const hash = await factory['deploy(address[],uint8)'](sorted, threshold, { + gasLimit: addBufferToGasLimit(estimatedGas), ...overrides, - gasLimit: estimatedGas.add(estimatedGas.div(10)), // 10% buffer }); await this.multiProvider.handleTx(chain, hash); @@ -536,13 +544,13 @@ export class HyperlaneIsmFactory extends HyperlaneApp { const estimatedGas = await factory.estimateGas[ 'deploy((address,uint96)[],uint96)' ](sorted, thresholdWeight, overrides); - // add 10% buffer + // add gas buffer const hash = await factory['deploy((address,uint96)[],uint96)']( sorted, thresholdWeight, { + gasLimit: addBufferToGasLimit(estimatedGas), ...overrides, - gasLimit: estimatedGas.add(estimatedGas.div(10)), // 10% buffer }, ); diff --git a/typescript/sdk/src/ism/metadata/aggregation.ts b/typescript/sdk/src/ism/metadata/aggregation.ts index 52f725219..baa4d91ea 100644 --- a/typescript/sdk/src/ism/metadata/aggregation.ts +++ b/typescript/sdk/src/ism/metadata/aggregation.ts @@ -26,7 +26,7 @@ export interface AggregationMetadata { const RANGE_SIZE = 4; -// adapted from rust/agents/relayer/src/msg/metadata/aggregation.rs +// adapted from rust/main/agents/relayer/src/msg/metadata/aggregation.rs export class AggregationMetadataBuilder implements MetadataBuilder { protected logger = rootLogger.child({ module: 'AggregationIsmMetadataBuilder', @@ -44,7 +44,7 @@ export class AggregationMetadataBuilder implements MetadataBuilder { 'Building aggregation metadata', ); assert(maxDepth > 0, 'Max depth reached'); - const promises = await Promise.allSettled( + const results = await Promise.allSettled( context.ism.modules.map((module) => timeout( this.base.build( @@ -58,10 +58,20 @@ export class AggregationMetadataBuilder implements MetadataBuilder { ), ), ); - const metadatas = promises.map((r) => - r.status === 'fulfilled' ? r.value ?? null : null, - ); - const included = metadatas.filter((m) => m !== null).length; + + const metadatas = results.map((result, index) => { + if (result.status === 'rejected') { + this.logger.warn( + `Failed to build for submodule ${index}: ${result.reason}`, + ); + return null; + } else { + this.logger.debug(`Built metadata for submodule ${index}`); + return result.value; + } + }); + + const included = metadatas.filter((meta) => meta !== null).length; assert( included >= context.ism.threshold, `Only built ${included} of ${context.ism.threshold} required modules`, diff --git a/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts b/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts index 519571067..f2b66d9cb 100644 --- a/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts +++ b/typescript/sdk/src/ism/metadata/arbL2ToL1.hardhat-test.ts @@ -41,7 +41,7 @@ import { ArbL2ToL1MetadataBuilder } from './arbL2ToL1.js'; import { MetadataContext } from './builder.js'; describe('ArbL2ToL1MetadataBuilder', () => { - const origin: ChainName = 'test1'; + const origin: ChainName = 'test4'; const destination: ChainName = 'test2'; let core: HyperlaneCore; let ismFactory: HyperlaneIsmFactory; @@ -93,14 +93,29 @@ describe('ArbL2ToL1MetadataBuilder', () => { [], ); hookConfig = { - test1: { + test4: { type: HookType.ARB_L2_TO_L1, arbSys: mockArbSys.address, destinationChain: destination, + childHook: { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + beneficiary: relayer.address, + owner: relayer.address, + oracleKey: relayer.address, + overhead: { + [destination]: 200000, + }, + oracleConfig: { + [destination]: { + gasPrice: '20', + tokenExchangeRate: '10000000000', + }, + }, + }, }, }; - factoryContracts = contractsMap.test1; + factoryContracts = contractsMap.test4; proxyFactoryAddresses = Object.keys(factoryContracts).reduce((acc, key) => { acc[key] = contractsMap[origin][key as keyof ProxyFactoryFactories].address; @@ -111,11 +126,11 @@ describe('ArbL2ToL1MetadataBuilder', () => { new MockArbBridge__factory(), [], ); - hookConfig.test1.bridge = arbBridge.address; + hookConfig.test4.bridge = arbBridge.address; const hookModule = await EvmHookModule.create({ chain: origin, - config: hookConfig.test1, + config: hookConfig.test4, proxyFactoryFactories: proxyFactoryAddresses, coreAddresses: core.getAddresses(origin), multiProvider, diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index f8c162f86..cb570e29e 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -418,7 +418,7 @@ export function buildAgentConfig( chains: ChainName[], multiProvider: MultiProvider, addresses: ChainMap, - startBlocks: ChainMap, + startBlocks: ChainMap, additionalConfig?: ChainMap, ): AgentConfig { const chainConfigs: ChainMap = {}; @@ -438,9 +438,11 @@ export function buildAgentConfig( ...metadata, ...addresses[chain], ...(additionalConfig ? additionalConfig[chain] : {}), - index: { - from: startBlocks[chain], - }, + ...(startBlocks[chain] !== undefined && { + index: { + from: startBlocks[chain], + }, + }), }; chainConfigs[chain] = chainConfig; } diff --git a/typescript/sdk/src/metadata/blockExplorer.ts b/typescript/sdk/src/metadata/blockExplorer.ts index 2da003937..e08f99ba6 100644 --- a/typescript/sdk/src/metadata/blockExplorer.ts +++ b/typescript/sdk/src/metadata/blockExplorer.ts @@ -2,13 +2,19 @@ import { ProtocolType } from '@hyperlane-xyz/utils'; import { ChainMetadata, ExplorerFamily } from './chainMetadataTypes.js'; -export function getExplorerBaseUrl(metadata: ChainMetadata): string | null { +export function getExplorerBaseUrl( + metadata: ChainMetadata, + index = 0, +): string | null { if (!metadata?.blockExplorers?.length) return null; - const url = new URL(metadata.blockExplorers[0].url); + const url = new URL(metadata.blockExplorers[index].url); return url.toString(); } -export function getExplorerApi(metadata: ChainMetadata): { +export function getExplorerApi( + metadata: ChainMetadata, + index = 0, +): { apiUrl: string; apiKey?: string | undefined; family?: ExplorerFamily | undefined; @@ -16,20 +22,21 @@ export function getExplorerApi(metadata: ChainMetadata): { const { protocol, blockExplorers } = metadata; // TODO solana + cosmos support here as needed if (protocol !== ProtocolType.Ethereum) return null; - if (!blockExplorers?.length || !blockExplorers[0].apiUrl) return null; + if (!blockExplorers?.length || !blockExplorers[index].apiUrl) return null; return { - apiUrl: blockExplorers[0].apiUrl, - apiKey: blockExplorers[0].apiKey, - family: blockExplorers[0].family, + apiUrl: blockExplorers[index].apiUrl, + apiKey: blockExplorers[index].apiKey, + family: blockExplorers[index].family, }; } -export function getExplorerApiUrl(metadata: ChainMetadata): string | null { - const { protocol, blockExplorers } = metadata; - // TODO solana + cosmos support here as needed - if (protocol !== ProtocolType.Ethereum) return null; - if (!blockExplorers?.length || !blockExplorers[0].apiUrl) return null; - const { apiUrl, apiKey } = blockExplorers[0]; +export function getExplorerApiUrl( + metadata: ChainMetadata, + index = 0, +): string | null { + const explorer = getExplorerApi(metadata, index)!; + if (!explorer) return null; + const { apiUrl, apiKey } = explorer; if (!apiKey) return apiUrl; const url = new URL(apiUrl); url.searchParams.set('apikey', apiKey); diff --git a/typescript/sdk/src/metadata/chainMetadata.test.ts b/typescript/sdk/src/metadata/chainMetadata.test.ts index 773eb9c03..f38a59e46 100644 --- a/typescript/sdk/src/metadata/chainMetadata.test.ts +++ b/typescript/sdk/src/metadata/chainMetadata.test.ts @@ -2,7 +2,11 @@ import { expect } from 'chai'; import { ProtocolType } from '@hyperlane-xyz/utils'; -import { ChainMetadata, isValidChainMetadata } from './chainMetadataTypes.js'; +import { + ChainMetadata, + EthJsonRpcBlockParameterTag, + isValidChainMetadata, +} from './chainMetadataTypes.js'; const minimalSchema: ChainMetadata = { chainId: 5, @@ -62,6 +66,16 @@ describe('ChainMetadataSchema', () => { grpcUrls: [], }), ).to.eq(true); + + expect( + isValidChainMetadata({ + ...minimalSchema, + blocks: { + confirmations: 1, + reorgPeriod: EthJsonRpcBlockParameterTag.Finalized, + }, + }), + ).to.eq(true); }); it('Rejects invalid schemas', () => { diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index 3abb48cf8..bf41cd4ac 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -4,10 +4,20 @@ */ import { SafeParseReturnType, z } from 'zod'; -import { ProtocolType } from '@hyperlane-xyz/utils'; +import { ProtocolType, objMerge } from '@hyperlane-xyz/utils'; + +import { ChainMap } from '../types.js'; import { ZChainName, ZNzUint, ZUint } from './customZodTypes.js'; +export enum EthJsonRpcBlockParameterTag { + Earliest = 'earliest', + Latest = 'latest', + Safe = 'safe', + Finalized = 'finalized', + Pending = 'pending', +} + export enum ExplorerFamily { Etherscan = 'etherscan', Blockscout = 'blockscout', @@ -17,6 +27,10 @@ export enum ExplorerFamily { export enum ChainTechnicalStack { ArbitrumNitro = 'arbitrumnitro', + OpStack = 'opstack', + PolygonCDK = 'polygoncdk', + PolkadotSubstrate = 'polkadotsubstrate', + ZkSync = 'zksync', Other = 'other', } @@ -67,6 +81,29 @@ export const RpcUrlSchema = z.object({ export type RpcUrl = z.infer; +export const BlockExplorerSchema = z.object({ + name: z.string().describe('A human readable name for the explorer.'), + url: z.string().url().describe('The base URL for the explorer.'), + apiUrl: z + .string() + .url() + .describe('The base URL for requests to the explorer API.'), + apiKey: z + .string() + .optional() + .describe( + 'An API key for the explorer (recommended for better reliability).', + ), + family: z + .nativeEnum(ExplorerFamily) + .optional() + .describe( + 'The type of the block explorer. See ExplorerFamily for valid values.', + ), +}); + +export type BlockExplorer = z.infer; + export const NativeTokenSchema = z.object({ name: z.string(), symbol: z.string(), @@ -87,28 +124,7 @@ export const ChainMetadataSchemaObject = z.object({ .describe('The human readable address prefix for the chains using bech32.'), blockExplorers: z - .array( - z.object({ - name: z.string().describe('A human readable name for the explorer.'), - url: z.string().url().describe('The base URL for the explorer.'), - apiUrl: z - .string() - .url() - .describe('The base URL for requests to the explorer API.'), - apiKey: z - .string() - .optional() - .describe( - 'An API key for the explorer (recommended for better reliability).', - ), - family: z - .nativeEnum(ExplorerFamily) - .optional() - .describe( - 'The type of the block explorer. See ExplorerFamily for valid values.', - ), - }), - ) + .array(BlockExplorerSchema) .optional() .describe('A list of block explorers with data for this chain'), @@ -117,9 +133,12 @@ export const ChainMetadataSchemaObject = z.object({ confirmations: ZUint.describe( 'Number of blocks to wait before considering a transaction confirmed.', ), - reorgPeriod: ZUint.optional().describe( - 'Number of blocks before a transaction has a near-zero chance of reverting.', - ), + reorgPeriod: z + .union([ZUint, z.string()]) + .optional() + .describe( + 'Number of blocks before a transaction has a near-zero chance of reverting or block tag.', + ), estimateBlockTime: z .number() .positive() @@ -230,7 +249,7 @@ export const ChainMetadataSchemaObject = z.object({ rpcUrls: z .array(RpcUrlSchema) - .nonempty() + .min(1) .describe('The list of RPC endpoints for interacting with the chain.'), slip44: z.number().optional().describe('The SLIP-0044 coin type.'), @@ -248,8 +267,11 @@ export const ChainMetadataSchemaObject = z.object({ .describe('Properties to include when forming transaction requests.'), }); +// Passthrough allows for extra fields to remain in the object (such as extensions consumers may want like `mailbox`) +const ChainMetadataSchemaExtensible = ChainMetadataSchemaObject.passthrough(); + // Add refinements to the object schema to conditionally validate certain fields -export const ChainMetadataSchema = ChainMetadataSchemaObject.refine( +export const ChainMetadataSchema = ChainMetadataSchemaExtensible.refine( (metadata) => { if ( [ProtocolType.Ethereum, ProtocolType.Sealevel].includes( @@ -333,14 +355,11 @@ export const ChainMetadataSchema = ChainMetadataSchemaObject.refine( }, ); -export type ChainMetadata = z.infer & +export type ChainMetadata = z.infer< + typeof ChainMetadataSchemaObject +> & Ext; -export type BlockExplorer = Exclude< - ChainMetadata['blockExplorers'], - undefined ->[number]; - export function safeParseChainMetadata( c: ChainMetadata, ): SafeParseReturnType { @@ -363,8 +382,22 @@ export function getChainIdNumber(chainMetadata: ChainMetadata): number { else throw new Error('ChainId is not a number, chain may be of Cosmos type'); } -export function getReorgPeriod(chainMetadata: ChainMetadata): number { +export function getReorgPeriod(chainMetadata: ChainMetadata): string | number { if (chainMetadata.blocks?.reorgPeriod !== undefined) return chainMetadata.blocks.reorgPeriod; else throw new Error('Chain has no reorg period'); } + +export function mergeChainMetadata( + base: ChainMetadata, + overrides: Partial | undefined, +): ChainMetadata { + return objMerge(base, overrides || {}, 10, true); +} + +export function mergeChainMetadataMap( + base: ChainMap, + overrides: ChainMap | undefined> | undefined, +): ChainMap { + return objMerge>(base, overrides || {}, 10, true); +} diff --git a/typescript/sdk/src/metadata/warpRouteConfig.ts b/typescript/sdk/src/metadata/warpRouteConfig.ts index 2fd69ce03..8c1ee346c 100644 --- a/typescript/sdk/src/metadata/warpRouteConfig.ts +++ b/typescript/sdk/src/metadata/warpRouteConfig.ts @@ -10,6 +10,7 @@ const TokenConfigSchema = z.object({ type: z.nativeEnum(TokenType), hypAddress: z.string(), // HypERC20Collateral, HypERC20Synthetic, HypNativeToken address tokenAddress: z.string().optional(), // external token address needed for collateral type eg tokenAddress.balanceOf(hypAddress) + tokenCoinGeckoId: z.string().optional(), // CoinGecko id for token name: z.string(), symbol: z.string(), decimals: z.number(), diff --git a/typescript/sdk/src/middleware/account/InterchainAccount.ts b/typescript/sdk/src/middleware/account/InterchainAccount.ts index 67ec07754..d705fedf3 100644 --- a/typescript/sdk/src/middleware/account/InterchainAccount.ts +++ b/typescript/sdk/src/middleware/account/InterchainAccount.ts @@ -31,6 +31,12 @@ export class InterchainAccount extends RouterApp { super(contractsMap, multiProvider); } + override async remoteChains(chainName: string): Promise { + return Object.keys(this.contractsMap).filter( + (chain) => chain !== chainName, + ); + } + router( contracts: HyperlaneContracts, ): InterchainAccountRouter { @@ -114,6 +120,8 @@ export class InterchainAccount extends RouterApp { .getProvider(destinationChain) .getCode(destinationAccount)) === '0x' ) { + const txOverrides = + this.multiProvider.getTransactionOverrides(destinationChain); await this.multiProvider.handleTx( destinationChain, destinationRouter[ @@ -123,6 +131,7 @@ export class InterchainAccount extends RouterApp { config.owner, originRouterAddress, destinationIsmAddress, + txOverrides, ), ); this.logger.debug(`Interchain account deployed at ${destinationAccount}`); diff --git a/typescript/sdk/src/providers/MultiProvider.ts b/typescript/sdk/src/providers/MultiProvider.ts index 307122e43..b37aba1c5 100644 --- a/typescript/sdk/src/providers/MultiProvider.ts +++ b/typescript/sdk/src/providers/MultiProvider.ts @@ -9,7 +9,12 @@ import { } from 'ethers'; import { Logger } from 'pino'; -import { Address, pick, rootLogger } from '@hyperlane-xyz/utils'; +import { + Address, + addBufferToGasLimit, + pick, + rootLogger, +} from '@hyperlane-xyz/utils'; import { testChainMetadata, testChains } from '../consts/testChains.js'; import { ChainMetadataManager } from '../metadata/ChainMetadataManager.js'; @@ -317,9 +322,9 @@ export class MultiProvider extends ChainMetadataManager { const deployTx = contractFactory.getDeployTransaction(...params); const gasEstimated = await signer.estimateGas(deployTx); - // deploy with 10% buffer on gas limit + // deploy with buffer on gas limit const contract = await contractFactory.deploy(...params, { - gasLimit: gasEstimated.add(gasEstimated.div(10)), // 10% buffer + gasLimit: addBufferToGasLimit(gasEstimated), ...overrides, }); diff --git a/typescript/sdk/src/providers/ProviderType.ts b/typescript/sdk/src/providers/ProviderType.ts index d7d84f152..c74a9995d 100644 --- a/typescript/sdk/src/providers/ProviderType.ts +++ b/typescript/sdk/src/providers/ProviderType.ts @@ -30,6 +30,7 @@ export enum ProviderType { SolanaWeb3 = 'solana-web3', CosmJs = 'cosmjs', CosmJsWasm = 'cosmjs-wasm', + GnosisTxBuilder = 'gnosis-txBuilder', } export const PROTOCOL_TO_DEFAULT_PROVIDER_TYPE: Record< diff --git a/typescript/sdk/src/providers/explorerHealthTest.ts b/typescript/sdk/src/providers/explorerHealthTest.ts new file mode 100644 index 000000000..4e6d8be3f --- /dev/null +++ b/typescript/sdk/src/providers/explorerHealthTest.ts @@ -0,0 +1,63 @@ +import { ChainMetadata } from '@hyperlane-xyz/sdk'; +import { Address, ProtocolType, rootLogger } from '@hyperlane-xyz/utils'; + +import { + getExplorerAddressUrl, + getExplorerBaseUrl, + getExplorerTxUrl, +} from '../metadata/blockExplorer.js'; + +const PROTOCOL_TO_ADDRESS: Record = { + [ProtocolType.Ethereum]: '0x0000000000000000000000000000000000000000', + [ProtocolType.Sealevel]: '11111111111111111111111111111111', + [ProtocolType.Cosmos]: 'cosmos100000000000000000000000000000000000000', +}; + +const PROTOCOL_TO_TX_HASH: Partial> = { + [ProtocolType.Ethereum]: + '0x0000000000000000000000000000000000000000000000000000000000000000', + [ProtocolType.Cosmos]: + '0000000000000000000000000000000000000000000000000000000000000000', +}; + +export async function isBlockExplorerHealthy( + chainMetadata: ChainMetadata, + explorerIndex: number, + address?: Address, + txHash?: string, +): Promise { + const baseUrl = getExplorerBaseUrl(chainMetadata, explorerIndex); + address ??= PROTOCOL_TO_ADDRESS[chainMetadata.protocol]; + txHash ??= PROTOCOL_TO_TX_HASH[chainMetadata.protocol]; + + if (!baseUrl) return false; + rootLogger.debug(`Got base url: ${baseUrl}`); + + rootLogger.debug(`Checking explorer home for ${chainMetadata.name}`); + await fetch(baseUrl); + rootLogger.debug(`Explorer home exists for ${chainMetadata.name}`); + + if (address) { + rootLogger.debug( + `Checking explorer address page for ${chainMetadata.name}`, + ); + const addressUrl = getExplorerAddressUrl(chainMetadata, address); + if (!addressUrl) return false; + rootLogger.debug(`Got address url: ${addressUrl}`); + const addressReq = await fetch(addressUrl); + if (!addressReq.ok && addressReq.status !== 404) return false; + rootLogger.debug(`Explorer address page okay for ${chainMetadata.name}`); + } + + if (txHash) { + rootLogger.debug(`Checking explorer tx page for ${chainMetadata.name}`); + const txUrl = getExplorerTxUrl(chainMetadata, txHash); + if (!txUrl) return false; + rootLogger.debug(`Got tx url: ${txUrl}`); + const txReq = await fetch(txUrl); + if (!txReq.ok && txReq.status !== 404) return false; + rootLogger.debug(`Explorer tx page okay for ${chainMetadata.name}`); + } + + return true; +} diff --git a/typescript/sdk/src/providers/providerBuilders.ts b/typescript/sdk/src/providers/providerBuilders.ts index b3a16762e..bc8051b1b 100644 --- a/typescript/sdk/src/providers/providerBuilders.ts +++ b/typescript/sdk/src/providers/providerBuilders.ts @@ -123,6 +123,7 @@ export type ProviderBuilderMap = Record< >; export const defaultProviderBuilderMap: ProviderBuilderMap = { [ProviderType.EthersV5]: defaultEthersV5ProviderBuilder, + [ProviderType.GnosisTxBuilder]: defaultEthersV5ProviderBuilder, [ProviderType.Viem]: defaultViemProviderBuilder, [ProviderType.SolanaWeb3]: defaultSolProviderBuilder, [ProviderType.CosmJs]: defaultCosmJsProviderBuilder, diff --git a/typescript/sdk/src/providers/rpcHealthTest.ts b/typescript/sdk/src/providers/rpcHealthTest.ts new file mode 100644 index 000000000..47b9ece24 --- /dev/null +++ b/typescript/sdk/src/providers/rpcHealthTest.ts @@ -0,0 +1,85 @@ +import { Mailbox__factory } from '@hyperlane-xyz/core'; +import { Address, rootLogger } from '@hyperlane-xyz/utils'; + +import { ChainMetadata } from '../metadata/chainMetadataTypes.js'; + +import { + CosmJsProvider, + CosmJsWasmProvider, + EthersV5Provider, + ProviderType, + SolanaWeb3Provider, +} from './ProviderType.js'; +import { protocolToDefaultProviderBuilder } from './providerBuilders.js'; + +export async function isRpcHealthy( + metadata: ChainMetadata, + rpcIndex: number, +): Promise { + const rpc = metadata.rpcUrls[rpcIndex]; + const builder = protocolToDefaultProviderBuilder[metadata.protocol]; + const provider = builder([rpc], metadata.chainId); + if (provider.type === ProviderType.EthersV5) + return isEthersV5ProviderHealthy(provider.provider, metadata); + else if (provider.type === ProviderType.SolanaWeb3) + return isSolanaWeb3ProviderHealthy(provider.provider, metadata); + else if ( + provider.type === ProviderType.CosmJsWasm || + provider.type === ProviderType.CosmJs + ) + return isCosmJsProviderHealthy(provider.provider, metadata); + else + throw new Error( + `Unsupported provider type ${provider.type}, new health check required`, + ); +} + +export async function isEthersV5ProviderHealthy( + provider: EthersV5Provider['provider'], + metadata: ChainMetadata, + mailboxAddress?: Address, +): Promise { + const chainName = metadata.name; + const blockNumber = await provider.getBlockNumber(); + if (!blockNumber || blockNumber < 0) return false; + rootLogger.debug(`Block number is okay for ${chainName}`); + + if (mailboxAddress) { + const mailbox = Mailbox__factory.createInterface(); + const topics = mailbox.encodeFilterTopics( + mailbox.events['DispatchId(bytes32)'], + [], + ); + rootLogger.debug(`Checking mailbox logs for ${chainName}`); + const mailboxLogs = await provider.getLogs({ + address: mailboxAddress, + topics, + fromBlock: blockNumber - 99, + toBlock: blockNumber, + }); + if (!mailboxLogs) return false; + rootLogger.debug(`Mailbox logs okay for ${chainName}`); + } + return true; +} + +export async function isSolanaWeb3ProviderHealthy( + provider: SolanaWeb3Provider['provider'], + metadata: ChainMetadata, +): Promise { + const blockNumber = await provider.getBlockHeight(); + if (!blockNumber || blockNumber < 0) return false; + rootLogger.debug(`Block number is okay for ${metadata.name}`); + return true; +} + +export async function isCosmJsProviderHealthy( + provider: CosmJsProvider['provider'] | CosmJsWasmProvider['provider'], + metadata: ChainMetadata, +): Promise { + const readyProvider = await provider; + const blockNumber = await readyProvider.getHeight(); + if (!blockNumber || blockNumber < 0) return false; + rootLogger.debug(`Block number is okay for ${metadata.name}`); + return true; +} diff --git a/typescript/sdk/src/providers/transactions/schemas.test.ts b/typescript/sdk/src/providers/transactions/schemas.test.ts index 248878d30..8631a3f55 100644 --- a/typescript/sdk/src/providers/transactions/schemas.test.ts +++ b/typescript/sdk/src/providers/transactions/schemas.test.ts @@ -2,43 +2,16 @@ import { expect } from 'chai'; import { Address } from '@hyperlane-xyz/utils'; -import { CallDataSchema, PopulatedTransactionSchema } from './schemas.js'; -import { CallData, PopulatedTransaction } from './types.js'; +import { CallDataSchema } from './schemas.js'; +import { CallData } from './types.js'; describe('transactions schemas', () => { const ADDRESS_MOCK: Address = '0x1234567890123456789012345678901234567890'; const DATA_MOCK: string = '0xabcdef'; - const CHAIN_ID_MOCK: number = 1; const VALUE_MOCK: string = '100'; const INVALID_ADDRESS: Address = '0x1'; - describe('PopulatedTransactionSchema', () => { - it('should parse valid PopulatedTransaction', () => { - const validPopulatedTransaction: PopulatedTransaction = { - to: ADDRESS_MOCK, - data: DATA_MOCK, - chainId: CHAIN_ID_MOCK, - }; - const result = PopulatedTransactionSchema.safeParse( - validPopulatedTransaction, - ); - expect(result.success).to.be.true; - }); - - it('should fail parsing invalid PopulatedTransaction', () => { - const invalidPopulatedTransaction: PopulatedTransaction = { - to: INVALID_ADDRESS, - data: DATA_MOCK, - chainId: CHAIN_ID_MOCK, - }; - const result = PopulatedTransactionSchema.safeParse( - invalidPopulatedTransaction, - ); - expect(result.success).to.be.false; - }); - }); - describe('CallDataSchema', () => { it('should parse valid CallData', () => { const validCallData: CallData = { diff --git a/typescript/sdk/src/providers/transactions/schemas.ts b/typescript/sdk/src/providers/transactions/schemas.ts index b9071ae1c..7547f47e4 100644 --- a/typescript/sdk/src/providers/transactions/schemas.ts +++ b/typescript/sdk/src/providers/transactions/schemas.ts @@ -4,17 +4,6 @@ import { ZHash } from '../../metadata/customZodTypes.js'; export const BigNumberSchema = z.string(); -export const PopulatedTransactionSchema = z.object({ - to: ZHash, - data: z.string(), - chainId: z.number(), -}); - -export const PopulatedTransactionsSchema = - PopulatedTransactionSchema.array().refine((txs) => txs.length > 0, { - message: 'Populated Transactions cannot be empty', - }); - export const CallDataSchema = z.object({ to: ZHash, data: z.string(), diff --git a/typescript/sdk/src/providers/transactions/submitter/TxSubmitterInterface.ts b/typescript/sdk/src/providers/transactions/submitter/TxSubmitterInterface.ts index 69f8718e2..eab6fa575 100644 --- a/typescript/sdk/src/providers/transactions/submitter/TxSubmitterInterface.ts +++ b/typescript/sdk/src/providers/transactions/submitter/TxSubmitterInterface.ts @@ -23,5 +23,9 @@ export interface TxSubmitterInterface { */ submit( ...txs: ProtocolTypedTransaction['transaction'][] - ): Promise['receipt'][] | void>; + ): Promise< + | ProtocolTypedReceipt['receipt'] + | ProtocolTypedReceipt['receipt'][] + | void + >; } diff --git a/typescript/sdk/src/providers/transactions/submitter/TxSubmitterTypes.ts b/typescript/sdk/src/providers/transactions/submitter/TxSubmitterTypes.ts index 7b3b47840..60018c83c 100644 --- a/typescript/sdk/src/providers/transactions/submitter/TxSubmitterTypes.ts +++ b/typescript/sdk/src/providers/transactions/submitter/TxSubmitterTypes.ts @@ -2,4 +2,5 @@ export enum TxSubmitterType { JSON_RPC = 'jsonRpc', IMPERSONATED_ACCOUNT = 'impersonatedAccount', GNOSIS_SAFE = 'gnosisSafe', + GNOSIS_TX_BUILDER = 'gnosisSafeTxBuilder', } diff --git a/typescript/sdk/src/providers/transactions/submitter/builder/TxSubmitterBuilder.ts b/typescript/sdk/src/providers/transactions/submitter/builder/TxSubmitterBuilder.ts index 0d77040c6..a113e681d 100644 --- a/typescript/sdk/src/providers/transactions/submitter/builder/TxSubmitterBuilder.ts +++ b/typescript/sdk/src/providers/transactions/submitter/builder/TxSubmitterBuilder.ts @@ -74,7 +74,11 @@ export class TxSubmitterBuilder */ public async submit( ...txs: ProtocolTypedTransaction['transaction'][] - ): Promise['receipt'][] | void> { + ): Promise< + | ProtocolTypedReceipt['receipt'] + | ProtocolTypedReceipt['receipt'][] + | void + > { this.logger.debug( `Submitting ${txs.length} transactions to the ${this.currentSubmitter.txSubmitterType} submitter...`, ); diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts new file mode 100644 index 000000000..7e8b61408 --- /dev/null +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxBuilder.ts @@ -0,0 +1,77 @@ +import { SafeTransactionData } from '@safe-global/safe-core-sdk-types'; + +import { assert } from '@hyperlane-xyz/utils'; + +// prettier-ignore +// @ts-ignore +import { getSafe, getSafeService } from '../../../../utils/gnosisSafe.js'; +import { MultiProvider } from '../../../MultiProvider.js'; +import { AnnotatedEV5Transaction } from '../../../ProviderType.js'; +import { TxSubmitterType } from '../TxSubmitterTypes.js'; + +import { EV5GnosisSafeTxSubmitter } from './EV5GnosisSafeTxSubmitter.js'; +import { EV5GnosisSafeTxBuilderProps } from './types.js'; + +// TODO: Use this return type in submit() +export interface GnosisTransactionBuilderPayload { + version: string; + chainId: string; + meta: {}; + transactions: SafeTransactionData[]; +} + +/** + * This class is used to create a Safe Transaction Builder compatible object. + * It is not a true Submitter because it does not submits any transactions. + */ +export class EV5GnosisSafeTxBuilder extends EV5GnosisSafeTxSubmitter { + public readonly txSubmitterType: TxSubmitterType = + TxSubmitterType.GNOSIS_TX_BUILDER; + constructor( + public readonly multiProvider: MultiProvider, + public readonly props: EV5GnosisSafeTxBuilderProps, + safe: any, + safeService: any, + ) { + super(multiProvider, props, safe, safeService); + } + + static async create( + multiProvider: MultiProvider, + props: EV5GnosisSafeTxBuilderProps, + ): Promise { + const { chain, safeAddress } = props; + const { gnosisSafeTransactionServiceUrl } = + multiProvider.getChainMetadata(chain); + assert( + gnosisSafeTransactionServiceUrl, + `Must set gnosisSafeTransactionServiceUrl in the Registry metadata for ${chain}`, + ); + const safe = await getSafe(chain, multiProvider, safeAddress); + const safeService = await getSafeService(chain, multiProvider); + + return new EV5GnosisSafeTxBuilder(multiProvider, props, safe, safeService); + } + + /** + * Creates a Gnosis Safe transaction builder object using the PopulatedTransactions + * + * @param txs - An array of populated transactions + */ + public async submit(...txs: AnnotatedEV5Transaction[]): Promise { + const transactions: SafeTransactionData[] = await Promise.all( + txs.map( + async (tx: AnnotatedEV5Transaction) => + ( + await this.createSafeTransaction(tx) + ).data, + ), + ); + return { + version: this.props.version, + chainId: this.multiProvider.getChainId(this.props.chain).toString(), + meta: {}, + transactions, + }; + } +} diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts index ff120d6d3..9ed8ef98b 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.ts @@ -1,3 +1,4 @@ +import { SafeTransaction } from '@safe-global/safe-core-sdk-types'; import { Logger } from 'pino'; import { Address, assert, rootLogger } from '@hyperlane-xyz/utils'; @@ -6,7 +7,7 @@ import { Address, assert, rootLogger } from '@hyperlane-xyz/utils'; // @ts-ignore import { canProposeSafeTransactions, getSafe, getSafeService } from '../../../../utils/gnosisSafe.js'; import { MultiProvider } from '../../../MultiProvider.js'; -import { PopulatedTransaction, PopulatedTransactions } from '../../types.js'; +import { AnnotatedEV5Transaction } from '../../../ProviderType.js'; import { TxSubmitterType } from '../TxSubmitterTypes.js'; import { EV5TxSubmitterInterface } from './EV5TxSubmitterInterface.js'; @@ -62,25 +63,44 @@ export class EV5GnosisSafeTxSubmitter implements EV5TxSubmitterInterface { ); } - public async submit(...txs: PopulatedTransactions): Promise { + public async createSafeTransaction({ + to, + data, + value, + chainId, + }: AnnotatedEV5Transaction): Promise { const nextNonce: number = await this.safeService.getNextNonce( this.props.safeAddress, ); - const safeTransactionBatch: any[] = txs.map( - ({ to, data, value, chainId }: PopulatedTransaction) => { - const txChain = this.multiProvider.getChainName(chainId); - assert( - txChain === this.props.chain, - `Invalid PopulatedTransaction: Cannot submit ${txChain} tx to ${this.props.chain} submitter.`, - ); - return { to, data, value: value?.toString() ?? '0' }; - }, + assert(chainId, 'Invalid PopulatedTransaction: chainId is required'); + const txChain = this.multiProvider.getChainName(chainId); + assert( + txChain === this.props.chain, + `Invalid PopulatedTransaction: Cannot submit ${txChain} tx to ${this.props.chain} submitter.`, ); - const safeTransaction = await this.safe.createTransaction({ - safeTransactionData: safeTransactionBatch, + return this.safe.createTransaction({ + safeTransactionData: [{ to, data, value: value?.toString() ?? '0' }], options: { nonce: nextNonce }, }); - const safeTransactionData: any = safeTransaction.data; + } + + public async submit(...txs: AnnotatedEV5Transaction[]): Promise { + return this.proposeIndividualTransactions(txs); + } + + private async proposeIndividualTransactions(txs: AnnotatedEV5Transaction[]) { + const safeTransactions: SafeTransaction[] = []; + for (const tx of txs) { + const safeTransaction = await this.createSafeTransaction(tx); + await this.proposeSafeTransaction(safeTransaction); + safeTransactions.push(safeTransaction); + } + return safeTransactions; + } + + private async proposeSafeTransaction( + safeTransaction: SafeTransaction, + ): Promise { const safeTxHash: string = await this.safe.getTransactionHash( safeTransaction, ); @@ -96,7 +116,7 @@ export class EV5GnosisSafeTxSubmitter implements EV5TxSubmitterInterface { return this.safeService.proposeTransaction({ safeAddress: this.props.safeAddress, - safeTransactionData, + safeTransactionData: safeTransaction.data, safeTxHash, senderAddress, senderSignature, diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.ts index 9a6866e35..f0fea448a 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5ImpersonatedAccountTxSubmitter.ts @@ -8,7 +8,7 @@ import { stopImpersonatingAccount, } from '../../../../utils/fork.js'; import { MultiProvider } from '../../../MultiProvider.js'; -import { PopulatedTransactions } from '../../types.js'; +import { AnnotatedEV5Transaction } from '../../../ProviderType.js'; import { TxSubmitterType } from '../TxSubmitterTypes.js'; import { EV5JsonRpcTxSubmitter } from './EV5JsonRpcTxSubmitter.js'; @@ -30,7 +30,7 @@ export class EV5ImpersonatedAccountTxSubmitter extends EV5JsonRpcTxSubmitter { } public async submit( - ...txs: PopulatedTransactions + ...txs: AnnotatedEV5Transaction[] ): Promise { const impersonatedAccount = await impersonateAccount( this.props.userAddress, diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.ts index 30b60137d..4a3d11d47 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.ts @@ -5,7 +5,7 @@ import { Logger } from 'pino'; import { assert, rootLogger } from '@hyperlane-xyz/utils'; import { MultiProvider } from '../../../MultiProvider.js'; -import { PopulatedTransactions } from '../../types.js'; +import { AnnotatedEV5Transaction } from '../../../ProviderType.js'; import { TxSubmitterType } from '../TxSubmitterTypes.js'; import { EV5TxSubmitterInterface } from './EV5TxSubmitterInterface.js'; @@ -20,7 +20,7 @@ export class EV5JsonRpcTxSubmitter implements EV5TxSubmitterInterface { constructor(public readonly multiProvider: MultiProvider) {} public async submit( - ...txs: PopulatedTransactions + ...txs: AnnotatedEV5Transaction[] ): Promise { const receipts: TransactionReceipt[] = []; for (const tx of txs) { diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts index 7b5cb9a6c..931eb11bf 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/schemas.ts @@ -7,6 +7,12 @@ export const EV5GnosisSafeTxSubmitterPropsSchema = z.object({ safeAddress: ZHash, }); +export const EV5GnosisSafeTxBuilderPropsSchema = z.object({ + version: z.string().default('1.0'), + chain: ZChainName, + safeAddress: ZHash, +}); + export const EV5ImpersonatedAccountTxSubmitterPropsSchema = z.object({ userAddress: ZHash, }); diff --git a/typescript/sdk/src/providers/transactions/submitter/ethersV5/types.ts b/typescript/sdk/src/providers/transactions/submitter/ethersV5/types.ts index 67edd01d4..ee41edfa4 100644 --- a/typescript/sdk/src/providers/transactions/submitter/ethersV5/types.ts +++ b/typescript/sdk/src/providers/transactions/submitter/ethersV5/types.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import { + EV5GnosisSafeTxBuilderPropsSchema, EV5GnosisSafeTxSubmitterPropsSchema, EV5ImpersonatedAccountTxSubmitterPropsSchema, } from './schemas.js'; @@ -8,6 +9,9 @@ import { export type EV5GnosisSafeTxSubmitterProps = z.infer< typeof EV5GnosisSafeTxSubmitterPropsSchema >; +export type EV5GnosisSafeTxBuilderProps = z.infer< + typeof EV5GnosisSafeTxBuilderPropsSchema +>; export type EV5ImpersonatedAccountTxSubmitterProps = z.infer< typeof EV5ImpersonatedAccountTxSubmitterPropsSchema >; diff --git a/typescript/sdk/src/providers/transactions/submitter/schemas.ts b/typescript/sdk/src/providers/transactions/submitter/schemas.ts index 9ec67f19b..89c891c09 100644 --- a/typescript/sdk/src/providers/transactions/submitter/schemas.ts +++ b/typescript/sdk/src/providers/transactions/submitter/schemas.ts @@ -2,6 +2,7 @@ import { z } from 'zod'; import { TxSubmitterType } from './TxSubmitterTypes.js'; import { + EV5GnosisSafeTxBuilderPropsSchema, EV5GnosisSafeTxSubmitterPropsSchema, EV5ImpersonatedAccountTxSubmitterPropsSchema, } from './ethersV5/schemas.js'; @@ -18,4 +19,8 @@ export const SubmitterMetadataSchema = z.discriminatedUnion('type', [ type: z.literal(TxSubmitterType.GNOSIS_SAFE), ...EV5GnosisSafeTxSubmitterPropsSchema.shape, }), + z.object({ + type: z.literal(TxSubmitterType.GNOSIS_TX_BUILDER), + ...EV5GnosisSafeTxBuilderPropsSchema.shape, + }), ]); diff --git a/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts b/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts index 62aefcf09..b2cce124f 100644 --- a/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts +++ b/typescript/sdk/src/providers/transactions/transformer/ethersV5/EV5InterchainAccountTxTransformer.ts @@ -9,11 +9,8 @@ import { } from '../../../../middleware/account/InterchainAccount.js'; import { ChainName } from '../../../../types.js'; import { MultiProvider } from '../../../MultiProvider.js'; -import { - CallData, - PopulatedTransaction, - PopulatedTransactions, -} from '../../types.js'; +import { AnnotatedEV5Transaction } from '../../../ProviderType.js'; +import { CallData } from '../../types.js'; import { TxTransformerType } from '../TxTransformerTypes.js'; import { EV5TxTransformerInterface } from './EV5TxTransformerInterface.js'; @@ -39,13 +36,16 @@ export class EV5InterchainAccountTxTransformer } public async transform( - ...txs: PopulatedTransactions + ...txs: AnnotatedEV5Transaction[] ): Promise { const txChainsToInnerCalls: Record = txs.reduce( ( txChainToInnerCalls: Record, - { to, data, chainId }: PopulatedTransaction, + { to, data, chainId }: AnnotatedEV5Transaction, ) => { + assert(chainId, 'Invalid PopulatedTransaction: chainId is required'); + assert(to, 'Invalid PopulatedTransaction: to is required'); + assert(data, 'Invalid PopulatedTransaction: data is required'); const txChain = this.multiProvider.getChainName(chainId); txChainToInnerCalls[txChain] ||= []; txChainToInnerCalls[txChain].push({ to, data }); diff --git a/typescript/sdk/src/providers/transactions/types.ts b/typescript/sdk/src/providers/transactions/types.ts index 4ed4e2319..49585f215 100644 --- a/typescript/sdk/src/providers/transactions/types.ts +++ b/typescript/sdk/src/providers/transactions/types.ts @@ -1,17 +1,5 @@ -import { ethers } from 'ethers'; import { z } from 'zod'; -import { - CallDataSchema, - PopulatedTransactionSchema, - PopulatedTransactionsSchema, -} from './schemas.js'; - -export type PopulatedTransaction = z.infer & - ethers.PopulatedTransaction; -export type PopulatedTransactions = z.infer< - typeof PopulatedTransactionsSchema -> & - ethers.PopulatedTransaction[]; +import { CallDataSchema } from './schemas.js'; export type CallData = z.infer; diff --git a/typescript/sdk/src/router/GasRouterDeployer.ts b/typescript/sdk/src/router/GasRouterDeployer.ts index 76aabb460..1c64251d1 100644 --- a/typescript/sdk/src/router/GasRouterDeployer.ts +++ b/typescript/sdk/src/router/GasRouterDeployer.ts @@ -52,6 +52,7 @@ export abstract class GasRouterDeployer< chain, this.router(contracts)['setDestinationGas((uint32,uint256)[])']( remoteConfigs, + this.multiProvider.getTransactionOverrides(chain), ), ); } diff --git a/typescript/sdk/src/router/HyperlaneRouterChecker.ts b/typescript/sdk/src/router/HyperlaneRouterChecker.ts index 492b3f655..2dde30791 100644 --- a/typescript/sdk/src/router/HyperlaneRouterChecker.ts +++ b/typescript/sdk/src/router/HyperlaneRouterChecker.ts @@ -1,6 +1,12 @@ import { ethers } from 'ethers'; -import { addressToBytes32, assert, eqAddress } from '@hyperlane-xyz/utils'; +import { + AddressBytes32, + addressToBytes32, + assert, + eqAddress, + rootLogger, +} from '@hyperlane-xyz/utils'; import { HyperlaneFactories } from '../contracts/types.js'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker.js'; @@ -29,6 +35,7 @@ export class HyperlaneRouterChecker< app: App, configMap: ChainMap, readonly ismFactory?: HyperlaneIsmFactory, + readonly logger = rootLogger.child({ module: 'HyperlaneRouterChecker' }), ) { super(multiProvider, app, configMap); } @@ -120,24 +127,43 @@ export class HyperlaneRouterChecker< async checkEnrolledRouters(chain: ChainName): Promise { const router = this.app.router(this.app.getContracts(chain)); const remoteChains = await this.app.remoteChains(chain); + const currentRouters: ChainMap = {}; + const expectedRouters: ChainMap = {}; + const routerDiff: ChainMap<{ + actual: AddressBytes32; + expected: AddressBytes32; + }> = {}; + await Promise.all( remoteChains.map(async (remoteChain) => { const remoteRouterAddress = this.app.routerAddress(remoteChain); const remoteDomainId = this.multiProvider.getDomainId(remoteChain); const actualRouter = await router.routers(remoteDomainId); const expectedRouter = addressToBytes32(remoteRouterAddress); + + currentRouters[remoteChain] = actualRouter; + expectedRouters[remoteChain] = expectedRouter; + if (actualRouter !== expectedRouter) { - const violation: RouterViolation = { - chain, - remoteChain, - type: RouterViolationType.EnrolledRouter, - contract: router, + routerDiff[remoteChain] = { actual: actualRouter, expected: expectedRouter, }; - this.addViolation(violation); } }), ); + + if (Object.keys(routerDiff).length > 0) { + const violation: RouterViolation = { + chain, + type: RouterViolationType.EnrolledRouter, + contract: router, + actual: currentRouters, + expected: expectedRouters, + routerDiff, + description: `Routers for some domains are missing or not enrolled correctly`, + }; + this.addViolation(violation); + } } } diff --git a/typescript/sdk/src/router/HyperlaneRouterDeployer.ts b/typescript/sdk/src/router/HyperlaneRouterDeployer.ts index 54f502847..bc68d047c 100644 --- a/typescript/sdk/src/router/HyperlaneRouterDeployer.ts +++ b/typescript/sdk/src/router/HyperlaneRouterDeployer.ts @@ -1,6 +1,7 @@ import { Ownable, Router } from '@hyperlane-xyz/core'; import { Address, + addBufferToGasLimit, addressToBytes32, objFilter, objMap, @@ -90,7 +91,7 @@ export abstract class HyperlaneRouterDeployer< ); // deploy with 10% buffer on gas limit const enrollTx = await router.enrollRemoteRouters(domains, addresses, { - gasLimit: estimatedGas.add(estimatedGas.div(10)), + gasLimit: addBufferToGasLimit(estimatedGas), ...this.multiProvider.getTransactionOverrides(chain), }); await this.multiProvider.handleTx(chain, enrollTx); diff --git a/typescript/sdk/src/router/ProxiedRouterDeployer.ts b/typescript/sdk/src/router/ProxiedRouterDeployer.ts index 8f4d03d2f..e938c096c 100644 --- a/typescript/sdk/src/router/ProxiedRouterDeployer.ts +++ b/typescript/sdk/src/router/ProxiedRouterDeployer.ts @@ -111,7 +111,10 @@ export abstract class ProxiedRouterDeployer< ); return this.multiProvider.handleTx( chain, - proxyAdmin.transferOwnership(adminOwner), + proxyAdmin.transferOwnership( + adminOwner, + this.multiProvider.getTransactionOverrides(chain), + ), ); } return; diff --git a/typescript/sdk/src/router/RouterApps.ts b/typescript/sdk/src/router/RouterApps.ts index 5251a93f2..306eec65a 100644 --- a/typescript/sdk/src/router/RouterApps.ts +++ b/typescript/sdk/src/router/RouterApps.ts @@ -45,14 +45,14 @@ export abstract class RouterApp< // check onchain for remote enrollments override async remoteChains(chainName: string): Promise { const router = this.router(this.contractsMap[chainName]); - const domainIds = (await router.domains()).map((domain) => { + const onchainRemoteChainNames = (await router.domains()).map((domain) => { const chainName = this.multiProvider.tryGetChainName(domain); if (chainName === null) { throw new Error(`Chain name not found for domain: ${domain}`); } return chainName; }); - return domainIds; + return onchainRemoteChainNames; } getSecurityModules(): Promise> { diff --git a/typescript/sdk/src/router/schemas.ts b/typescript/sdk/src/router/schemas.ts index abccd9161..d49a72dbd 100644 --- a/typescript/sdk/src/router/schemas.ts +++ b/typescript/sdk/src/router/schemas.ts @@ -32,6 +32,13 @@ export const RouterConfigSchema = MailboxClientConfigSchema.merge( }), ); +const DestinationGasDomain = z.string(); +const DestinationGasAmount = z.string(); // This must be a string type to match Ether's type +export const DestinationGasSchema = z.record( + DestinationGasDomain, + DestinationGasAmount, +); export const GasRouterConfigSchema = RouterConfigSchema.extend({ gas: z.number().optional(), + destinationGas: DestinationGasSchema.optional(), }); diff --git a/typescript/sdk/src/router/types.ts b/typescript/sdk/src/router/types.ts index 8fdf7c98b..17e36ead6 100644 --- a/typescript/sdk/src/router/types.ts +++ b/typescript/sdk/src/router/types.ts @@ -6,13 +6,15 @@ import { Router, TimelockController__factory, } from '@hyperlane-xyz/core'; -import { Address } from '@hyperlane-xyz/utils'; +import { Address, AddressBytes32 } from '@hyperlane-xyz/utils'; import { HyperlaneFactories } from '../contracts/types.js'; import { UpgradeConfig } from '../deploy/proxy.js'; import { CheckerViolation } from '../deploy/types.js'; +import { ChainMap } from '../types.js'; import { + DestinationGasSchema, GasRouterConfigSchema, MailboxClientConfigSchema, RemoteRoutersSchema, @@ -56,11 +58,13 @@ export enum RouterViolationType { export interface RouterViolation extends CheckerViolation { type: RouterViolationType.EnrolledRouter; - remoteChain: string; contract: Router; - actual: string; - expected: string; + routerDiff: ChainMap<{ + actual: AddressBytes32; + expected: AddressBytes32; + }>; description?: string; } export type RemoteRouters = z.infer; +export type DestinationGas = z.infer; diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts index 82a8e77a4..33b9bdaa4 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.hardhat-test.ts @@ -24,7 +24,6 @@ import { TestChainName, serializeContracts, } from '@hyperlane-xyz/sdk'; -import { normalizeConfig } from '@hyperlane-xyz/utils'; import { TestCoreApp } from '../core/TestCoreApp.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; @@ -36,6 +35,7 @@ import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; import { RemoteRouters } from '../router/types.js'; import { randomAddress } from '../test/testUtils.js'; import { ChainMap } from '../types.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmERC20WarpModule } from './EvmERC20WarpModule.js'; import { TokenType } from './config.js'; @@ -111,7 +111,7 @@ describe('EvmERC20WarpHyperlaneModule', async () => { }); it('should create with a collateral config', async () => { - const config = { + const config: TokenRouterConfig = { ...baseConfig, type: TokenType.collateral, token: token.address, @@ -139,7 +139,7 @@ describe('EvmERC20WarpHyperlaneModule', async () => { TOKEN_NAME, TOKEN_NAME, ); - const config = { + const config: TokenRouterConfig = { type: TokenType.collateralVault, token: vault.address, hook: hookAddress, @@ -172,9 +172,8 @@ describe('EvmERC20WarpHyperlaneModule', async () => { }); it('should create with a synthetic config', async () => { - const config = { + const config: TokenRouterConfig = { type: TokenType.synthetic, - token: token.address, hook: hookAddress, name: TOKEN_NAME, symbol: TOKEN_NAME, @@ -519,5 +518,39 @@ describe('EvmERC20WarpHyperlaneModule', async () => { }); expect(txs.length).to.equal(0); }); + + it('should update the destination gas', async () => { + const domain = 3; + const config: TokenRouterConfig = { + ...baseConfig, + type: TokenType.native, + hook: hookAddress, + ismFactoryAddresses, + remoteRouters: { + [domain]: randomAddress(), + }, + }; + + // Deploy using WarpModule + const evmERC20WarpModule = await EvmERC20WarpModule.create({ + chain, + config: { + ...config, + }, + multiProvider, + }); + await sendTxs( + await evmERC20WarpModule.update({ + ...config, + destinationGas: { + [domain]: '5000', + }, + }), + ); + + const updatedConfig = await evmERC20WarpModule.read(); + expect(Object.keys(updatedConfig.destinationGas!).length).to.be.equal(1); + expect(updatedConfig.destinationGas![domain]).to.equal('5000'); + }); }); }); diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.ts b/typescript/sdk/src/token/EvmERC20WarpModule.ts index 72abe4cc5..462a5b646 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.ts @@ -1,4 +1,7 @@ +import { BigNumberish } from 'ethers'; + import { + GasRouter__factory, MailboxClient__factory, TokenRouter__factory, } from '@hyperlane-xyz/core'; @@ -12,10 +15,11 @@ import { assert, deepEquals, isObjEmpty, - normalizeConfig, + objMap, rootLogger, } from '@hyperlane-xyz/utils'; +import { transferOwnershipTransactions } from '../contracts/contracts.js'; import { HyperlaneModule, HyperlaneModuleParams, @@ -25,6 +29,7 @@ import { DerivedIsmConfig } from '../ism/EvmIsmReader.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { AnnotatedEV5Transaction } from '../providers/ProviderType.js'; import { ChainNameOrId } from '../types.js'; +import { normalizeConfig } from '../utils/ism.js'; import { EvmERC20WarpRouteReader } from './EvmERC20WarpRouteReader.js'; import { HypERC20Deployer } from './deploy.js'; @@ -92,9 +97,16 @@ export class EvmERC20WarpModule extends HyperlaneModule< const transactions = []; + /** + * @remark + * The order of operations matter + * 1. createOwnershipUpdateTxs() must always be LAST because no updates possible after ownership transferred + * 2. createRemoteRoutersUpdateTxs() must always be BEFORE createSetDestinationGasUpdateTxs() because gas enumeration depends on domains + */ transactions.push( ...(await this.createIsmUpdateTxs(actualConfig, expectedConfig)), ...this.createRemoteRoutersUpdateTxs(actualConfig, expectedConfig), + ...this.createSetDestinationGasUpdateTxs(actualConfig, expectedConfig), ...this.createOwnershipUpdateTxs(actualConfig, expectedConfig), ); @@ -152,6 +164,57 @@ export class EvmERC20WarpModule extends HyperlaneModule< return updateTransactions; } + /** + * Create a transaction to update the remote routers for the Warp Route contract. + * + * @param actualConfig - The on-chain router configuration, including the remoteRouters array. + * @param expectedConfig - The expected token router configuration. + * @returns A array with a single Ethereum transaction that need to be executed to enroll the routers + */ + createSetDestinationGasUpdateTxs( + actualConfig: TokenRouterConfig, + expectedConfig: TokenRouterConfig, + ): AnnotatedEV5Transaction[] { + const updateTransactions: AnnotatedEV5Transaction[] = []; + if (!expectedConfig.destinationGas) { + return []; + } + + assert(actualConfig.destinationGas, 'actualDestinationGas is undefined'); + assert(expectedConfig.destinationGas, 'actualDestinationGas is undefined'); + + const { destinationGas: actualDestinationGas } = actualConfig; + const { destinationGas: expectedDestinationGas } = expectedConfig; + + if (!deepEquals(actualDestinationGas, expectedDestinationGas)) { + const contractToUpdate = GasRouter__factory.connect( + this.args.addresses.deployedTokenRoute, + this.multiProvider.getProvider(this.domainId), + ); + + // Convert { 1: 2, 2: 3, ... } to [{ 1: 2 }, { 2: 3 }] + const gasRouterConfigs: { domain: BigNumberish; gas: BigNumberish }[] = + []; + objMap(expectedDestinationGas, (domain: string, gas: string) => { + gasRouterConfigs.push({ + domain, + gas, + }); + }); + + updateTransactions.push({ + annotation: `Setting destination gas for ${this.args.addresses.deployedTokenRoute} on ${this.args.chain}`, + chainId: this.domainId, + to: contractToUpdate.address, + data: contractToUpdate.interface.encodeFunctionData( + 'setDestinationGas((uint32,uint256)[])', + [gasRouterConfigs], + ), + }); + } + return updateTransactions; + } + /** * Create transactions to update an existing ISM config, or deploy a new ISM and return a tx to setInterchainSecurityModule * @@ -214,12 +277,13 @@ export class EvmERC20WarpModule extends HyperlaneModule< actualConfig: TokenRouterConfig, expectedConfig: TokenRouterConfig, ): AnnotatedEV5Transaction[] { - return EvmERC20WarpModule.createTransferOwnershipTx({ - actualOwner: actualConfig.owner, - expectedOwner: expectedConfig.owner, - deployedAddress: this.args.addresses.deployedTokenRoute, - chainId: this.domainId, - }); + return transferOwnershipTransactions( + this.multiProvider.getDomainId(this.args.chain), + this.args.addresses.deployedTokenRoute, + actualConfig, + expectedConfig, + `${expectedConfig.type} Warp Route`, + ); } /** diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts index d5f7a6ee1..782acc3d2 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts @@ -14,14 +14,16 @@ import { HyperlaneContractsMap, RouterConfig, TestChainName, - TokenRouterConfig, + WarpRouteDeployConfig, test3, } from '@hyperlane-xyz/sdk'; +import { assert } from '@hyperlane-xyz/utils'; import { TestCoreApp } from '../core/TestCoreApp.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; import { ProxyFactoryFactories } from '../deploy/contracts.js'; +import { DerivedIsmConfig } from '../ism/EvmIsmReader.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { ChainMap } from '../types.js'; @@ -49,7 +51,7 @@ describe('ERC20WarpRouterReader', async () => { let mailbox: Mailbox; let evmERC20WarpRouteReader: EvmERC20WarpRouteReader; let vault: ERC4626; - beforeEach(async () => { + before(async () => { [signer] = await hre.ethers.getSigners(); multiProvider = MultiProvider.createTestMultiProvider({ signer }); const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); @@ -77,6 +79,12 @@ describe('ERC20WarpRouterReader', async () => { vault = await vaultFactory.deploy(token.address, TOKEN_NAME, TOKEN_NAME); }); + beforeEach(async () => { + // Reset the MultiProvider and create a new deployer for each test + multiProvider = MultiProvider.createTestMultiProvider({ signer }); + deployer = new HypERC20Deployer(multiProvider); + }); + it('should derive a token type from contract', async () => { const typesToDerive = [ TokenType.collateral, @@ -116,12 +124,12 @@ describe('ERC20WarpRouterReader', async () => { it('should derive collateral config correctly', async () => { // Create config - const config = { + const config: WarpRouteDeployConfig = { [chain]: { type: TokenType.collateral, token: token.address, hook: await mailbox.defaultHook(), - interchainsecurityModule: await mailbox.defaultIsm(), + interchainSecurityModule: await mailbox.defaultIsm(), ...baseConfig, }, }; @@ -144,8 +152,10 @@ describe('ERC20WarpRouterReader', async () => { config[chain].hook as string, ), ); - // Check ism. should return undefined - expect(derivedConfig.interchainSecurityModule).to.be.undefined; + // Check ism + expect( + (derivedConfig.interchainSecurityModule as DerivedIsmConfig).address, + ).to.be.equal(await mailbox.defaultIsm()); // Check if token values matches if (derivedConfig.type === TokenType.collateral) { @@ -154,13 +164,44 @@ describe('ERC20WarpRouterReader', async () => { expect(derivedConfig.decimals).to.equal(TOKEN_DECIMALS); } }); + it('should derive synthetic rebase config correctly', async () => { + // Create config + const config: WarpRouteDeployConfig = { + [chain]: { + type: TokenType.syntheticRebase, + collateralChainName: TestChainName.test4, + hook: await mailbox.defaultHook(), + name: TOKEN_NAME, + symbol: TOKEN_NAME, + decimals: TOKEN_DECIMALS, + ...baseConfig, + }, + }; + // Deploy with config + const warpRoute = await deployer.deploy(config); + + // Derive config and check if each value matches + const derivedConfig = await evmERC20WarpRouteReader.deriveWarpRouteConfig( + warpRoute[chain].syntheticRebase.address, + ); + for (const [key, value] of Object.entries(derivedConfig)) { + const deployedValue = (config[chain] as any)[key]; + if (deployedValue && typeof value === 'string') + expect(deployedValue).to.equal(value); + } + + // Check if token values matches + if (derivedConfig.type === TokenType.collateral) { + expect(derivedConfig.name).to.equal(TOKEN_NAME); + expect(derivedConfig.symbol).to.equal(TOKEN_NAME); + } + }); it('should derive synthetic config correctly', async () => { // Create config - const config = { + const config: WarpRouteDeployConfig = { [chain]: { type: TokenType.synthetic, - token: token.address, hook: await mailbox.defaultHook(), name: TOKEN_NAME, symbol: TOKEN_NAME, @@ -191,13 +232,13 @@ describe('ERC20WarpRouterReader', async () => { it('should derive native config correctly', async () => { // Create config - const config = { + const config: WarpRouteDeployConfig = { [chain]: { type: TokenType.native, hook: await mailbox.defaultHook(), ...baseConfig, }, - } as ChainMap; + }; // Deploy with config const warpRoute = await deployer.deploy(config); @@ -215,9 +256,61 @@ describe('ERC20WarpRouterReader', async () => { expect(derivedConfig.decimals).to.equal(TOKEN_DECIMALS); }); + it('should derive collateral vault config correctly', async () => { + // Create config + const config: WarpRouteDeployConfig = { + [chain]: { + type: TokenType.collateralVault, + token: vault.address, + ...baseConfig, + }, + }; + // Deploy with config + const warpRoute = await deployer.deploy(config); + // Derive config and check if each value matches + const derivedConfig = await evmERC20WarpRouteReader.deriveWarpRouteConfig( + warpRoute[chain].collateralVault.address, + ); + + assert( + derivedConfig.type === TokenType.collateralVault, + 'Must be collateralVault', + ); + expect(derivedConfig.type).to.equal(config[chain].type); + expect(derivedConfig.mailbox).to.equal(config[chain].mailbox); + expect(derivedConfig.owner).to.equal(config[chain].owner); + expect(derivedConfig.token).to.equal(token.address); + }); + + it('should derive rebase collateral vault config correctly', async () => { + // Create config + const config: WarpRouteDeployConfig = { + [chain]: { + type: TokenType.collateralVaultRebase, + token: vault.address, + ...baseConfig, + }, + }; + // Deploy with config + const warpRoute = await deployer.deploy(config); + // Derive config and check if each value matches + const derivedConfig = await evmERC20WarpRouteReader.deriveWarpRouteConfig( + warpRoute[chain].collateralVaultRebase.address, + ); + + assert( + derivedConfig.type === TokenType.collateralVaultRebase, + 'Must be collateralVaultRebase', + ); + expect(derivedConfig.type).to.equal(config[chain].type); + expect(derivedConfig.mailbox).to.equal(config[chain].mailbox); + expect(derivedConfig.owner).to.equal(config[chain].owner); + expect(derivedConfig.token).to.equal(token.address); + }); + it('should return undefined if ism is not set onchain', async () => { // Create config - const config = { + const config: WarpRouteDeployConfig = { [chain]: { type: TokenType.collateral, token: token.address, @@ -240,7 +333,7 @@ describe('ERC20WarpRouterReader', async () => { // Create config const otherChain = TestChainName.test3; const otherChainMetadata = test3; - const config = { + const config: WarpRouteDeployConfig = { [chain]: { type: TokenType.collateral, token: token.address, diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 2eb3838d5..0be06f7ca 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -4,6 +4,8 @@ import { HypERC20Collateral__factory, HypERC20__factory, HypERC4626Collateral__factory, + HypERC4626OwnerCollateral__factory, + HypERC4626__factory, TokenRouter__factory, } from '@hyperlane-xyz/core'; import { @@ -23,7 +25,7 @@ import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js'; import { EvmHookReader } from '../hook/EvmHookReader.js'; import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { MultiProvider } from '../providers/MultiProvider.js'; -import { RemoteRouters } from '../router/types.js'; +import { DestinationGas, RemoteRouters } from '../router/types.js'; import { ChainNameOrId } from '../types.js'; import { HyperlaneReader } from '../utils/HyperlaneReader.js'; @@ -62,11 +64,13 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { const baseMetadata = await this.fetchMailboxClientConfig(warpRouteAddress); const tokenMetadata = await this.fetchTokenMetadata(type, warpRouteAddress); const remoteRouters = await this.fetchRemoteRouters(warpRouteAddress); + const destinationGas = await this.fetchDestinationGas(warpRouteAddress); return { ...baseMetadata, ...tokenMetadata, remoteRouters, + destinationGas, type, } as TokenRouterConfig; } @@ -81,15 +85,23 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { const contractTypes: Partial< Record > = { - collateralVault: { + [TokenType.collateralVaultRebase]: { factory: HypERC4626Collateral__factory, + method: 'NULL_RECIPIENT', + }, + [TokenType.collateralVault]: { + factory: HypERC4626OwnerCollateral__factory, method: 'vault', }, - collateral: { + [TokenType.collateral]: { factory: HypERC20Collateral__factory, method: 'wrappedToken', }, - synthetic: { + [TokenType.syntheticRebase]: { + factory: HypERC4626__factory, + method: 'collateralDomain', + }, + [TokenType.synthetic]: { factory: HypERC20__factory, method: 'decimals', }, @@ -106,11 +118,11 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { try { const warpRoute = factory.connect(warpRouteAddress, this.provider); await warpRoute[method](); - - this.setSmartProviderLogLevel(getLogLevel()); // returns to original level defined by rootLogger return tokenType as TokenType; } catch (e) { continue; + } finally { + this.setSmartProviderLogLevel(getLogLevel()); // returns to original level defined by rootLogger } } @@ -186,7 +198,10 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { await this.fetchERC20Metadata(token); return { name, symbol, decimals, totalSupply, token }; - } else if (type === TokenType.synthetic) { + } else if ( + type === TokenType.synthetic || + type === TokenType.syntheticRebase + ) { return this.fetchERC20Metadata(tokenAddress); } else if (type === TokenType.native) { const chainMetadata = this.multiProvider.getChainMetadata(this.chain); @@ -232,4 +247,28 @@ export class EvmERC20WarpRouteReader extends HyperlaneReader { ), ); } + + async fetchDestinationGas( + warpRouteAddress: Address, + ): Promise { + const warpRoute = TokenRouter__factory.connect( + warpRouteAddress, + this.provider, + ); + + /** + * @remark + * Router.domains() is used to enumerate the destination gas because GasRouter.destinationGas is not EnumerableMapExtended type + * This means that if a domain is removed, then we cannot read the destinationGas for it. This may impact updates. + */ + const domains = await warpRoute.domains(); + + return Object.fromEntries( + await Promise.all( + domains.map(async (domain) => { + return [domain, (await warpRoute.destinationGas(domain)).toString()]; + }), + ), + ); + } } diff --git a/typescript/sdk/src/token/Token.test.ts b/typescript/sdk/src/token/Token.test.ts index 4085bf137..1a6ac978a 100644 --- a/typescript/sdk/src/token/Token.test.ts +++ b/typescript/sdk/src/token/Token.test.ts @@ -1,4 +1,5 @@ /* eslint-disable no-console */ +import { SystemProgram } from '@solana/web3.js'; import { expect } from 'chai'; import { ethers } from 'ethers'; @@ -47,6 +48,24 @@ const STANDARD_TO_TOKEN: Record = { symbol: 'USDC', name: 'USDC', }, + [TokenStandard.EvmHypRebaseCollateral]: { + chainName: TestChainName.test3, + standard: TokenStandard.EvmHypRebaseCollateral, + addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131', + collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930', + decimals: 18, + symbol: 'USDC', + name: 'USDC', + }, + [TokenStandard.EvmHypOwnerCollateral]: { + chainName: TestChainName.test3, + standard: TokenStandard.EvmHypOwnerCollateral, + addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131', + collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930', + decimals: 18, + symbol: 'USDC', + name: 'USDC', + }, [TokenStandard.EvmHypCollateralFiat]: { chainName: TestChainName.test3, standard: TokenStandard.EvmHypCollateralFiat, @@ -64,6 +83,14 @@ const STANDARD_TO_TOKEN: Record = { symbol: 'USDC', name: 'USDC', }, + [TokenStandard.EvmHypSyntheticRebase]: { + chainName: TestChainName.test2, + standard: TokenStandard.EvmHypSyntheticRebase, + addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147', + decimals: 6, + symbol: 'USDC', + name: 'USDC', + }, [TokenStandard.EvmHypXERC20]: { chainName: TestChainName.test2, standard: TokenStandard.EvmHypXERC20, @@ -100,9 +127,32 @@ const STANDARD_TO_TOKEN: Record = { }, [TokenStandard.SealevelNative]: Token.FromChainMetadataNativeToken(testSealevelChain), - [TokenStandard.SealevelHypNative]: null, - [TokenStandard.SealevelHypCollateral]: null, - [TokenStandard.SealevelHypSynthetic]: null, + [TokenStandard.SealevelHypNative]: { + chainName: testSealevelChain.name, + standard: TokenStandard.SealevelHypNative, + addressOrDenom: '4UMNyNWW75zo69hxoJaRX5iXNUa5FdRPZZa9vDVCiESg', + decimals: 9, + symbol: 'SOL', + name: 'SOL', + }, + [TokenStandard.SealevelHypCollateral]: { + chainName: testSealevelChain.name, + standard: TokenStandard.SealevelHypCollateral, + addressOrDenom: 'Fefw54S6NDdwNbPngPePvW4tiFTFQDT7gBPvFoDFeGqg', + collateralAddressOrDenom: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', + decimals: 6, + symbol: 'USDC', + name: 'USDC', + }, + [TokenStandard.SealevelHypSynthetic]: { + chainName: testSealevelChain.name, + standard: TokenStandard.SealevelHypSynthetic, + addressOrDenom: 'GLpdg3jt6w4eVYiCMhokVZ4mX6hmRvPhcL5RoCjzGr5k', + collateralAddressOrDenom: '8SuhHnSEogAN2udZsoychjTafnaGgM9MCidYZEP8vuVY', + decimals: 9, + symbol: 'SOL', + name: 'SOL', + }, // Cosmos [TokenStandard.CosmosIcs20]: null, @@ -152,18 +202,19 @@ const STANDARD_TO_TOKEN: Record = { [TokenStandard.CwHypSynthetic]: null, }; -const PROTOCOL_TO_ADDRESS: Partial> = { +const PROTOCOL_TO_ADDRESS_FOR_BALANCE_CHECK: Partial< + Record +> = { [ProtocolType.Ethereum]: ethers.constants.AddressZero, [ProtocolType.Cosmos]: 'neutron13we0myxwzlpx8l5ark8elw5gj5d59dl6cjkzmt80c5q5cv5rt54qvzkv2a', + [ProtocolType.Sealevel]: 'EK6cs8jNnu2d9pmKTGf1Bvre9oW2xNhcCKNdLKx6t74w', }; -const STANDARD_TO_ADDRESS: Partial> = { +const STANDARD_TO_ADDRESS_FOR_BALANCE_CHECK: Partial< + Record +> = { [TokenStandard.SealevelSpl]: 'HVSZJ2juJnMxd6yCNarTL56YmgUqzfUiwM7y7LtTXKHR', - [TokenStandard.SealevelSpl2022]: - 'EK6cs8jNnu2d9pmKTGf1Bvre9oW2xNhcCKNdLKx6t74w', - [TokenStandard.SealevelNative]: - 'EK6cs8jNnu2d9pmKTGf1Bvre9oW2xNhcCKNdLKx6t74w', [TokenStandard.CwHypNative]: 'inj1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3lj7tt0', }; @@ -172,16 +223,21 @@ describe('Token', () => { if (!tokenArgs) continue; it(`Handles ${tokenArgs.standard} standard`, async () => { const multiProvider = - MultiProtocolProvider.createTestMultiProtocolProvider(); + MultiProtocolProvider.createTestMultiProtocolProvider<{ + mailbox?: string; + }>(); + // A placeholder mailbox address for the sealevel chain + multiProvider.metadata[testSealevelChain.name].mailbox = + SystemProgram.programId.toBase58(); console.debug('Testing token standard', tokenArgs.standard); const token = new Token(tokenArgs); expect(token.standard).to.eql(tokenArgs.standard); const adapter = token.getAdapter(multiProvider); - const address = - STANDARD_TO_ADDRESS[token.standard] ?? - PROTOCOL_TO_ADDRESS[token.protocol]; - if (!address) + const balanceCheckAddress = + STANDARD_TO_ADDRESS_FOR_BALANCE_CHECK[token.standard] ?? + PROTOCOL_TO_ADDRESS_FOR_BALANCE_CHECK[token.protocol]; + if (!balanceCheckAddress) throw new Error(`No address for standard ${tokenArgs.standard}`); const sandbox = stubMultiProtocolProvider(multiProvider); @@ -190,7 +246,7 @@ describe('Token', () => { balanceOf: async () => '100', }; - const balance = await adapter.getBalance(address); + const balance = await adapter.getBalance(balanceCheckAddress); expect(typeof balance).to.eql('bigint'); sandbox.restore(); }); diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts index cf9d5e65d..527adc2b5 100644 --- a/typescript/sdk/src/token/Token.ts +++ b/typescript/sdk/src/token/Token.ts @@ -170,13 +170,8 @@ export class Token implements IToken { multiProvider: MultiProtocolProvider<{ mailbox?: Address }>, destination?: ChainName, ): IHypTokenAdapter { - const { - protocol, - standard, - chainName, - addressOrDenom, - collateralAddressOrDenom, - } = this; + const { standard, chainName, addressOrDenom, collateralAddressOrDenom } = + this; const chainMetadata = multiProvider.tryGetChainMetadata(chainName); const mailbox = chainMetadata?.mailbox; @@ -190,32 +185,23 @@ export class Token implements IToken { `Token chain ${chainName} not found in multiProvider`, ); - let sealevelAddresses; - if (protocol === ProtocolType.Sealevel) { - assert(mailbox, `Mailbox required for Sealevel hyp tokens`); - assert( - collateralAddressOrDenom, - `collateralAddressOrDenom required for Sealevel hyp tokens`, - ); - sealevelAddresses = { - warpRouter: addressOrDenom, - token: collateralAddressOrDenom, - mailbox, - }; - } if (standard === TokenStandard.EvmHypNative) { return new EvmHypNativeAdapter(chainName, multiProvider, { token: addressOrDenom, }); - } else if (standard === TokenStandard.EvmHypCollateral) { - return new EvmHypCollateralAdapter(chainName, multiProvider, { - token: addressOrDenom, - }); - } else if (standard === TokenStandard.EvmHypCollateralFiat) { + } else if ( + standard === TokenStandard.EvmHypCollateral || + standard === TokenStandard.EvmHypCollateralFiat || + standard === TokenStandard.EvmHypOwnerCollateral || + standard === TokenStandard.EvmHypRebaseCollateral + ) { return new EvmHypCollateralAdapter(chainName, multiProvider, { token: addressOrDenom, }); - } else if (standard === TokenStandard.EvmHypSynthetic) { + } else if ( + standard === TokenStandard.EvmHypSynthetic || + standard === TokenStandard.EvmHypSyntheticRebase + ) { return new EvmHypSyntheticAdapter(chainName, multiProvider, { token: addressOrDenom, }); @@ -228,24 +214,46 @@ export class Token implements IToken { token: addressOrDenom, }); } else if (standard === TokenStandard.SealevelHypNative) { + assert(mailbox, `Mailbox required for Sealevel hyp tokens`); return new SealevelHypNativeAdapter( chainName, multiProvider, - sealevelAddresses!, + { + warpRouter: addressOrDenom, + mailbox, + }, false, ); } else if (standard === TokenStandard.SealevelHypCollateral) { + assert(mailbox, `Mailbox required for Sealevel hyp tokens`); + assert( + collateralAddressOrDenom, + `collateralAddressOrDenom required for Sealevel hyp collateral tokens`, + ); return new SealevelHypCollateralAdapter( chainName, multiProvider, - sealevelAddresses!, + { + warpRouter: addressOrDenom, + token: collateralAddressOrDenom, + mailbox, + }, false, ); } else if (standard === TokenStandard.SealevelHypSynthetic) { + assert(mailbox, `Mailbox required for Sealevel hyp tokens`); + assert( + collateralAddressOrDenom, + `collateralAddressOrDenom required for Sealevel hyp synthetic tokens`, + ); return new SealevelHypSyntheticAdapter( chainName, multiProvider, - sealevelAddresses!, + { + warpRouter: addressOrDenom, + token: collateralAddressOrDenom, + mailbox, + }, false, ); } else if (standard === TokenStandard.CwHypNative) { diff --git a/typescript/sdk/src/token/TokenStandard.ts b/typescript/sdk/src/token/TokenStandard.ts index 2605b9b3c..ee434b777 100644 --- a/typescript/sdk/src/token/TokenStandard.ts +++ b/typescript/sdk/src/token/TokenStandard.ts @@ -14,8 +14,11 @@ export enum TokenStandard { EvmNative = 'EvmNative', EvmHypNative = 'EvmHypNative', EvmHypCollateral = 'EvmHypCollateral', + EvmHypOwnerCollateral = 'EvmHypOwnerCollateral', + EvmHypRebaseCollateral = 'EvmHypRebaseCollateral', EvmHypCollateralFiat = 'EvmHypCollateralFiat', EvmHypSynthetic = 'EvmHypSynthetic', + EvmHypSyntheticRebase = 'EvmHypSyntheticRebase', EvmHypXERC20 = 'EvmHypXERC20', EvmHypXERC20Lockbox = 'EvmHypXERC20Lockbox', @@ -50,8 +53,11 @@ export const TOKEN_STANDARD_TO_PROTOCOL: Record = { EvmNative: ProtocolType.Ethereum, EvmHypNative: ProtocolType.Ethereum, EvmHypCollateral: ProtocolType.Ethereum, + EvmHypOwnerCollateral: ProtocolType.Ethereum, + EvmHypRebaseCollateral: ProtocolType.Ethereum, EvmHypCollateralFiat: ProtocolType.Ethereum, EvmHypSynthetic: ProtocolType.Ethereum, + EvmHypSyntheticRebase: ProtocolType.Ethereum, EvmHypXERC20: ProtocolType.Ethereum, EvmHypXERC20Lockbox: ProtocolType.Ethereum, @@ -111,7 +117,10 @@ export const TOKEN_HYP_STANDARDS = [ TokenStandard.EvmHypNative, TokenStandard.EvmHypCollateral, TokenStandard.EvmHypCollateralFiat, + TokenStandard.EvmHypOwnerCollateral, + TokenStandard.EvmHypRebaseCollateral, TokenStandard.EvmHypSynthetic, + TokenStandard.EvmHypSyntheticRebase, TokenStandard.EvmHypXERC20, TokenStandard.EvmHypXERC20Lockbox, TokenStandard.SealevelHypNative, @@ -144,10 +153,12 @@ export const TOKEN_TYPE_TO_STANDARD: Record = { [TokenType.collateralFiat]: TokenStandard.EvmHypCollateralFiat, [TokenType.XERC20]: TokenStandard.EvmHypXERC20, [TokenType.XERC20Lockbox]: TokenStandard.EvmHypXERC20Lockbox, - [TokenType.collateralVault]: TokenStandard.EvmHypCollateral, + [TokenType.collateralVault]: TokenStandard.EvmHypOwnerCollateral, + [TokenType.collateralVaultRebase]: TokenStandard.EvmHypRebaseCollateral, [TokenType.collateralUri]: TokenStandard.EvmHypCollateral, [TokenType.fastCollateral]: TokenStandard.EvmHypCollateral, [TokenType.synthetic]: TokenStandard.EvmHypSynthetic, + [TokenType.syntheticRebase]: TokenStandard.EvmHypSyntheticRebase, [TokenType.syntheticUri]: TokenStandard.EvmHypSynthetic, [TokenType.fastSynthetic]: TokenStandard.EvmHypSynthetic, [TokenType.nativeScaled]: TokenStandard.EvmHypNative, diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index 5cae1811f..1df0d8637 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -20,7 +20,8 @@ import { Domain, addressToBytes, eqAddress, - isZeroishAddress, + median, + padBytesToLength, } from '@hyperlane-xyz/utils'; import { BaseSealevelAdapter } from '../../app/MultiProtocolApp.js'; @@ -50,7 +51,31 @@ import { SealevelTransferRemoteSchema, } from './serialization.js'; -// author @tkporter @jmrossy +const NON_EXISTENT_ACCOUNT_ERROR = 'could not find account'; + +/** + * The compute limit to set for the transfer remote instruction. + * This is typically around ~160k, but can be higher depending on + * the index in the merkle tree, which can result in more moderately + * more expensive merkle tree insertion. + * Because a higher compute limit doesn't increase the fee for a transaction, + * we generously request 1M units. + */ +const TRANSFER_REMOTE_COMPUTE_LIMIT = 1_000_000; + +/** + * The factor by which to multiply the median prioritization fee + * instruction added to transfer transactions. + */ +const PRIORITY_FEE_PADDING_FACTOR = 2; + +/** + * The minimum priority fee to use if the median fee is + * unavailable or too low, set in micro-lamports. + * 100,000 * 1e-6 * 1,000,000 (compute unit limit) / 1e9 == 0.0001 SOL + */ +const MINIMUM_PRIORITY_FEE = 100_000; + // Interacts with native currencies export class SealevelNativeTokenAdapter extends BaseSealevelAdapter @@ -95,7 +120,7 @@ export class SealevelTokenAdapter extends BaseSealevelAdapter implements ITokenAdapter { - public readonly tokenProgramPubKey: PublicKey; + public readonly tokenMintPubKey: PublicKey; constructor( public readonly chainName: ChainName, @@ -104,15 +129,20 @@ export class SealevelTokenAdapter public readonly isSpl2022: boolean = false, ) { super(chainName, multiProvider, addresses); - this.tokenProgramPubKey = new PublicKey(addresses.token); + this.tokenMintPubKey = new PublicKey(addresses.token); } async getBalance(owner: Address): Promise { const tokenPubKey = this.deriveAssociatedTokenAccount(new PublicKey(owner)); - const response = await this.getProvider().getTokenAccountBalance( - tokenPubKey, - ); - return BigInt(response.value.amount); + try { + const response = await this.getProvider().getTokenAccountBalance( + tokenPubKey, + ); + return BigInt(response.value.amount); + } catch (error: any) { + if (error.message?.includes(NON_EXISTENT_ACCOUNT_ERROR)) return 0n; + throw error; + } } async getMetadata(_isNft?: boolean): Promise { @@ -154,7 +184,7 @@ export class SealevelTokenAdapter deriveAssociatedTokenAccount(owner: PublicKey): PublicKey { return getAssociatedTokenAddressSync( - this.tokenProgramPubKey, + this.tokenMintPubKey, owner, true, this.getTokenProgramId(), @@ -162,36 +192,28 @@ export class SealevelTokenAdapter } } -// The compute limit to set for the transfer remote instruction. -// This is typically around ~160k, but can be higher depending on -// the index in the merkle tree, which can result in more moderately -// more expensive merkle tree insertion. -// Because a higher compute limit doesn't increase the fee for a transaction, -// we generously request 1M units. -const TRANSFER_REMOTE_COMPUTE_LIMIT = 1_000_000; +interface HypTokenAddresses { + token: Address; + warpRouter: Address; + mailbox: Address; +} export abstract class SealevelHypTokenAdapter extends SealevelTokenAdapter implements IHypTokenAdapter { public readonly warpProgramPubKey: PublicKey; + public readonly addresses: HypTokenAddresses; protected cachedTokenAccountData: SealevelHyperlaneTokenData | undefined; constructor( public readonly chainName: ChainName, public readonly multiProvider: MultiProtocolProvider, - public readonly addresses: { - token: Address; - warpRouter: Address; - mailbox: Address; - }, + addresses: HypTokenAddresses, public readonly isSpl2022: boolean = false, ) { - // Pass in placeholder address to avoid errors for native token addresses (which as represented here as 0s) - const superTokenProgramId = isZeroishAddress(addresses.token) - ? SystemProgram.programId.toBase58() - : addresses.token; - super(chainName, multiProvider, { token: superTokenProgramId }, isSpl2022); + super(chainName, multiProvider, { token: addresses.token }, isSpl2022); + this.addresses = addresses; this.warpProgramPubKey = new PublicKey(addresses.warpRouter); } @@ -274,7 +296,7 @@ export abstract class SealevelHypTokenAdapter instruction: SealevelHypTokenInstruction.TransferRemote, data: new SealevelTransferRemoteInstruction({ destination_domain: destination, - recipient: addressToBytes(recipient), + recipient: padBytesToLength(addressToBytes(recipient), 32), amount_or_id: BigInt(weiAmountOrId), }), }); @@ -292,11 +314,17 @@ export abstract class SealevelHypTokenAdapter }); const setComputeLimitInstruction = ComputeBudgetProgram.setComputeUnitLimit( - { - units: TRANSFER_REMOTE_COMPUTE_LIMIT, - }, + { units: TRANSFER_REMOTE_COMPUTE_LIMIT }, ); + // For more info about priority fees, see: + // https://solanacookbook.com/references/basic-transactions.html#how-to-change-compute-budget-fee-priority-for-a-transaction + // https://docs.phantom.app/developer-powertools/solana-priority-fees + // https://www.helius.dev/blog/priority-fees-understanding-solanas-transaction-fee-mechanics + const setPriorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: (await this.getMedianPriorityFee()) || 0, + }); + const recentBlockhash = ( await this.getProvider().getLatestBlockhash('finalized') ).blockhash; @@ -308,6 +336,7 @@ export abstract class SealevelHypTokenAdapter recentBlockhash, }) .add(setComputeLimitInstruction) + .add(setPriorityFeeInstruction) .add(transferRemoteInstruction); tx.partialSign(randomWallet); return tx; @@ -479,6 +508,38 @@ export abstract class SealevelHypTokenAdapter this.warpProgramPubKey, ); } + + /** + * Fetches the median prioritization fee for transfers of the collateralAddress token. + * @returns The median prioritization fee in micro-lamports + */ + async getMedianPriorityFee(): Promise { + this.logger.debug('Fetching priority fee history for token transfer'); + + const collateralAddress = this.addresses.token; + const fees = await this.getProvider().getRecentPrioritizationFees({ + lockedWritableAccounts: [new PublicKey(collateralAddress)], + }); + + const nonZeroFees = fees + .filter((fee) => fee.prioritizationFee > 0) + .map((fee) => fee.prioritizationFee); + + if (nonZeroFees.length < 3) { + this.logger.warn( + 'Insufficient historical prioritization fee data for padding, skipping', + ); + return MINIMUM_PRIORITY_FEE; + } + + const medianFee = Math.max( + Math.floor(median(nonZeroFees) * PRIORITY_FEE_PADDING_FACTOR), + MINIMUM_PRIORITY_FEE, + ); + + this.logger.debug(`Median priority fee: ${medianFee}`); + return medianFee; + } } // Interacts with Hyp Native token programs @@ -488,14 +549,21 @@ export class SealevelHypNativeAdapter extends SealevelHypTokenAdapter { constructor( public readonly chainName: ChainName, public readonly multiProvider: MultiProtocolProvider, - public readonly addresses: { - token: Address; + addresses: { + // A 'token' address is not required for hyp native tokens (e.g. hypSOL) + token?: Address; warpRouter: Address; mailbox: Address; }, public readonly isSpl2022: boolean = false, ) { - super(chainName, multiProvider, addresses, isSpl2022); + // Pass in placeholder address for 'token' to avoid errors in the parent classes + super( + chainName, + multiProvider, + { ...addresses, token: SystemProgram.programId.toBase58() }, + isSpl2022, + ); this.wrappedNative = new SealevelNativeTokenAdapter( chainName, multiProvider, @@ -504,6 +572,12 @@ export class SealevelHypNativeAdapter extends SealevelHypTokenAdapter { } override async getBalance(owner: Address): Promise { + if (eqAddress(owner, this.addresses.warpRouter)) { + const collateralAccount = this.deriveNativeTokenCollateralAccount(); + const balance = await this.getProvider().getBalance(collateralAccount); + // TODO: account for rent in https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/4558 + return BigInt(balance.toString()); + } return this.wrappedNative.getBalance(owner); } @@ -511,6 +585,12 @@ export class SealevelHypNativeAdapter extends SealevelHypTokenAdapter { return this.wrappedNative.getMetadata(); } + override async getMedianPriorityFee(): Promise { + // Native tokens don't have a collateral address, so we don't fetch + // prioritization fee history + return undefined; + } + getTransferInstructionKeyList(params: KeyListParams): Array { return [ ...super.getTransferInstructionKeyList(params), @@ -560,7 +640,7 @@ export class SealevelHypCollateralAdapter extends SealevelHypTokenAdapter { /// 9. [executable] The SPL token program for the mint. { pubkey: this.getTokenProgramId(), isSigner: false, isWritable: false }, /// 10. [writeable] The mint. - { pubkey: this.tokenProgramPubKey, isSigner: false, isWritable: true }, + { pubkey: this.tokenMintPubKey, isSigner: false, isWritable: true }, /// 11. [writeable] The token sender's associated token account, from which tokens will be sent. { pubkey: this.deriveAssociatedTokenAccount(params.sender), @@ -606,8 +686,20 @@ export class SealevelHypSyntheticAdapter extends SealevelHypTokenAdapter { override async getBalance(owner: Address): Promise { const tokenPubKey = this.deriveAssociatedTokenAccount(new PublicKey(owner)); - const response = await this.getProvider().getTokenAccountBalance( - tokenPubKey, + try { + const response = await this.getProvider().getTokenAccountBalance( + tokenPubKey, + ); + return BigInt(response.value.amount); + } catch (error: any) { + if (error.message?.includes(NON_EXISTENT_ACCOUNT_ERROR)) return 0n; + throw error; + } + } + + async getTotalSupply(): Promise { + const response = await this.getProvider().getTokenSupply( + this.tokenMintPubKey, ); return BigInt(response.value.amount); } diff --git a/typescript/sdk/src/token/config.ts b/typescript/sdk/src/token/config.ts index a89264ee6..08fb750f2 100644 --- a/typescript/sdk/src/token/config.ts +++ b/typescript/sdk/src/token/config.ts @@ -1,9 +1,11 @@ export enum TokenType { synthetic = 'synthetic', + syntheticRebase = 'syntheticRebase', fastSynthetic = 'fastSynthetic', syntheticUri = 'syntheticUri', collateral = 'collateral', collateralVault = 'collateralVault', + collateralVaultRebase = 'collateralVaultRebase', XERC20 = 'xERC20', XERC20Lockbox = 'xERC20Lockbox', collateralFiat = 'collateralFiat', @@ -16,6 +18,7 @@ export enum TokenType { export const CollateralExtensions = [ TokenType.collateral, TokenType.collateralVault, + TokenType.collateralVaultRebase, ]; export const gasOverhead = (tokenType: TokenType): number => { diff --git a/typescript/sdk/src/token/contracts.ts b/typescript/sdk/src/token/contracts.ts index 281c084ff..a9ced3cb1 100644 --- a/typescript/sdk/src/token/contracts.ts +++ b/typescript/sdk/src/token/contracts.ts @@ -8,6 +8,8 @@ import { HypERC721URIStorage__factory, HypERC721__factory, HypERC4626Collateral__factory, + HypERC4626OwnerCollateral__factory, + HypERC4626__factory, HypFiatToken__factory, HypNativeScaled__factory, HypNative__factory, @@ -21,11 +23,13 @@ export const hypERC20contracts = { [TokenType.fastCollateral]: 'FastHypERC20Collateral', [TokenType.fastSynthetic]: 'FastHypERC20', [TokenType.synthetic]: 'HypERC20', + [TokenType.syntheticRebase]: 'HypERC4626', [TokenType.collateral]: 'HypERC20Collateral', [TokenType.collateralFiat]: 'HypFiatToken', [TokenType.XERC20]: 'HypXERC20', [TokenType.XERC20Lockbox]: 'HypXERC20Lockbox', - [TokenType.collateralVault]: 'HypERC20CollateralVaultDeposit', + [TokenType.collateralVault]: 'HypERC4626OwnerCollateral', + [TokenType.collateralVaultRebase]: 'HypERC4626Collateral', [TokenType.native]: 'HypNative', [TokenType.nativeScaled]: 'HypNativeScaled', }; @@ -36,7 +40,9 @@ export const hypERC20factories = { [TokenType.fastSynthetic]: new FastHypERC20__factory(), [TokenType.synthetic]: new HypERC20__factory(), [TokenType.collateral]: new HypERC20Collateral__factory(), - [TokenType.collateralVault]: new HypERC4626Collateral__factory(), + [TokenType.collateralVault]: new HypERC4626OwnerCollateral__factory(), + [TokenType.collateralVaultRebase]: new HypERC4626Collateral__factory(), + [TokenType.syntheticRebase]: new HypERC4626__factory(), [TokenType.collateralFiat]: new HypFiatToken__factory(), [TokenType.XERC20]: new HypXERC20__factory(), [TokenType.XERC20Lockbox]: new HypXERC20Lockbox__factory(), diff --git a/typescript/sdk/src/token/deploy.ts b/typescript/sdk/src/token/deploy.ts index 843f933f1..1c5519126 100644 --- a/typescript/sdk/src/token/deploy.ts +++ b/typescript/sdk/src/token/deploy.ts @@ -33,6 +33,7 @@ import { isCollateralConfig, isNativeConfig, isSyntheticConfig, + isSyntheticRebaseConfig, isTokenMetadata, } from './schemas.js'; import { TokenMetadata, WarpRouteDeployConfig } from './types.js'; @@ -64,6 +65,11 @@ abstract class TokenDeployer< } else if (isSyntheticConfig(config)) { assert(config.decimals, 'decimals is undefined for config'); // decimals must be defined by this point return [config.decimals, config.mailbox]; + } else if (isSyntheticRebaseConfig(config)) { + const collateralDomain = this.multiProvider.getDomainId( + config.collateralChainName, + ); + return [config.decimals, config.mailbox, collateralDomain]; } else { throw new Error('Unknown token type when constructing arguments'); } @@ -84,6 +90,8 @@ abstract class TokenDeployer< return defaultArgs; } else if (isSyntheticConfig(config)) { return [config.totalSupply, config.name, config.symbol, ...defaultArgs]; + } else if (isSyntheticRebaseConfig(config)) { + return [0, config.name, config.symbol, ...defaultArgs]; } else { throw new Error('Unknown collateral type when initializing arguments'); } diff --git a/typescript/sdk/src/token/schemas.test.ts b/typescript/sdk/src/token/schemas.test.ts index 65f780cd0..1a8d606ea 100644 --- a/typescript/sdk/src/token/schemas.test.ts +++ b/typescript/sdk/src/token/schemas.test.ts @@ -1,8 +1,15 @@ import { expect } from 'chai'; import { ethers } from 'ethers'; +import { assert } from '@hyperlane-xyz/utils'; + import { TokenType } from './config.js'; -import { WarpRouteDeployConfigSchema } from './schemas.js'; +import { + WarpRouteDeployConfigSchema, + WarpRouteDeployConfigSchemaErrors, + isCollateralConfig, +} from './schemas.js'; +import { WarpRouteDeployConfig } from './types.js'; const SOME_ADDRESS = ethers.Wallet.createRandom().address; const COLLATERAL_TYPES = [ @@ -19,7 +26,7 @@ const NON_COLLATERAL_TYPES = [ ]; describe('WarpRouteDeployConfigSchema refine', () => { - let config: any; + let config: WarpRouteDeployConfig; beforeEach(() => { config = { arbitrum: { @@ -33,18 +40,24 @@ describe('WarpRouteDeployConfigSchema refine', () => { it('should require token type', () => { expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; + + //@ts-ignore delete config.arbitrum.type; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; }); it('should require token address', () => { expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; + + //@ts-ignore delete config.arbitrum.token; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; }); it('should require mailbox address', () => { expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; + + //@ts-ignore delete config.arbitrum.mailbox; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; }); @@ -52,6 +65,9 @@ describe('WarpRouteDeployConfigSchema refine', () => { it('should throw if collateral type and token is empty', async () => { for (const type of COLLATERAL_TYPES) { config.arbitrum.type = type; + assert(isCollateralConfig(config.arbitrum), 'must be collateral'); + + //@ts-ignore config.arbitrum.token = undefined; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; @@ -61,13 +77,8 @@ describe('WarpRouteDeployConfigSchema refine', () => { } }); - it('should accept native type if token is empty', async () => { - config.arbitrum.type = TokenType.native; - config.arbitrum.token = undefined; - expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; - }); - it('should succeed if non-collateral type, token is empty, metadata is defined', async () => { + //@ts-ignore delete config.arbitrum.token; config.arbitrum.totalSupply = '0'; config.arbitrum.name = 'name'; @@ -81,4 +92,93 @@ describe('WarpRouteDeployConfigSchema refine', () => { expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; } }); + + it(`should throw if deploying rebasing collateral with anything other than ${TokenType.syntheticRebase}`, async () => { + config = { + arbitrum: { + type: TokenType.collateralVaultRebase, + token: SOME_ADDRESS, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + }, + ethereum: { + type: TokenType.collateralVault, + token: SOME_ADDRESS, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + }, + optimism: { + type: TokenType.syntheticRebase, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + collateralChainName: '', + }, + }; + let parseResults = WarpRouteDeployConfigSchema.safeParse(config); + assert(!parseResults.success, 'must be false'); // Needed so 'message' shows up because parseResults is a discriminate union + expect(parseResults.error.issues[0].message).to.equal( + WarpRouteDeployConfigSchemaErrors.ONLY_SYNTHETIC_REBASE, + ); + + config.ethereum.type = TokenType.syntheticRebase; + //@ts-ignore + config.ethereum.collateralChainName = ''; + parseResults = WarpRouteDeployConfigSchema.safeParse(config); + //@ts-ignore + expect(parseResults.success).to.be.true; + }); + + it(`should throw if deploying only ${TokenType.collateralVaultRebase}`, async () => { + config = { + arbitrum: { + type: TokenType.collateralVaultRebase, + token: SOME_ADDRESS, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + }, + }; + let parseResults = WarpRouteDeployConfigSchema.safeParse(config); + expect(parseResults.success).to.be.false; + + config.ethereum = { + type: TokenType.collateralVaultRebase, + token: SOME_ADDRESS, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + }; + parseResults = WarpRouteDeployConfigSchema.safeParse(config); + expect(parseResults.success).to.be.false; + }); + + it(`should derive the collateral chain name for ${TokenType.syntheticRebase}`, async () => { + config = { + arbitrum: { + type: TokenType.collateralVaultRebase, + token: SOME_ADDRESS, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + }, + ethereum: { + type: TokenType.syntheticRebase, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + collateralChainName: '', + }, + optimism: { + type: TokenType.syntheticRebase, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, + collateralChainName: '', + }, + }; + const parseResults = WarpRouteDeployConfigSchema.safeParse(config); + assert(parseResults.success, 'must be true'); + const warpConfig: WarpRouteDeployConfig = parseResults.data; + + assert( + warpConfig.optimism.type === TokenType.syntheticRebase, + 'must be syntheticRebase', + ); + expect(warpConfig.optimism.collateralChainName).to.equal('arbitrum'); + }); }); diff --git a/typescript/sdk/src/token/schemas.ts b/typescript/sdk/src/token/schemas.ts index 8ce070c1e..c92f98ea9 100644 --- a/typescript/sdk/src/token/schemas.ts +++ b/typescript/sdk/src/token/schemas.ts @@ -1,10 +1,16 @@ import { z } from 'zod'; +import { objMap } from '@hyperlane-xyz/utils'; + import { GasRouterConfigSchema } from '../router/schemas.js'; import { isCompliant } from '../utils/schemas.js'; import { TokenType } from './config.js'; +export const WarpRouteDeployConfigSchemaErrors = { + ONLY_SYNTHETIC_REBASE: `Config with ${TokenType.collateralVaultRebase} must be deployed with ${TokenType.syntheticRebase}`, + NO_SYNTHETIC_ONLY: `Config must include Native or Collateral OR all synthetics must define token metadata`, +}; export const TokenMetadataSchema = z.object({ name: z.string(), symbol: z.string(), @@ -18,6 +24,7 @@ export const CollateralConfigSchema = TokenMetadataSchema.partial().extend({ type: z.enum([ TokenType.collateral, TokenType.collateralVault, + TokenType.collateralVaultRebase, TokenType.XERC20, TokenType.XERC20Lockbox, TokenType.collateralFiat, @@ -33,6 +40,21 @@ export const NativeConfigSchema = TokenMetadataSchema.partial().extend({ type: z.enum([TokenType.native, TokenType.nativeScaled]), }); +export const CollateralRebaseConfigSchema = TokenMetadataSchema.omit({ + totalSupply: true, +}) + .partial() + .extend({ + type: z.literal(TokenType.collateralVaultRebase), + }); + +export const SyntheticRebaseConfigSchema = TokenMetadataSchema.partial().extend( + { + type: z.literal(TokenType.syntheticRebase), + collateralChainName: z.string(), + }, +); + export const SyntheticConfigSchema = TokenMetadataSchema.partial().extend({ type: z.enum([ TokenType.synthetic, @@ -50,6 +72,7 @@ export const TokenConfigSchema = z.discriminatedUnion('type', [ NativeConfigSchema, CollateralConfigSchema, SyntheticConfigSchema, + SyntheticRebaseConfigSchema, ]); export const TokenRouterConfigSchema = TokenConfigSchema.and( @@ -61,6 +84,10 @@ export type NativeConfig = z.infer; export type CollateralConfig = z.infer; export const isSyntheticConfig = isCompliant(SyntheticConfigSchema); +export const isSyntheticRebaseConfig = isCompliant(SyntheticRebaseConfigSchema); +export const isCollateralRebaseConfig = isCompliant( + CollateralRebaseConfigSchema, +); export const isCollateralConfig = isCompliant(CollateralConfigSchema); export const isNativeConfig = isCompliant(NativeConfigSchema); export const isTokenMetadata = isCompliant(TokenMetadataSchema); @@ -71,7 +98,49 @@ export const WarpRouteDeployConfigSchema = z const entries = Object.entries(configMap); return ( entries.some( - ([_, config]) => isCollateralConfig(config) || isNativeConfig(config), + ([_, config]) => + isCollateralConfig(config) || + isCollateralRebaseConfig(config) || + isNativeConfig(config), ) || entries.every(([_, config]) => isTokenMetadata(config)) ); - }, `Config must include Native or Collateral OR all synthetics must define token metadata`); + }, WarpRouteDeployConfigSchemaErrors.NO_SYNTHETIC_ONLY) + .transform((warpRouteDeployConfig, ctx) => { + const collateralRebaseEntry = Object.entries(warpRouteDeployConfig).find( + ([_, config]) => isCollateralRebaseConfig(config), + ); + if (!collateralRebaseEntry) return warpRouteDeployConfig; // Pass through for other token types + + if (isCollateralRebasePairedCorrectly(warpRouteDeployConfig)) { + const collateralChainName = collateralRebaseEntry[0]; + return objMap(warpRouteDeployConfig, (_, config) => { + if (config.type === TokenType.syntheticRebase) + config.collateralChainName = collateralChainName; + return config; + }) as Record; + } + + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: WarpRouteDeployConfigSchemaErrors.ONLY_SYNTHETIC_REBASE, + }); + + return z.NEVER; // Causes schema validation to throw with above issue + }); + +function isCollateralRebasePairedCorrectly( + warpRouteDeployConfig: Record, +): boolean { + // Filter out all the non-collateral rebase configs to check if they are only synthetic rebase tokens + const otherConfigs = Object.entries(warpRouteDeployConfig).filter( + ([_, config]) => !isCollateralRebaseConfig(config), + ); + + if (otherConfigs.length === 0) return false; + + // The other configs MUST be synthetic rebase + const allOthersSynthetic: boolean = otherConfigs.every( + ([_, config], _index) => isSyntheticRebaseConfig(config), + ); + return allOthersSynthetic; +} diff --git a/typescript/sdk/src/utils/gnosisSafe.js b/typescript/sdk/src/utils/gnosisSafe.js index 148650e76..400931043 100644 --- a/typescript/sdk/src/utils/gnosisSafe.js +++ b/typescript/sdk/src/utils/gnosisSafe.js @@ -67,12 +67,10 @@ export async function getSafe(chain, multiProvider, safeAddress) { multiSend = getMultiSendDeployment({ version: multiSendVersion, network: domainId, - released: true, }); multiSendCallOnly = getMultiSendCallOnlyDeployment({ version: multiSendCallOnlyVersion, network: domainId, - released: true, }); } @@ -80,12 +78,13 @@ export async function getSafe(chain, multiProvider, safeAddress) { ethAdapter, safeAddress, contractNetworks: { + // DomainId == ChainId for EVM Chains [domainId]: { // Use the safe address for multiSendAddress and multiSendCallOnlyAddress - // if the contract is not deployed or if the version is not found - multiSendAddress: multiSend?.defaultAddress || safeAddress, + // if the contract is not deployed or if the version is not found. + multiSendAddress: multiSend?.networkAddresses[domainId] || safeAddress, multiSendCallOnlyAddress: - multiSendCallOnly?.defaultAddress || safeAddress, + multiSendCallOnly?.networkAddresses[domainId] || safeAddress, }, }, }); diff --git a/typescript/sdk/src/utils/ism.ts b/typescript/sdk/src/utils/ism.ts index ad7c11596..92c267e66 100644 --- a/typescript/sdk/src/utils/ism.ts +++ b/typescript/sdk/src/utils/ism.ts @@ -1,3 +1,5 @@ +import { WithAddress } from '@hyperlane-xyz/utils'; + import { multisigIsmVerifyCosts } from '../consts/multisigIsmVerifyCosts.js'; export function multisigIsmVerificationCost(m: number, n: number): number { @@ -11,3 +13,61 @@ export function multisigIsmVerificationCost(m: number, n: number): number { // @ts-ignore return multisigIsmVerifyCosts[`${n}`][`${m}`]; } + +// Function to recursively remove 'address' properties and lowercase string properties +export function normalizeConfig(obj: WithAddress): any { + return sortArraysInConfig(lowerCaseConfig(obj)); +} + +function lowerCaseConfig(obj: any): any { + if (Array.isArray(obj)) { + return obj.map(normalizeConfig); + } else if (obj !== null && typeof obj === 'object') { + const newObj: any = {}; + for (const key in obj) { + if (key !== 'address') { + newObj[key] = key === 'type' ? obj[key] : normalizeConfig(obj[key]); + } + } + return newObj; + } else if (typeof obj === 'string') { + return obj.toLowerCase(); + } + + return obj; +} + +// write a function that will go through an object and sort any arrays it finds +export function sortArraysInConfig(config: any): any { + // Check if the current object is an array + if (Array.isArray(config)) { + return config.map(sortArraysInConfig); + } + // Check if it's an object and not null + else if (typeof config === 'object' && config !== null) { + const sortedConfig: any = {}; + for (const key in config) { + if (key === 'validators' && Array.isArray(config[key])) { + // Sort the validators array in lexicographical order (since they're already lowercase) + sortedConfig[key] = [...config[key]].sort(); + } + // if key is modules or hooks, sort the objects in the array by their 'type' property + else if ( + (key === 'modules' || key === 'hooks') && + Array.isArray(config[key]) + ) { + sortedConfig[key] = [...config[key]].sort((a: any, b: any) => { + if (a.type < b.type) return -1; + if (a.type > b.type) return 1; + return 0; + }); + } else { + // Recursively apply sorting to other fields + sortedConfig[key] = sortArraysInConfig(config[key]); + } + } + return sortedConfig; + } + + return config; +} diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index 753045908..16903b5fa 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -528,7 +528,7 @@ export class WarpCore { token.addressOrDenom, amount, ); - this.logger.info( + this.logger.debug( `Approval is${isRequired ? '' : ' not'} required for transfer of ${ token.symbol }`, diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 8692aac9b..bc910f9f8 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,57 @@ # @hyperlane-xyz/utils +## 5.6.2 + +### Patch Changes + +- 5fd4267e7: Supported non-32 byte non-EVM recipients when sending warps from Sealevel +- a36fc5fb2: fix: isObject utils fn should return only boolean value + +## 5.6.1 + +## 5.6.0 + +### Minor Changes + +- 29341950e: Adds new `core check` command to compare local configuration and on chain deployments. Adds memoization to the EvmHookReader to avoid repeating configuration derivation + +### Patch Changes + +- f1712deb7: Fix objMerge implementation + +## 5.5.0 + +### Minor Changes + +- 2afc484a2: Migrate fetchWithTimeout from widgets to utils + Add objSlice function and improve types for objMerge + Add isUrl function + +## 5.4.0 + +### Minor Changes + +- 4415ac224: Add Gnosis safe transaction builder to warp apply + +## 5.3.0 + +### Minor Changes + +- 746eeb9d9: Add parseTokenMessage util for decoding warp route transfers + +### Patch Changes + +- 50319d8ba: Ensure runWithTimeout cleans up after itself properly + +## 5.2.1 + +## 5.2.0 + +### Minor Changes + +- d6de34ad5: Add sortArraysInConfig method, normalizeConfig implementation to call sortArraysInConfig after current behavior +- 291c5fe36: Add addBufferToGasLimit for gas limit buffer calculations + ## 5.1.0 ## 5.0.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index dfee030d5..e3b85b1e8 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/utils", "description": "General utilities and types for the Hyperlane network", - "version": "5.1.0", + "version": "5.6.2", "dependencies": { "@cosmjs/encoding": "^0.32.4", "@solana/web3.js": "^1.78.0", @@ -9,14 +9,17 @@ "ethers": "^5.7.2", "lodash-es": "^4.17.21", "pino": "^8.19.0", - "yaml": "^2.4.1" + "yaml": "2.4.5" }, "devDependencies": { "@types/lodash-es": "^4.17.12", "@types/mocha": "^10.0.1", - "chai": "^4.3.6", + "@types/sinon": "^17.0.1", + "@types/sinon-chai": "^3.2.12", + "chai": "4.5.0", "mocha": "^10.2.0", "prettier": "^2.8.8", + "sinon": "^13.0.2", "typescript": "5.3.3" }, "homepage": "https://www.hyperlane.xyz", diff --git a/typescript/utils/src/addresses.test.ts b/typescript/utils/src/addresses.test.ts index d25e33cf8..2313ce384 100644 --- a/typescript/utils/src/addresses.test.ts +++ b/typescript/utils/src/addresses.test.ts @@ -4,6 +4,7 @@ import { addressToBytes, bytesToProtocolAddress, isZeroishAddress, + padBytesToLength, } from './addresses.js'; import { ProtocolType } from './types.js'; @@ -42,6 +43,17 @@ describe('Address utilities', () => { }); }); + describe('padBytesToLength', () => { + it('Pads bytes to a given length', () => { + const bytes = Buffer.from([1, 2, 3]); + expect(padBytesToLength(bytes, 5).equals(Buffer.from([0, 0, 1, 2, 3]))); + }); + it('Rejects bytes that exceed the target length', () => { + const bytes = Buffer.from([1, 2, 3]); + expect(() => padBytesToLength(bytes, 2)).to.throw(Error); + }); + }); + describe('bytesToProtocolAddress', () => { it('Converts bytes to address', () => { expect( diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index 88532464f..01f9fdb10 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -316,6 +316,14 @@ export function bytesToBytes32(bytes: Uint8Array): string { ); } +// Pad bytes to a certain length, padding with 0s at the start +export function padBytesToLength(bytes: Uint8Array, length: number) { + if (bytes.length > length) { + throw new Error(`bytes must be ${length} bytes or less`); + } + return Buffer.concat([Buffer.alloc(length - bytes.length), bytes]); +} + export function bytesToAddressEvm(bytes: Uint8Array): Address { return bytes32ToAddress(Buffer.from(bytes).toString('hex')); } diff --git a/typescript/utils/src/amount.ts b/typescript/utils/src/amount.ts index 02f6467dd..f415c268d 100644 --- a/typescript/utils/src/amount.ts +++ b/typescript/utils/src/amount.ts @@ -1,5 +1,6 @@ import { formatUnits, parseUnits } from '@ethersproject/units'; import { BigNumber } from 'bignumber.js'; +import { ethers } from 'ethers'; const DEFAULT_DISPLAY_DECIMALS = 4; const DEFAULT_TOKEN_DECIMALS = 18; @@ -131,3 +132,20 @@ export function convertDecimals( return amount.times(BigNumber(10).pow(difference)).toString(10); } } + +// Default gas limit buffer percentage +const DEFAULT_GAS_LIMIT_BUFFER_PERCENT = 10; + +/** + * Calculates the gas limit with a buffer added to the estimated gas. + * @param estimatedGas The estimated gas for the transaction. + * @param bufferPercent The percentage to add as a buffer (default: 10%). + * @returns The calculated gas limit with the buffer added. + */ +export function addBufferToGasLimit( + estimatedGas: ethers.BigNumber, + bufferPercent: number = DEFAULT_GAS_LIMIT_BUFFER_PERCENT, +): ethers.BigNumber { + const bufferMultiplier = 100 + bufferPercent; + return estimatedGas.mul(bufferMultiplier).div(100); +} diff --git a/typescript/utils/src/arrays.test.ts b/typescript/utils/src/arrays.test.ts new file mode 100644 index 000000000..88273591b --- /dev/null +++ b/typescript/utils/src/arrays.test.ts @@ -0,0 +1,54 @@ +import { expect } from 'chai'; + +import { chunk, exclude, randomElement } from './arrays.js'; + +describe('Arrays utilities', () => { + describe('chunk', () => { + it('should split an array into chunks of the specified size', () => { + const result = chunk([1, 2, 3, 4, 5], 2); + expect(result).to.deep.equal([[1, 2], [3, 4], [5]]); + }); + + it('should return an empty array when input is empty', () => { + const result = chunk([], 2); + expect(result).to.deep.equal([]); + }); + + it('should handle chunk size larger than array length', () => { + const result = chunk([1, 2], 5); + expect(result).to.deep.equal([[1, 2]]); + }); + }); + + describe('exclude', () => { + it('should exclude the specified item from the list', () => { + const result = exclude(2, [1, 2, 3, 2]); + expect(result).to.deep.equal([1, 3]); + }); + + it('should return the same list if item is not found', () => { + const result = exclude(4, [1, 2, 3]); + expect(result).to.deep.equal([1, 2, 3]); + }); + + it('should return an empty list if all items are excluded', () => { + const result = exclude(1, [1, 1, 1]); + expect(result).to.deep.equal([]); + }); + }); + + describe('randomElement', () => { + beforeEach(() => {}); + + it('should return a random element from the list', () => { + const list = [10, 20, 30]; + const result = randomElement(list); + expect(result).to.be.oneOf(list); + }); + + it('should handle an empty list gracefully', () => { + const result = randomElement([]); + expect(result).to.be.undefined; + }); + }); +}); diff --git a/typescript/utils/src/async.test.ts b/typescript/utils/src/async.test.ts new file mode 100644 index 000000000..004abb1d1 --- /dev/null +++ b/typescript/utils/src/async.test.ts @@ -0,0 +1,146 @@ +import { expect } from 'chai'; + +import { + concurrentMap, + fetchWithTimeout, + pollAsync, + raceWithContext, + retryAsync, + runWithTimeout, + sleep, + timeout, +} from './async.js'; + +describe('Async Utilities', () => { + describe('sleep', () => { + it('should resolve after sleep duration', async () => { + const start = Date.now(); + await sleep(100); + const duration = Date.now() - start; + expect(duration).to.be.at.least(100); + expect(duration).to.be.lessThan(200); + }); + }); + + describe('timeout', () => { + it('should timeout a promise', async () => { + const promise = new Promise((resolve) => setTimeout(resolve, 200)); + try { + await timeout(promise, 100); + throw new Error('Expected timeout error'); + } catch (error: any) { + expect(error.message).to.equal('Timeout reached'); + } + }); + }); + + describe('runWithTimeout', () => { + it('should run a callback with a timeout', async () => { + const result = await runWithTimeout(100, async () => { + await sleep(50); + return 'success'; + }); + expect(result).to.equal('success'); + }); + }); + + describe('fetchWithTimeout', () => { + it('should fetch with timeout', async () => { + // Mock fetch for testing + global.fetch = async () => { + await sleep(50); + return new Response('ok'); + }; + + const response = await fetchWithTimeout('https://example.com', {}, 100); + expect(await response.text()).to.equal('ok'); + }); + }); + + describe('retryAsync', () => { + it('should retry async function with exponential backoff', async () => { + let attempt = 0; + const runner = async () => { + attempt++; + if (attempt < 3) throw new Error('fail'); + return 'success'; + }; + + const result = await retryAsync(runner, 5, 10); + expect(result).to.equal('success'); + }); + }); + + describe('pollAsync', () => { + it('should poll async function until success', async () => { + let attempt = 0; + const runner = async () => { + attempt++; + if (attempt < 3) throw new Error('fail'); + return 'success'; + }; + + const result = await pollAsync(runner, 10, 5); + expect(result).to.equal('success'); + }); + + it('should fail after reaching max retries', async () => { + let attempt = 0; + const runner = async () => { + attempt++; + throw new Error('fail'); + }; + + try { + await pollAsync(runner, 10, 3); // Set maxAttempts to 3 + throw new Error('Expected pollAsync to throw an error'); + } catch (error: any) { + expect(attempt).to.equal(3); // Ensure it attempted 3 times + expect(error.message).to.equal('fail'); + } + }); + }); + + describe('raceWithContext', () => { + it('should race with context', async () => { + const promises = [ + sleep(50).then(() => 'first'), + sleep(100).then(() => 'second'), + ]; + + const result = await raceWithContext(promises); + expect(result.resolved).to.equal('first'); + expect(result.index).to.equal(0); + }); + }); + + describe('concurrentMap', () => { + it('should map concurrently with correct results', async () => { + const xs = [1, 2, 3, 4, 5, 6]; + const mapFn = async (val: number) => { + await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate async work + return val * 2; + }; + const result = await concurrentMap(2, xs, mapFn); + expect(result).to.deep.equal([2, 4, 6, 8, 10, 12]); + }); + + it('should respect concurrency limit', async () => { + const xs = [1, 2, 3, 4, 5, 6]; + const concurrency = 2; + let activeTasks = 0; + let maxActiveTasks = 0; + + const mapFn = async (val: number) => { + activeTasks++; + maxActiveTasks = Math.max(maxActiveTasks, activeTasks); + await new Promise((resolve) => setTimeout(resolve, 50)); // Simulate async work + activeTasks--; + return val * 2; + }; + + await concurrentMap(concurrency, xs, mapFn); + expect(maxActiveTasks).to.equal(concurrency); + }); + }); +}); diff --git a/typescript/utils/src/async.ts b/typescript/utils/src/async.ts index c90f059b6..f9bda74eb 100644 --- a/typescript/utils/src/async.ts +++ b/typescript/utils/src/async.ts @@ -33,23 +33,48 @@ export function timeout( * @param timeoutMs How long to wait for the promise in milliseconds. * @param callback The callback to run. * @returns callback return value + * @throws Error if the timeout is reached before the callback completes */ export async function runWithTimeout( timeoutMs: number, callback: () => Promise, -): Promise { - let timeout: NodeJS.Timeout; - const timeoutProm = new Promise( - (_, reject) => - (timeout = setTimeout( - () => reject(new Error(`Timed out in ${timeoutMs}ms.`)), - timeoutMs, - )), - ); - const ret = await Promise.race([callback(), timeoutProm]); - // @ts-ignore timeout gets set immediately by the promise constructor - clearTimeout(timeout); - return ret; +): Promise { + let timeoutId: NodeJS.Timeout; + const timeoutProm = new Promise((_, reject) => { + timeoutId = setTimeout(() => { + reject(new Error(`Timed out in ${timeoutMs}ms.`)); + }, timeoutMs); + }); + + try { + const result = await Promise.race([callback(), timeoutProm]); + return result as T; + } finally { + // @ts-ignore timeout gets set immediately by the promise constructor + clearTimeout(timeoutId); + } +} + +/** + * Executes a fetch request that fails after a timeout via an AbortController. + * @param resource resource to fetch (e.g URL) + * @param options fetch call options object + * @param timeout timeout MS (default 10_000) + * @returns fetch response + */ +export async function fetchWithTimeout( + resource: RequestInfo, + options?: RequestInit, + timeout = 10_000, +) { + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + const response = await fetch(resource, { + ...options, + signal: controller.signal, + }); + clearTimeout(id); + return response; } /** diff --git a/typescript/utils/src/base58.test.ts b/typescript/utils/src/base58.test.ts new file mode 100644 index 000000000..45416c787 --- /dev/null +++ b/typescript/utils/src/base58.test.ts @@ -0,0 +1,37 @@ +import { expect } from 'chai'; +import { utils } from 'ethers'; + +import { base58ToBuffer, bufferToBase58, hexOrBase58ToHex } from './base58.js'; + +describe('Base58 Utilities', () => { + describe('base58ToBuffer', () => { + it('should convert a base58 string to a buffer', () => { + const base58String = '3mJr7AoUXx2Wqd'; + const expectedBuffer = Buffer.from(utils.base58.decode(base58String)); + expect(base58ToBuffer(base58String)).to.deep.equal(expectedBuffer); + }); + }); + + describe('bufferToBase58', () => { + it('should convert a buffer to a base58 string', () => { + const buffer = Buffer.from([1, 2, 3, 4]); + const expectedBase58String = utils.base58.encode(buffer); + expect(bufferToBase58(buffer)).to.equal(expectedBase58String); + }); + }); + + describe('hexOrBase58ToHex', () => { + it('should return the hex string as is if it starts with 0x', () => { + const hexString = '0x1234abcd'; + expect(hexOrBase58ToHex(hexString)).to.equal(hexString); + }); + + it('should convert a base58 string to a hex string', () => { + const base58String = '3mJr7AoUXx2Wqd'; + const expectedHexString = utils.hexlify( + Buffer.from(utils.base58.decode(base58String)), + ); + expect(hexOrBase58ToHex(base58String)).to.equal(expectedHexString); + }); + }); +}); diff --git a/typescript/utils/src/base64.test.ts b/typescript/utils/src/base64.test.ts new file mode 100644 index 000000000..ca6cfc151 --- /dev/null +++ b/typescript/utils/src/base64.test.ts @@ -0,0 +1,74 @@ +import { expect } from 'chai'; +import Sinon from 'sinon'; + +import { fromBase64, toBase64 } from './base64.js'; +import { rootLogger } from './logging.js'; + +describe('Base64 Utility Functions', () => { + let loggerStub: sinon.SinonStub; + + beforeEach(() => { + loggerStub = Sinon.stub(rootLogger, 'error'); + }); + + afterEach(() => { + loggerStub.restore(); + }); + + describe('toBase64', () => { + it('should encode a valid object to a base64 string', () => { + const data = { key: 'value' }; + const result = toBase64(data); + expect(result).to.be.a('string'); + expect(result).to.equal(btoa(JSON.stringify(data))); + }); + + it('should return undefined for null or undefined input', () => { + expect(toBase64(null)).to.be.undefined; + expect(toBase64(undefined)).to.be.undefined; + }); + + it('should log an error for invalid input', () => { + toBase64(null); + expect(loggerStub.calledOnce).to.be.true; + expect( + loggerStub.calledWith( + 'Unable to serialize + encode data to base64', + null, + ), + ).to.be.true; + }); + }); + + describe('fromBase64', () => { + it('should decode a valid base64 string to an object', () => { + const data = { key: 'value' }; + const base64String = btoa(JSON.stringify(data)); + const result = fromBase64(base64String); + expect(result).to.deep.equal(data); + }); + + it('should return undefined for null or undefined input', () => { + expect(fromBase64(null as any)).to.be.undefined; + expect(fromBase64(undefined as any)).to.be.undefined; + }); + + it('should handle array input and decode the first element', () => { + const data = { key: 'value' }; + const base64String = btoa(JSON.stringify(data)); + const result = fromBase64([base64String, 'anotherString']); + expect(result).to.deep.equal(data); + }); + + it('should log an error for invalid base64 input', () => { + fromBase64('invalidBase64'); + expect(loggerStub.calledOnce).to.be.true; + expect( + loggerStub.calledWith( + 'Unable to decode + deserialize data from base64', + 'invalidBase64', + ), + ).to.be.true; + }); + }); +}); diff --git a/typescript/utils/src/checkpoints.test.ts b/typescript/utils/src/checkpoints.test.ts new file mode 100644 index 000000000..c35f73b9d --- /dev/null +++ b/typescript/utils/src/checkpoints.test.ts @@ -0,0 +1,120 @@ +import { expect } from 'chai'; + +import { + isCheckpoint, + isS3Checkpoint, + isS3CheckpointWithId, + isValidSignature, +} from './checkpoints.js'; +import { Checkpoint, S3Checkpoint, S3CheckpointWithId } from './types.js'; + +describe('Checkpoints', () => { + describe('isValidSignature', () => { + it('should return true for valid string signature', () => { + const signature = '0x' + 'a'.repeat(130); // Example of a valid hex string + expect(isValidSignature(signature)).to.be.true; + }); + + it('should return true for valid object signature', () => { + const signature = { + r: '0x' + 'a'.repeat(64), + s: '0x' + 'b'.repeat(64), + v: 27, + }; + expect(isValidSignature(signature)).to.be.true; + }); + + it('should return false for invalid signature', () => { + const signature = { + r: '0x' + 'a'.repeat(64), + s: '0x' + 'b'.repeat(64), + v: 'invalid', + }; + expect(isValidSignature(signature)).to.be.false; + }); + }); + + describe('isCheckpoint', () => { + it('should return true for valid checkpoint', () => { + const checkpoint: Checkpoint = { + root: '0x' + 'a'.repeat(64), + index: 1, + merkle_tree_hook_address: '0x' + 'b'.repeat(40), + mailbox_domain: 123, + }; + expect(isCheckpoint(checkpoint)).to.be.true; + }); + + it('should return false for invalid checkpoint', () => { + const checkpoint = { + root: 'invalid', + index: 'invalid', + merkle_tree_hook_address: 'invalid', + mailbox_domain: 'invalid', + }; + expect(isCheckpoint(checkpoint)).to.be.false; + }); + }); + + describe('isS3Checkpoint', () => { + it('should return true for valid S3Checkpoint', () => { + const s3Checkpoint: S3Checkpoint = { + signature: '0x' + 'a'.repeat(130), + value: { + root: '0x' + 'a'.repeat(64), + index: 1, + merkle_tree_hook_address: '0x' + 'b'.repeat(40), + mailbox_domain: 123, + }, + }; + expect(isS3Checkpoint(s3Checkpoint)).to.be.true; + }); + + it('should return false for invalid S3Checkpoint', () => { + const s3Checkpoint = { + signature: 'invalid', + value: { + root: 'invalid', + index: 'invalid', + merkle_tree_hook_address: 'invalid', + mailbox_domain: 'invalid', + }, + }; + expect(isS3Checkpoint(s3Checkpoint)).to.be.false; + }); + }); + + describe('isS3CheckpointWithId', () => { + it('should return true for valid S3CheckpointWithId', () => { + const s3CheckpointWithId: S3CheckpointWithId = { + signature: '0x' + 'a'.repeat(130), + value: { + checkpoint: { + root: '0x' + 'a'.repeat(64), + index: 1, + merkle_tree_hook_address: '0x' + 'b'.repeat(40), + mailbox_domain: 123, + }, + message_id: '0x' + 'c'.repeat(64), + }, + }; + expect(isS3CheckpointWithId(s3CheckpointWithId)).to.be.true; + }); + + it('should return false for invalid S3CheckpointWithId', () => { + const s3CheckpointWithId = { + signature: 'invalid', + value: { + checkpoint: { + root: 'invalid', + index: 'invalid', + merkle_tree_hook_address: 'invalid', + mailbox_domain: 'invalid', + }, + message_id: 'invalid', + }, + }; + expect(isS3CheckpointWithId(s3CheckpointWithId)).to.be.false; + }); + }); +}); diff --git a/typescript/utils/src/checkpoints.ts b/typescript/utils/src/checkpoints.ts index d83d54c68..88afc705e 100644 --- a/typescript/utils/src/checkpoints.ts +++ b/typescript/utils/src/checkpoints.ts @@ -7,7 +7,7 @@ import { SignatureLike, } from './types.js'; -function isValidSignature(signature: any): signature is SignatureLike { +export function isValidSignature(signature: any): signature is SignatureLike { return typeof signature === 'string' ? utils.isHexString(signature) : utils.isHexString(signature.r) && diff --git a/typescript/utils/src/env.test.ts b/typescript/utils/src/env.test.ts new file mode 100644 index 000000000..3772fcb4d --- /dev/null +++ b/typescript/utils/src/env.test.ts @@ -0,0 +1,18 @@ +import { expect } from 'chai'; + +import { safelyAccessEnvVar } from './env.js'; + +describe('Env Utilities', () => { + describe('safelyAccessEnvVar', () => { + it('should return the environment variable', () => { + process.env.TEST_VAR = '0xTEST_VAR'; + expect(safelyAccessEnvVar('TEST_VAR')).to.equal('0xTEST_VAR'); + expect(safelyAccessEnvVar('TEST_VAR', true)).to.equal('0xtest_var'); + }); + + it('should return undefined if the environment variable is not set', () => { + expect(safelyAccessEnvVar('NON_EXISTENT_VAR')).to.be.undefined; + expect(safelyAccessEnvVar('NON_EXISTENT_VAR', true)).to.be.undefined; + }); + }); +}); diff --git a/typescript/utils/src/ids.test.ts b/typescript/utils/src/ids.test.ts new file mode 100644 index 000000000..5008eeaf1 --- /dev/null +++ b/typescript/utils/src/ids.test.ts @@ -0,0 +1,52 @@ +import { expect } from 'chai'; +import { utils } from 'ethers'; + +import { canonizeId, evmId } from './ids.js'; + +describe('ID Utilities', () => { + describe('canonizeId', () => { + it('should convert a 20-byte ID to a 32-byte ID', () => { + const id = '0x1234567890123456789012345678901234567890'; + const result = canonizeId(id); + expect(result).to.be.instanceOf(Uint8Array); + expect(result.length).to.equal(32); + expect(utils.hexlify(result)).to.equal( + '0x0000000000000000000000001234567890123456789012345678901234567890', + ); + }); + + it('should throw an error for IDs longer than 32 bytes', () => { + const id = '0x' + '12'.repeat(33); + expect(() => canonizeId(id)).to.throw('Too long'); + }); + + it('should throw an error for IDs not 20 or 32 bytes', () => { + const id = '0x1234567890'; + expect(() => canonizeId(id)).to.throw( + 'bad input, expect address or bytes32', + ); + }); + }); + + describe('evmId', () => { + it('should convert a 32-byte ID to a 20-byte EVM address', () => { + const id = + '0x' + '00'.repeat(12) + '1234567890123456789012345678901234567890'; + const result = evmId(id); + expect(result).to.equal('0x1234567890123456789012345678901234567890'); + }); + + it('should return the same 20-byte ID as a 20-byte EVM address', () => { + const id = '0x1234567890123456789012345678901234567890'; + const result = evmId(id); + expect(result).to.equal(id); + }); + + it('should throw an error for IDs not 20 or 32 bytes', () => { + const id = '0x1234567890'; + expect(() => evmId(id)).to.throw( + 'Invalid id length. expected 20 or 32. Got 5', + ); + }); + }); +}); diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index baf23002d..26921fa1c 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -35,10 +35,12 @@ export { normalizeAddressCosmos, normalizeAddressEvm, normalizeAddressSealevel, + padBytesToLength, shortenAddress, strip0x, } from './addresses.js'; export { + addBufferToGasLimit, convertDecimals, eqAmountApproximate, fromWei, @@ -49,6 +51,7 @@ export { export { chunk, exclude, randomElement } from './arrays.js'; export { concurrentMap, + fetchWithTimeout, pollAsync, raceWithContext, retryAsync, @@ -89,31 +92,44 @@ export { setRootLogger, } from './logging.js'; export { mean, median, randomInt, stdDev, sum } from './math.js'; -export { formatMessage, messageId, parseMessage } from './messages.js'; +export { + formatMessage, + messageId, + parseMessage, + parseWarpRouteMessage, +} from './messages.js'; export { formatLegacyMultisigIsmMetadata, parseLegacyMultisigIsmMetadata, } from './multisig.js'; export { + ObjectDiff, ValueOf, arrayToObject, deepCopy, deepEquals, + diffObjMerge, invertKeysAndValues, isObjEmpty, isObject, - normalizeConfig, objFilter, objKeys, objLength, objMap, objMapEntries, objMerge, + objOmit, pick, promiseObjAll, stringifyObject, } from './objects.js'; -export { difference, setEquality, symmetricDifference } from './sets.js'; +export { Result, failure, success } from './result.js'; +export { + difference, + intersection, + setEquality, + symmetricDifference, +} from './sets.js'; export { errorToString, fromHexString, @@ -151,6 +167,7 @@ export { TokenCaip19Id, WithAddress, } from './types.js'; -export { isHttpsUrl } from './url.js'; +export { isHttpsUrl, isUrl } from './url.js'; export { assert } from './validation.js'; export { BaseValidator, ValidatorConfig } from './validator.js'; +export { tryParseJsonOrYaml } from './yaml.js'; diff --git a/typescript/utils/src/logging.test.ts b/typescript/utils/src/logging.test.ts new file mode 100644 index 000000000..812ac8f5a --- /dev/null +++ b/typescript/utils/src/logging.test.ts @@ -0,0 +1,39 @@ +import { expect } from 'chai'; +import { BigNumber } from 'ethers'; + +import { ethersBigNumberSerializer } from './logging.js'; + +describe('Logging Utilities', () => { + describe('ethersBigNumberSerializer', () => { + it('should serialize a BigNumber object correctly', () => { + const key = 'testKey'; + const value = { + type: 'BigNumber', + hex: '0x1a', + }; + const result = ethersBigNumberSerializer(key, value); + expect(result).to.equal(BigNumber.from(value.hex).toString()); + }); + + it('should return the value unchanged if it is not a BigNumber', () => { + const key = 'testKey'; + const value = { some: 'object' }; + const result = ethersBigNumberSerializer(key, value); + expect(result).to.equal(value); + }); + + it('should return the value unchanged if it is null', () => { + const key = 'testKey'; + const value = null; + const result = ethersBigNumberSerializer(key, value); + expect(result).to.equal(value); + }); + + it('should return the value unchanged if it is not an object', () => { + const key = 'testKey'; + const value = 'string'; + const result = ethersBigNumberSerializer(key, value); + expect(result).to.equal(value); + }); + }); +}); diff --git a/typescript/utils/src/math.test.ts b/typescript/utils/src/math.test.ts new file mode 100644 index 000000000..85cbee588 --- /dev/null +++ b/typescript/utils/src/math.test.ts @@ -0,0 +1,53 @@ +import { expect } from 'chai'; + +import { mean, median, randomInt, stdDev, sum } from './math.js'; + +describe('Math Utility Functions', () => { + describe('median', () => { + it('should return the median of an odd-length array', () => { + expect(median([1, 3, 2])).to.equal(2); + }); + + it('should return the median of an even-length array', () => { + expect(median([1, 2, 3, 4])).to.equal(2.5); + }); + + it('should return the median of an even-length array with non sorted numbers', () => { + expect(median([1, 2, 0, 4, 5, 6])).to.equal(3); + }); + }); + + describe('sum', () => { + it('should return the sum of an array', () => { + expect(sum([1, 2, 3, 4])).to.equal(10); + expect(sum([1, -2, 3, 4])).to.equal(6); + }); + }); + + describe('mean', () => { + it('should return the mean of an array', () => { + expect(mean([1, 2, 3, 4])).to.equal(2.5); + }); + }); + + describe('stdDev', () => { + it('should return the standard deviation of an array', () => { + expect(stdDev([1, 2, 3, 4])).to.be.closeTo(1.118, 0.001); + }); + + it('should return the standard deviation of an array with negative numbers', () => { + expect(stdDev([-1, -2, -3, -4])).to.be.closeTo(1.118, 0.001); + }); + }); + + describe('randomInt', () => { + it('should return a random integer within the specified range', () => { + const min = 1; + const max = 10; + const result = randomInt(max, min); + expect(result).to.be.at.least(min); + expect(result).to.be.below(max); + expect(result % 1).to.equal(0); // its an integer + }); + }); +}); diff --git a/typescript/utils/src/math.ts b/typescript/utils/src/math.ts index ebca6e75e..4cc71bf80 100644 --- a/typescript/utils/src/math.ts +++ b/typescript/utils/src/math.ts @@ -2,7 +2,7 @@ export function median(a: number[]): number { const sorted = a.slice().sort(); const mid = Math.floor(sorted.length / 2); const median = - sorted.length % 2 == 0 ? (sorted[mid] + sorted[mid + 1]) / 2 : sorted[mid]; + sorted.length % 2 == 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]; return median; } diff --git a/typescript/utils/src/messages.ts b/typescript/utils/src/messages.ts index 2873edcdd..581391af0 100644 --- a/typescript/utils/src/messages.ts +++ b/typescript/utils/src/messages.ts @@ -1,7 +1,14 @@ import { BigNumber, ethers, utils } from 'ethers'; import { addressToBytes32 } from './addresses.js'; -import { Address, Domain, HexString, ParsedMessage } from './types.js'; +import { fromHexString, toHexString } from './strings.js'; +import { + Address, + Domain, + HexString, + ParsedMessage, + ParsedWarpRouteMessage, +} from './types.js'; /** * JS Implementation of solidity/contracts/libs/Message.sol#formatMessage @@ -61,9 +68,27 @@ export function parseMessage(message: string): ParsedMessage { const version = buf.readUint8(VERSION_OFFSET); const nonce = buf.readUInt32BE(NONCE_OFFSET); const origin = buf.readUInt32BE(ORIGIN_OFFSET); - const sender = utils.hexlify(buf.slice(SENDER_OFFSET, DESTINATION_OFFSET)); + const sender = utils.hexlify(buf.subarray(SENDER_OFFSET, DESTINATION_OFFSET)); const destination = buf.readUInt32BE(DESTINATION_OFFSET); - const recipient = utils.hexlify(buf.slice(RECIPIENT_OFFSET, BODY_OFFSET)); - const body = utils.hexlify(buf.slice(BODY_OFFSET)); + const recipient = utils.hexlify(buf.subarray(RECIPIENT_OFFSET, BODY_OFFSET)); + const body = utils.hexlify(buf.subarray(BODY_OFFSET)); return { version, nonce, origin, sender, destination, recipient, body }; } + +export function parseWarpRouteMessage( + messageBody: string, +): ParsedWarpRouteMessage { + const RECIPIENT_OFFSET = 0; + const AMOUNT_OFFSET = 32; + const buf = fromHexString(messageBody); + const recipient = toHexString( + buf.subarray(RECIPIENT_OFFSET, RECIPIENT_OFFSET + 32), + ); + const amount = BigInt( + toHexString(buf.subarray(AMOUNT_OFFSET, AMOUNT_OFFSET + 32)), + ); + return { + recipient, + amount, + }; +} diff --git a/typescript/utils/src/objects.test.ts b/typescript/utils/src/objects.test.ts index 61d3b5a76..3318c682e 100644 --- a/typescript/utils/src/objects.test.ts +++ b/typescript/utils/src/objects.test.ts @@ -1,6 +1,25 @@ import { expect } from 'chai'; -import { deepCopy, deepEquals } from './objects.js'; +import { + arrayToObject, + deepCopy, + deepEquals, + deepFind, + diffObjMerge, + invertKeysAndValues, + isObjEmpty, + isObject, + objFilter, + objKeys, + objLength, + objMap, + objMapEntries, + objMerge, + objOmit, + pick, + promiseObjAll, + stringifyObject, +} from './objects.js'; describe('Object utilities', () => { it('deepEquals', () => { @@ -13,4 +32,312 @@ describe('Object utilities', () => { expect(deepCopy({ a: 1, b: 2 })).to.eql({ a: 1, b: 2 }); expect(deepCopy({ a: 1, b: 2 })).to.not.eql({ a: 1, b: 3 }); }); + + it('objMerge', () => { + const obj1 = { a: 1, b: 2, c: { d: '4' } }; + const obj2 = { b: 3, c: { d: '5' } }; + const merged = objMerge(obj1, obj2); + expect(merged).to.eql({ a: 1, b: 3, c: { d: '5' } }); + }); + + it('objMerge with array', () => { + const obj1 = { a: 1, b: { c: ['arr1'] } }; + const obj2 = { a: 2, b: { c: ['arr2'] } }; + const merged = objMerge(obj1, obj2, 10, true); + expect(merged).to.eql({ a: 2, b: { c: ['arr2', 'arr1'] } }); + }); + + it('objMerge without array', () => { + const obj1 = { a: 1, b: { c: ['arr1'] } }; + const obj2 = { a: 2, b: { c: ['arr2'] } }; + const merged = objMerge(obj1, obj2, 10, false); + expect(merged).to.eql({ a: 2, b: { c: ['arr2'] } }); + }); + + it('objMerge overwrites nested values', () => { + const obj1 = { a: { b: 10 }, c: 'value' }; + const obj2 = { a: { b: 20 } }; + const merged = objMerge(obj1, obj2); + expect(merged).to.eql({ a: { b: 20 }, c: 'value' }); + }); + + it('objOmit', () => { + const obj1 = { a: 1, b: { c: ['arr1'], d: 'string' } }; + const obj2 = { a: true, b: { c: true } }; + const omitted = objOmit(obj1, obj2); + expect(omitted).to.eql({ b: { d: 'string' } }); + }); + + it('objOmit with array', () => { + const obj1 = { a: 1, b: { c: ['arr1', 'arr2'], d: 'string' } }; + const obj2 = { b: { c: ['arr1'] } }; + const omitted1_2 = objOmit(obj1, obj2, 10, true); + expect(omitted1_2).to.eql({ a: 1, b: { c: ['arr2'], d: 'string' } }); + + const obj3 = { a: [{ b: 1 }], c: 2 }; + const obj4 = { a: [{ b: 1 }] }; + const omitted3_4 = objOmit(obj3, obj4, 10, true); + expect(omitted3_4).to.eql({ a: [], c: 2 }); + }); + + it('objOmit without array', () => { + const obj1 = { a: 1, b: { c: ['arr1', 'arr2'], d: 'string' } }; + const obj2 = { b: { c: ['arr1'] } }; + const omitted1_2 = objOmit(obj1, obj2, 10, false); + expect(omitted1_2).to.eql({ a: 1, b: { d: 'string' } }); + }); + + it('isObject', () => { + expect(isObject({})).to.be.true; + expect(isObject([])).to.be.false; + expect(isObject(null)).to.be.false; + expect(isObject(undefined)).to.be.false; + expect(isObject(42)).to.be.false; + }); + + it('objKeys', () => { + const obj = { a: 1, b: 2 }; + expect(objKeys(obj)).to.eql(['a', 'b']); + }); + + it('objLength', () => { + const obj = { a: 1, b: 2 }; + expect(objLength(obj)).to.equal(2); + }); + + it('isObjEmpty', () => { + expect(isObjEmpty({})).to.be.true; + expect(isObjEmpty({ a: 1 })).to.be.false; + }); + + it('objMapEntries', () => { + const obj = { a: 1, b: 2 }; + const result = objMapEntries(obj, (k, v) => v * 2); + expect(result).to.eql([ + ['a', 2], + ['b', 4], + ]); + }); + + it('objMap', () => { + const obj = { a: 1, b: 2 }; + const result = objMap(obj, (k, v) => v * 2); + expect(result).to.eql({ a: 2, b: 4 }); + }); + + it('objFilter', () => { + const obj = { a: 1, b: 2, c: 3 }; + const result = objFilter(obj, (k: string, v: number): v is number => v > 1); + expect(result).to.eql({ b: 2, c: 3 }); + }); + + it('deepFind should find nested object', () => { + const obj = { a: { b: { c: 3 } } }; + const result = deepFind( + obj, + (v: any): v is { c: number } => v && v.c === 3, + ); + expect(result).to.eql({ c: 3 }); + }); + + it('deepFind should return undefined if object is not found', () => { + const obj = { a: { b: { c: 3 } } }; + const result = deepFind( + obj, + (v: any): v is { c: number } => v && v.c === 4, + ); + expect(result).to.be.undefined; + }); + + it('promiseObjAll', async () => { + const obj = { a: Promise.resolve(1), b: Promise.resolve(2) }; + const result = await promiseObjAll(obj); + expect(result).to.eql({ a: 1, b: 2 }); + }); + + it('pick should return a subset of the object', () => { + const obj = { a: 1, b: 2, c: 3 }; + const result = pick(obj, ['a', 'c']); + expect(result).to.eql({ a: 1, c: 3 }); + }); + + it('pick should return an empty object if no keys are provided', () => { + const obj = { a: 1, b: 2, c: 3 }; + const result = pick(obj, []); + expect(result).to.eql({}); + }); + + it("pick should return an empty object if the object doesn't contain the keys", () => { + const obj = { c: 4, d: 5 }; + const result = pick(obj as any, ['a', 'b']); + expect(result).to.eql({}); + }); + + describe('invertKeysAndValues', () => { + it('invertKeysAndValues should invert the keys and values', () => { + const obj = { a: '1', b: '2' }; + const result = invertKeysAndValues(obj); + expect(result).to.eql({ '1': 'a', '2': 'b' }); + }); + + it('invertKeysAndValues should return an empty object if the object is empty', () => { + const obj = {}; + const result = invertKeysAndValues(obj); + expect(result).to.eql({}); + }); + + it('invertKeysAndValues should return an object if the object has duplicate values', () => { + const obj = { a: '1', b: '1' }; + const result = invertKeysAndValues(obj); + expect(result).to.eql({ '1': 'b' }); + }); + + it('invertKeysAndValues should return an object if the object has undefined/null values', () => { + const obj = { a: '1', b: '2', c: undefined, d: null, e: 0 }; + const result = invertKeysAndValues(obj); + expect(result).to.eql({ '1': 'a', '2': 'b', '0': 'e' }); + }); + }); + + it('arrayToObject', () => { + const keys = ['a', 'b']; + const result = arrayToObject(keys); + expect(result).to.eql({ a: true, b: true }); + }); + + it('stringifyObject', () => { + const obj = { a: 1, b: 2 }; + const jsonResult = stringifyObject(obj, 'json'); + expect(jsonResult).to.equal('{"a":1,"b":2}'); + const yamlResult = stringifyObject(obj, 'yaml'); + expect(yamlResult).to.include('a: 1\nb: 2'); + }); + + describe('diffObjMerge', () => { + it('should merge objects with equal values', () => { + const actual = { a: 1, b: 2 }; + const expected = { a: 1, b: 2 }; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: false, + mergedObject: { a: 1, b: 2 }, + }); + }); + + it('should return a diff for objects with different values', () => { + const actual = { a: 1, b: 2 }; + const expected = { a: 1, b: 3 }; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: true, + mergedObject: { + a: 1, + b: { actual: 2, expected: 3 }, + }, + }); + }); + + it('should detect missing fields in the top level object', () => { + const actual = { a: 1 }; + const expected = { a: 1, b: 3 }; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: true, + mergedObject: { + a: 1, + b: { actual: '', expected: 3 }, + }, + }); + }); + + it('should detect extra fields in the top level object', () => { + const actual = { a: 1, b: 2 }; + const expected = { a: 1 }; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: true, + mergedObject: { + a: 1, + b: { actual: 2, expected: '' }, + }, + }); + }); + + it('should merge nested objects and show differences', () => { + const actual = { a: 1, b: { c: 2, d: 4 } }; + const expected = { a: 1, b: { c: 2, d: 3 } }; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: true, + mergedObject: { + a: 1, + b: { + c: 2, + d: { actual: 4, expected: 3 }, + }, + }, + }); + }); + + it('should throw an error when maxDepth is exceeded', () => { + const actual = { a: { b: { c: { d: { e: 5 } } } } }; + const expected = { a: { b: { c: { d: { e: 5 } } } } }; + + expect(() => diffObjMerge(actual, expected, 3)).to.Throw( + 'diffObjMerge tried to go too deep', + ); + }); + + it('should merge arrays of equal length and show the diffs', () => { + const actual = [1, 2, 3]; + const expected = [1, 2, 4]; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: true, + mergedObject: [1, 2, { actual: 3, expected: 4 }], + }); + }); + + it('should return a diff for arrays of different lengths', () => { + const actual = [1, 2]; + const expected = [1, 2, 3]; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: true, + mergedObject: { + actual, + expected, + }, + }); + }); + + it('should handle null and undefined values properly', () => { + const actual = { a: null, b: 2 }; + const expected = { a: undefined, b: 2 }; + + const result = diffObjMerge(actual, expected); + + expect(result).to.eql({ + isInvalid: false, + mergedObject: { + a: undefined, + b: 2, + }, + }); + }); + }); }); diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index 554552050..0c021587f 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -2,11 +2,11 @@ import { cloneDeep, isEqual } from 'lodash-es'; import { stringify as yamlStringify } from 'yaml'; import { ethersBigNumberSerializer } from './logging.js'; -import { WithAddress } from './types.js'; +import { isNullish } from './typeof.js'; import { assert } from './validation.js'; -export function isObject(item: any) { - return item && typeof item === 'object' && !Array.isArray(item); +export function isObject(item: any): boolean { + return !!item && typeof item === 'object' && !Array.isArray(item); } export function deepEquals(v1: any, v2: any) { @@ -99,39 +99,102 @@ export function pick(obj: Record, keys: K[]) { return ret as Record; } -// Recursively merges b into a -// Where there are conflicts, b takes priority over a -export function objMerge( +/** + * Returns a new object that recursively merges B into A + * Where there are conflicts, B takes priority over A + * If B has a value for a key that A does not have, B's value is used + * If B has a value for a key that A has, and both are objects, the merge recurses into those objects + * If B has a value for a key that A has, and both are arrays, the merge concatenates them with B's values taking priority + * @param a - The first object + * @param b - The second object + * @param max_depth - The maximum depth to recurse + * @param mergeArrays - If true, arrays will be concatenated instead of replaced + */ +export function objMerge( a: Record, b: Record, max_depth = 10, -): any { + mergeArrays = false, +): T { + // If we've reached the max depth, throw an error if (max_depth === 0) { throw new Error('objMerge tried to go too deep'); } - if (isObject(a) && isObject(b)) { - const ret: Record = {}; - const aKeys = new Set(Object.keys(a)); - const bKeys = new Set(Object.keys(b)); - const allKeys = new Set([...aKeys, ...bKeys]); - for (const key of allKeys.values()) { - if (aKeys.has(key) && bKeys.has(key)) { - ret[key] = objMerge(a[key], b[key], max_depth - 1); - } else if (aKeys.has(key)) { - ret[key] = a[key]; - } else { - ret[key] = b[key]; + // If either A or B is not an object, return the other value + if (!isObject(a) || !isObject(b)) { + return (b ?? a) as T; + } + // Initialize returned object with values from A + const ret: Record = { ...a }; + // Iterate over keys in B + for (const key in b) { + // If both A and B have the same key, recursively merge the values from B into A + if (isObject(a[key]) && isObject(b[key])) { + ret[key] = objMerge(a[key], b[key], max_depth - 1, mergeArrays); + } + // If A & B are both arrays, and we're merging them, concatenate them with B's values taking priority before A + else if (mergeArrays && Array.isArray(a[key]) && Array.isArray(b[key])) { + ret[key] = [...b[key], ...a[key]]; + } + // If B has a value for the key, set the value to B's value + // This better handles the case where A has a value for the key, but B does not + // In which case we want to keep A's value + else if (b[key] !== undefined) { + ret[key] = b[key]; + } + } + // Return the merged object + return ret as T; +} + +/** + * Return a new object with the fields in b removed from a + * @param a Base object to remove fields from + * @param b The partial object to remove from the base object + * @param max_depth The maximum depth to recurse + * @param sliceArrays If true, arrays will have values sliced out instead of being removed entirely + */ +export function objOmit = any>( + a: Record, + b: Record, + max_depth = 10, + sliceArrays = false, +): T { + if (max_depth === 0) { + throw new Error('objSlice tried to go too deep'); + } + if (!isObject(a) || !isObject(b)) { + return a as T; + } + const ret: Record = {}; + const aKeys = new Set(Object.keys(a)); + const bKeys = new Set(Object.keys(b)); + for (const key of aKeys.values()) { + if (bKeys.has(key)) { + if (sliceArrays && Array.isArray(a[key]) && Array.isArray(b[key])) { + ret[key] = a[key].filter( + (v: any) => !b[key].some((bv: any) => deepEquals(v, bv)), + ); + } else if (isObject(a[key]) && isObject(b[key])) { + const sliced = objOmit(a[key], b[key], max_depth - 1, sliceArrays); + if (Object.keys(sliced).length > 0) { + ret[key] = sliced; + } + } else if (!!b[key] == false) { + ret[key] = objOmit(a[key], b[key], max_depth - 1, sliceArrays); } + } else { + ret[key] = a[key]; } - return ret; - } else { - return b ? b : a; } + return ret as T; } export function invertKeysAndValues(data: any) { return Object.fromEntries( - Object.entries(data).map(([key, value]) => [value, key]), + Object.entries(data) + .filter(([_, value]) => value !== undefined && value !== null) // Filter out undefined and null values + .map(([key, value]) => [value, key]), ); } @@ -157,21 +220,102 @@ export function stringifyObject( return yamlStringify(JSON.parse(json), null, space); } -// Function to recursively remove 'address' properties and lowercase string properties -export function normalizeConfig(obj: WithAddress): any { - if (Array.isArray(obj)) { - return obj.map(normalizeConfig); - } else if (obj !== null && typeof obj === 'object') { - const newObj: any = {}; - for (const key in obj) { - if (key !== 'address') { - newObj[key] = key === 'type' ? obj[key] : normalizeConfig(obj[key]); +interface ObjectDiffOutput { + actual: any; + expected: any; +} + +export type ObjectDiff = + | { + [key: string]: ObjectDiffOutput | ObjectDiff; + } + | ObjectDiff[] + | undefined; + +/** + * Merges 2 objects showing any difference in value for common fields. + */ +export function diffObjMerge( + actual: Record, + expected: Record, + maxDepth = 10, +): { + mergedObject: ObjectDiff; + isInvalid: boolean; +} { + if (maxDepth === 0) { + throw new Error('diffObjMerge tried to go too deep'); + } + + let isDiff = false; + if (!isObject(actual) && !isObject(expected) && actual === expected) { + return { + isInvalid: isDiff, + mergedObject: actual, + }; + } + + if (isNullish(actual) && isNullish(expected)) { + return { mergedObject: undefined, isInvalid: isDiff }; + } + + if (isObject(actual) && isObject(expected)) { + const ret: Record = {}; + + const actualKeys = new Set(Object.keys(actual)); + const expectedKeys = new Set(Object.keys(expected)); + const allKeys = new Set([...actualKeys, ...expectedKeys]); + for (const key of allKeys.values()) { + if (actualKeys.has(key) && expectedKeys.has(key)) { + const { mergedObject, isInvalid } = + diffObjMerge(actual[key], expected[key], maxDepth - 1) ?? {}; + ret[key] = mergedObject; + isDiff ||= isInvalid; + } else if (actualKeys.has(key) && !isNullish(actual[key])) { + ret[key] = { + actual: actual[key], + expected: '' as any, + }; + isDiff = true; + } else if (!isNullish(expected[key])) { + ret[key] = { + actual: '' as any, + expected: expected[key], + }; + isDiff = true; } } - return newObj; - } else if (typeof obj === 'string') { - return obj.toLowerCase(); + return { + isInvalid: isDiff, + mergedObject: ret, + }; + } + + // Merge the elements of the array to see if there are any differences + if ( + Array.isArray(actual) && + Array.isArray(expected) && + actual.length === expected.length + ) { + const merged = actual.reduce( + (acc: [ObjectDiff[], boolean], curr, idx) => { + const { isInvalid, mergedObject } = diffObjMerge(curr, expected[idx]); + + acc[0].push(mergedObject); + acc[1] ||= isInvalid; + + return acc; + }, + [[], isDiff], + ); + return { + isInvalid: merged[1], + mergedObject: merged[0], + }; } - return obj; + return { + mergedObject: { expected: expected ?? '', actual: actual ?? '' }, + isInvalid: true, + }; } diff --git a/typescript/utils/src/result.ts b/typescript/utils/src/result.ts new file mode 100644 index 000000000..27c01ad53 --- /dev/null +++ b/typescript/utils/src/result.ts @@ -0,0 +1,18 @@ +/********* RESULT MONAD *********/ +export type Result = + | { + success: true; + data: T; + } + | { + success: false; + error: string; + }; + +export function success(data: T): Result { + return { success: true, data }; +} + +export function failure(error: string): Result { + return { success: false, error }; +} diff --git a/typescript/utils/src/sets.test.ts b/typescript/utils/src/sets.test.ts new file mode 100644 index 000000000..3f4195b4d --- /dev/null +++ b/typescript/utils/src/sets.test.ts @@ -0,0 +1,39 @@ +import { expect } from 'chai'; + +import { difference, setEquality, symmetricDifference } from './sets.js'; + +describe('Set Operations', () => { + describe('difference', () => { + it('should return the difference of two sets', () => { + const setA = new Set([1, 2, 3, undefined]); + const setB = new Set([2, 3, 4]); + const result = difference(setA, setB); + expect(result).to.deep.equal(new Set([1, undefined])); + }); + }); + + describe('symmetricDifference', () => { + it('should return the symmetric difference of two sets', () => { + const setA = new Set([1, 2, 3]); + const setB = new Set([2, 3, 4]); + const result = symmetricDifference(setA, setB); + expect(result).to.deep.equal(new Set([1, 4])); + }); + }); + + describe('setEquality', () => { + it('should return true for equal sets', () => { + const setA = new Set([1, 2, 3]); + const setB = new Set([1, 2, 3]); + const result = setEquality(setA, setB); + expect(result).to.be.true; + }); + + it('should return false for non-equal sets', () => { + const setA = new Set([1, 2, 3]); + const setB = new Set([1, 2, 4]); + const result = setEquality(setA, setB); + expect(result).to.be.false; + }); + }); +}); diff --git a/typescript/utils/src/sets.ts b/typescript/utils/src/sets.ts index 18149ae37..587c00a62 100644 --- a/typescript/utils/src/sets.ts +++ b/typescript/utils/src/sets.ts @@ -22,3 +22,13 @@ export function symmetricDifference(a: Set, b: Set) { export function setEquality(a: Set, b: Set) { return symmetricDifference(a, b).size === 0; } + +export function intersection(a: Set, b: Set) { + const _intersection = new Set(); + a.forEach((elem) => { + if (b.has(elem)) { + _intersection.add(elem); + } + }); + return _intersection; +} diff --git a/typescript/utils/src/strings.test.ts b/typescript/utils/src/strings.test.ts new file mode 100644 index 000000000..3cf479823 --- /dev/null +++ b/typescript/utils/src/strings.test.ts @@ -0,0 +1,57 @@ +import { expect } from 'chai'; +import { Readable } from 'stream'; + +import { + errorToString, + fromHexString, + sanitizeString, + streamToString, + toHexString, + toTitleCase, + trimToLength, +} from './strings.js'; + +describe('String Utilities', () => { + it('should convert string to title case', () => { + expect(toTitleCase('hello world')).to.equal('Hello World'); + expect(toTitleCase('HELLO WORLD')).to.equal('Hello World'); + expect(toTitleCase('4ELLO WORLD')).to.equal('4ello World'); + expect(toTitleCase('')).to.equal(''); + }); + + it('should sanitize string by removing non-alphanumeric characters', () => { + expect(sanitizeString('Hello, World!')).to.equal('helloworld'); + expect(sanitizeString('123-456')).to.equal('123456'); + expect(sanitizeString('')).to.equal(''); + }); + + it('should trim string to specified length', () => { + expect(trimToLength('Hello, World!', 5)).to.equal('Hello...'); + expect(trimToLength('Short', 10)).to.equal('Short'); + expect(trimToLength('', 10)).to.equal(''); + }); + + it('should convert stream to string', async () => { + const stream = new Readable(); + stream.push('Hello, '); + stream.push('World!'); + stream.push(null); + + const result = await streamToString(stream); + expect(result).to.equal('Hello, World!'); + }); + + it('should convert error to string', () => { + expect(errorToString('Error message')).to.equal('Error message'); + expect(errorToString({ message: 'Error object' })).to.equal('Error object'); + expect(errorToString(404)).to.equal('Error code: 404'); + expect(errorToString(null)).to.equal('Unknown Error'); + }); + + it('should convert hex string to buffer and back', () => { + const hexString = '0x48656c6c6f'; + const buffer = fromHexString(hexString); + expect(buffer.toString('utf8')).to.equal('Hello'); + expect(toHexString(buffer)).to.equal(hexString); + }); +}); diff --git a/typescript/utils/src/typeof.test.ts b/typescript/utils/src/typeof.test.ts new file mode 100644 index 000000000..31382a374 --- /dev/null +++ b/typescript/utils/src/typeof.test.ts @@ -0,0 +1,42 @@ +import { expect } from 'chai'; + +import { isNullish, isNumeric } from './typeof.js'; + +describe('isNullish', () => { + it('should return true for null', () => { + expect(isNullish(null)).to.be.true; + }); + + it('should return true for undefined', () => { + expect(isNullish(undefined)).to.be.true; + }); + + it('should return false for non-nullish values', () => { + expect(isNullish('')).to.be.false; + expect(isNullish(0)).to.be.false; + expect(isNullish(false)).to.be.false; + }); +}); + +describe('isNumeric', () => { + it('should return true for numeric strings', () => { + expect(isNumeric('123')).to.be.true; + }); + + it('should return true for numbers', () => { + expect(isNumeric(123)).to.be.true; + }); + + it('should return true for negative numbers', () => { + expect(isNumeric(-123)).to.be.true; + }); + + it('should return true for floating point numbers', () => { + expect(isNumeric(123.45)).to.be.true; + }); + + it('should return false for non-numeric strings', () => { + expect(isNumeric('abc')).to.be.false; + expect(isNumeric('123abc')).to.be.false; + }); +}); diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index 122a4f82a..3d79f0058 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -98,6 +98,11 @@ export type ParsedMessage = { body: string; }; +export type ParsedWarpRouteMessage = { + recipient: string; + amount: bigint; +}; + export type ParsedLegacyMultisigIsmMetadata = { checkpointRoot: string; checkpointIndex: number; diff --git a/typescript/utils/src/url.ts b/typescript/utils/src/url.ts index c93e96cf7..e1cca81ff 100644 --- a/typescript/utils/src/url.ts +++ b/typescript/utils/src/url.ts @@ -1,3 +1,12 @@ +export function isUrl(value: string) { + try { + const url = new URL(value); + return !!url.hostname; + } catch (error) { + return false; + } +} + export function isHttpsUrl(value: string) { try { const url = new URL(value); diff --git a/typescript/utils/src/validation.test.ts b/typescript/utils/src/validation.test.ts new file mode 100644 index 000000000..7521a4ebf --- /dev/null +++ b/typescript/utils/src/validation.test.ts @@ -0,0 +1,13 @@ +import { expect } from 'chai'; + +import { assert } from './validation.js'; + +describe('assert', () => { + it('should not throw an error when the predicate is true', () => { + expect(() => assert(true, 'Error message')).to.not.throw(); + }); + + it('should throw an error when the predicate is false', () => { + expect(() => assert(false, 'Error message')).to.throw('Error message'); + }); +}); diff --git a/typescript/utils/src/yaml.test.ts b/typescript/utils/src/yaml.test.ts new file mode 100644 index 000000000..3e69dd309 --- /dev/null +++ b/typescript/utils/src/yaml.test.ts @@ -0,0 +1,33 @@ +import { expect } from 'chai'; + +import { tryParseJsonOrYaml } from './yaml.js'; + +describe('tryParseJsonOrYaml', () => { + it('should parse valid JSON string', () => { + const jsonString = '{"key": "value"}'; + const result: any = tryParseJsonOrYaml(jsonString); + expect(result.success).to.be.true; + expect(result.data).to.deep.equal({ key: 'value' }); + }); + + it('should parse valid YAML string', () => { + const yamlString = 'key: value'; + const result: any = tryParseJsonOrYaml(yamlString); + expect(result.success).to.be.true; + expect(result.data).to.deep.equal({ key: 'value' }); + }); + + it('should fail for invalid JSON string', () => { + const invalidJsonString = '{"key": "value"'; + const result: any = tryParseJsonOrYaml(invalidJsonString); + expect(result.success).to.be.false; + expect(result.error).to.equal('Input is not valid JSON or YAML'); + }); + + it('should fail for invalid YAML string', () => { + const invalidYamlString = 'key: value:'; + const result: any = tryParseJsonOrYaml(invalidYamlString); + expect(result.success).to.be.false; + expect(result.error).to.equal('Input is not valid JSON or YAML'); + }); +}); diff --git a/typescript/utils/src/yaml.ts b/typescript/utils/src/yaml.ts new file mode 100644 index 000000000..48ff7ecd9 --- /dev/null +++ b/typescript/utils/src/yaml.ts @@ -0,0 +1,17 @@ +import { parse as yamlParse } from 'yaml'; + +import { rootLogger } from './logging.js'; +import { Result, failure, success } from './result.js'; + +export function tryParseJsonOrYaml(input: string): Result { + try { + if (input.startsWith('{')) { + return success(JSON.parse(input)); + } else { + return success(yamlParse(input)); + } + } catch (error) { + rootLogger.error('Error parsing JSON or YAML', error); + return failure('Input is not valid JSON or YAML'); + } +} diff --git a/typescript/widgets/.storybook/main.ts b/typescript/widgets/.storybook/main.ts index 564564080..2afe9d398 100644 --- a/typescript/widgets/.storybook/main.ts +++ b/typescript/widgets/.storybook/main.ts @@ -1,4 +1,5 @@ import type { StorybookConfig } from '@storybook/react-vite'; +import { mergeConfig } from 'vite'; const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'], @@ -15,5 +16,10 @@ const config: StorybookConfig = { docs: { autodocs: true, }, + async viteFinal(config, { configType }) { + return mergeConfig(config, { + define: { 'process.env': {} }, + }); + }, }; export default config; diff --git a/typescript/widgets/CHANGELOG.md b/typescript/widgets/CHANGELOG.md index 14bd5608e..0371d680c 100644 --- a/typescript/widgets/CHANGELOG.md +++ b/typescript/widgets/CHANGELOG.md @@ -1,5 +1,112 @@ # @hyperlane-xyz/widgets +## 5.6.2 + +### Patch Changes + +- Updated dependencies [5fd4267e7] +- Updated dependencies [a36fc5fb2] + - @hyperlane-xyz/utils@5.6.2 + - @hyperlane-xyz/sdk@5.6.2 + +## 5.6.1 + +### Patch Changes + +- @hyperlane-xyz/sdk@5.6.1 +- @hyperlane-xyz/utils@5.6.1 + +## 5.6.0 + +### Patch Changes + +- e89f9e35d: Update registry to v4.7.0 +- Updated dependencies [f1712deb7] +- Updated dependencies [46044a2e9] +- Updated dependencies [02a5b92ba] +- Updated dependencies [29341950e] +- Updated dependencies [8001bbbd6] +- Updated dependencies [32d0a67c2] +- Updated dependencies [b1ff48bd1] +- Updated dependencies [d41aa6928] +- Updated dependencies [c3e9268f1] +- Updated dependencies [7d7bcc1a3] +- Updated dependencies [7f3e0669d] +- Updated dependencies [2317eca3c] + - @hyperlane-xyz/utils@5.6.0 + - @hyperlane-xyz/sdk@5.6.0 + +## 5.5.0 + +### Minor Changes + +- 2afc484a2: Create ChainSearchMenu and ChainDetailsMenu components + Add required icon and button components + Add persisted zustand store and hooks + Add clipboard utility functions + +### Patch Changes + +- Updated dependencies [2afc484a2] +- Updated dependencies [2afc484a2] +- Updated dependencies [3254472e0] +- Updated dependencies [fcfe91113] +- Updated dependencies [6176c9861] + - @hyperlane-xyz/sdk@5.5.0 + - @hyperlane-xyz/utils@5.5.0 + +## 5.4.0 + +### Patch Changes + +- Updated dependencies [4415ac224] + - @hyperlane-xyz/sdk@5.4.0 + +## 5.3.0 + +### Minor Changes + +- 35d4503b9: Update to registry v4.3.6 + +### Patch Changes + +- Updated dependencies [eb47aaee8] +- Updated dependencies [50319d8ba] +- Updated dependencies [8de531fa4] +- Updated dependencies [fd536a79a] + - @hyperlane-xyz/sdk@5.3.0 + +## 5.2.1 + +### Patch Changes + +- @hyperlane-xyz/sdk@5.2.1 + +## 5.2.0 + +### Patch Changes + +- Updated dependencies [a19e882fd] +- Updated dependencies [518a1bef9] +- Updated dependencies [203084df2] +- Updated dependencies [74a592e58] +- Updated dependencies [739af9a34] +- Updated dependencies [44588c31d] +- Updated dependencies [2bd540e0f] +- Updated dependencies [291c5fe36] +- Updated dependencies [69f17d99a] +- Updated dependencies [3ad5918da] +- Updated dependencies [9563a8beb] +- Updated dependencies [73c232b3a] +- Updated dependencies [445b6222c] +- Updated dependencies [d6de34ad5] +- Updated dependencies [2e6176f67] +- Updated dependencies [f2783c03b] +- Updated dependencies [2ffb78f5c] +- Updated dependencies [3c07ded5b] +- Updated dependencies [815542dd7] + - @hyperlane-xyz/sdk@5.2.0 + ## 5.1.0 ### Minor Changes diff --git a/typescript/widgets/package.json b/typescript/widgets/package.json index f1b421e19..20fe43cb1 100644 --- a/typescript/widgets/package.json +++ b/typescript/widgets/package.json @@ -1,16 +1,20 @@ { "name": "@hyperlane-xyz/widgets", "description": "Common react components for Hyperlane projects", - "version": "5.1.0", + "version": "5.6.2", "peerDependencies": { "react": "^18", "react-dom": "^18" }, "dependencies": { - "@hyperlane-xyz/registry": "2.5.0", - "@hyperlane-xyz/sdk": "5.1.0" + "@headlessui/react": "^2.1.8", + "@hyperlane-xyz/sdk": "5.6.2", + "@hyperlane-xyz/utils": "5.6.2", + "clsx": "^2.1.1", + "react-tooltip": "^5.28.0" }, "devDependencies": { + "@hyperlane-xyz/registry": "4.7.0", "@storybook/addon-essentials": "^7.6.14", "@storybook/addon-interactions": "^7.6.14", "@storybook/addon-links": "^7.6.14", @@ -34,7 +38,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "storybook": "^7.6.14", - "tailwindcss": "^3.2.4", + "tailwindcss": "^3.4.13", "ts-node": "^10.8.0", "typescript": "5.3.3", "vite": "^5.1.1" diff --git a/typescript/widgets/src/animations/Fade.tsx b/typescript/widgets/src/animations/Fade.tsx new file mode 100644 index 000000000..5e98e53cf --- /dev/null +++ b/typescript/widgets/src/animations/Fade.tsx @@ -0,0 +1,28 @@ +import React, { PropsWithChildren, useEffect, useState } from 'react'; + +export function Fade(props: PropsWithChildren<{ show: boolean }>) { + const { show, children } = props; + const [render, setRender] = useState(show); + + useEffect(() => { + if (show) setRender(true); + }, [show]); + + const onAnimationEnd = () => { + if (!show) setRender(false); + }; + + return render ? ( +
+ {children} +
+ ) : null; +} diff --git a/typescript/widgets/src/chains/ChainAddMenu.tsx b/typescript/widgets/src/chains/ChainAddMenu.tsx new file mode 100644 index 000000000..1b8beb617 --- /dev/null +++ b/typescript/widgets/src/chains/ChainAddMenu.tsx @@ -0,0 +1,189 @@ +import clsx from 'clsx'; +import React, { useState } from 'react'; + +import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry'; +import { + ChainMap, + ChainMetadata, + ChainMetadataSchema, + MultiProtocolProvider, +} from '@hyperlane-xyz/sdk'; +import { + Result, + failure, + success, + tryParseJsonOrYaml, +} from '@hyperlane-xyz/utils'; + +import { ColorPalette } from '../color.js'; +import { Button } from '../components/Button.js'; +import { CopyButton } from '../components/CopyButton.js'; +import { LinkButton } from '../components/LinkButton.js'; +import { ChevronIcon } from '../icons/Chevron.js'; +import { PlusIcon } from '../icons/Plus.js'; + +export interface ChainAddMenuProps { + chainMetadata: ChainMap; + overrideChainMetadata?: ChainMap | undefined>; + onChangeOverrideMetadata: ( + overrides?: ChainMap | undefined>, + ) => void; + onClickBack?: () => void; +} + +export function ChainAddMenu(props: ChainAddMenuProps) { + return ( +
+
+
+
+ ); +} + +function Header({ onClickBack }: Pick) { + return ( +
+ {!!onClickBack && ( + +
+ + Back +
+
+ )} +

Add chain metadata

+

+ Add metadata for chains not yet included in the{' '} + + Hyperlane Canonical Registry + + . Note, this data will only be used locally in your own browser. It does + not affect the registry. +

+
+ ); +} + +function Form({ + chainMetadata, + overrideChainMetadata, + onChangeOverrideMetadata, + onClickBack, +}: ChainAddMenuProps) { + const [textInput, setTextInput] = useState(''); + const [error, setError] = useState(null); + + const onChangeInput = (e: React.ChangeEvent) => { + setTextInput(e.target.value); + setError(null); + }; + + const onClickAdd = () => { + const result = tryParseMetadataInput(textInput, chainMetadata); + if (result.success) { + onChangeOverrideMetadata({ + ...overrideChainMetadata, + [result.data.name]: result.data, + }); + setTextInput(''); + onClickBack?.(); + } else { + setError(`Invalid config: ${result.error}`); + } + }; + + return ( +
+
+ + {error &&
{error}
} + +
+ +
+ ); +} + +function tryParseMetadataInput( + input: string, + existingChainMetadata: ChainMap, +): Result { + const parsed = tryParseJsonOrYaml(input); + if (!parsed.success) return parsed; + + const result = ChainMetadataSchema.safeParse(parsed.data); + + if (!result.success) { + console.error('Error validating chain config', result.error); + const firstIssue = result.error.issues[0]; + return failure(`${firstIssue.path} => ${firstIssue.message}`); + } + + const newMetadata = result.data as ChainMetadata; + const multiProvider = new MultiProtocolProvider(existingChainMetadata); + + if (multiProvider.tryGetChainMetadata(newMetadata.name)) { + return failure('name is already in use by another chain'); + } + + if (multiProvider.tryGetChainMetadata(newMetadata.chainId)) { + return failure('chainId is already in use by another chain'); + } + + if ( + newMetadata.domainId && + multiProvider.tryGetChainMetadata(newMetadata.domainId) + ) { + return failure('domainId is already in use by another chain'); + } + + return success(newMetadata); +} + +const placeholderText = `# YAML data +--- +chainId: 11155111 +name: sepolia +displayName: Sepolia +protocol: ethereum +rpcUrls: + - http: https://foobar.com +blockExplorers: + - name: Sepolia Etherscan + family: etherscan + url: https://sepolia.etherscan.io + apiUrl: https://api-sepolia.etherscan.io/api + apiKey: '12345' +blocks: + confirmations: 1 + estimateBlockTime: 13 +`; diff --git a/typescript/widgets/src/chains/ChainDetailsMenu.tsx b/typescript/widgets/src/chains/ChainDetailsMenu.tsx new file mode 100644 index 000000000..7c175d0d8 --- /dev/null +++ b/typescript/widgets/src/chains/ChainDetailsMenu.tsx @@ -0,0 +1,505 @@ +import clsx from 'clsx'; +import React, { PropsWithChildren, useEffect, useMemo, useState } from 'react'; +import { stringify as yamlStringify } from 'yaml'; + +import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry'; +import { + ChainMetadata, + isValidChainMetadata, + mergeChainMetadata, +} from '@hyperlane-xyz/sdk'; +import { + Result, + failure, + isNullish, + isUrl, + objMerge, + objOmit, + success, + tryParseJsonOrYaml, +} from '@hyperlane-xyz/utils'; + +import { ColorPalette } from '../color.js'; +import { CopyButton } from '../components/CopyButton.js'; +import { IconButton } from '../components/IconButton.js'; +import { LinkButton } from '../components/LinkButton.js'; +import { TextInput } from '../components/TextInput.js'; +import { Tooltip } from '../components/Tooltip.js'; +import { BoxArrowIcon } from '../icons/BoxArrow.js'; +import { CheckmarkIcon } from '../icons/Checkmark.js'; +import { ChevronIcon } from '../icons/Chevron.js'; +import { Circle } from '../icons/Circle.js'; +import { PlusCircleIcon } from '../icons/PlusCircle.js'; +import { SpinnerIcon } from '../icons/Spinner.js'; +import { XIcon } from '../icons/X.js'; +import { useConnectionHealthTest } from '../utils/useChainConnectionTest.js'; + +import { ChainLogo } from './ChainLogo.js'; +import { ChainConnectionType } from './types.js'; + +export interface ChainDetailsMenuProps { + chainMetadata: ChainMetadata; + overrideChainMetadata?: Partial; + onChangeOverrideMetadata: (overrides?: Partial) => void; + onClickBack?: () => void; + onRemoveChain?: () => void; +} + +export function ChainDetailsMenu(props: ChainDetailsMenuProps) { + const mergedMetadata = useMemo( + () => + mergeChainMetadata( + props.chainMetadata || {}, + props.overrideChainMetadata || {}, + ), + [props], + ); + + return ( +
+ + + + + + +
+ ); +} + +function ChainHeader({ + chainMetadata, + onClickBack, +}: Pick) { + return ( +
+ {!!onClickBack && ( + +
+ + Back +
+
+ )} +
+ +

{`${chainMetadata.displayName} Metadata`}

+
+
+ ); +} + +function ButtonRow({ chainMetadata, onRemoveChain }: ChainDetailsMenuProps) { + const { name } = chainMetadata; + + const copyValue = useMemo( + () => yamlStringify(chainMetadata), + [chainMetadata], + ); + + return ( +
+ +
+ + Copy Metadata + +
+ {onRemoveChain && ( + + + Delete Chain + + )} +
+ ); +} + +function ChainRpcs(props: ChainDetailsMenuProps) { + return ( + + ); +} + +function ChainExplorers(props: ChainDetailsMenuProps) { + return ( + + ); +} + +function ConnectionsSection({ + chainMetadata, + overrideChainMetadata, + onChangeOverrideMetadata, + header, + type, + tooltip, +}: ChainDetailsMenuProps & { + header: string; + type: ChainConnectionType; + tooltip?: string; +}) { + const values = getConnectionValues(chainMetadata, type); + + return ( +
+ {header} + {values.map((_, i) => ( + + ))} + +
+ ); +} + +function AddConnectionButton({ + chainMetadata, + overrideChainMetadata, + onChangeOverrideMetadata, + type, +}: ChainDetailsMenuProps & { + type: ChainConnectionType; +}) { + const [isAdding, setIsAdding] = useState(false); + const [isInvalid, setIsInvalid] = useState(false); + const [url, setUrl] = useState(''); + + const onClickDismiss = () => { + setIsAdding(false); + setIsInvalid(false); + setUrl(''); + }; + + const onClickAdd = (e?: React.FormEvent) => { + e?.preventDefault(); + + const currentValues = getConnectionValues(chainMetadata, type); + const newValue = url?.trim(); + if (!newValue || !isUrl(newValue) || currentValues.includes(newValue)) { + setIsInvalid(true); + return; + } + let newOverrides: Partial = {}; + if (type === ChainConnectionType.RPC) { + newOverrides = { + rpcUrls: [{ http: newValue }], + }; + } else if (type === ChainConnectionType.Explorer) { + const hostName = new URL(newValue).hostname; + newOverrides = { + blockExplorers: [{ url: newValue, apiUrl: newValue, name: hostName }], + }; + } + onChangeOverrideMetadata( + objMerge>( + overrideChainMetadata || {}, + newOverrides, + 10, + true, + ), + ); + onClickDismiss(); + }; + + if (!isAdding) { + return ( + setIsAdding(true)}> + +
{`Add new ${type}`}
+
+ ); + } + + return ( + onClickAdd(e)} + > + +
+ + onClickAdd()} + className="htw-bg-gray-600 htw-rounded-sm htw-px-1" + > + + + + + +
+ + ); +} + +function ChainInfo({ chainMetadata }: { chainMetadata: ChainMetadata }) { + const { chainId, domainId, deployer, isTestnet } = chainMetadata; + + return ( +
+ Chain Information +
+
+ Chain Id + {chainId} +
+
+ Domain Id + {domainId} +
+
+ + Contract Deployer + + + {deployer?.name || 'Unknown'} + +
+
+ Chain Type + + {isTestnet ? 'Testnet' : 'Mainnet'} + +
+
+
+ ); +} + +function MetadataOverride({ + chainMetadata, + overrideChainMetadata, + onChangeOverrideMetadata, +}: ChainDetailsMenuProps) { + const stringified = overrideChainMetadata + ? yamlStringify(overrideChainMetadata) + : ''; + const [overrideInput, setOverrideInput] = useState(stringified); + const showButton = overrideInput !== stringified; + const [isInvalid, setIsInvalid] = useState(false); + + // Keep input in sync with external changes + useEffect(() => { + setOverrideInput(stringified); + }, [stringified]); + + const onChangeInput = (e: React.ChangeEvent) => { + setOverrideInput(e.target.value); + setIsInvalid(false); + }; + + const onClickSetOverride = () => { + const trimmed = overrideInput?.trim(); + if (!trimmed) { + onChangeOverrideMetadata(undefined); + return; + } + const result = tryParseInput(trimmed, chainMetadata); + if (result.success) { + onChangeOverrideMetadata(result.data); + setOverrideInput(trimmed); + setIsInvalid(false); + } else { + setIsInvalid(true); + } + }; + + return ( +
+ + Metadata Overrides + +
+ + + + +
+
+ ); +} + +function SectionHeader({ + children, + className, + tooltip, +}: PropsWithChildren<{ className?: string; tooltip?: string }>) { + return ( +
+

+ {children} +

+ {tooltip && } +
+ ); +} + +function ConnectionRow({ + chainMetadata, + overrideChainMetadata = {}, + onChangeOverrideMetadata, + index, + type, +}: ChainDetailsMenuProps & { + index: number; + type: ChainConnectionType; +}) { + const isHealthy = useConnectionHealthTest(chainMetadata, index, type); + const value = getConnectionValues(chainMetadata, type)[index]; + const isRemovable = isOverrideConnection(overrideChainMetadata, type, value); + + const onClickRemove = () => { + let toOmit: Partial = {}; + if (type === ChainConnectionType.RPC) { + toOmit = { + rpcUrls: [ + overrideChainMetadata.rpcUrls!.find((r) => r.http === value)!, + ], + }; + } else if (type === ChainConnectionType.Explorer) { + toOmit = { + blockExplorers: [ + overrideChainMetadata.blockExplorers!.find((r) => r.url === value)!, + ], + }; + } + onChangeOverrideMetadata( + objOmit>(overrideChainMetadata, toOmit, 10, true), + ); + }; + + return ( +
+ {isNullish(isHealthy) && type == ChainConnectionType.RPC && ( + + )} + {isNullish(isHealthy) && type == ChainConnectionType.Explorer && ( + + )} + {!isNullish(isHealthy) && ( + + )} +
{value}
+ {isRemovable && ( + + + + )} +
+ ); +} + +function getConnectionValues( + chainMetadata: Partial, + type: ChainConnectionType, +) { + return ( + (type === ChainConnectionType.RPC + ? chainMetadata.rpcUrls?.map((r) => r.http) + : chainMetadata.blockExplorers?.map((b) => b.url)) || [] + ); +} + +function isOverrideConnection( + overrides: Partial | undefined, + type: ChainConnectionType, + value: string, +) { + return getConnectionValues(overrides || {}, type).includes(value); +} + +function tryParseInput( + input: string, + existingChainMetadata: ChainMetadata, +): Result> { + const parsed = tryParseJsonOrYaml>(input); + if (!parsed.success) return parsed; + const merged = mergeChainMetadata(existingChainMetadata, parsed.data); + const isValid = isValidChainMetadata(merged); + return isValid ? success(parsed.data) : failure('Invalid metadata overrides'); +} diff --git a/typescript/widgets/src/icons/ChainLogo.tsx b/typescript/widgets/src/chains/ChainLogo.tsx similarity index 74% rename from typescript/widgets/src/icons/ChainLogo.tsx rename to typescript/widgets/src/chains/ChainLogo.tsx index 6384477c8..082c8eaf3 100644 --- a/typescript/widgets/src/icons/ChainLogo.tsx +++ b/typescript/widgets/src/chains/ChainLogo.tsx @@ -2,8 +2,8 @@ import React, { ReactElement, useEffect, useState } from 'react'; import type { IRegistry } from '@hyperlane-xyz/registry'; -import { Circle } from './Circle.js'; -import { QuestionMarkIcon } from './QuestionMark.js'; +import { Circle } from '../icons/Circle.js'; +import { QuestionMarkIcon } from '../icons/QuestionMark.js'; type SvgIcon = (props: { width: number; @@ -13,7 +13,8 @@ type SvgIcon = (props: { export interface ChainLogoProps { chainName: string; - registry: IRegistry; + logoUri?: string; + registry?: IRegistry; size?: number; background?: boolean; Icon?: SvgIcon; // Optional override for the logo in the registry @@ -21,6 +22,7 @@ export interface ChainLogoProps { export function ChainLogo({ chainName, + logoUri, registry, size = 32, background = false, @@ -31,17 +33,18 @@ export function ChainLogo({ const iconSize = Math.floor(size / 1.9); const [svgLogos, setSvgLogos] = useState({}); - const logoUri = svgLogos[chainName]; + const uri = logoUri || svgLogos[chainName]; useEffect(() => { - if (!chainName || svgLogos[chainName] || Icon) return; + if (!chainName || svgLogos[chainName] || logoUri || Icon || !registry) + return; registry .getChainLogoUri(chainName) .then((uri) => uri && setSvgLogos({ ...svgLogos, [chainName]: uri })) .catch((err) => console.error(err)); }, [chainName, registry, svgLogos, Icon]); - if (!logoUri && !Icon) { + if (!uri && !Icon) { return ( {chainName ? ( @@ -55,11 +58,11 @@ export function ChainLogo({ if (background) { return ( - + {Icon ? ( ) : ( - {title} + {title} )} ); @@ -67,7 +70,7 @@ export function ChainLogo({ return Icon ? ( ) : ( - {title} + {title} ); } } diff --git a/typescript/widgets/src/chains/ChainSearchMenu.tsx b/typescript/widgets/src/chains/ChainSearchMenu.tsx new file mode 100644 index 000000000..bb3faf7f1 --- /dev/null +++ b/typescript/widgets/src/chains/ChainSearchMenu.tsx @@ -0,0 +1,322 @@ +import React, { useCallback, useMemo } from 'react'; + +import { + ChainMap, + ChainMetadata, + ChainName, + mergeChainMetadataMap, +} from '@hyperlane-xyz/sdk'; +import { ProtocolType } from '@hyperlane-xyz/utils'; + +import { + SearchMenu, + SortOrderOption, + SortState, +} from '../components/SearchMenu.js'; +import { SegmentedControl } from '../components/SegmentedControl.js'; + +import { ChainAddMenu } from './ChainAddMenu.js'; +import { ChainDetailsMenu } from './ChainDetailsMenu.js'; +import { ChainLogo } from './ChainLogo.js'; + +enum ChainSortByOption { + Name = 'name', + ChainId = 'chain id', + Protocol = 'protocol', +} + +enum FilterTestnetOption { + Testnet = 'testnet', + Mainnet = 'mainnet', +} + +interface ChainFilterState { + type?: FilterTestnetOption; + protocol?: ProtocolType; +} + +const defaultFilterState: ChainFilterState = { + type: undefined, + protocol: undefined, +}; + +interface CustomListItemField { + header: string; + data: ChainMap<{ display: string; sortValue: number }>; +} + +export interface ChainSearchMenuProps { + chainMetadata: ChainMap; + overrideChainMetadata?: ChainMap | undefined>; + onChangeOverrideMetadata: ( + overrides?: ChainMap | undefined>, + ) => void; + onClickChain: (chain: ChainMetadata) => void; + // Replace the default 2nd column (deployer) with custom data + customListItemField?: CustomListItemField; + // Auto-navigate to a chain details menu + showChainDetails?: ChainName; + // Auto-navigate to a chain add menu + showAddChainMenu?: boolean; + // Include add button above list + showAddChainButton?: boolean; +} + +export function ChainSearchMenu({ + chainMetadata, + onChangeOverrideMetadata, + overrideChainMetadata, + onClickChain, + customListItemField, + showChainDetails, + showAddChainButton, + showAddChainMenu, +}: ChainSearchMenuProps) { + const [drilldownChain, setDrilldownChain] = React.useState< + ChainName | undefined + >(showChainDetails); + + const [addChain, setAddChain] = React.useState(showAddChainMenu || false); + + const { listData, mergedMetadata } = useMemo(() => { + const mergedMetadata = mergeChainMetadataMap( + chainMetadata, + overrideChainMetadata, + ); + return { mergedMetadata, listData: Object.values(mergedMetadata) }; + }, [chainMetadata]); + + const { ListComponent, searchFn, sortOptions, defaultSortState } = + useCustomizedListItems(customListItemField); + + if (drilldownChain && mergedMetadata[drilldownChain]) { + const isLocalOverrideChain = !chainMetadata[drilldownChain]; + const onRemoveChain = () => { + const newOverrides = { ...overrideChainMetadata }; + delete newOverrides[drilldownChain]; + onChangeOverrideMetadata(newOverrides); + }; + + return ( + + onChangeOverrideMetadata({ + ...overrideChainMetadata, + [drilldownChain]: o, + }) + } + onClickBack={() => setDrilldownChain(undefined)} + onRemoveChain={isLocalOverrideChain ? onRemoveChain : undefined} + /> + ); + } + + if (addChain) { + return ( + setAddChain(false)} + /> + ); + } + + return ( + , + ChainSortByOption, + ChainFilterState + > + data={listData} + ListComponent={ListComponent} + searchFn={searchFn} + onClickItem={onClickChain} + onClickEditItem={(chain) => setDrilldownChain(chain.name)} + sortOptions={sortOptions} + defaultSortState={defaultSortState} + FilterComponent={ChainFilters} + defaultFilterState={defaultFilterState} + placeholder="Chain Name or ID" + onClickAddItem={showAddChainButton ? () => setAddChain(true) : undefined} + /> + ); +} + +function ChainListItem({ + data: chain, + customField, +}: { + data: ChainMetadata; + customField?: CustomListItemField; +}) { + return ( + <> +
+
+ +
+
+
+ {chain.displayName} +
+
+ {chain.isTestnet ? 'Testnet' : 'Mainnet'} +
+
+
+
+
+ {customField + ? customField.data[chain.name].display || 'Unknown' + : chain.deployer?.name || 'Unknown deployer'} +
+
+ {customField ? customField.header : 'Deployer'} +
+
+ + ); +} + +function ChainFilters({ + value, + onChange, +}: { + value: ChainFilterState; + onChange: (s: ChainFilterState) => void; +}) { + return ( +
+
+ + onChange({ ...value, type: selected })} + allowEmpty + /> +
+
+ + onChange({ ...value, protocol: selected })} + allowEmpty + /> +
+
+ ); +} + +function chainSearch({ + data, + query, + sort, + filter, + customListItemField, +}: { + data: ChainMetadata[]; + query: string; + sort: SortState; + filter: ChainFilterState; + customListItemField?: CustomListItemField; +}) { + const queryFormatted = query.trim().toLowerCase(); + return ( + data + // Query search + .filter( + (chain) => + chain.name.includes(queryFormatted) || + chain.displayName?.toLowerCase().includes(queryFormatted) || + chain.chainId.toString().includes(queryFormatted) || + chain.domainId?.toString().includes(queryFormatted), + ) + // Filter options + .filter((chain) => { + let included = true; + if (filter.type) { + included &&= + !!chain.isTestnet === (filter.type === FilterTestnetOption.Testnet); + } + if (filter.protocol) { + included &&= chain.protocol === filter.protocol; + } + return included; + }) + // Sort options + .sort((c1, c2) => { + // Special case handling for if the chains are being sorted by the + // custom field provided to ChainSearchMenu + if (customListItemField && sort.sortBy === customListItemField.header) { + const result = + customListItemField.data[c1.name].sortValue - + customListItemField.data[c2.name].sortValue; + return sort.sortOrder === SortOrderOption.Asc ? result : -result; + } + + // Otherwise sort by the default options + let sortValue1 = c1.name; + let sortValue2 = c2.name; + if (sort.sortBy === ChainSortByOption.ChainId) { + sortValue1 = c1.chainId.toString(); + sortValue2 = c2.chainId.toString(); + } else if (sort.sortBy === ChainSortByOption.Protocol) { + sortValue1 = c1.protocol; + sortValue2 = c2.protocol; + } + return sort.sortOrder === SortOrderOption.Asc + ? sortValue1.localeCompare(sortValue2) + : sortValue2.localeCompare(sortValue1); + }) + ); +} + +/** + * This hook creates closures around the provided customListItemField data + * This is useful because SearchMenu will do handle the list item rendering and + * management but the custom data is more or a chain-search-specific concern + */ +function useCustomizedListItems(customListItemField) { + // Create closure of ChainListItem but with customField pre-bound + const ListComponent = useCallback( + ({ data }: { data: ChainMetadata<{ disabled?: boolean }> }) => ( + + ), + [ChainListItem, customListItemField], + ); + + // Bind the custom field to the search function + const searchFn = useCallback( + (args: Parameters[0]) => + chainSearch({ ...args, customListItemField }), + [customListItemField], + ); + + // Merge the custom field into the sort options if a custom field exists + const sortOptions = useMemo( + () => [ + ...(customListItemField ? [customListItemField.header] : []), + ...Object.values(ChainSortByOption), + ], + [customListItemField], + ) as ChainSortByOption[]; + + // Sort by the custom field by default, if one is provided + const defaultSortState = useMemo( + () => + customListItemField + ? { + sortBy: customListItemField.header, + sortOrder: SortOrderOption.Desc, + } + : undefined, + [customListItemField], + ) as SortState | undefined; + + return { ListComponent, searchFn, sortOptions, defaultSortState }; +} diff --git a/typescript/widgets/src/chains/types.ts b/typescript/widgets/src/chains/types.ts new file mode 100644 index 000000000..bf00bb9de --- /dev/null +++ b/typescript/widgets/src/chains/types.ts @@ -0,0 +1,4 @@ +export enum ChainConnectionType { + RPC = 'rpc', + Explorer = 'explorer', +} diff --git a/typescript/widgets/src/color.ts b/typescript/widgets/src/color.ts index f3c3c3be2..273572c71 100644 --- a/typescript/widgets/src/color.ts +++ b/typescript/widgets/src/color.ts @@ -1,10 +1,11 @@ export enum ColorPalette { Black = '#010101', White = '#FFFFFF', - Blue = '#2362C0', + Blue = '#2764C1', DarkBlue = '#162A4A', LightBlue = '#82A8E4', Pink = '#CF2FB3', + LightGray = '#D3D4D7', Gray = '#6B7280', Beige = '#F1EDE9', Red = '#BF1B15', diff --git a/typescript/widgets/src/components/Button.tsx b/typescript/widgets/src/components/Button.tsx new file mode 100644 index 000000000..3177af05b --- /dev/null +++ b/typescript/widgets/src/components/Button.tsx @@ -0,0 +1,21 @@ +import clsx from 'clsx'; +import React, { ButtonHTMLAttributes, PropsWithChildren } from 'react'; + +type Props = PropsWithChildren>; + +export function Button(props: Props) { + const { className, children, ...rest } = props; + + const base = + 'htw-flex htw-items-center htw-justify-center htw-rounded-sm htw-transition-all'; + const onHover = 'hover:htw-opacity-80'; + const onDisabled = 'disabled:htw-opacity-30 disabled:htw-cursor-default'; + const onActive = 'active:htw-scale-95'; + const allClasses = clsx(base, onHover, onDisabled, onActive, className); + + return ( + + ); +} diff --git a/typescript/widgets/src/components/CopyButton.tsx b/typescript/widgets/src/components/CopyButton.tsx new file mode 100644 index 000000000..8db0b5ccb --- /dev/null +++ b/typescript/widgets/src/components/CopyButton.tsx @@ -0,0 +1,51 @@ +import React, { + ButtonHTMLAttributes, + PropsWithChildren, + useState, +} from 'react'; + +import { CheckmarkIcon } from '../icons/Checkmark.js'; +import { CopyIcon } from '../icons/Copy.js'; +import { tryClipboardSet } from '../utils/clipboard.js'; + +type Props = PropsWithChildren> & { + width?: number; + height?: number; + copyValue: string; +}; + +export function CopyButton({ + width, + height, + copyValue, + className, + children, + ...rest +}: Props) { + const [showCheckmark, setShowCheckmark] = useState(false); + + const onClick = async () => { + const result = await tryClipboardSet(copyValue); + if (result) { + setShowCheckmark(true); + setTimeout(() => setShowCheckmark(false), 2000); + } + }; + + return ( + + ); +} diff --git a/typescript/widgets/src/components/DatetimeField.tsx b/typescript/widgets/src/components/DatetimeField.tsx new file mode 100644 index 000000000..cc0406038 --- /dev/null +++ b/typescript/widgets/src/components/DatetimeField.tsx @@ -0,0 +1,36 @@ +import React, { ChangeEvent } from 'react'; + +interface Props { + className?: string; + timestamp: number | null; + onChange: (t: number | null) => void; + name?: string; +} + +export function DatetimeField({ className, timestamp, onChange, name }: Props) { + const handleChange = (e: ChangeEvent) => { + if (!e.target['validity'].valid) { + onChange(null); + } else { + const datetime = e.target['value'] + ':00Z'; + const newTimestamp = new Date(datetime).getTime(); + onChange(newTimestamp); + } + }; + + return ( + + ); +} + +function toShortIsoString(timestamp: number | null) { + if (!timestamp) return ''; + // Trim milliseconds and timezone to match input field format + return new Date(timestamp).toISOString().split('.')[0]; +} diff --git a/typescript/widgets/src/components/IconButton.tsx b/typescript/widgets/src/components/IconButton.tsx new file mode 100644 index 000000000..bc515ee7e --- /dev/null +++ b/typescript/widgets/src/components/IconButton.tsx @@ -0,0 +1,24 @@ +import clsx from 'clsx'; +import React, { ButtonHTMLAttributes, PropsWithChildren } from 'react'; + +type Props = PropsWithChildren> & { + width?: number; + height?: number; +}; + +export function IconButton(props: Props) { + const { className, children, ...rest } = props; + + const base = + 'htw-flex htw-items-center htw-justify-center htw-transition-all'; + const onHover = 'hover:htw-opacity-70 hover:htw-scale-105'; + const onDisabled = 'disabled:htw-opacity-30 disabled:htw-cursor-default'; + const onActive = 'active:htw-opacity-60'; + const allClasses = clsx(base, onHover, onDisabled, onActive, className); + + return ( + + ); +} diff --git a/typescript/widgets/src/components/LinkButton.tsx b/typescript/widgets/src/components/LinkButton.tsx new file mode 100644 index 000000000..026bf6ba1 --- /dev/null +++ b/typescript/widgets/src/components/LinkButton.tsx @@ -0,0 +1,21 @@ +import clsx from 'clsx'; +import React, { ButtonHTMLAttributes, PropsWithChildren } from 'react'; + +type Props = PropsWithChildren>; + +export function LinkButton(props: Props) { + const { className, children, ...rest } = props; + + const base = + 'htw-flex htw-items-center htw-justify-center htw-transition-all'; + const onHover = 'hover:htw-underline htw-underline-offset-2'; + const onDisabled = 'disabled:htw-opacity-30 disabled:htw-cursor-default'; + const onActive = 'active:htw-opacity-70'; + const allClasses = clsx(base, onHover, onDisabled, onActive, className); + + return ( + + ); +} diff --git a/typescript/widgets/src/components/SearchMenu.tsx b/typescript/widgets/src/components/SearchMenu.tsx new file mode 100644 index 000000000..74c900704 --- /dev/null +++ b/typescript/widgets/src/components/SearchMenu.tsx @@ -0,0 +1,344 @@ +import clsx from 'clsx'; +import React, { ComponentType, useMemo, useState } from 'react'; + +import { deepEquals, isObject, toTitleCase } from '@hyperlane-xyz/utils'; + +import { ColorPalette } from '../color.js'; +import { ArrowIcon } from '../icons/Arrow.js'; +import { PencilIcon } from '../icons/Pencil.js'; +import { PlusIcon } from '../icons/Plus.js'; +import { SearchIcon } from '../icons/Search.js'; +import { XIcon } from '../icons/X.js'; +import { DropdownMenu } from '../layout/DropdownMenu.js'; +import { Popover } from '../layout/Popover.js'; + +import { IconButton } from './IconButton.js'; +import { InputProps, TextInput } from './TextInput.js'; + +export interface SearchMenuProps< + ListItemData extends { disabled?: boolean }, + SortBy extends string, + FilterState, +> { + // The list of data items to show + data: ListItemData[]; + // The component with which the list items will be rendered + ListComponent: ComponentType<{ data: ListItemData }>; + // Handler for list item click event + onClickItem: (item: ListItemData) => void; + // Handler for edit list item click event + onClickEditItem: (item: ListItemData) => void; + // Handler for searching through list item data + searchFn: (args: { + data: ListItemData[]; + query: string; + sort: SortState; + filter: FilterState; + }) => ListItemData[]; + // List of sort options + sortOptions: SortBy[]; + // Default sort state for list data + defaultSortState?: SortState; + // The component with which the filter state will be rendered + FilterComponent: ComponentType<{ + value: FilterState; + onChange: (s: FilterState) => void; + }>; + // Default filter state for list data + defaultFilterState: FilterState; + // Placeholder text for the search input + placeholder?: string; + // Handler for add button click event + onClickAddItem?: () => void; +} + +export function SearchMenu< + ListItem extends { disabled?: boolean }, + SortBy extends string, + FilterState, +>({ + data, + ListComponent, + searchFn, + onClickItem, + onClickEditItem, + sortOptions, + defaultSortState, + FilterComponent, + defaultFilterState, + placeholder, + onClickAddItem, +}: SearchMenuProps) { + const [searchQuery, setSearchQuery] = useState(''); + const [isEditMode, setIsEditMode] = useState(false); + const [sortState, setSortState] = useState>( + defaultSortState || { + sortBy: sortOptions[0], + sortOrder: SortOrderOption.Asc, + }, + ); + const [filterState, setFilterState] = + useState(defaultFilterState); + + const results = useMemo( + () => + searchFn({ + data, + query: searchQuery, + sort: sortState, + filter: filterState, + }), + [data, searchQuery, sortState, filterState, searchFn], + ); + + return ( +
+ +
+
+ + +
+
+ setIsEditMode(!isEditMode)} + className="htw-p-1.5 htw-border htw-border-gray-200 htw-rounded-full" + title="Edit items" + > + + + {onClickAddItem && ( + + + + )} +
+
+
+ {results.length ? ( + results.map((data, i) => ( + + )) + ) : ( +
+ No results found +
+ )} +
+
+ ); +} + +function SearchBar(props: InputProps) { + return ( +
+ + +
+ ); +} + +function SortDropdown({ + options, + value, + onChange, +}: { + options: SortBy[]; + value: SortState; + onChange: (v: SortState) => void; +}) { + const onToggleOrder = () => { + onChange({ + ...value, + sortOrder: + value.sortOrder === SortOrderOption.Asc + ? SortOrderOption.Desc + : SortOrderOption.Asc, + }); + }; + + const onSetSortBy = (sortBy: SortBy) => { + onChange({ + ...value, + sortBy, + }); + }; + + return ( +
+
+ Sort +
+ + {toTitleCase(value.sortBy)} + + } + buttonClassname="htw-flex htw-items-stretch hover:htw-bg-gray-100 active:htw-scale-95" + menuClassname="htw-py-1.5 htw-px-2 htw-flex htw-flex-col htw-gap-2 htw-text-sm htw-border htw-border-gray-100" + menuItems={options.map((o) => ( +
onSetSortBy(o)} + > + {toTitleCase(o)} +
+ ))} + menuProps={{ anchor: 'bottom start' }} + /> + + + +
+ ); +} + +function FilterDropdown({ + value, + defaultValue, + onChange, + FilterComponent, +}: { + value: FilterState; + defaultValue: FilterState; + onChange: (v: FilterState) => void; + FilterComponent: ComponentType<{ + value: FilterState; + onChange: (s: FilterState) => void; + }>; +}) { + const filterValues = useMemo(() => { + if (!value || !isObject(value)) return []; + const modifiedKeys = Object.keys(value).filter( + (k) => !deepEquals(value[k], defaultValue[k]), + ); + return modifiedKeys.map((k) => value[k]); + }, [value]); + const hasFilters = filterValues.length > 0; + + const onClear = () => { + onChange(defaultValue); + }; + + return ( +
+
+ Filter +
+ + {hasFilters ? filterValues.map(toTitleCase).join(', ') : 'None'} + + } + buttonClassname="htw-h-full htw-flex htw-items-stretch hover:htw-bg-gray-100 active:htw-scale-95" + > + + + + + +
+ ); +} + +interface ListItemProps { + data: ListItemData; + isEditMode: boolean; + onClickItem: (item: ListItemData) => void; + onClickEditItem: (item: ListItemData) => void; + ListComponent: ComponentType<{ data: ListItemData }>; +} + +function ListItem({ + data, + isEditMode, + onClickEditItem, + onClickItem, + ListComponent, +}: ListItemProps) { + return ( + + ); +} + +export interface SortState { + sortBy: SortBy; + sortOrder: SortOrderOption; +} + +export enum SortOrderOption { + Asc = 'asc', + Desc = 'desc', +} diff --git a/typescript/widgets/src/components/SegmentedControl.tsx b/typescript/widgets/src/components/SegmentedControl.tsx new file mode 100644 index 000000000..71f0096a0 --- /dev/null +++ b/typescript/widgets/src/components/SegmentedControl.tsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; + +import { toTitleCase } from '@hyperlane-xyz/utils'; + +interface SegmentedControlProps { + options: O[]; + onChange: (selected: O | undefined) => void; + allowEmpty?: boolean; +} + +export function SegmentedControl({ + options, + onChange, + allowEmpty, +}: SegmentedControlProps) { + // State to keep track of the selected option index + const [selectedIndex, setSelectedIndex] = useState( + allowEmpty ? undefined : 0, + ); + + const handleSelect = (index: number) => { + // Unselect when the same option is re-clicked + if (selectedIndex === index && allowEmpty) { + setSelectedIndex(undefined); + onChange(undefined); + } else { + setSelectedIndex(index); + onChange(options[index]); + } + }; + + return ( +
+ {options.map((option, index) => ( + + ))} +
+ ); +} diff --git a/typescript/widgets/src/components/SelectField.tsx b/typescript/widgets/src/components/SelectField.tsx new file mode 100644 index 000000000..11ed0e1d0 --- /dev/null +++ b/typescript/widgets/src/components/SelectField.tsx @@ -0,0 +1,45 @@ +import React, { ChangeEvent } from 'react'; + +export type SelectOption = { + display: string; + value: string; +}; + +type Props = React.DetailedHTMLProps< + React.SelectHTMLAttributes, + HTMLSelectElement +> & { + options: Array; + value: string; + onValueSelect: (value: string) => void; + classes?: string; +}; + +export function SelectField({ + options, + value, + onValueSelect, + classes, + ...passThruProps +}: Props) { + const onChangeSelect = (event: ChangeEvent) => { + onValueSelect(event?.target?.value || ''); + }; + + return ( + + ); +} diff --git a/typescript/widgets/src/components/TextInput.tsx b/typescript/widgets/src/components/TextInput.tsx new file mode 100644 index 000000000..2ddff66cc --- /dev/null +++ b/typescript/widgets/src/components/TextInput.tsx @@ -0,0 +1,25 @@ +import React, { ChangeEvent, InputHTMLAttributes } from 'react'; + +export type InputProps = Omit< + InputHTMLAttributes, + 'onChange' +> & { + onChange?: (v: string) => void; + className?: string; +}; + +export function TextInput({ onChange, className, ...props }: InputProps) { + const handleChange = (e: ChangeEvent) => { + if (onChange) onChange(e?.target?.value || ''); + }; + + return ( + + ); +} diff --git a/typescript/widgets/src/components/Tooltip.tsx b/typescript/widgets/src/components/Tooltip.tsx new file mode 100644 index 000000000..24c591132 --- /dev/null +++ b/typescript/widgets/src/components/Tooltip.tsx @@ -0,0 +1,28 @@ +import React, { AnchorHTMLAttributes } from 'react'; +import { Tooltip as ReactTooltip } from 'react-tooltip'; + +import { Circle } from '../icons/Circle.js'; +import { QuestionMarkIcon } from '../icons/QuestionMark.js'; + +type Props = AnchorHTMLAttributes & { + id: string; + content: string; + size?: number; +}; + +export function Tooltip({ id, content, size = 16, ...rest }: Props) { + return ( + <> + + + + + + + + ); +} diff --git a/typescript/widgets/src/icons/Airplane.tsx b/typescript/widgets/src/icons/Airplane.tsx index c5e2d2782..b2bb4ec3f 100644 --- a/typescript/widgets/src/icons/Airplane.tsx +++ b/typescript/widgets/src/icons/Airplane.tsx @@ -2,23 +2,12 @@ import React, { memo } from 'react'; import { ColorPalette } from '../color.js'; -interface Props { - width?: string | number; - height?: string | number; - color?: string; - classes?: string; -} +import { DefaultIconProps } from './types.js'; // Paper airplane shape -function _AirplaneIcon({ width, height, color, classes }: Props) { +function _AirplaneIcon({ color, ...rest }: DefaultIconProps) { return ( - + + + + ); +} + +export const ArrowIcon = memo(_ArrowIcon); diff --git a/typescript/widgets/src/icons/BoxArrow.tsx b/typescript/widgets/src/icons/BoxArrow.tsx new file mode 100644 index 000000000..43ae2def2 --- /dev/null +++ b/typescript/widgets/src/icons/BoxArrow.tsx @@ -0,0 +1,24 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _BoxArrowIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + + ); +} + +export const BoxArrowIcon = memo(_BoxArrowIcon); diff --git a/typescript/widgets/src/icons/Checkmark.tsx b/typescript/widgets/src/icons/Checkmark.tsx new file mode 100644 index 000000000..d20c3619b --- /dev/null +++ b/typescript/widgets/src/icons/Checkmark.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _CheckmarkIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const CheckmarkIcon = memo(_CheckmarkIcon); diff --git a/typescript/widgets/src/icons/Chevron.tsx b/typescript/widgets/src/icons/Chevron.tsx new file mode 100644 index 000000000..7ca74727a --- /dev/null +++ b/typescript/widgets/src/icons/Chevron.tsx @@ -0,0 +1,46 @@ +import React, { SVGProps, memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +type Props = SVGProps & { + direction: 'n' | 'e' | 's' | 'w'; +}; + +function _ChevronIcon({ color, className, direction, ...rest }: Props) { + let directionClass; + switch (direction) { + case 'n': + directionClass = 'htw-rotate-180'; + break; + case 'e': + directionClass = '-htw-rotate-90'; + break; + case 's': + directionClass = ''; + break; + case 'w': + directionClass = 'htw-rotate-90'; + break; + default: + throw new Error(`Invalid direction ${direction}`); + } + + return ( + + + + ); +} + +export const ChevronIcon = memo(_ChevronIcon); diff --git a/typescript/widgets/src/icons/Circle.tsx b/typescript/widgets/src/icons/Circle.tsx index ef6bac364..6e847e12a 100644 --- a/typescript/widgets/src/icons/Circle.tsx +++ b/typescript/widgets/src/icons/Circle.tsx @@ -6,13 +6,13 @@ export function Circle({ size, title, bgColorSeed, - classes, + className, children, }: PropsWithChildren<{ size: string | number; title?: string; bgColorSeed?: number; - classes?: string; + className?: string; }>) { const bgColor = bgColorSeed === null || bgColorSeed == undefined @@ -21,7 +21,7 @@ export function Circle({ return (
{children} diff --git a/typescript/widgets/src/icons/Copy.tsx b/typescript/widgets/src/icons/Copy.tsx new file mode 100644 index 000000000..faf8bdbb0 --- /dev/null +++ b/typescript/widgets/src/icons/Copy.tsx @@ -0,0 +1,34 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _CopyIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + + ); +} + +export const CopyIcon = memo(_CopyIcon); diff --git a/typescript/widgets/src/icons/Discord.tsx b/typescript/widgets/src/icons/Discord.tsx new file mode 100644 index 000000000..28a3d93f1 --- /dev/null +++ b/typescript/widgets/src/icons/Discord.tsx @@ -0,0 +1,25 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _Discord({ color, ...rest }: DefaultIconProps) { + return ( + + + + + + + + + + + ); +} + +export const DiscordIcon = memo(_Discord); diff --git a/typescript/widgets/src/icons/Docs.tsx b/typescript/widgets/src/icons/Docs.tsx new file mode 100644 index 000000000..656c79c0e --- /dev/null +++ b/typescript/widgets/src/icons/Docs.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _DocsIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const DocsIcon = memo(_DocsIcon); diff --git a/typescript/widgets/src/icons/Envelope.tsx b/typescript/widgets/src/icons/Envelope.tsx index 777a6548e..1dd0198c6 100644 --- a/typescript/widgets/src/icons/Envelope.tsx +++ b/typescript/widgets/src/icons/Envelope.tsx @@ -2,23 +2,12 @@ import React, { memo } from 'react'; import { ColorPalette } from '../color.js'; -interface Props { - width?: string | number; - height?: string | number; - color?: string; - classes?: string; -} +import { DefaultIconProps } from './types.js'; // Envelope with checkmark -function _EnvelopeIcon({ width, height, color, classes }: Props) { +function _EnvelopeIcon({ color, ...rest }: DefaultIconProps) { return ( - + + + + ); +} + +export const FilterIcon = memo(_FilterIcon); diff --git a/typescript/widgets/src/icons/Funnel.tsx b/typescript/widgets/src/icons/Funnel.tsx new file mode 100644 index 000000000..d2bfc5b50 --- /dev/null +++ b/typescript/widgets/src/icons/Funnel.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _FunnelIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const FunnelIcon = memo(_FunnelIcon); diff --git a/typescript/widgets/src/icons/Gear.tsx b/typescript/widgets/src/icons/Gear.tsx new file mode 100644 index 000000000..044cc828c --- /dev/null +++ b/typescript/widgets/src/icons/Gear.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _GearIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const GearIcon = memo(_GearIcon); diff --git a/typescript/widgets/src/icons/Github.tsx b/typescript/widgets/src/icons/Github.tsx new file mode 100644 index 000000000..5d41670fb --- /dev/null +++ b/typescript/widgets/src/icons/Github.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _Github({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const GithubIcon = memo(_Github); diff --git a/typescript/widgets/src/icons/History.tsx b/typescript/widgets/src/icons/History.tsx new file mode 100644 index 000000000..f6e185296 --- /dev/null +++ b/typescript/widgets/src/icons/History.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _HistoryIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const HistoryIcon = memo(_HistoryIcon); diff --git a/typescript/widgets/src/icons/LinkedIn.tsx b/typescript/widgets/src/icons/LinkedIn.tsx new file mode 100644 index 000000000..88e042c6d --- /dev/null +++ b/typescript/widgets/src/icons/LinkedIn.tsx @@ -0,0 +1,16 @@ +import React, { memo } from 'react'; + +import { DefaultIconProps } from './types.js'; + +function _Linkedin({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const LinkedInIcon = memo(_Linkedin); diff --git a/typescript/widgets/src/icons/Lock.tsx b/typescript/widgets/src/icons/Lock.tsx index da55c978f..f160712a1 100644 --- a/typescript/widgets/src/icons/Lock.tsx +++ b/typescript/widgets/src/icons/Lock.tsx @@ -2,22 +2,11 @@ import React, { memo } from 'react'; import { ColorPalette } from '../color.js'; -interface Props { - width?: string | number; - height?: string | number; - color?: string; - classes?: string; -} +import { DefaultIconProps } from './types.js'; -function _LockIcon({ width, height, color, classes }: Props) { +function _LockIcon({ color, ...rest }: DefaultIconProps) { return ( - + + + + ); +} + +export const MediumIcon = memo(_Medium); diff --git a/typescript/widgets/src/icons/Pencil.tsx b/typescript/widgets/src/icons/Pencil.tsx new file mode 100644 index 000000000..3e968cffa --- /dev/null +++ b/typescript/widgets/src/icons/Pencil.tsx @@ -0,0 +1,23 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _PencilIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + + ); +} + +export const PencilIcon = memo(_PencilIcon); diff --git a/typescript/widgets/src/icons/Plus.tsx b/typescript/widgets/src/icons/Plus.tsx new file mode 100644 index 000000000..2e9643020 --- /dev/null +++ b/typescript/widgets/src/icons/Plus.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _PlusIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const PlusIcon = memo(_PlusIcon); diff --git a/typescript/widgets/src/icons/PlusCircle.tsx b/typescript/widgets/src/icons/PlusCircle.tsx new file mode 100644 index 000000000..1fa0e10c0 --- /dev/null +++ b/typescript/widgets/src/icons/PlusCircle.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _PlusCircleIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const PlusCircleIcon = memo(_PlusCircleIcon); diff --git a/typescript/widgets/src/icons/QuestionMark.tsx b/typescript/widgets/src/icons/QuestionMark.tsx index 57cf8ef18..4d987746a 100644 --- a/typescript/widgets/src/icons/QuestionMark.tsx +++ b/typescript/widgets/src/icons/QuestionMark.tsx @@ -2,24 +2,13 @@ import React, { memo } from 'react'; import { ColorPalette } from '../color.js'; -interface Props { - width?: string | number; - height?: string | number; - color?: string; - classes?: string; -} +import { DefaultIconProps } from './types.js'; -function _QuestionMarkIcon({ width, height, color, classes }: Props) { +function _QuestionMarkIcon({ color, ...rest }: DefaultIconProps) { return ( - + diff --git a/typescript/widgets/src/icons/Search.tsx b/typescript/widgets/src/icons/Search.tsx new file mode 100644 index 000000000..5fa4bdb8a --- /dev/null +++ b/typescript/widgets/src/icons/Search.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _SearchIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const SearchIcon = memo(_SearchIcon); diff --git a/typescript/widgets/src/icons/Shield.tsx b/typescript/widgets/src/icons/Shield.tsx index 9e97a02de..168593d41 100644 --- a/typescript/widgets/src/icons/Shield.tsx +++ b/typescript/widgets/src/icons/Shield.tsx @@ -2,23 +2,12 @@ import React, { memo } from 'react'; import { ColorPalette } from '../color.js'; -interface Props { - width?: string | number; - height?: string | number; - color?: string; - classes?: string; -} +import { DefaultIconProps } from './types.js'; // Shield with checkmark -function _ShieldIcon({ width, height, color, classes }: Props) { +function _ShieldIcon({ color, ...rest }: DefaultIconProps) { return ( - + + + + + ); +} + +export const SpinnerIcon = memo(_Spinner); diff --git a/typescript/widgets/src/icons/Twitter.tsx b/typescript/widgets/src/icons/Twitter.tsx new file mode 100644 index 000000000..feffaa495 --- /dev/null +++ b/typescript/widgets/src/icons/Twitter.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _Twitter({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const TwitterIcon = memo(_Twitter); diff --git a/typescript/widgets/src/icons/UpDownArrows.tsx b/typescript/widgets/src/icons/UpDownArrows.tsx new file mode 100644 index 000000000..6a84eb2ff --- /dev/null +++ b/typescript/widgets/src/icons/UpDownArrows.tsx @@ -0,0 +1,19 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _UpDownArrowsIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const UpDownArrowsIcon = memo(_UpDownArrowsIcon); diff --git a/typescript/widgets/src/icons/Wallet.tsx b/typescript/widgets/src/icons/Wallet.tsx new file mode 100644 index 000000000..7216791cc --- /dev/null +++ b/typescript/widgets/src/icons/Wallet.tsx @@ -0,0 +1,18 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _WalletIcon({ color, ...rest }: DefaultIconProps) { + return ( + + + + ); +} + +export const WalletIcon = memo(_WalletIcon); diff --git a/typescript/widgets/src/icons/Web.tsx b/typescript/widgets/src/icons/Web.tsx new file mode 100644 index 000000000..8915ed026 --- /dev/null +++ b/typescript/widgets/src/icons/Web.tsx @@ -0,0 +1,68 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; + +import { DefaultIconProps } from './types.js'; + +function _Web({ color = ColorPalette.Black, ...rest }: DefaultIconProps) { + return ( + + + + + + + + + + + + + + + + ); +} + +export const WebIcon = memo(_Web); diff --git a/typescript/widgets/src/icons/WideChevron.tsx b/typescript/widgets/src/icons/WideChevron.tsx index 8cb91412d..21d15238b 100644 --- a/typescript/widgets/src/icons/WideChevron.tsx +++ b/typescript/widgets/src/icons/WideChevron.tsx @@ -2,14 +2,12 @@ import React, { memo } from 'react'; import { ColorPalette } from '../color.js'; -export interface WideChevronProps { - width?: string | number; - height?: string | number; +import { DefaultIconProps } from './types.js'; + +export type WideChevronProps = DefaultIconProps & { direction: 'n' | 'e' | 's' | 'w'; - color?: string; rounded?: boolean; - classes?: string; -} +}; function _WideChevron({ width, @@ -17,7 +15,8 @@ function _WideChevron({ direction, color, rounded, - classes, + className, + ...rest }: WideChevronProps) { let directionClass; switch (direction) { @@ -45,7 +44,8 @@ function _WideChevron({ width={width} height={height} fill={color || ColorPalette.Blue} - className={`${directionClass} ${classes}`} + className={`${directionClass} ${className}`} + {...rest} > @@ -57,7 +57,8 @@ function _WideChevron({ viewBox="0 0 28 27" width={width} height={height} - className={`${directionClass} ${classes}`} + className={`${directionClass} ${className}`} + {...rest} > + + + ); +} + +export const XIcon = memo(_XIcon); diff --git a/typescript/widgets/src/icons/types.ts b/typescript/widgets/src/icons/types.ts new file mode 100644 index 000000000..6e4756c51 --- /dev/null +++ b/typescript/widgets/src/icons/types.ts @@ -0,0 +1,5 @@ +import { SVGProps } from 'react'; + +export type DefaultIconProps = SVGProps & { + color?: string; +}; diff --git a/typescript/widgets/src/index.ts b/typescript/widgets/src/index.ts index 43fe2b7f6..2bb43315c 100644 --- a/typescript/widgets/src/index.ts +++ b/typescript/widgets/src/index.ts @@ -1,8 +1,60 @@ +export { Fade } from './animations/Fade.js'; +export { + ChainDetailsMenu, + type ChainDetailsMenuProps, +} from './chains/ChainDetailsMenu.js'; +export { ChainLogo } from './chains/ChainLogo.js'; +export { + ChainSearchMenu, + type ChainSearchMenuProps, +} from './chains/ChainSearchMenu.js'; export { ColorPalette, seedToBgColor } from './color.js'; -export * from './consts.js'; -export { ChainLogo } from './icons/ChainLogo.js'; +export { Button } from './components/Button.js'; +export { CopyButton } from './components/CopyButton.js'; +export { DatetimeField } from './components/DatetimeField.js'; +export { IconButton } from './components/IconButton.js'; +export { LinkButton } from './components/LinkButton.js'; +export { SegmentedControl } from './components/SegmentedControl.js'; +export { SelectField, type SelectOption } from './components/SelectField.js'; +export { TextInput } from './components/TextInput.js'; +export { Tooltip } from './components/Tooltip.js'; +export { HYPERLANE_EXPLORER_API_URL } from './consts.js'; +export { AirplaneIcon } from './icons/Airplane.js'; +export { ArrowIcon } from './icons/Arrow.js'; +export { BoxArrowIcon } from './icons/BoxArrow.js'; +export { CheckmarkIcon } from './icons/Checkmark.js'; +export { ChevronIcon } from './icons/Chevron.js'; export { Circle } from './icons/Circle.js'; -export { WideChevron } from './icons/WideChevron.js'; +export { CopyIcon } from './icons/Copy.js'; +export { DiscordIcon } from './icons/Discord.js'; +export { DocsIcon } from './icons/Docs.js'; +export { EnvelopeIcon } from './icons/Envelope.js'; +export { FilterIcon } from './icons/Filter.js'; +export { FunnelIcon } from './icons/Funnel.js'; +export { GearIcon } from './icons/Gear.js'; +export { GithubIcon } from './icons/Github.js'; +export { HistoryIcon } from './icons/History.js'; +export { LinkedInIcon } from './icons/LinkedIn.js'; +export { LockIcon } from './icons/Lock.js'; +export { MediumIcon } from './icons/Medium.js'; +export { PencilIcon } from './icons/Pencil.js'; +export { PlusIcon } from './icons/Plus.js'; +export { PlusCircleIcon } from './icons/PlusCircle.js'; +export { QuestionMarkIcon } from './icons/QuestionMark.js'; +export { SearchIcon } from './icons/Search.js'; +export { ShieldIcon } from './icons/Shield.js'; +export { SpinnerIcon } from './icons/Spinner.js'; +export { TwitterIcon } from './icons/Twitter.js'; +export { UpDownArrowsIcon } from './icons/UpDownArrows.js'; +export { WalletIcon } from './icons/Wallet.js'; +export { WebIcon } from './icons/Web.js'; +export { WideChevronIcon } from './icons/WideChevron.js'; +export { XIcon } from './icons/X.js'; +export { type DefaultIconProps } from './icons/types.js'; +export { DropdownMenu, type DropdownMenuProps } from './layout/DropdownMenu.js'; +export { Modal, useModal, type ModalProps } from './layout/Modal.js'; +export { Popover, type PopoverProps } from './layout/Popover.js'; +export { HyperlaneLogo } from './logos/Hyperlane.js'; export { MessageTimeline } from './messages/MessageTimeline.js'; export { MessageStage, @@ -13,3 +65,9 @@ export { export { useMessage } from './messages/useMessage.js'; export { useMessageStage } from './messages/useMessageStage.js'; export { useMessageTimeline } from './messages/useMessageTimeline.js'; +export { + isClipboardReadSupported, + tryClipboardGet, + tryClipboardSet, +} from './utils/clipboard.js'; +export { useConnectionHealthTest } from './utils/useChainConnectionTest.js'; diff --git a/typescript/widgets/src/layout/DropdownMenu.tsx b/typescript/widgets/src/layout/DropdownMenu.tsx new file mode 100644 index 000000000..837a219e7 --- /dev/null +++ b/typescript/widgets/src/layout/DropdownMenu.tsx @@ -0,0 +1,45 @@ +import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'; +import clsx from 'clsx'; +import React, { ComponentProps, ReactNode } from 'react'; + +export type DropdownMenuProps = { + button: ReactNode; + buttonClassname?: string; + buttonProps?: ComponentProps; + menuClassname?: string; + menuProps?: ComponentProps; + menuItems: Array['children']>; +}; + +export function DropdownMenu({ + button, + buttonClassname, + buttonProps, + menuClassname, + menuProps, + menuItems, +}: DropdownMenuProps) { + return ( + + + {button} + + + {menuItems.map((mi, i) => ( + {mi} + ))} + + + ); +} diff --git a/typescript/widgets/src/layout/Modal.tsx b/typescript/widgets/src/layout/Modal.tsx new file mode 100644 index 000000000..87c36e831 --- /dev/null +++ b/typescript/widgets/src/layout/Modal.tsx @@ -0,0 +1,74 @@ +import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react'; +import clsx from 'clsx'; +import React, { ComponentProps, PropsWithChildren, useState } from 'react'; + +import { IconButton } from '../components/IconButton.js'; +import { XIcon } from '../icons/X.js'; + +export function useModal() { + const [isOpen, setIsOpen] = useState(false); + const open = () => setIsOpen(true); + const close = () => setIsOpen(false); + return { isOpen, open, close }; +} + +export type ModalProps = PropsWithChildren<{ + isOpen: boolean; + close: () => void; + dialogClassname?: string; + dialogProps?: ComponentProps; + panelClassname?: string; + panelProps?: ComponentProps; + showCloseButton?: boolean; +}>; + +export function Modal({ + isOpen, + close, + dialogClassname, + dialogProps, + panelClassname, + panelProps, + showCloseButton, + children, +}: ModalProps) { + return ( + + +
+
+ + {children} + {showCloseButton && ( +
+ + + +
+ )} +
+
+
+
+ ); +} diff --git a/typescript/widgets/src/layout/Popover.tsx b/typescript/widgets/src/layout/Popover.tsx new file mode 100644 index 000000000..34e7793fb --- /dev/null +++ b/typescript/widgets/src/layout/Popover.tsx @@ -0,0 +1,47 @@ +import { + PopoverButton, + PopoverPanel, + Popover as _Popover, +} from '@headlessui/react'; +import clsx from 'clsx'; +import React, { ComponentProps, ReactNode } from 'react'; + +export type PopoverProps = { + button: ReactNode; + buttonClassname?: string; + buttonProps?: ComponentProps; + panelClassname?: string; + panelProps?: ComponentProps; + children: ComponentProps['children']; +}; + +export function Popover({ + button, + buttonClassname, + buttonProps, + panelClassname, + panelProps, + children, +}: PopoverProps) { + return ( + <_Popover> + + {button} + + + {children} + + + ); +} diff --git a/typescript/widgets/src/logos/Hyperlane.tsx b/typescript/widgets/src/logos/Hyperlane.tsx new file mode 100644 index 000000000..4fc430315 --- /dev/null +++ b/typescript/widgets/src/logos/Hyperlane.tsx @@ -0,0 +1,25 @@ +import React, { memo } from 'react'; + +import { ColorPalette } from '../color.js'; +import { DefaultIconProps } from '../icons/types.js'; + +function _HyperlaneLogo({ color, ...rest }: DefaultIconProps) { + return ( + + + + + + ); +} + +export const HyperlaneLogo = memo(_HyperlaneLogo); diff --git a/typescript/widgets/src/messages/MessageTimeline.tsx b/typescript/widgets/src/messages/MessageTimeline.tsx index 3bf153ca5..74a50b976 100644 --- a/typescript/widgets/src/messages/MessageTimeline.tsx +++ b/typescript/widgets/src/messages/MessageTimeline.tsx @@ -5,7 +5,7 @@ import { AirplaneIcon } from '../icons/Airplane.js'; import { EnvelopeIcon } from '../icons/Envelope.js'; import { LockIcon } from '../icons/Lock.js'; import { ShieldIcon } from '../icons/Shield.js'; -import { WideChevron } from '../icons/WideChevron.js'; +import { WideChevronIcon } from '../icons/WideChevron.js'; import { MessageStatus, MessageStage as Stage, StageTimings } from './types.js'; @@ -157,7 +157,12 @@ function StageIcon({ Icon, size }: { Icon: any; size?: number }) { function ChevronWhite() { return (
- +
); } @@ -165,7 +170,7 @@ function ChevronWhite() { function ChevronBlue() { return (
- +
); } diff --git a/typescript/widgets/src/messages/useMessageStage.ts b/typescript/widgets/src/messages/useMessageStage.ts index 2910a6667..54345d4a9 100644 --- a/typescript/widgets/src/messages/useMessageStage.ts +++ b/typescript/widgets/src/messages/useMessageStage.ts @@ -1,10 +1,10 @@ import { useCallback, useState } from 'react'; import type { MultiProvider } from '@hyperlane-xyz/sdk'; +import { fetchWithTimeout } from '@hyperlane-xyz/utils'; import { HYPERLANE_EXPLORER_API_URL } from '../consts.js'; import { queryExplorerForBlock } from '../utils/explorers.js'; -import { fetchWithTimeout } from '../utils/timeout.js'; import { useInterval } from '../utils/useInterval.js'; import { diff --git a/typescript/widgets/src/stories/ChainDetailsMenu.stories.tsx b/typescript/widgets/src/stories/ChainDetailsMenu.stories.tsx new file mode 100644 index 000000000..37bac19be --- /dev/null +++ b/typescript/widgets/src/stories/ChainDetailsMenu.stories.tsx @@ -0,0 +1,42 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { chainMetadata } from '@hyperlane-xyz/registry'; + +import { ChainDetailsMenu } from '../chains/ChainDetailsMenu.js'; + +const meta = { + title: 'ChainDetailsMenu', + component: ChainDetailsMenu, +} satisfies Meta; +export default meta; +type Story = StoryObj; + +export const DefaultChainDetails = { + args: { + chainMetadata: chainMetadata['ethereum'], + overrideChainMetadata: undefined, + onChangeOverrideMetadata: () => {}, + onClickBack: undefined, + onRemoveChain: undefined, + }, +} satisfies Story; + +export const PartialOverrideChainDetails = { + args: { + chainMetadata: chainMetadata['ethereum'], + overrideChainMetadata: { rpcUrls: [{ http: 'https://rpc.fakeasdf.com' }] }, + onChangeOverrideMetadata: () => {}, + onClickBack: undefined, + onRemoveChain: undefined, + }, +} satisfies Story; + +export const FullOverrideChainDetails = { + args: { + chainMetadata: chainMetadata['arbitrum'], + overrideChainMetadata: chainMetadata['arbitrum'], + onChangeOverrideMetadata: () => {}, + onClickBack: () => {}, + onRemoveChain: () => {}, + }, +} satisfies Story; diff --git a/typescript/widgets/src/stories/ChainLogo.stories.tsx b/typescript/widgets/src/stories/ChainLogo.stories.tsx index 190db6ec3..312103cbf 100644 --- a/typescript/widgets/src/stories/ChainLogo.stories.tsx +++ b/typescript/widgets/src/stories/ChainLogo.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { GithubRegistry } from '@hyperlane-xyz/registry'; -import { ChainLogo } from '../icons/ChainLogo.js'; +import { ChainLogo } from '../chains/ChainLogo.js'; export default { title: 'ChainLogo', @@ -15,6 +15,7 @@ const Template: ComponentStory = (args) => ( ); const registry = new GithubRegistry(); +await registry.getMetadata(); export const ChainNoBackground = Template.bind({}); ChainNoBackground.args = { @@ -56,3 +57,11 @@ FakeChainName.args = { chainName: 'myfakechain', registry, }; + +export const SpecificLogoUri = Template.bind({}); +SpecificLogoUri.args = { + chainName: 'myfakechain', + logoUri: + 'https://raw.githubusercontent.com/hyperlane-xyz/hyperlane-registry/main/chains/arbitrum/logo.svg', + registry, +}; diff --git a/typescript/widgets/src/stories/ChainSearchMenu.stories.tsx b/typescript/widgets/src/stories/ChainSearchMenu.stories.tsx new file mode 100644 index 000000000..0d31f1c6c --- /dev/null +++ b/typescript/widgets/src/stories/ChainSearchMenu.stories.tsx @@ -0,0 +1,48 @@ +import { Meta, StoryObj } from '@storybook/react'; + +import { chainMetadata } from '@hyperlane-xyz/registry'; +import { pick } from '@hyperlane-xyz/utils'; + +import { ChainSearchMenu } from '../chains/ChainSearchMenu.js'; + +const meta = { + title: 'ChainSearchMenu', + component: ChainSearchMenu, +} satisfies Meta; +export default meta; +type Story = StoryObj; + +export const DefaultChainSearch = { + args: { + chainMetadata, + onChangeOverrideMetadata: () => {}, + onClickChain: (chain) => console.log('Clicked', chain), + }, +} satisfies Story; + +export const WithCustomField = { + args: { + chainMetadata: pick(chainMetadata, ['alfajores', 'arbitrum', 'ethereum']), + onChangeOverrideMetadata: () => {}, + customListItemField: { + header: 'Warp Routes', + data: { + alfajores: { display: '1 token', sortValue: 1 }, + arbitrum: { display: '2 tokens', sortValue: 2 }, + ethereum: { display: '1 token', sortValue: 1 }, + }, + }, + showAddChainButton: true, + }, +} satisfies Story; + +export const WithOverrideChain = { + args: { + chainMetadata: pick(chainMetadata, ['alfajores']), + overrideChainMetadata: { + arbitrum: { ...chainMetadata['arbitrum'], displayName: 'Fake Arb' }, + }, + onChangeOverrideMetadata: () => {}, + showAddChainButton: true, + }, +} satisfies Story; diff --git a/typescript/widgets/src/stories/Fade.stories.tsx b/typescript/widgets/src/stories/Fade.stories.tsx new file mode 100644 index 000000000..7c6a582b5 --- /dev/null +++ b/typescript/widgets/src/stories/Fade.stories.tsx @@ -0,0 +1,21 @@ +import { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +import { Fade } from '../animations/Fade'; + +function MyFadeAnimation({ show }: { show: boolean }) { + return ( + +
Hello Fade
+
+ ); +} + +const meta = { + title: 'Fade', + component: MyFadeAnimation, +} satisfies Meta; +export default meta; +type Story = StoryObj; + +export const BaseFadeAnimation = { args: { show: false } } satisfies Story; diff --git a/typescript/widgets/src/stories/IconList.stories.tsx b/typescript/widgets/src/stories/IconList.stories.tsx new file mode 100644 index 000000000..48570f106 --- /dev/null +++ b/typescript/widgets/src/stories/IconList.stories.tsx @@ -0,0 +1,102 @@ +import { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +import * as Hyperlane from '../index'; + +interface StoryIconProps { + width?: number; + height?: number; + color?: string; + direction?: 'n' | 'e' | 's' | 'w'; + rounded?: boolean; +} + +const iconList = Object.entries(Hyperlane) + .filter(([name]) => name.includes('Icon') && !name.includes('IconButton')) + .map(([_, Component]) => Component as React.ComponentType); + +function IconList({ + width, + height, + color, + direction, + bgColorSeed, + roundedWideChevron, +}: { + width: number; + height: number; + color: string; + direction: 'n' | 'e' | 's' | 'w'; + bgColorSeed: number | undefined; + roundedWideChevron: boolean; +}) { + return ( + <> +
+ {iconList.map((Icon) => ( + + {Icon.displayName} + + + ))} + + Circle + + +
+ + ); +} + +function IconContainer({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} + +const meta = { + title: 'Icon List', + component: IconList, + argTypes: { + direction: { + options: ['n', 'e', 's', 'w'], + control: { type: 'select' }, + }, + }, +} satisfies Meta; +export default meta; +type Story = StoryObj; + +export const DefaultIconList = { + args: { + width: 24, + height: 24, + color: Hyperlane.ColorPalette.Black, + direction: 's', + bgColorSeed: 0, + roundedWideChevron: false, + }, +} satisfies Story; diff --git a/typescript/widgets/src/stories/Modal.stories.tsx b/typescript/widgets/src/stories/Modal.stories.tsx new file mode 100644 index 000000000..ea667df31 --- /dev/null +++ b/typescript/widgets/src/stories/Modal.stories.tsx @@ -0,0 +1,36 @@ +import { Button } from '@headlessui/react'; +import { Meta, StoryObj } from '@storybook/react'; +import React, { useState } from 'react'; + +import { Modal } from '../layout/Modal.js'; + +function MyModal() { + const [isOpen, setIsOpen] = useState(false); + const open = () => setIsOpen(true); + const close = () => setIsOpen(false); + + return ( + <> + + +
Hello Modal
+
+ + ); +} + +const meta = { + title: 'Modal', + component: MyModal, +} satisfies Meta; +export default meta; +type Story = StoryObj; + +export const BasicModal = { + args: {}, +} satisfies Story; diff --git a/typescript/widgets/src/stories/WideChevron.stories.tsx b/typescript/widgets/src/stories/WideChevron.stories.tsx index cb81ae5c3..0738b5451 100644 --- a/typescript/widgets/src/stories/WideChevron.stories.tsx +++ b/typescript/widgets/src/stories/WideChevron.stories.tsx @@ -1,16 +1,16 @@ -import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Meta, StoryFn } from '@storybook/react'; import React from 'react'; import { ColorPalette } from '../color.js'; -import { WideChevron } from '../icons/WideChevron.js'; +import { WideChevronIcon } from '../icons/WideChevron.js'; export default { title: 'WideChevron', - component: WideChevron, -} as ComponentMeta; + component: WideChevronIcon, +} as Meta; -const Template: ComponentStory = (args) => ( - +const Template: StoryFn = (args) => ( + ); export const BlueEastRounded = Template.bind({}); diff --git a/typescript/widgets/src/styles.css b/typescript/widgets/src/styles.css index b5c61c956..c97653790 100755 --- a/typescript/widgets/src/styles.css +++ b/typescript/widgets/src/styles.css @@ -1,3 +1,16 @@ @tailwind base; @tailwind components; @tailwind utilities; + +/* Tailwind extension to hide scrollbar */ +@layer utilities { + /* Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } + + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } +} diff --git a/typescript/widgets/src/utils/clipboard.ts b/typescript/widgets/src/utils/clipboard.ts new file mode 100644 index 000000000..eb331af45 --- /dev/null +++ b/typescript/widgets/src/utils/clipboard.ts @@ -0,0 +1,24 @@ +export function isClipboardReadSupported() { + return !!navigator?.clipboard?.readText; +} + +export async function tryClipboardSet(value: string) { + try { + await navigator.clipboard.writeText(value); + return true; + } catch (error) { + console.error('Failed to set clipboard', error); + return false; + } +} + +export async function tryClipboardGet() { + try { + // Note: doesn't work in firefox, which only allows extensions to read clipboard + const value = await navigator.clipboard.readText(); + return value; + } catch (error) { + console.error('Failed to read from clipboard', error); + return null; + } +} diff --git a/typescript/widgets/src/utils/explorers.ts b/typescript/widgets/src/utils/explorers.ts index 95e9f264a..38c470d31 100644 --- a/typescript/widgets/src/utils/explorers.ts +++ b/typescript/widgets/src/utils/explorers.ts @@ -1,6 +1,5 @@ import type { MultiProvider } from '@hyperlane-xyz/sdk'; - -import { fetchWithTimeout } from './timeout.js'; +import { fetchWithTimeout } from '@hyperlane-xyz/utils'; export interface ExplorerQueryResponse { status: string; diff --git a/typescript/widgets/src/utils/timeout.ts b/typescript/widgets/src/utils/timeout.ts deleted file mode 100644 index fe2641ef7..000000000 --- a/typescript/widgets/src/utils/timeout.ts +++ /dev/null @@ -1,14 +0,0 @@ -export async function fetchWithTimeout( - resource: RequestInfo, - options?: RequestInit, - timeout = 10000, -) { - const controller = new AbortController(); - const id = setTimeout(() => controller.abort(), timeout); - const response = await fetch(resource, { - ...options, - signal: controller.signal, - }); - clearTimeout(id); - return response; -} diff --git a/typescript/widgets/src/utils/useChainConnectionTest.ts b/typescript/widgets/src/utils/useChainConnectionTest.ts new file mode 100644 index 000000000..920b2e7dd --- /dev/null +++ b/typescript/widgets/src/utils/useChainConnectionTest.ts @@ -0,0 +1,32 @@ +import { useEffect, useState } from 'react'; + +import { + ChainMetadata, + isBlockExplorerHealthy, + isRpcHealthy, +} from '@hyperlane-xyz/sdk'; +import { timeout } from '@hyperlane-xyz/utils'; + +import { ChainConnectionType } from '../chains/types.js'; + +const HEALTH_TEST_TIMEOUT = 5000; // 5s + +export function useConnectionHealthTest( + chainMetadata: ChainMetadata, + index: number, + type: ChainConnectionType, +) { + const [isHealthy, setIsHealthy] = useState(undefined); + const tester = + type === ChainConnectionType.RPC ? isRpcHealthy : isBlockExplorerHealthy; + + useEffect(() => { + // TODO run explorer test through CORS proxy, otherwise it's blocked by browser + if (type === ChainConnectionType.Explorer) return; + timeout(tester(chainMetadata, index), HEALTH_TEST_TIMEOUT) + .then((result) => setIsHealthy(result)) + .catch(() => setIsHealthy(false)); + }, [chainMetadata, index, tester]); + + return isHealthy; +} diff --git a/typescript/widgets/tailwind.config.cjs b/typescript/widgets/tailwind.config.cjs index 2fba31f93..f410968ec 100644 --- a/typescript/widgets/tailwind.config.cjs +++ b/typescript/widgets/tailwind.config.cjs @@ -25,7 +25,7 @@ module.exports = { 200: '#A7C2EC', 300: '#82A8E4', 400: '#5385D2', - 500: '#2362C0', + 500: '#2764c1', 600: '#1D4685', 700: '#162A4A', 800: '#11213B', @@ -66,16 +66,16 @@ module.exports = { 900: '#0F2F1E', }, pink: { - 50: '#FAEAF7', - 100: '#F0C0E8', - 200: '#EBABE0', - 300: '#E282D1', - 400: '#D858C2', - 500: '#CF2FB3', - 600: '#BA2AA1', - 700: '#A5258F', - 800: '#90207D', - 900: '#7C1C6B', + 50: '#FAEAF8', + 100: '#F2C1EA', + 200: '#EA98DC', + 300: '#E26ECE', + 400: '#DA45C0', + 500: '#D631B9', + 600: '#C02CA6', + 700: '#952281', + 800: '#6B185C', + 900: '#400E37', } }, fontSize: { diff --git a/yarn.lock b/yarn.lock index a619bd60b..884d2b208 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5472,6 +5472,84 @@ __metadata: languageName: node linkType: hard +"@cloudflare/kv-asset-handler@npm:0.3.4": + version: 0.3.4 + resolution: "@cloudflare/kv-asset-handler@npm:0.3.4" + dependencies: + mime: "npm:^3.0.0" + checksum: f02840c2da8e75f3dbfe769f3ba99b99fb87b438c518c06c279334882ce7745ba5dd7ab66a07695dde0596e297758ce66761f9aac2365e7dfb83d7001cd44afa + languageName: node + linkType: hard + +"@cloudflare/vitest-pool-workers@npm:^0.4.5": + version: 0.4.28 + resolution: "@cloudflare/vitest-pool-workers@npm:0.4.28" + dependencies: + birpc: "npm:0.2.14" + cjs-module-lexer: "npm:^1.2.3" + devalue: "npm:^4.3.0" + esbuild: "npm:0.17.19" + miniflare: "npm:3.20240821.1" + semver: "npm:^7.5.1" + wrangler: "npm:3.74.0" + zod: "npm:^3.22.3" + peerDependencies: + "@vitest/runner": 1.3.x - 1.5.x + "@vitest/snapshot": 1.3.x - 1.5.x + vitest: 1.3.x - 1.5.x + checksum: f0eb2438743d53930762444b436669f8072f8230bf4847bcb3fcbcfe2e13fb16317e674855b01523bf93085cabc6831ffd3e81ae51f430125570fa4d2abf2ee0 + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-64@npm:1.20240821.1": + version: 1.20240821.1 + resolution: "@cloudflare/workerd-darwin-64@npm:1.20240821.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-arm64@npm:1.20240821.1": + version: 1.20240821.1 + resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20240821.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-64@npm:1.20240821.1": + version: 1.20240821.1 + resolution: "@cloudflare/workerd-linux-64@npm:1.20240821.1" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-arm64@npm:1.20240821.1": + version: 1.20240821.1 + resolution: "@cloudflare/workerd-linux-arm64@npm:1.20240821.1" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-windows-64@npm:1.20240821.1": + version: 1.20240821.1 + resolution: "@cloudflare/workerd-windows-64@npm:1.20240821.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workers-shared@npm:0.4.1": + version: 0.4.1 + resolution: "@cloudflare/workers-shared@npm:0.4.1" + checksum: 6f6356ccd6633052f4713ae0c074a34608bcda8406cce3a52bded9775800c023c75a443406765adfb63a5c96fca08673531a82977395537932e6e24d823f9b70 + languageName: node + linkType: hard + +"@cloudflare/workers-types@npm:^4.20240821.1": + version: 4.20240821.1 + resolution: "@cloudflare/workers-types@npm:4.20240821.1" + checksum: 981e86ba1b0899f5e1f58087f39ec3720202df9513263704efc9558038c590d67ae9b75b489250c6687ff2dde28b6652cf64d6a6442c6dfef54be3d4d7a70442 + languageName: node + linkType: hard + "@colors/colors@npm:1.5.0": version: 1.5.0 resolution: "@colors/colors@npm:1.5.0" @@ -5642,7 +5720,7 @@ __metadata: languageName: node linkType: hard -"@cspotcode/source-map-support@npm:^0.8.0": +"@cspotcode/source-map-support@npm:0.8.1, @cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" dependencies: @@ -5667,6 +5745,27 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-globals-polyfill@npm:^0.2.3": + version: 0.2.3 + resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" + peerDependencies: + esbuild: "*" + checksum: 6452637b55da3d577b03bb6e9e9c5b88ec153a2c260a71d4f237fac1b46577e3536059030524b7088c9af7bc8da2afd926a5ebb72653876ce83621cc63d57efc + languageName: node + linkType: hard + +"@esbuild-plugins/node-modules-polyfill@npm:^0.2.2": + version: 0.2.2 + resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.2.2" + dependencies: + escape-string-regexp: "npm:^4.0.0" + rollup-plugin-node-polyfills: "npm:^0.2.1" + peerDependencies: + esbuild: "*" + checksum: 0f5601f0ce46b33079c16881142966afff2a528799f85667db7cab38e53607157ef53d8e48cdb1d082b410688a536e14d87b7cd2971784b3afc15befb9b86520 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.19.12": version: 0.19.12 resolution: "@esbuild/aix-ppc64@npm:0.19.12" @@ -5681,6 +5780,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/aix-ppc64@npm:0.23.1" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/android-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-arm64@npm:0.17.19" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -5702,6 +5815,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/android-arm64@npm:0.23.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/android-arm@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-arm@npm:0.17.19" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm@npm:0.18.20" @@ -5723,6 +5850,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/android-arm@npm:0.23.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@esbuild/android-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-x64@npm:0.17.19" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-x64@npm:0.18.20" @@ -5744,6 +5885,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/android-x64@npm:0.23.1" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/darwin-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/darwin-arm64@npm:0.17.19" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-arm64@npm:0.18.20" @@ -5765,6 +5920,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/darwin-arm64@npm:0.23.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/darwin-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/darwin-x64@npm:0.17.19" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-x64@npm:0.18.20" @@ -5786,6 +5955,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/darwin-x64@npm:0.23.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/freebsd-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/freebsd-arm64@npm:0.17.19" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-arm64@npm:0.18.20" @@ -5807,6 +5990,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/freebsd-arm64@npm:0.23.1" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/freebsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/freebsd-x64@npm:0.17.19" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-x64@npm:0.18.20" @@ -5828,6 +6025,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/freebsd-x64@npm:0.23.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/linux-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-arm64@npm:0.17.19" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm64@npm:0.18.20" @@ -5849,6 +6060,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-arm64@npm:0.23.1" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/linux-arm@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-arm@npm:0.17.19" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm@npm:0.18.20" @@ -5870,6 +6095,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-arm@npm:0.23.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@esbuild/linux-ia32@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-ia32@npm:0.17.19" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ia32@npm:0.18.20" @@ -5891,6 +6130,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-ia32@npm:0.23.1" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/linux-loong64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-loong64@npm:0.17.19" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-loong64@npm:0.18.20" @@ -5912,6 +6165,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-loong64@npm:0.23.1" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + +"@esbuild/linux-mips64el@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-mips64el@npm:0.17.19" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-mips64el@npm:0.18.20" @@ -5933,6 +6200,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-mips64el@npm:0.23.1" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + +"@esbuild/linux-ppc64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-ppc64@npm:0.17.19" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ppc64@npm:0.18.20" @@ -5954,6 +6235,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-ppc64@npm:0.23.1" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + +"@esbuild/linux-riscv64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-riscv64@npm:0.17.19" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-riscv64@npm:0.18.20" @@ -5975,6 +6270,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-riscv64@npm:0.23.1" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + +"@esbuild/linux-s390x@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-s390x@npm:0.17.19" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-s390x@npm:0.18.20" @@ -5996,6 +6305,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-s390x@npm:0.23.1" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + +"@esbuild/linux-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-x64@npm:0.17.19" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-x64@npm:0.18.20" @@ -6017,6 +6340,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/linux-x64@npm:0.23.1" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/netbsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/netbsd-x64@npm:0.17.19" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/netbsd-x64@npm:0.18.20" @@ -6038,6 +6375,27 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/netbsd-x64@npm:0.23.1" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/openbsd-arm64@npm:0.23.1" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/openbsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/openbsd-x64@npm:0.17.19" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/openbsd-x64@npm:0.18.20" @@ -6059,6 +6417,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/openbsd-x64@npm:0.23.1" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/sunos-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/sunos-x64@npm:0.17.19" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/sunos-x64@npm:0.18.20" @@ -6080,6 +6452,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/sunos-x64@npm:0.23.1" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/win32-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-arm64@npm:0.17.19" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-arm64@npm:0.18.20" @@ -6101,6 +6487,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/win32-arm64@npm:0.23.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@esbuild/win32-ia32@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-ia32@npm:0.17.19" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-ia32@npm:0.18.20" @@ -6122,6 +6522,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/win32-ia32@npm:0.23.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@esbuild/win32-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-x64@npm:0.17.19" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-x64@npm:0.18.20" @@ -6143,6 +6557,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.23.1": + version: 0.23.1 + resolution: "@esbuild/win32-x64@npm:0.23.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -7117,6 +7538,13 @@ __metadata: languageName: node linkType: hard +"@faker-js/faker@npm:^8.4.1": + version: 8.4.1 + resolution: "@faker-js/faker@npm:8.4.1" + checksum: 5983c2ea64f26055ad6648de748878e11ebe2fb751e3c7435ae141cdffabc2dccfe4c4f49da69a3d2add71e21b415c683ac5fba196fab0d5ed6779fbec436c80 + languageName: node + linkType: hard + "@fal-works/esbuild-plugin-global-externals@npm:^2.1.2": version: 2.1.2 resolution: "@fal-works/esbuild-plugin-global-externals@npm:2.1.2" @@ -7143,6 +7571,16 @@ __metadata: languageName: node linkType: hard +"@floating-ui/dom@npm:^1.6.1": + version: 1.6.10 + resolution: "@floating-ui/dom@npm:1.6.10" + dependencies: + "@floating-ui/core": "npm:^1.6.0" + "@floating-ui/utils": "npm:^0.2.7" + checksum: c100f5ecb37fc1bea4e551977eae3992f8eba351e6b7f2642e2f84a4abd269406d5a46a14505bc583caf25ddee900a667829244c4eecf1cf60f08c1dabdf3ee9 + languageName: node + linkType: hard + "@floating-ui/react-dom@npm:^2.0.0": version: 2.1.1 resolution: "@floating-ui/react-dom@npm:2.1.1" @@ -7155,6 +7593,32 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react-dom@npm:^2.1.2": + version: 2.1.2 + resolution: "@floating-ui/react-dom@npm:2.1.2" + dependencies: + "@floating-ui/dom": "npm:^1.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 2a67dc8499674e42ff32c7246bded185bb0fdd492150067caf9568569557ac4756a67787421d8604b0f241e5337de10762aee270d9aeef106d078a0ff13596c4 + languageName: node + linkType: hard + +"@floating-ui/react@npm:^0.26.16": + version: 0.26.24 + resolution: "@floating-ui/react@npm:0.26.24" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.2" + "@floating-ui/utils": "npm:^0.2.8" + tabbable: "npm:^6.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 903ffbee2c6726d117086e2a83f43d6ad339970758ce7979fd16cc7cf8dc0f5b869bd72c2c8ee1bcd6c63b190bb0960effd4d403e63685fb5aeed6b185041b08 + languageName: node + linkType: hard + "@floating-ui/utils@npm:^0.2.5": version: 0.2.5 resolution: "@floating-ui/utils@npm:0.2.5" @@ -7162,6 +7626,20 @@ __metadata: languageName: node linkType: hard +"@floating-ui/utils@npm:^0.2.7": + version: 0.2.7 + resolution: "@floating-ui/utils@npm:0.2.7" + checksum: 56b1bb3f73f6ec9aabf9b1fd3dc584e0f2384d319c1a6119050eab102ae6ca8b9b0eed711c2f235ffe035188cbe9727bf36e8dcb54c8bd32176737e4be47efa8 + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.2.8": + version: 0.2.8 + resolution: "@floating-ui/utils@npm:0.2.8" + checksum: 3e3ea3b2de06badc4baebdf358b3dbd77ccd9474a257a6ef237277895943db2acbae756477ec64de65a2a1436d94aea3107129a1feeef6370675bf2b161c1abc + languageName: node + linkType: hard + "@ganache/ethereum-address@npm:0.1.4": version: 0.1.4 resolution: "@ganache/ethereum-address@npm:0.1.4" @@ -7278,6 +7756,21 @@ __metadata: languageName: node linkType: hard +"@headlessui/react@npm:^2.1.8": + version: 2.1.8 + resolution: "@headlessui/react@npm:2.1.8" + dependencies: + "@floating-ui/react": "npm:^0.26.16" + "@react-aria/focus": "npm:^3.17.1" + "@react-aria/interactions": "npm:^3.21.3" + "@tanstack/react-virtual": "npm:^3.8.1" + peerDependencies: + react: ^18 + react-dom: ^18 + checksum: a82f115877dcc5e3d16a6b0502b6796a5bd3f38936835e241833a538c002d4ecfc3317868b0d1e9655e5de93201b0806f51bc10dbf32604e270cda4fc1636024 + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.14": version: 0.11.14 resolution: "@humanwhocodes/config-array@npm:0.11.14" @@ -7330,18 +7823,23 @@ __metadata: "@aws-sdk/client-s3": "npm:^3.577.0" "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" - "@hyperlane-xyz/registry": "npm:2.5.0" - "@hyperlane-xyz/sdk": "npm:5.1.0" - "@hyperlane-xyz/utils": "npm:5.1.0" + "@hyperlane-xyz/registry": "npm:4.7.0" + "@hyperlane-xyz/sdk": "npm:5.6.2" + "@hyperlane-xyz/utils": "npm:5.6.2" + "@inquirer/core": "npm:9.0.10" + "@inquirer/figures": "npm:1.0.5" "@inquirer/prompts": "npm:^3.0.0" + "@types/chai-as-promised": "npm:^8" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" "@types/yargs": "npm:^17.0.24" "@typescript-eslint/eslint-plugin": "npm:^7.4.0" "@typescript-eslint/parser": "npm:^7.4.0" + ansi-escapes: "npm:^7.0.0" asn1.js: "npm:^5.4.1" bignumber.js: "npm:^9.1.1" - chai: "npm:^4.3.6" + chai: "npm:^4.5.0" + chai-as-promised: "npm:^8.0.0" chalk: "npm:^5.3.0" eslint: "npm:^8.57.0" eslint-config-prettier: "npm:^9.1.0" @@ -7351,8 +7849,8 @@ __metadata: prettier: "npm:^2.8.8" terminal-link: "npm:^3.0.0" tsx: "npm:^4.7.1" - typescript: "npm:^5.1.6" - yaml: "npm:^2.4.1" + typescript: "npm:5.3.3" + yaml: "npm:2.4.5" yargs: "npm:^17.7.2" zod: "npm:^3.21.2" zod-validation-error: "npm:^3.3.0" @@ -7362,13 +7860,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:5.1.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:5.6.1, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@arbitrum/nitro-contracts": "npm:^1.2.1" "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:5.1.0" + "@hyperlane-xyz/utils": "npm:5.6.2" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -7379,7 +7877,7 @@ __metadata: "@typechain/ethers-v6": "npm:^0.5.1" "@typechain/hardhat": "npm:^9.1.0" "@types/node": "npm:^18.14.5" - chai: "npm:^4.3.6" + chai: "npm:4.5.0" ethereum-waffle: "npm:^4.0.10" ethers: "npm:^5.7.2" fx-portal: "npm:^1.0.3" @@ -7394,6 +7892,7 @@ __metadata: solidity-coverage: "npm:^0.8.3" ts-generator: "npm:^0.1.1" ts-node: "npm:^10.8.0" + tsx: "npm:^4.19.1" typechain: "patch:typechain@npm%3A8.3.2#~/.yarn/patches/typechain-npm-8.3.2-b02e27439e.patch" typescript: "npm:5.3.3" peerDependencies: @@ -7403,13 +7902,28 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:5.1.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/github-proxy@workspace:typescript/github-proxy": + version: 0.0.0-use.local + resolution: "@hyperlane-xyz/github-proxy@workspace:typescript/github-proxy" + dependencies: + "@cloudflare/vitest-pool-workers": "npm:^0.4.5" + "@cloudflare/workers-types": "npm:^4.20240821.1" + "@faker-js/faker": "npm:^8.4.1" + chai: "npm:4.5.0" + prettier: "npm:^2.8.8" + typescript: "npm:5.3.3" + vitest: "npm:1.4.0" + wrangler: "npm:^3.74.0" + languageName: unknown + linkType: soft + +"@hyperlane-xyz/helloworld@npm:5.6.2, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: - "@hyperlane-xyz/core": "npm:5.1.0" - "@hyperlane-xyz/registry": "npm:2.5.0" - "@hyperlane-xyz/sdk": "npm:5.1.0" + "@hyperlane-xyz/core": "npm:5.6.1" + "@hyperlane-xyz/registry": "npm:4.7.0" + "@hyperlane-xyz/sdk": "npm:5.6.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -7419,7 +7933,7 @@ __metadata: "@typechain/hardhat": "npm:^9.1.0" "@typescript-eslint/eslint-plugin": "npm:^7.4.0" "@typescript-eslint/parser": "npm:^7.4.0" - chai: "npm:^4.3.6" + chai: "npm:4.5.0" eslint: "npm:^8.57.0" eslint-config-prettier: "npm:^9.1.0" ethereum-waffle: "npm:^4.0.10" @@ -7456,10 +7970,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:5.1.0" - "@hyperlane-xyz/registry": "npm:2.5.0" - "@hyperlane-xyz/sdk": "npm:5.1.0" - "@hyperlane-xyz/utils": "npm:5.1.0" + "@hyperlane-xyz/helloworld": "npm:5.6.2" + "@hyperlane-xyz/registry": "npm:4.10.0" + "@hyperlane-xyz/sdk": "npm:5.6.2" + "@hyperlane-xyz/utils": "npm:5.6.2" "@inquirer/prompts": "npm:^5.3.8" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" @@ -7477,7 +7991,7 @@ __metadata: "@types/yargs": "npm:^17.0.24" asn1.js: "npm:5.4.1" aws-kms-ethers-signer: "npm:^0.1.3" - chai: "npm:^4.3.6" + chai: "npm:4.5.0" deep-object-diff: "npm:^1.1.9" dotenv: "npm:^10.0.0" ethereum-waffle: "npm:^4.0.10" @@ -7490,7 +8004,7 @@ __metadata: prompts: "npm:^2.4.2" tsx: "npm:^4.7.1" typescript: "npm:5.3.3" - yaml: "npm:^2.4.5" + yaml: "npm:2.4.5" yargs: "npm:^17.7.2" peerDependencies: "@ethersproject/abi": "*" @@ -7515,17 +8029,27 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/registry@npm:2.5.0": - version: 2.5.0 - resolution: "@hyperlane-xyz/registry@npm:2.5.0" +"@hyperlane-xyz/registry@npm:4.10.0": + version: 4.10.0 + resolution: "@hyperlane-xyz/registry@npm:4.10.0" + dependencies: + yaml: "npm:2.4.5" + zod: "npm:^3.21.2" + checksum: 22bb18f426cbada8b97db0894fe5d0980dfc08ecbd5174c978b7aeb6d8df9706f93d7e9cf0630644d2455ad05feee714dc2a38ec515a717b0b257184637902fb + languageName: node + linkType: hard + +"@hyperlane-xyz/registry@npm:4.7.0": + version: 4.7.0 + resolution: "@hyperlane-xyz/registry@npm:4.7.0" dependencies: - yaml: "npm:^2" + yaml: "npm:2.4.5" zod: "npm:^3.21.2" - checksum: 24f433c0938dc31913b0f7a93b880a9aa15eabebf664eb11f319c51f6b38163929429405dd996b8964f8a2f43dfa4ba5424b157ad6fd39432653e46e128e0ffc + checksum: d5b0090869417c3fc263c379791f439070113aee239990ffc20d9d90d74102b77008f3c630ce955a9b3f1f92f79b1df67d83a097b327cd5db2b01b382bf40f18 languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:5.1.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:5.6.2, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: @@ -7533,13 +8057,13 @@ __metadata: "@aws-sdk/client-s3": "npm:^3.74.0" "@cosmjs/cosmwasm-stargate": "npm:^0.32.4" "@cosmjs/stargate": "npm:^0.32.4" - "@hyperlane-xyz/core": "npm:5.1.0" - "@hyperlane-xyz/utils": "npm:5.1.0" + "@hyperlane-xyz/core": "npm:5.6.1" + "@hyperlane-xyz/utils": "npm:5.6.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" "@safe-global/protocol-kit": "npm:1.3.0" - "@safe-global/safe-deployments": "npm:1.37.3" + "@safe-global/safe-deployments": "npm:1.37.8" "@solana/spl-token": "npm:^0.3.8" "@solana/web3.js": "npm:^1.78.0" "@types/coingecko-api": "npm:^1.0.10" @@ -7550,7 +8074,7 @@ __metadata: "@types/ws": "npm:^8.5.5" "@wagmi/chains": "npm:^1.8.0" bignumber.js: "npm:^9.1.1" - chai: "npm:^4.3.6" + chai: "npm:4.5.0" coingecko-api: "npm:^1.0.10" cosmjs-types: "npm:^0.9.0" cross-fetch: "npm:^3.1.5" @@ -7567,7 +8091,7 @@ __metadata: tsx: "npm:^4.7.1" typescript: "npm:5.3.3" viem: "npm:^1.20.0" - yaml: "npm:^2.4.1" + yaml: "npm:2.4.5" zod: "npm:^3.21.2" peerDependencies: "@ethersproject/abi": "*" @@ -7575,7 +8099,7 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:5.1.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:5.6.2, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -7583,15 +8107,18 @@ __metadata: "@solana/web3.js": "npm:^1.78.0" "@types/lodash-es": "npm:^4.17.12" "@types/mocha": "npm:^10.0.1" + "@types/sinon": "npm:^17.0.1" + "@types/sinon-chai": "npm:^3.2.12" bignumber.js: "npm:^9.1.1" - chai: "npm:^4.3.6" + chai: "npm:4.5.0" ethers: "npm:^5.7.2" lodash-es: "npm:^4.17.21" mocha: "npm:^10.2.0" pino: "npm:^8.19.0" prettier: "npm:^2.8.8" + sinon: "npm:^13.0.2" typescript: "npm:5.3.3" - yaml: "npm:^2.4.1" + yaml: "npm:2.4.5" languageName: unknown linkType: soft @@ -7599,8 +8126,10 @@ __metadata: version: 0.0.0-use.local resolution: "@hyperlane-xyz/widgets@workspace:typescript/widgets" dependencies: - "@hyperlane-xyz/registry": "npm:2.5.0" - "@hyperlane-xyz/sdk": "npm:5.1.0" + "@headlessui/react": "npm:^2.1.8" + "@hyperlane-xyz/registry": "npm:4.7.0" + "@hyperlane-xyz/sdk": "npm:5.6.2" + "@hyperlane-xyz/utils": "npm:5.6.2" "@storybook/addon-essentials": "npm:^7.6.14" "@storybook/addon-interactions": "npm:^7.6.14" "@storybook/addon-links": "npm:^7.6.14" @@ -7616,6 +8145,7 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^7.4.0" "@typescript-eslint/parser": "npm:^7.4.0" babel-loader: "npm:^8.3.0" + clsx: "npm:^2.1.1" eslint: "npm:^8.57.0" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-storybook: "npm:^0.6.15" @@ -7623,8 +8153,9 @@ __metadata: prettier: "npm:^2.8.8" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" + react-tooltip: "npm:^5.28.0" storybook: "npm:^7.6.14" - tailwindcss: "npm:^3.2.4" + tailwindcss: "npm:^3.4.13" ts-node: "npm:^10.8.0" typescript: "npm:5.3.3" vite: "npm:^5.1.1" @@ -7681,6 +8212,27 @@ __metadata: languageName: node linkType: hard +"@inquirer/core@npm:9.0.10, @inquirer/core@npm:^9.0.10": + version: 9.0.10 + resolution: "@inquirer/core@npm:9.0.10" + dependencies: + "@inquirer/figures": "npm:^1.0.5" + "@inquirer/type": "npm:^1.5.2" + "@types/mute-stream": "npm:^0.0.4" + "@types/node": "npm:^22.1.0" + "@types/wrap-ansi": "npm:^3.0.0" + ansi-escapes: "npm:^4.3.2" + cli-spinners: "npm:^2.9.2" + cli-width: "npm:^4.1.0" + mute-stream: "npm:^1.0.0" + signal-exit: "npm:^4.1.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + yoctocolors-cjs: "npm:^2.1.2" + checksum: 1bcb1deb7393d78f2dac5b8774d10692ad50b70e3ebc24684d13259d0c6c863dd1bce8ab4d4a806a6e90d5a2517aa8f9981993b1a256c9be68d9ef5e748481c6 + languageName: node + linkType: hard + "@inquirer/core@npm:^3.0.0": version: 3.0.0 resolution: "@inquirer/core@npm:3.0.0" @@ -7703,27 +8255,6 @@ __metadata: languageName: node linkType: hard -"@inquirer/core@npm:^9.0.10": - version: 9.0.10 - resolution: "@inquirer/core@npm:9.0.10" - dependencies: - "@inquirer/figures": "npm:^1.0.5" - "@inquirer/type": "npm:^1.5.2" - "@types/mute-stream": "npm:^0.0.4" - "@types/node": "npm:^22.1.0" - "@types/wrap-ansi": "npm:^3.0.0" - ansi-escapes: "npm:^4.3.2" - cli-spinners: "npm:^2.9.2" - cli-width: "npm:^4.1.0" - mute-stream: "npm:^1.0.0" - signal-exit: "npm:^4.1.0" - strip-ansi: "npm:^6.0.1" - wrap-ansi: "npm:^6.2.0" - yoctocolors-cjs: "npm:^2.1.2" - checksum: 1bcb1deb7393d78f2dac5b8774d10692ad50b70e3ebc24684d13259d0c6c863dd1bce8ab4d4a806a6e90d5a2517aa8f9981993b1a256c9be68d9ef5e748481c6 - languageName: node - linkType: hard - "@inquirer/editor@npm:^1.2.4": version: 1.2.4 resolution: "@inquirer/editor@npm:1.2.4" @@ -7770,7 +8301,7 @@ __metadata: languageName: node linkType: hard -"@inquirer/figures@npm:^1.0.5": +"@inquirer/figures@npm:1.0.5, @inquirer/figures@npm:^1.0.5": version: 1.0.5 resolution: "@inquirer/figures@npm:1.0.5" checksum: 60a51b2cdef03c89be25071c23d8c4ae427c56d8ac1b00bf054ca7be446674adc4edd66c15465fe6a81ff0726b024bf37f8a2903a8387ef968d33058da3e7a15 @@ -10173,6 +10704,81 @@ __metadata: languageName: node linkType: hard +"@react-aria/focus@npm:^3.17.1": + version: 3.18.2 + resolution: "@react-aria/focus@npm:3.18.2" + dependencies: + "@react-aria/interactions": "npm:^3.22.2" + "@react-aria/utils": "npm:^3.25.2" + "@react-types/shared": "npm:^3.24.1" + "@swc/helpers": "npm:^0.5.0" + clsx: "npm:^2.0.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 4243764952737ec33f463534e69c7d581073d5531ae87504d574083a4d9a08a9e3b5a8e2b69a936bf6476a35eb8cf38db751d52629e66451be58a6c635ce9449 + languageName: node + linkType: hard + +"@react-aria/interactions@npm:^3.21.3, @react-aria/interactions@npm:^3.22.2": + version: 3.22.2 + resolution: "@react-aria/interactions@npm:3.22.2" + dependencies: + "@react-aria/ssr": "npm:^3.9.5" + "@react-aria/utils": "npm:^3.25.2" + "@react-types/shared": "npm:^3.24.1" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: df0ce7d438b6f9d04774120ed6a3b66ef928e8e8ce97af42b12a5feabcd8d6cdd858e14cd6ccf602bbe8c0dbb620ce94bd974f1e2b832f497c7125647f8be471 + languageName: node + linkType: hard + +"@react-aria/ssr@npm:^3.9.5": + version: 3.9.5 + resolution: "@react-aria/ssr@npm:3.9.5" + dependencies: + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 0284561e7b084c567fd8f35e7982f201582acc937b950be8411678352682c7b45ad3ab99272cd2d6f0b4919ddaa5b0e553d784f190d1d05ceb8594bfee3f763e + languageName: node + linkType: hard + +"@react-aria/utils@npm:^3.25.2": + version: 3.25.2 + resolution: "@react-aria/utils@npm:3.25.2" + dependencies: + "@react-aria/ssr": "npm:^3.9.5" + "@react-stately/utils": "npm:^3.10.3" + "@react-types/shared": "npm:^3.24.1" + "@swc/helpers": "npm:^0.5.0" + clsx: "npm:^2.0.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: c0dbbff1f93b3f275e6db2f01c7a09ffd96da57fd373a8b3b3cb5dbb0aca99d721c2453fbd742800d0df2fbb0ffa5f3052669bbb2998db753b1090f573d5ef7b + languageName: node + linkType: hard + +"@react-stately/utils@npm:^3.10.3": + version: 3.10.3 + resolution: "@react-stately/utils@npm:3.10.3" + dependencies: + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 0ac737e678d949787d05889bfd67047ed0ee91d93a8d727c89d7a7568a027d0cf4a53cebad13e6526c2322f51069bbaa40d5912364230e6b9374cf653683a73d + languageName: node + linkType: hard + +"@react-types/shared@npm:^3.24.1": + version: 3.24.1 + resolution: "@react-types/shared@npm:3.24.1" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 5472ae35f65b2ed7c12d5ea4459f34b4aec065d2633844031d27945495b6dca6fa9bf02b6392b901fac97252e58d9b91a4baf53f4c281397fb81ce85c73b8648 + languageName: node + linkType: hard + "@resolver-engine/core@npm:^0.3.3": version: 0.3.3 resolution: "@resolver-engine/core@npm:0.3.3" @@ -10241,6 +10847,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm-eabi@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-android-arm-eabi@npm:4.21.2" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@rollup/rollup-android-arm64@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-android-arm64@npm:4.19.1" @@ -10248,6 +10861,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-android-arm64@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-android-arm64@npm:4.21.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-arm64@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-darwin-arm64@npm:4.19.1" @@ -10255,6 +10875,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-arm64@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-darwin-arm64@npm:4.21.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-darwin-x64@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-darwin-x64@npm:4.19.1" @@ -10262,6 +10889,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-darwin-x64@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-darwin-x64@npm:4.21.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-gnueabihf@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.19.1" @@ -10269,6 +10903,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-gnueabihf@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.21.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm-musleabihf@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.19.1" @@ -10276,6 +10917,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm-musleabihf@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.21.2" + conditions: os=linux & cpu=arm & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-gnu@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.19.1" @@ -10283,6 +10931,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.21.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-arm64-musl@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-arm64-musl@npm:4.19.1" @@ -10290,6 +10945,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-arm64-musl@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-arm64-musl@npm:4.21.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.19.1" @@ -10297,6 +10959,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.21.2" + conditions: os=linux & cpu=ppc64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-riscv64-gnu@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.19.1" @@ -10304,6 +10973,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-riscv64-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.21.2" + conditions: os=linux & cpu=riscv64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-s390x-gnu@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.19.1" @@ -10311,6 +10987,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-s390x-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.21.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-gnu@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-x64-gnu@npm:4.19.1" @@ -10318,6 +11001,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-gnu@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-x64-gnu@npm:4.21.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@rollup/rollup-linux-x64-musl@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-linux-x64-musl@npm:4.19.1" @@ -10325,6 +11015,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-linux-x64-musl@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-linux-x64-musl@npm:4.21.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@rollup/rollup-win32-arm64-msvc@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.19.1" @@ -10332,6 +11029,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-arm64-msvc@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.21.2" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@rollup/rollup-win32-ia32-msvc@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.19.1" @@ -10339,6 +11043,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-ia32-msvc@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.21.2" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@rollup/rollup-win32-x64-msvc@npm:4.19.1": version: 4.19.1 resolution: "@rollup/rollup-win32-x64-msvc@npm:4.19.1" @@ -10346,6 +11057,13 @@ __metadata: languageName: node linkType: hard +"@rollup/rollup-win32-x64-msvc@npm:4.21.2": + version: 4.21.2 + resolution: "@rollup/rollup-win32-x64-msvc@npm:4.21.2" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@safe-global/api-kit@npm:1.3.0": version: 1.3.0 resolution: "@safe-global/api-kit@npm:1.3.0" @@ -10401,12 +11119,12 @@ __metadata: languageName: node linkType: hard -"@safe-global/safe-deployments@npm:1.37.3": - version: 1.37.3 - resolution: "@safe-global/safe-deployments@npm:1.37.3" +"@safe-global/safe-deployments@npm:1.37.8": + version: 1.37.8 + resolution: "@safe-global/safe-deployments@npm:1.37.8" dependencies: semver: "npm:^7.6.2" - checksum: 3d1fcaac850a1d1100eaa5ff753c88c9bb889c678bb09248323c6b0c9d6228225a88be47973a89bb32829fe2d13ed17c21f854b9fdc29cc1b3c734021761f15c + checksum: bc8fce2c4d557e547a6cceebb611f9584d998dfb459cd50cf338409de986bed247ebca9425b0984a6e1a6accab42c7c4d1c68811e09cc981756183ba50a5e5a9 languageName: node linkType: hard @@ -12102,6 +12820,15 @@ __metadata: languageName: node linkType: hard +"@swc/helpers@npm:^0.5.0": + version: 0.5.13 + resolution: "@swc/helpers@npm:0.5.13" + dependencies: + tslib: "npm:^2.4.0" + checksum: 6ba2f7e215d32d71fce139e2cfc426b3ed7eaa709febdeb07b97260a4c9eea4784cf047cc1271be273990b08220b576b94a42b5780947c0b3be84973a847a24d + languageName: node + linkType: hard + "@szmarczak/http-timer@npm:^5.0.1": version: 5.0.1 resolution: "@szmarczak/http-timer@npm:5.0.1" @@ -12111,6 +12838,25 @@ __metadata: languageName: node linkType: hard +"@tanstack/react-virtual@npm:^3.8.1": + version: 3.10.8 + resolution: "@tanstack/react-virtual@npm:3.10.8" + dependencies: + "@tanstack/virtual-core": "npm:3.10.8" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 40a5d6089908096634fec2aa0cd646ca47c044c745e1b0d190ecbf9905ad2e6266ccd56c2550ed92f47349954dc11eb6930beac1354441ce7c98af81c5454d3f + languageName: node + linkType: hard + +"@tanstack/virtual-core@npm:3.10.8": + version: 3.10.8 + resolution: "@tanstack/virtual-core@npm:3.10.8" + checksum: 047e95fa72a0d341c0da8468799c176fd448481432f976a4780911bb4a2256aa4788d828f79fad78d127fe859b785189c13ca0fea10c560bf14d8ab8cb2c7790 + languageName: node + linkType: hard + "@testing-library/dom@npm:^9.3.1": version: 9.3.4 resolution: "@testing-library/dom@npm:9.3.4" @@ -12391,6 +13137,15 @@ __metadata: languageName: node linkType: hard +"@types/chai-as-promised@npm:^8": + version: 8.0.0 + resolution: "@types/chai-as-promised@npm:8.0.0" + dependencies: + "@types/chai": "npm:*" + checksum: f6db5698e4f28fd6e3914740810f356269b7f4e93a0650b38a9b01a1bae030593487c80bc57a0e69dd0bfb069a61d3dd285bfcfba6d1daf66ef3939577b68169 + languageName: node + linkType: hard + "@types/chai@npm:*, @types/chai@npm:^4.2.21": version: 4.3.1 resolution: "@types/chai@npm:4.3.1" @@ -12797,6 +13552,15 @@ __metadata: languageName: node linkType: hard +"@types/node-forge@npm:^1.3.0": + version: 1.3.11 + resolution: "@types/node-forge@npm:1.3.11" + dependencies: + "@types/node": "npm:*" + checksum: 670c9b377c48189186ec415e3c8ed371f141ecc1a79ab71b213b20816adeffecba44dae4f8406cc0d09e6349a4db14eb8c5893f643d8e00fa19fc035cf49dee0 + languageName: node + linkType: hard + "@types/node@npm:*": version: 17.0.42 resolution: "@types/node@npm:17.0.42" @@ -13471,6 +14235,17 @@ __metadata: languageName: node linkType: hard +"@vitest/expect@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/expect@npm:1.4.0" + dependencies: + "@vitest/spy": "npm:1.4.0" + "@vitest/utils": "npm:1.4.0" + chai: "npm:^4.3.10" + checksum: 00d794a807b7e496d8450133430c8528d4b6cfaba9520bf49640c941b14acaa7b28f151c249b44d935740cae887f0648980db63f38e37bdeb6c2906387e15188 + languageName: node + linkType: hard + "@vitest/expect@npm:^0.34.2": version: 0.34.7 resolution: "@vitest/expect@npm:0.34.7" @@ -13482,6 +14257,28 @@ __metadata: languageName: node linkType: hard +"@vitest/runner@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/runner@npm:1.4.0" + dependencies: + "@vitest/utils": "npm:1.4.0" + p-limit: "npm:^5.0.0" + pathe: "npm:^1.1.1" + checksum: 7b8a692de5cef72ef698e83eb5bbb89076924e7a557ed087e80c5080e000a575f34c481f3b880aa2588da5a095504dc55216c319f6924eddfcfc3412f10a27b2 + languageName: node + linkType: hard + +"@vitest/snapshot@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/snapshot@npm:1.4.0" + dependencies: + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + pretty-format: "npm:^29.7.0" + checksum: 43e22f8aeef4b87bcce79b37775415d4b558e32d906992d4a0acbe81c8e84cbfe3e488dd32c504c4f4d8f2c3f96842acb524b4b210036fda6796e64d0140d5f6 + languageName: node + linkType: hard + "@vitest/spy@npm:0.34.7, @vitest/spy@npm:^0.34.1": version: 0.34.7 resolution: "@vitest/spy@npm:0.34.7" @@ -13491,6 +14288,15 @@ __metadata: languageName: node linkType: hard +"@vitest/spy@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/spy@npm:1.4.0" + dependencies: + tinyspy: "npm:^2.2.0" + checksum: 0e48f9a64f62801c2abf10df1013ec5e5b75c47bdca6a5d4c8246b3dd7bdf01ade3df6c99fd0751a870a16bd63c127b3e58e0f5cbc320c48d0727ab5da89d028 + languageName: node + linkType: hard + "@vitest/utils@npm:0.34.7, @vitest/utils@npm:^0.34.6": version: 0.34.7 resolution: "@vitest/utils@npm:0.34.7" @@ -13502,6 +14308,18 @@ __metadata: languageName: node linkType: hard +"@vitest/utils@npm:1.4.0": + version: 1.4.0 + resolution: "@vitest/utils@npm:1.4.0" + dependencies: + diff-sequences: "npm:^29.6.3" + estree-walker: "npm:^3.0.3" + loupe: "npm:^2.3.7" + pretty-format: "npm:^29.7.0" + checksum: 2261705e2edc10376f2524a4bf6616688680094d94fff683681a1ef8d3d59271dee2d80893efad8e6437bbdb00390e2edd754d94cf42100db86f2cfd9c44826f + languageName: node + linkType: hard + "@wagmi/chains@npm:^1.8.0": version: 1.8.0 resolution: "@wagmi/chains@npm:1.8.0" @@ -13697,6 +14515,15 @@ __metadata: languageName: node linkType: hard +"acorn-walk@npm:^8.2.0, acorn-walk@npm:^8.3.2": + version: 8.3.3 + resolution: "acorn-walk@npm:8.3.3" + dependencies: + acorn: "npm:^8.11.0" + checksum: 59701dcb7070679622ba8e9c7f37577b4935565747ca0fd7c1c3ad30b3f1b1b008276282664e323b5495eb49f77fa12d3816fd06dc68e18f90fbebe759f71450 + languageName: node + linkType: hard + "acorn@npm:^7.4.1": version: 7.4.1 resolution: "acorn@npm:7.4.1" @@ -13706,7 +14533,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.11.3, acorn@npm:^8.12.1": +"acorn@npm:^8.11.0, acorn@npm:^8.11.3, acorn@npm:^8.12.1, acorn@npm:^8.8.0": version: 8.12.1 resolution: "acorn@npm:8.12.1" bin: @@ -13901,6 +14728,15 @@ __metadata: languageName: node linkType: hard +"ansi-escapes@npm:^7.0.0": + version: 7.0.0 + resolution: "ansi-escapes@npm:7.0.0" + dependencies: + environment: "npm:^1.0.0" + checksum: 2d0e2345087bd7ae6bf122b9cc05ee35560d40dcc061146edcdc02bc2d7c7c50143cd12a22e69a0b5c0f62b948b7bc9a4539ee888b80f5bd33cdfd82d01a70ab + languageName: node + linkType: hard + "ansi-regex@npm:^2.0.0": version: 2.1.1 resolution: "ansi-regex@npm:2.1.1" @@ -14221,6 +15057,15 @@ __metadata: languageName: node linkType: hard +"as-table@npm:^1.0.36": + version: 1.0.55 + resolution: "as-table@npm:1.0.55" + dependencies: + printable-characters: "npm:^1.0.42" + checksum: 8bbfbd7b6f240efb22f6553f756e89d1cae074e9f7a24580282e9d247c1bd9cf1fd9fb49056202a78a5e69907209d8bf032d8b6c3eaaab5fb6ad92da64a7894a + languageName: node + linkType: hard + "asap@npm:~2.0.6": version: 2.0.6 resolution: "asap@npm:2.0.6" @@ -14741,6 +15586,13 @@ __metadata: languageName: node linkType: hard +"birpc@npm:0.2.14": + version: 0.2.14 + resolution: "birpc@npm:0.2.14" + checksum: 4c6329a05f6fc27ac524d3b94f44c3532ee9be75e8a773db950f8ad7a64962905cbe589873cb390294d770e67fd1a1fe4df98f1eab59874aa62d6dd07abe163b + languageName: node + linkType: hard + "bl@npm:^4.0.3, bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -14752,6 +15604,13 @@ __metadata: languageName: node linkType: hard +"blake3-wasm@npm:^2.1.5": + version: 2.1.5 + resolution: "blake3-wasm@npm:2.1.5" + checksum: 7138aa209ac8411755ba07df7d035974886aac1fb4bb8cf710d354732037069bacc9984c19b3bc68bf5e17cc203f454cc9cfcb7115393aaf21ce865630dbf920 + languageName: node + linkType: hard + "blakejs@npm:^1.1.0": version: 1.2.1 resolution: "blakejs@npm:1.2.1" @@ -15181,6 +16040,13 @@ __metadata: languageName: node linkType: hard +"cac@npm:^6.7.14": + version: 6.7.14 + resolution: "cac@npm:6.7.14" + checksum: 002769a0fbfc51c062acd2a59df465a2a947916b02ac50b56c69ec6c018ee99ac3e7f4dd7366334ea847f1ecacf4defaa61bcd2ac283db50156ce1f1d8c8ad42 + languageName: node + linkType: hard + "cacache@npm:^16.1.0": version: 16.1.1 resolution: "cacache@npm:16.1.1" @@ -15338,6 +16204,16 @@ __metadata: languageName: node linkType: hard +"capnp-ts@npm:^0.7.0": + version: 0.7.0 + resolution: "capnp-ts@npm:0.7.0" + dependencies: + debug: "npm:^4.3.1" + tslib: "npm:^2.2.0" + checksum: 186a76662e31ab16fe46fe0785ed2a511969d0c5198e2d7baec6b44f71c9b3bf8c05e7627036dc86c2d3ddc229c846559350c13f904fdd8da3590d7054715ba8 + languageName: node + linkType: hard + "case@npm:^1.6.3": version: 1.6.3 resolution: "case@npm:1.6.3" @@ -15378,7 +16254,18 @@ __metadata: languageName: node linkType: hard -"chai@npm:^4.3.10, chai@npm:^4.3.7": +"chai-as-promised@npm:^8.0.0": + version: 8.0.0 + resolution: "chai-as-promised@npm:8.0.0" + dependencies: + check-error: "npm:^2.0.0" + peerDependencies: + chai: ">= 2.1.2 < 6" + checksum: 91d6a49caac7965440b8f8af421ebe6f060a3b5523599ae143816d08fc19d9a971ea2bc5401f82ce88d15d8bc7b64d356bf3e53542ace9e2f25cc454164d3247 + languageName: node + linkType: hard + +"chai@npm:4.5.0, chai@npm:^4.3.10, chai@npm:^4.3.7, chai@npm:^4.5.0": version: 4.5.0 resolution: "chai@npm:4.5.0" dependencies: @@ -15393,7 +16280,7 @@ __metadata: languageName: node linkType: hard -"chai@npm:^4.3.4, chai@npm:^4.3.6": +"chai@npm:^4.3.4": version: 4.3.6 resolution: "chai@npm:4.3.6" dependencies: @@ -15498,6 +16385,13 @@ __metadata: languageName: node linkType: hard +"check-error@npm:^2.0.0": + version: 2.1.1 + resolution: "check-error@npm:2.1.1" + checksum: d785ed17b1d4a4796b6e75c765a9a290098cf52ff9728ce0756e8ffd4293d2e419dd30c67200aee34202463b474306913f2fcfaf1890641026d9fc6966fea27a + languageName: node + linkType: hard + "chokidar@npm:3.3.0": version: 3.3.0 resolution: "chokidar@npm:3.3.0" @@ -15622,6 +16516,13 @@ __metadata: languageName: node linkType: hard +"cjs-module-lexer@npm:^1.2.3": + version: 1.4.0 + resolution: "cjs-module-lexer@npm:1.4.0" + checksum: b041096749792526120d8b8756929f8ef5dd4596502a0e1013f857e3027acd6091915fea77037921d70ee1a99988a100d994d3d3c2e323b04dd4c5ffd516cf13 + languageName: node + linkType: hard + "class-is@npm:^1.1.0": version: 1.1.0 resolution: "class-is@npm:1.1.0" @@ -15643,6 +16544,13 @@ __metadata: languageName: node linkType: hard +"classnames@npm:^2.3.0": + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: 58eb394e8817021b153bb6e7d782cfb667e4ab390cb2e9dac2fc7c6b979d1cc2b2a733093955fc5c94aa79ef5c8c89f11ab77780894509be6afbb91dddd79d15 + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -15825,6 +16733,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.0.0, clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: cdfb57fa6c7649bbff98d9028c2f0de2f91c86f551179541cf784b1cfdc1562dcb951955f46d54d930a3879931a980e32a46b598acaea274728dbe068deca919 + languageName: node + linkType: hard + "co@npm:^4.6.0": version: 4.6.0 resolution: "co@npm:4.6.0" @@ -16133,7 +17048,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0": +"cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" checksum: aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 @@ -16418,6 +17333,20 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^2.0.0": + version: 2.0.2 + resolution: "data-uri-to-buffer@npm:2.0.2" + checksum: 152bec5e77513ee253a7c686700a1723246f582dad8b614e8eaaaba7fa45a15c8671ae4b8f4843f4f3a002dae1d3e7a20f852f7d7bdc8b4c15cfe7adfdfb07f8 + languageName: node + linkType: hard + +"date-fns@npm:^3.6.0": + version: 3.6.0 + resolution: "date-fns@npm:3.6.0" + checksum: cac35c58926a3b5d577082ff2b253612ec1c79eb6754fddef46b6a8e826501ea2cb346ecbd211205f1ba382ddd1f9d8c3f00bf433ad63cc3063454d294e3a6b8 + languageName: node + linkType: hard + "death@npm:^1.1.0": version: 1.1.0 resolution: "death@npm:1.1.0" @@ -16816,6 +17745,13 @@ __metadata: languageName: node linkType: hard +"devalue@npm:^4.3.0": + version: 4.3.3 + resolution: "devalue@npm:4.3.3" + checksum: b53ab9fe1b7b9e45c3eeebe8ff59ef294aaa8f8206c61fa49dedcccb65622882e68b69f1c1c8e7129ff5590a405cd6fb5dbe88afd6fb0f6861d5393cdce93c76 + languageName: node + linkType: hard + "didyoumean@npm:^1.2.2": version: 1.2.2 resolution: "didyoumean@npm:1.2.2" @@ -17182,6 +18118,13 @@ __metadata: languageName: node linkType: hard +"environment@npm:^1.0.0": + version: 1.1.0 + resolution: "environment@npm:1.1.0" + checksum: dd3c1b9825e7f71f1e72b03c2344799ac73f2e9ef81b78ea8b373e55db021786c6b9f3858ea43a436a2c4611052670ec0afe85bc029c384cc71165feee2f4ba6 + languageName: node + linkType: hard + "erc721a@npm:^4.2.3": version: 4.2.3 resolution: "erc721a@npm:4.2.3" @@ -17438,6 +18381,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:0.17.19": + version: 0.17.19 + resolution: "esbuild@npm:0.17.19" + dependencies: + "@esbuild/android-arm": "npm:0.17.19" + "@esbuild/android-arm64": "npm:0.17.19" + "@esbuild/android-x64": "npm:0.17.19" + "@esbuild/darwin-arm64": "npm:0.17.19" + "@esbuild/darwin-x64": "npm:0.17.19" + "@esbuild/freebsd-arm64": "npm:0.17.19" + "@esbuild/freebsd-x64": "npm:0.17.19" + "@esbuild/linux-arm": "npm:0.17.19" + "@esbuild/linux-arm64": "npm:0.17.19" + "@esbuild/linux-ia32": "npm:0.17.19" + "@esbuild/linux-loong64": "npm:0.17.19" + "@esbuild/linux-mips64el": "npm:0.17.19" + "@esbuild/linux-ppc64": "npm:0.17.19" + "@esbuild/linux-riscv64": "npm:0.17.19" + "@esbuild/linux-s390x": "npm:0.17.19" + "@esbuild/linux-x64": "npm:0.17.19" + "@esbuild/netbsd-x64": "npm:0.17.19" + "@esbuild/openbsd-x64": "npm:0.17.19" + "@esbuild/sunos-x64": "npm:0.17.19" + "@esbuild/win32-arm64": "npm:0.17.19" + "@esbuild/win32-ia32": "npm:0.17.19" + "@esbuild/win32-x64": "npm:0.17.19" + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 86ada7cad6d37a3445858fee31ca39fc6c0436c7c00b2e07b9ce308235be67f36aefe0dda25da9ab08653fde496d1e759d6ad891ce9479f9e1fb4964c8f2a0fa + languageName: node + linkType: hard + "esbuild@npm:^0.18.0": version: 0.18.20 resolution: "esbuild@npm:0.18.20" @@ -17675,6 +18695,89 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:~0.23.0": + version: 0.23.1 + resolution: "esbuild@npm:0.23.1" + dependencies: + "@esbuild/aix-ppc64": "npm:0.23.1" + "@esbuild/android-arm": "npm:0.23.1" + "@esbuild/android-arm64": "npm:0.23.1" + "@esbuild/android-x64": "npm:0.23.1" + "@esbuild/darwin-arm64": "npm:0.23.1" + "@esbuild/darwin-x64": "npm:0.23.1" + "@esbuild/freebsd-arm64": "npm:0.23.1" + "@esbuild/freebsd-x64": "npm:0.23.1" + "@esbuild/linux-arm": "npm:0.23.1" + "@esbuild/linux-arm64": "npm:0.23.1" + "@esbuild/linux-ia32": "npm:0.23.1" + "@esbuild/linux-loong64": "npm:0.23.1" + "@esbuild/linux-mips64el": "npm:0.23.1" + "@esbuild/linux-ppc64": "npm:0.23.1" + "@esbuild/linux-riscv64": "npm:0.23.1" + "@esbuild/linux-s390x": "npm:0.23.1" + "@esbuild/linux-x64": "npm:0.23.1" + "@esbuild/netbsd-x64": "npm:0.23.1" + "@esbuild/openbsd-arm64": "npm:0.23.1" + "@esbuild/openbsd-x64": "npm:0.23.1" + "@esbuild/sunos-x64": "npm:0.23.1" + "@esbuild/win32-arm64": "npm:0.23.1" + "@esbuild/win32-ia32": "npm:0.23.1" + "@esbuild/win32-x64": "npm:0.23.1" + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: f55fbd0bfb0f86ce67a6d2c6f6780729d536c330999ecb9f5a38d578fb9fda820acbbc67d6d1d377eed8fed50fc38f14ff9cb014f86dafab94269a7fb2177018 + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -17956,6 +19059,13 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^0.6.1": + version: 0.6.1 + resolution: "estree-walker@npm:0.6.1" + checksum: b8da7815030c4e0b735f5f8af370af09525e052ee14e539cecabc24ad6da1782448778361417e7c438091a59e7ca9f4a0c11642f7da4f2ebf1ba7a150a590bcc + languageName: node + linkType: hard + "estree-walker@npm:^2.0.2": version: 2.0.2 resolution: "estree-walker@npm:2.0.2" @@ -17963,6 +19073,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": "npm:^1.0.0" + checksum: a65728d5727b71de172c5df323385755a16c0fdab8234dc756c3854cfee343261ddfbb72a809a5660fac8c75d960bb3e21aa898c2d7e9b19bb298482ca58a3af + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -18301,6 +19420,13 @@ __metadata: languageName: node linkType: hard +"exit-hook@npm:^2.2.1": + version: 2.2.1 + resolution: "exit-hook@npm:2.2.1" + checksum: 75835919e0aca624daa8d114c0014ae84506c4b79ac5806748cc7a86d1610a864ee974be58eec823c7757e5e6b07a5e332647e20ef84f6cc3dc3385c953c78c9 + languageName: node + linkType: hard + "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -19414,6 +20540,16 @@ __metadata: languageName: node linkType: hard +"get-source@npm:^2.0.12": + version: 2.0.12 + resolution: "get-source@npm:2.0.12" + dependencies: + data-uri-to-buffer: "npm:^2.0.0" + source-map: "npm:^0.6.1" + checksum: 6ba35ae0755046199b57d7fe254d50c6d7550d3b150e065a3607e3da8c55c617302f4c7cc3712252c7810954a04e2e56467ad02a0798c0841a5e980064bd3048 + languageName: node + linkType: hard + "get-stream@npm:^3.0.0": version: 3.0.0 resolution: "get-stream@npm:3.0.0" @@ -19463,6 +20599,15 @@ __metadata: languageName: node linkType: hard +"get-tsconfig@npm:^4.7.5": + version: 4.8.1 + resolution: "get-tsconfig@npm:4.8.1" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 3fb5a8ad57b9633eaea085d81661e9e5c9f78b35d8f8689eaf8b8b45a2a3ebf3b3422266d4d7df765e308cc1e6231648d114803ab3d018332e29916f2c1de036 + languageName: node + linkType: hard + "getpass@npm:^0.1.1": version: 0.1.7 resolution: "getpass@npm:0.1.7" @@ -21964,6 +23109,13 @@ __metadata: languageName: node linkType: hard +"js-tokens@npm:^9.0.0": + version: 9.0.0 + resolution: "js-tokens@npm:9.0.0" + checksum: 65e7a55a1a18d61f1cf94bfd7704da870b74337fa08d4c58118e69a8b10225b5ad887ff3ae595d720301b0924811a9b0594c679621a85ecbac6e3aac8533c53b + languageName: node + linkType: hard + "js-yaml@npm:3.13.1": version: 3.13.1 resolution: "js-yaml@npm:3.13.1" @@ -22631,6 +23783,16 @@ __metadata: languageName: node linkType: hard +"local-pkg@npm:^0.5.0": + version: 0.5.0 + resolution: "local-pkg@npm:0.5.0" + dependencies: + mlly: "npm:^1.4.2" + pkg-types: "npm:^1.0.3" + checksum: 20f4caba50dc6fb00ffcc1a78bc94b5acb33995e0aadf4d4edcdeab257e891aa08f50afddf02f3240b2c3d02432bc2078f2a916a280ed716b64753a3d250db70 + languageName: node + linkType: hard + "locate-path@npm:^2.0.0": version: 2.0.0 resolution: "locate-path@npm:2.0.0" @@ -22797,7 +23959,7 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^2.3.6": +"loupe@npm:^2.3.6, loupe@npm:^2.3.7": version: 2.3.7 resolution: "loupe@npm:2.3.7" dependencies: @@ -22892,6 +24054,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.25.3": + version: 0.25.9 + resolution: "magic-string@npm:0.25.9" + dependencies: + sourcemap-codec: "npm:^1.4.8" + checksum: 87a14b944bd169821cbd54b169a7ab6b0348fd44b5497266dc555dd70280744e9e88047da9dcb95675bdc23b1ce33f13398b0f70b3be7b858225ccb1d185ff51 + languageName: node + linkType: hard + "magic-string@npm:^0.27.0": version: 0.27.0 resolution: "magic-string@npm:0.27.0" @@ -22901,7 +24072,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.0": +"magic-string@npm:^0.30.0, magic-string@npm:^0.30.5": version: 0.30.11 resolution: "magic-string@npm:0.30.11" dependencies: @@ -23241,6 +24412,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: b2d31580deb58be89adaa1877cbbf152b7604b980fd7ef8f08b9e96bfedf7d605d9c23a8ba62aa12c8580b910cd7c1d27b7331d0f40f7a14e17d5a0bbec3b49f + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -23299,6 +24479,28 @@ __metadata: languageName: node linkType: hard +"miniflare@npm:3.20240821.1": + version: 3.20240821.1 + resolution: "miniflare@npm:3.20240821.1" + dependencies: + "@cspotcode/source-map-support": "npm:0.8.1" + acorn: "npm:^8.8.0" + acorn-walk: "npm:^8.2.0" + capnp-ts: "npm:^0.7.0" + exit-hook: "npm:^2.2.1" + glob-to-regexp: "npm:^0.4.1" + stoppable: "npm:^1.1.0" + undici: "npm:^5.28.4" + workerd: "npm:1.20240821.1" + ws: "npm:^8.17.1" + youch: "npm:^3.2.2" + zod: "npm:^3.22.3" + bin: + miniflare: bootstrap.js + checksum: ed6945094d714474fb88a157597148d447440c3dd581e08395da8046c9d2958c402976a017b2172981af517c5dd74b635103682a50e971ce4e565f73b89535b0 + languageName: node + linkType: hard + "minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": version: 1.0.1 resolution: "minimalistic-assert@npm:1.0.1" @@ -23558,7 +24760,7 @@ __metadata: languageName: node linkType: hard -"mlly@npm:^1.7.1": +"mlly@npm:^1.4.2, mlly@npm:^1.7.1": version: 1.7.1 resolution: "mlly@npm:1.7.1" dependencies: @@ -23784,6 +24986,15 @@ __metadata: languageName: node linkType: hard +"mustache@npm:^4.2.0": + version: 4.2.0 + resolution: "mustache@npm:4.2.0" + bin: + mustache: bin/mustache + checksum: 6e668bd5803255ab0779c3983b9412b5c4f4f90e822230e0e8f414f5449ed7a137eed29430e835aa689886f663385cfe05f808eb34b16e1f3a95525889b05cd3 + languageName: node + linkType: hard + "mute-stream@npm:^1.0.0": version: 1.0.0 resolution: "mute-stream@npm:1.0.0" @@ -23827,7 +25038,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.7": +"nanoid@npm:^3.3.3, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" bin: @@ -24011,6 +25222,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 05bab6868633bf9ad4c3b1dd50ec501c22ffd69f556cdf169a00998ca1d03e8107a6032ba013852f202035372021b845603aeccd7dfcb58cdb7430013b3daa8d + languageName: node + linkType: hard + "node-gyp-build@npm:4.3.0": version: 4.3.0 resolution: "node-gyp-build@npm:4.3.0" @@ -24597,6 +25815,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^5.0.0": + version: 5.0.0 + resolution: "p-limit@npm:5.0.0" + dependencies: + yocto-queue: "npm:^1.0.0" + checksum: 87bf5837dee6942f0dbeff318436179931d9a97848d1b07dbd86140a477a5d2e6b90d9701b210b4e21fe7beaea2979dfde366e4f576fa644a59bd4d6a6371da7 + languageName: node + linkType: hard + "p-locate@npm:^2.0.0": version: 2.0.0 resolution: "p-locate@npm:2.0.0" @@ -24865,6 +26092,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:^6.2.0": + version: 6.2.2 + resolution: "path-to-regexp@npm:6.2.2" + checksum: f7d11c1a9e02576ce0294f4efdc523c11b73894947afdf7b23a0d0f7c6465d7a7772166e770ddf1495a8017cc0ee99e3e8a15ed7302b6b948b89a6dd4eea895e + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -24872,7 +26106,7 @@ __metadata: languageName: node linkType: hard -"pathe@npm:^1.1.2": +"pathe@npm:^1.1.1, pathe@npm:^1.1.2": version: 1.1.2 resolution: "pathe@npm:1.1.2" checksum: f201d796351bf7433d147b92c20eb154a4e0ea83512017bf4ec4e492a5d6e738fb45798be4259a61aa81270179fce11026f6ff0d3fa04173041de044defe9d80 @@ -25040,6 +26274,17 @@ __metadata: languageName: node linkType: hard +"pkg-types@npm:^1.0.3": + version: 1.2.0 + resolution: "pkg-types@npm:1.2.0" + dependencies: + confbox: "npm:^0.1.7" + mlly: "npm:^1.7.1" + pathe: "npm:^1.1.2" + checksum: ed732842b86260395b82e31afc0dd8316e74642a78754ad148a5500ca5537565c6dfbd6c80c2dc92077afc1beb471b05a85a9572089cc8a1bba82248c331bf45 + languageName: node + linkType: hard + "pkg-types@npm:^1.1.1": version: 1.1.3 resolution: "pkg-types@npm:1.1.3" @@ -25148,6 +26393,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.43": + version: 8.4.44 + resolution: "postcss@npm:8.4.44" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.1" + source-map-js: "npm:^1.2.0" + checksum: aac7ed383fdcde9def6ed814ee03bc3de68b345e3f9bea414df2daca08185b6cfb4044fe9f67e1d9e886f29642373b34fd4fde5976204ca66a5481859afdcb7d + languageName: node + linkType: hard + "prebuild-install@npm:^5.3.4": version: 5.3.6 resolution: "prebuild-install@npm:5.3.6" @@ -25298,6 +26554,13 @@ __metadata: languageName: node linkType: hard +"printable-characters@npm:^1.0.42": + version: 1.0.42 + resolution: "printable-characters@npm:1.0.42" + checksum: 5fd9f44f2b24c9d875a97642a72be27f53aaac7f0f8f2792f969f3082e4516878db21cfa999f827606b002a890e6afeac0e0cc8dcb0d2d7965252975e634c6b2 + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -25931,6 +27194,19 @@ __metadata: languageName: node linkType: hard +"react-tooltip@npm:^5.28.0": + version: 5.28.0 + resolution: "react-tooltip@npm:5.28.0" + dependencies: + "@floating-ui/dom": "npm:^1.6.1" + classnames: "npm:^2.3.0" + peerDependencies: + react: ">=16.14.0" + react-dom: ">=16.14.0" + checksum: ec13ad0fafcae51c9c1193c6f0bccba4e7047e9d02eaf77231474cefd1a3d05254e76f27229808e79dad4c0a8c47b8e5cafdad47920e34a11d7a2703adf5f998 + languageName: node + linkType: hard + "react@npm:^18.2.0": version: 18.3.1 resolution: "react@npm:18.3.1" @@ -26399,7 +27675,7 @@ __metadata: languageName: node linkType: hard -"resolve.exports@npm:^2.0.0": +"resolve.exports@npm:^2.0.0, resolve.exports@npm:^2.0.2": version: 2.0.2 resolution: "resolve.exports@npm:2.0.2" checksum: f1cc0b6680f9a7e0345d783e0547f2a5110d8336b3c2a4227231dd007271ffd331fd722df934f017af90bae0373920ca0d4005da6f76cb3176c8ae426370f893 @@ -26435,7 +27711,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2": +"resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.2, resolve@npm:^1.22.8": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -26477,7 +27753,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin": +"resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -26622,6 +27898,35 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-inject@npm:^3.0.0": + version: 3.0.2 + resolution: "rollup-plugin-inject@npm:3.0.2" + dependencies: + estree-walker: "npm:^0.6.1" + magic-string: "npm:^0.25.3" + rollup-pluginutils: "npm:^2.8.1" + checksum: 34081611c4b00b582339fc76880844d9729d9a26ede987c9939440cb0affe5965d4c9b1ebb62a021bb67e118426420de77114731404fa57126e35186267548e7 + languageName: node + linkType: hard + +"rollup-plugin-node-polyfills@npm:^0.2.1": + version: 0.2.1 + resolution: "rollup-plugin-node-polyfills@npm:0.2.1" + dependencies: + rollup-plugin-inject: "npm:^3.0.0" + checksum: 283c108108f93684975c83fd2b274d028162a9df0db2225737bfd0f8cab9215f0228d3703928ef667a8ba2f4749649ba06c58b89f48a211d7116e7f98fc988dd + languageName: node + linkType: hard + +"rollup-pluginutils@npm:^2.8.1": + version: 2.8.2 + resolution: "rollup-pluginutils@npm:2.8.2" + dependencies: + estree-walker: "npm:^0.6.1" + checksum: f3dc20a8731523aff43e07fa50ed84857e9dd3ab81e2cfb0351d517c46820e585bfbd1530a5dddec3ac14d61d41eb9bf50b38ded987e558292790331cc5b0628 + languageName: node + linkType: hard + "rollup@npm:^2.25.0 || ^3.3.0": version: 3.29.4 resolution: "rollup@npm:3.29.4" @@ -26632,30 +27937,93 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 9e39d54e23731a4c4067e9c02910cdf7479a0f9a7584796e2dc6efaa34bb1e5e015c062c87d1e64d96038baca76cefd47681ff22604fae5827147f54123dc6d0 + checksum: 9e39d54e23731a4c4067e9c02910cdf7479a0f9a7584796e2dc6efaa34bb1e5e015c062c87d1e64d96038baca76cefd47681ff22604fae5827147f54123dc6d0 + languageName: node + linkType: hard + +"rollup@npm:^4.13.0": + version: 4.19.1 + resolution: "rollup@npm:4.19.1" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.19.1" + "@rollup/rollup-android-arm64": "npm:4.19.1" + "@rollup/rollup-darwin-arm64": "npm:4.19.1" + "@rollup/rollup-darwin-x64": "npm:4.19.1" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.19.1" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.19.1" + "@rollup/rollup-linux-arm64-gnu": "npm:4.19.1" + "@rollup/rollup-linux-arm64-musl": "npm:4.19.1" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.19.1" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.19.1" + "@rollup/rollup-linux-s390x-gnu": "npm:4.19.1" + "@rollup/rollup-linux-x64-gnu": "npm:4.19.1" + "@rollup/rollup-linux-x64-musl": "npm:4.19.1" + "@rollup/rollup-win32-arm64-msvc": "npm:4.19.1" + "@rollup/rollup-win32-ia32-msvc": "npm:4.19.1" + "@rollup/rollup-win32-x64-msvc": "npm:4.19.1" + "@types/estree": "npm:1.0.5" + fsevents: "npm:~2.3.2" + dependenciesMeta: + "@rollup/rollup-android-arm-eabi": + optional: true + "@rollup/rollup-android-arm64": + optional: true + "@rollup/rollup-darwin-arm64": + optional: true + "@rollup/rollup-darwin-x64": + optional: true + "@rollup/rollup-linux-arm-gnueabihf": + optional: true + "@rollup/rollup-linux-arm-musleabihf": + optional: true + "@rollup/rollup-linux-arm64-gnu": + optional: true + "@rollup/rollup-linux-arm64-musl": + optional: true + "@rollup/rollup-linux-powerpc64le-gnu": + optional: true + "@rollup/rollup-linux-riscv64-gnu": + optional: true + "@rollup/rollup-linux-s390x-gnu": + optional: true + "@rollup/rollup-linux-x64-gnu": + optional: true + "@rollup/rollup-linux-x64-musl": + optional: true + "@rollup/rollup-win32-arm64-msvc": + optional: true + "@rollup/rollup-win32-ia32-msvc": + optional: true + "@rollup/rollup-win32-x64-msvc": + optional: true + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 4e46275cb2280d999833c7a0c20a292a201281bff6ae9583673788a8125e2e2cc13238092fa1639dab220d864f92d91efcff07cca0d29d8dfded4839b100da51 languageName: node linkType: hard -"rollup@npm:^4.13.0": - version: 4.19.1 - resolution: "rollup@npm:4.19.1" - dependencies: - "@rollup/rollup-android-arm-eabi": "npm:4.19.1" - "@rollup/rollup-android-arm64": "npm:4.19.1" - "@rollup/rollup-darwin-arm64": "npm:4.19.1" - "@rollup/rollup-darwin-x64": "npm:4.19.1" - "@rollup/rollup-linux-arm-gnueabihf": "npm:4.19.1" - "@rollup/rollup-linux-arm-musleabihf": "npm:4.19.1" - "@rollup/rollup-linux-arm64-gnu": "npm:4.19.1" - "@rollup/rollup-linux-arm64-musl": "npm:4.19.1" - "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.19.1" - "@rollup/rollup-linux-riscv64-gnu": "npm:4.19.1" - "@rollup/rollup-linux-s390x-gnu": "npm:4.19.1" - "@rollup/rollup-linux-x64-gnu": "npm:4.19.1" - "@rollup/rollup-linux-x64-musl": "npm:4.19.1" - "@rollup/rollup-win32-arm64-msvc": "npm:4.19.1" - "@rollup/rollup-win32-ia32-msvc": "npm:4.19.1" - "@rollup/rollup-win32-x64-msvc": "npm:4.19.1" +"rollup@npm:^4.20.0": + version: 4.21.2 + resolution: "rollup@npm:4.21.2" + dependencies: + "@rollup/rollup-android-arm-eabi": "npm:4.21.2" + "@rollup/rollup-android-arm64": "npm:4.21.2" + "@rollup/rollup-darwin-arm64": "npm:4.21.2" + "@rollup/rollup-darwin-x64": "npm:4.21.2" + "@rollup/rollup-linux-arm-gnueabihf": "npm:4.21.2" + "@rollup/rollup-linux-arm-musleabihf": "npm:4.21.2" + "@rollup/rollup-linux-arm64-gnu": "npm:4.21.2" + "@rollup/rollup-linux-arm64-musl": "npm:4.21.2" + "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.21.2" + "@rollup/rollup-linux-riscv64-gnu": "npm:4.21.2" + "@rollup/rollup-linux-s390x-gnu": "npm:4.21.2" + "@rollup/rollup-linux-x64-gnu": "npm:4.21.2" + "@rollup/rollup-linux-x64-musl": "npm:4.21.2" + "@rollup/rollup-win32-arm64-msvc": "npm:4.21.2" + "@rollup/rollup-win32-ia32-msvc": "npm:4.21.2" + "@rollup/rollup-win32-x64-msvc": "npm:4.21.2" "@types/estree": "npm:1.0.5" fsevents: "npm:~2.3.2" dependenciesMeta: @@ -26695,7 +28063,7 @@ __metadata: optional: true bin: rollup: dist/bin/rollup - checksum: 4e46275cb2280d999833c7a0c20a292a201281bff6ae9583673788a8125e2e2cc13238092fa1639dab220d864f92d91efcff07cca0d29d8dfded4839b100da51 + checksum: 5d679af1a04170f7164e3e975a375adb76f9bbf34d1ad8d9c3fa789252d377e7d364dfee054a4283121f9f9368d7b35404b9d42fb260be314d34739243ab0722 languageName: node linkType: hard @@ -26896,6 +28264,16 @@ __metadata: languageName: node linkType: hard +"selfsigned@npm:^2.0.1": + version: 2.4.1 + resolution: "selfsigned@npm:2.4.1" + dependencies: + "@types/node-forge": "npm:^1.3.0" + node-forge: "npm:^1" + checksum: 52536623f1cfdeb2f8b9198377f2ce7931c677ea69421238d1dc1ea2983bbe258e56c19e7d1af87035cad7270f19b7e996eaab1212e724d887722502f68e17f2 + languageName: node + linkType: hard + "semaphore-async-await@npm:^1.5.1": version: 1.5.1 resolution: "semaphore-async-await@npm:1.5.1" @@ -26950,6 +28328,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.5.1": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 36b1fbe1a2b6f873559cd57b238f1094a053dbfd997ceeb8757d79d1d2089c56d1321b9f1069ce263dc64cfa922fa1d2ad566b39426fe1ac6c723c1487589e10 + languageName: node + linkType: hard + "semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" @@ -27196,6 +28583,13 @@ __metadata: languageName: node linkType: hard +"siginfo@npm:^2.0.0": + version: 2.0.0 + resolution: "siginfo@npm:2.0.0" + checksum: e93ff66c6531a079af8fb217240df01f980155b5dc408d2d7bebc398dd284e383eb318153bf8acd4db3c4fe799aa5b9a641e38b0ba3b1975700b1c89547ea4e7 + languageName: node + linkType: hard + "signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" @@ -27665,6 +29059,13 @@ __metadata: languageName: node linkType: hard +"sourcemap-codec@npm:^1.4.8": + version: 1.4.8 + resolution: "sourcemap-codec@npm:1.4.8" + checksum: 6fc57a151e982b5c9468362690c6d062f3a0d4d8520beb68a82f319c79e7a4d7027eeb1e396de0ecc2cd19491e1d602b2d06fd444feac9b63dd43fea4c55a857 + languageName: node + linkType: hard + "space-separated-tokens@npm:^1.0.0": version: 1.1.5 resolution: "space-separated-tokens@npm:1.1.5" @@ -27769,6 +29170,13 @@ __metadata: languageName: node linkType: hard +"stackback@npm:0.0.2": + version: 0.0.2 + resolution: "stackback@npm:0.0.2" + checksum: 2d4dc4e64e2db796de4a3c856d5943daccdfa3dd092e452a1ce059c81e9a9c29e0b9badba91b43ef0d5ff5c04ee62feb3bcc559a804e16faf447bac2d883aa99 + languageName: node + linkType: hard + "stacktrace-parser@npm:^0.1.10": version: 0.1.10 resolution: "stacktrace-parser@npm:0.1.10" @@ -27778,6 +29186,16 @@ __metadata: languageName: node linkType: hard +"stacktracey@npm:^2.1.8": + version: 2.1.8 + resolution: "stacktracey@npm:2.1.8" + dependencies: + as-table: "npm:^1.0.36" + get-source: "npm:^2.0.12" + checksum: c87f708b639636788c4b46ecc6e503c27b6124bec724bcdc3180d7cdddfab0dee370225009e3b407adaedf847362cfc77af64f01c805516e39a28d16c6d40df8 + languageName: node + linkType: hard + "statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" @@ -27785,6 +29203,13 @@ __metadata: languageName: node linkType: hard +"std-env@npm:^3.5.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 6ee0cca1add3fd84656b0002cfbc5bfa20340389d9ba4720569840f1caa34bce74322aef4c93f046391583e50649d0cf81a5f8fe1d411e50b659571690a45f12 + languageName: node + linkType: hard + "stealthy-require@npm:^1.1.1": version: 1.1.1 resolution: "stealthy-require@npm:1.1.1" @@ -27801,6 +29226,13 @@ __metadata: languageName: node linkType: hard +"stoppable@npm:^1.1.0": + version: 1.1.0 + resolution: "stoppable@npm:1.1.0" + checksum: 63104fcbdece130bc4906fd982061e763d2ef48065ed1ab29895e5ad00552c625f8a4c50c9cd2e3bfa805c8a2c3bfdda0f07c5ae39694bd2d5cb0bee1618d1e9 + languageName: node + linkType: hard + "store2@npm:^2.14.2": version: 2.14.3 resolution: "store2@npm:2.14.3" @@ -28124,6 +29556,15 @@ __metadata: languageName: node linkType: hard +"strip-literal@npm:^2.0.0": + version: 2.1.0 + resolution: "strip-literal@npm:2.1.0" + dependencies: + js-tokens: "npm:^9.0.0" + checksum: 21c813aa1e669944e7e2318c8c927939fb90b0c52f53f57282bfc3dd6e19d53f70004f1f1693e33e5e790ad5ef102b0fce2b243808229d1ce07ae71f326c0e82 + languageName: node + linkType: hard + "strnum@npm:^1.0.5": version: 1.0.5 resolution: "strnum@npm:1.0.5" @@ -28285,6 +29726,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^6.0.0": + version: 6.2.0 + resolution: "tabbable@npm:6.2.0" + checksum: 980fa73476026e99dcacfc0d6e000d41d42c8e670faf4682496d30c625495e412c4369694f2a15cf1e5252d22de3c396f2b62edbe8d60b5dadc40d09e3f2dde3 + languageName: node + linkType: hard + "table-layout@npm:^1.0.2": version: 1.0.2 resolution: "table-layout@npm:1.0.2" @@ -28323,9 +29771,9 @@ __metadata: languageName: node linkType: hard -"tailwindcss@npm:^3.2.4": - version: 3.4.7 - resolution: "tailwindcss@npm:3.4.7" +"tailwindcss@npm:^3.4.13": + version: 3.4.13 + resolution: "tailwindcss@npm:3.4.13" dependencies: "@alloc/quick-lru": "npm:^5.2.0" arg: "npm:^5.0.2" @@ -28352,7 +29800,7 @@ __metadata: bin: tailwind: lib/cli.js tailwindcss: lib/cli.js - checksum: bda3280905b05bb3e7e95a350e028a58a19336a854ebebe65816c7625ec49ba4d2af7ef82c169407f67cbf150fb6acbe1210e8ade7e0180fa8039e3607077304 + checksum: 01b8dd35a65a028474c632b9ea7fb38634060a2c70f1f3fdfa2fe6ec74dec8224e2ee1178a5428182849790dad324e7a810de7301a9126946528c59d37f455cf languageName: node linkType: hard @@ -28603,7 +30051,21 @@ __metadata: languageName: node linkType: hard -"tinyspy@npm:^2.1.1": +"tinybench@npm:^2.5.1": + version: 2.9.0 + resolution: "tinybench@npm:2.9.0" + checksum: cfa1e1418e91289219501703c4693c70708c91ffb7f040fd318d24aef419fb5a43e0c0160df9471499191968b2451d8da7f8087b08c3133c251c40d24aced06c + languageName: node + linkType: hard + +"tinypool@npm:^0.8.2": + version: 0.8.4 + resolution: "tinypool@npm:0.8.4" + checksum: 7365944c2532f240111443e7012be31a634faf1a02db08a91db3aa07361c26a374d0be00a0f2ea052c4bee39c107ba67f1f814c108d9d51dfc725c559c1a9c03 + languageName: node + linkType: hard + +"tinyspy@npm:^2.1.1, tinyspy@npm:^2.2.0": version: 2.2.1 resolution: "tinyspy@npm:2.2.1" checksum: 170d6232e87f9044f537b50b406a38fbfd6f79a261cd12b92879947bd340939a833a678632ce4f5c4a6feab4477e9c21cd43faac3b90b68b77dd0536c4149736 @@ -28862,6 +30324,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.2.0": + version: 2.7.0 + resolution: "tslib@npm:2.7.0" + checksum: 9a5b47ddac65874fa011c20ff76db69f97cf90c78cff5934799ab8894a5342db2d17b4e7613a087046bc1d133d21547ddff87ac558abeec31ffa929c88b7fce6 + languageName: node + linkType: hard + "tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" @@ -28887,6 +30356,22 @@ __metadata: languageName: node linkType: hard +"tsx@npm:^4.19.1": + version: 4.19.1 + resolution: "tsx@npm:4.19.1" + dependencies: + esbuild: "npm:~0.23.0" + fsevents: "npm:~2.3.3" + get-tsconfig: "npm:^4.7.5" + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: 1f5f0b7c4107fc18f523e94c79204b043641aa328f721324795cc961826879035652a1f19fe29ba420465d9f4bacb0f47e08f0bd4b934684ab45727eca110311 + languageName: node + linkType: hard + "tsx@npm:^4.7.1": version: 4.7.1 resolution: "tsx@npm:4.7.1" @@ -29193,16 +30678,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^5.1.6": - version: 5.3.2 - resolution: "typescript@npm:5.3.2" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 415e5fb6611f5713e460bad48039f00bcfdbde53a2f911727862d5aa9c5d5edd250059a419df382d8f031709e15a169c41eb62b6a401da5eec7ac0f4e359d6ac - languageName: node - linkType: hard - "typescript@patch:typescript@npm%3A5.3.3#optional!builtin": version: 5.3.3 resolution: "typescript@patch:typescript@npm%3A5.3.3#optional!builtin::version=5.3.3&hash=e012d7" @@ -29213,16 +30688,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.1.6#optional!builtin": - version: 5.3.2 - resolution: "typescript@patch:typescript@npm%3A5.3.2#optional!builtin::version=5.3.2&hash=e012d7" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 1b45cdfb577a78ae7a9a9d0b77a7b772142cb98ba05e4e5aefba7044a028ded885bcecef63166407a5986645cea816fe4986894336aacd5e791796ea79a6a7ed - languageName: node - linkType: hard - "typical@npm:^4.0.0": version: 4.0.0 resolution: "typical@npm:4.0.0" @@ -29244,7 +30709,7 @@ __metadata: languageName: node linkType: hard -"ufo@npm:^1.5.3": +"ufo@npm:^1.5.3, ufo@npm:^1.5.4": version: 1.5.4 resolution: "ufo@npm:1.5.4" checksum: a885ed421e656aea6ca64e9727b8118a9488715460b6f1a0f0427118adfe2f2830fe7c1d5bd9c5c754a332e6807516551cd663ea67ce9ed6a4e3edc739916335 @@ -29309,6 +30774,18 @@ __metadata: languageName: node linkType: hard +"unenv@npm:unenv-nightly@2.0.0-1724863496.70db6f1": + version: 2.0.0-1724863496.70db6f1 + resolution: "unenv-nightly@npm:2.0.0-1724863496.70db6f1" + dependencies: + defu: "npm:^6.1.4" + ohash: "npm:^1.1.3" + pathe: "npm:^1.1.2" + ufo: "npm:^1.5.4" + checksum: 1133947cc084eedc2d256025f81d0c3716600415efe6f49950091084b0ece18b02d676f07537e5de17237621f5499d3ed754c8f35cd13109252ed1b204b38c55 + languageName: node + linkType: hard + "unfetch@npm:^4.2.0": version: 4.2.0 resolution: "unfetch@npm:4.2.0" @@ -29751,6 +31228,64 @@ __metadata: languageName: node linkType: hard +"vite-node@npm:1.4.0": + version: 1.4.0 + resolution: "vite-node@npm:1.4.0" + dependencies: + cac: "npm:^6.7.14" + debug: "npm:^4.3.4" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + vite: "npm:^5.0.0" + bin: + vite-node: vite-node.mjs + checksum: 691e828c2abe6b62d44183c4e04bdfd119fed405439126fbdc5bfb791644baee3961c1ce429a67b360cc3d8b7c472160c7e82c59491f044a232b4ff480d8a2a2 + languageName: node + linkType: hard + +"vite@npm:^5.0.0": + version: 5.4.3 + resolution: "vite@npm:5.4.3" + dependencies: + esbuild: "npm:^0.21.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.43" + rollup: "npm:^4.20.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 864bf64fe57bd26b1527ea6d8dac2f4f8b834791f836590043463c0ded6a150a9407ffac6a859abc34ec013ecfcd23f405c20aa76f4ad99deab1176d5f017574 + languageName: node + linkType: hard + "vite@npm:^5.1.1": version: 5.3.5 resolution: "vite@npm:5.3.5" @@ -29791,6 +31326,56 @@ __metadata: languageName: node linkType: hard +"vitest@npm:1.4.0": + version: 1.4.0 + resolution: "vitest@npm:1.4.0" + dependencies: + "@vitest/expect": "npm:1.4.0" + "@vitest/runner": "npm:1.4.0" + "@vitest/snapshot": "npm:1.4.0" + "@vitest/spy": "npm:1.4.0" + "@vitest/utils": "npm:1.4.0" + acorn-walk: "npm:^8.3.2" + chai: "npm:^4.3.10" + debug: "npm:^4.3.4" + execa: "npm:^8.0.1" + local-pkg: "npm:^0.5.0" + magic-string: "npm:^0.30.5" + pathe: "npm:^1.1.1" + picocolors: "npm:^1.0.0" + std-env: "npm:^3.5.0" + strip-literal: "npm:^2.0.0" + tinybench: "npm:^2.5.1" + tinypool: "npm:^0.8.2" + vite: "npm:^5.0.0" + vite-node: "npm:1.4.0" + why-is-node-running: "npm:^2.2.2" + peerDependencies: + "@edge-runtime/vm": "*" + "@types/node": ^18.0.0 || >=20.0.0 + "@vitest/browser": 1.4.0 + "@vitest/ui": 1.4.0 + happy-dom: "*" + jsdom: "*" + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@types/node": + optional: true + "@vitest/browser": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + bin: + vitest: vitest.mjs + checksum: cf4675657f4a9ea755d0af70d62827fca9daee64e81d0392067c70a0d1f5f8fd4a47523e28ecf42d667e4d4d7c68b09d5e08389d4b58dc36065364f6c76cda7d + languageName: node + linkType: hard + "walker@npm:^1.0.8": version: 1.0.8 resolution: "walker@npm:1.0.8" @@ -30361,6 +31946,18 @@ __metadata: languageName: node linkType: hard +"why-is-node-running@npm:^2.2.2": + version: 2.3.0 + resolution: "why-is-node-running@npm:2.3.0" + dependencies: + siginfo: "npm:^2.0.0" + stackback: "npm:0.0.2" + bin: + why-is-node-running: cli.js + checksum: 0de6e6cd8f2f94a8b5ca44e84cf1751eadcac3ebedcdc6e5fbbe6c8011904afcbc1a2777c53496ec02ced7b81f2e7eda61e76bf8262a8bc3ceaa1f6040508051 + languageName: node + linkType: hard + "wide-align@npm:1.1.3": version: 1.1.3 resolution: "wide-align@npm:1.1.3" @@ -30412,6 +32009,32 @@ __metadata: languageName: node linkType: hard +"workerd@npm:1.20240821.1": + version: 1.20240821.1 + resolution: "workerd@npm:1.20240821.1" + dependencies: + "@cloudflare/workerd-darwin-64": "npm:1.20240821.1" + "@cloudflare/workerd-darwin-arm64": "npm:1.20240821.1" + "@cloudflare/workerd-linux-64": "npm:1.20240821.1" + "@cloudflare/workerd-linux-arm64": "npm:1.20240821.1" + "@cloudflare/workerd-windows-64": "npm:1.20240821.1" + dependenciesMeta: + "@cloudflare/workerd-darwin-64": + optional: true + "@cloudflare/workerd-darwin-arm64": + optional: true + "@cloudflare/workerd-linux-64": + optional: true + "@cloudflare/workerd-linux-arm64": + optional: true + "@cloudflare/workerd-windows-64": + optional: true + bin: + workerd: bin/workerd + checksum: 4784019f2cd638efd505bd895766c76bf21f56333e93f4f1665c97e8c8877f9ef46ef2ce1682127706aa607902e8ea4dcfec852427858e78454a0d6181de38ce + languageName: node + linkType: hard + "workerpool@npm:6.2.1": version: 6.2.1 resolution: "workerpool@npm:6.2.1" @@ -30419,6 +32042,44 @@ __metadata: languageName: node linkType: hard +"wrangler@npm:3.74.0, wrangler@npm:^3.74.0": + version: 3.74.0 + resolution: "wrangler@npm:3.74.0" + dependencies: + "@cloudflare/kv-asset-handler": "npm:0.3.4" + "@cloudflare/workers-shared": "npm:0.4.1" + "@esbuild-plugins/node-globals-polyfill": "npm:^0.2.3" + "@esbuild-plugins/node-modules-polyfill": "npm:^0.2.2" + blake3-wasm: "npm:^2.1.5" + chokidar: "npm:^3.5.3" + date-fns: "npm:^3.6.0" + esbuild: "npm:0.17.19" + fsevents: "npm:~2.3.2" + miniflare: "npm:3.20240821.1" + nanoid: "npm:^3.3.3" + path-to-regexp: "npm:^6.2.0" + resolve: "npm:^1.22.8" + resolve.exports: "npm:^2.0.2" + selfsigned: "npm:^2.0.1" + source-map: "npm:^0.6.1" + unenv: "npm:unenv-nightly@2.0.0-1724863496.70db6f1" + workerd: "npm:1.20240821.1" + xxhash-wasm: "npm:^1.0.1" + peerDependencies: + "@cloudflare/workers-types": ^4.20240821.1 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@cloudflare/workers-types": + optional: true + bin: + wrangler: bin/wrangler.js + wrangler2: bin/wrangler.js + checksum: 12e3b97eca2ac1ce6f28d7b584e5be3f17377d84c7794df95dc68f6eadccabcf1a6640db7f76ceebc8855159b603dbc1f453dc27ff536a831a9e913c6d681151 + languageName: node + linkType: hard + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" @@ -30571,7 +32232,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.2.3": +"ws@npm:^8.17.1, ws@npm:^8.2.3": version: 8.18.0 resolution: "ws@npm:8.18.0" peerDependencies: @@ -30646,6 +32307,13 @@ __metadata: languageName: node linkType: hard +"xxhash-wasm@npm:^1.0.1": + version: 1.0.2 + resolution: "xxhash-wasm@npm:1.0.2" + checksum: fb66e00f57c87353688ff31a8456ca71e16b1c13610d94d09f83cbd859a1985de07ccfc6aa912a045c991da0078d4122d78d409123e36557afab7ce5d3b04a98 + languageName: node + linkType: hard + "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3" @@ -30688,6 +32356,15 @@ __metadata: languageName: node linkType: hard +"yaml@npm:2.4.5": + version: 2.4.5 + resolution: "yaml@npm:2.4.5" + bin: + yaml: bin.mjs + checksum: b09bf5a615a65276d433d76b8e34ad6b4c0320b85eb3f1a39da132c61ae6e2ff34eff4624e6458d96d49566c93cf43408ba5e568218293a8c6541a2006883f64 + languageName: node + linkType: hard + "yaml@npm:^1.10.2": version: 1.10.2 resolution: "yaml@npm:1.10.2" @@ -30695,15 +32372,6 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2, yaml@npm:^2.4.1": - version: 2.4.1 - resolution: "yaml@npm:2.4.1" - bin: - yaml: bin.mjs - checksum: 2c54fd69ef59126758ae710f9756405a7d41abcbb61aca894250d0e81e76057c14dc9bb00a9528f72f99b8f24077f694a6f7fd09cdd6711fcec2eebfbb5df409 - languageName: node - linkType: hard - "yaml@npm:^2.3.4": version: 2.5.0 resolution: "yaml@npm:2.5.0" @@ -30713,15 +32381,6 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.4.5": - version: 2.4.5 - resolution: "yaml@npm:2.4.5" - bin: - yaml: bin.mjs - checksum: b09bf5a615a65276d433d76b8e34ad6b4c0320b85eb3f1a39da132c61ae6e2ff34eff4624e6458d96d49566c93cf43408ba5e568218293a8c6541a2006883f64 - languageName: node - linkType: hard - "yargs-parser@npm:13.1.2, yargs-parser@npm:^13.1.2": version: 13.1.2 resolution: "yargs-parser@npm:13.1.2" @@ -30877,6 +32536,13 @@ __metadata: languageName: node linkType: hard +"yocto-queue@npm:^1.0.0": + version: 1.1.1 + resolution: "yocto-queue@npm:1.1.1" + checksum: f2e05b767ed3141e6372a80af9caa4715d60969227f38b1a4370d60bffe153c9c5b33a862905609afc9b375ec57cd40999810d20e5e10229a204e8bde7ef255c + languageName: node + linkType: hard + "yoctocolors-cjs@npm:^2.1.2": version: 2.1.2 resolution: "yoctocolors-cjs@npm:2.1.2" @@ -30884,6 +32550,17 @@ __metadata: languageName: node linkType: hard +"youch@npm:^3.2.2": + version: 3.3.3 + resolution: "youch@npm:3.3.3" + dependencies: + cookie: "npm:^0.5.0" + mustache: "npm:^4.2.0" + stacktracey: "npm:^2.1.8" + checksum: d13fb6f1e756823397ba02dd0c6f8a1b060a21327238872b46677ea1e1570b6b90fe216560a2ff9f34ba2eead6c442403b70df44354c6224bbfe31cc9a182c38 + languageName: node + linkType: hard + "zksync-web3@npm:^0.14.3": version: 0.14.4 resolution: "zksync-web3@npm:0.14.4" @@ -30909,6 +32586,13 @@ __metadata: languageName: node linkType: hard +"zod@npm:^3.22.3": + version: 3.23.8 + resolution: "zod@npm:3.23.8" + checksum: 846fd73e1af0def79c19d510ea9e4a795544a67d5b34b7e1c4d0425bf6bfd1c719446d94cdfa1721c1987d891321d61f779e8236fde517dc0e524aa851a6eff1 + languageName: node + linkType: hard + "zx@npm:^8.1.4": version: 8.1.4 resolution: "zx@npm:8.1.4"