Merge remote-tracking branch 'origin' into dan/gas-escalator-middleware

pull/3852/head
Daniel Savu 5 months ago
commit cf6c177e9f
No known key found for this signature in database
GPG Key ID: 795E587829AF7E08
  1. 7
      .changeset/clean-numbers-know.md
  2. 5
      .changeset/sweet-pandas-brush.md
  3. 28
      .github/actions/checkout-registry/action.yml
  4. 4
      .github/workflows/agent-release-artifacts.yml
  5. 25
      .github/workflows/monorepo-docker.yml
  6. 4
      .github/workflows/release.yml
  7. 13
      .github/workflows/rust-docker.yml
  8. 3
      .github/workflows/rust-skipped.yml
  9. 16
      .github/workflows/rust.yml
  10. 10
      .github/workflows/static-analysis.yml
  11. 8
      .github/workflows/storage-analysis.yml
  12. 9
      .github/workflows/test-skipped.yml
  13. 246
      .github/workflows/test.yml
  14. 1
      .registryrc
  15. 9
      Dockerfile
  16. 2
      package.json
  17. 1
      rust/Cargo.lock
  18. 5
      rust/Cargo.toml
  19. 3
      rust/agents/relayer/Cargo.toml
  20. 123
      rust/agents/relayer/src/msg/blacklist.rs
  21. 3
      rust/agents/relayer/src/msg/metadata/base.rs
  22. 1
      rust/agents/relayer/src/msg/mod.rs
  23. 50
      rust/agents/relayer/src/msg/op_queue.rs
  24. 54
      rust/agents/relayer/src/msg/op_submitter.rs
  25. 244
      rust/agents/relayer/src/msg/pending_message.rs
  26. 59
      rust/agents/relayer/src/msg/processor.rs
  27. 34
      rust/agents/relayer/src/relayer.rs
  28. 64
      rust/agents/relayer/src/settings/mod.rs
  29. 24
      rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs
  30. 131
      rust/agents/validator/src/submit.rs
  31. 29
      rust/chains/hyperlane-cosmos/src/interchain_gas.rs
  32. 26
      rust/chains/hyperlane-cosmos/src/mailbox.rs
  33. 28
      rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs
  34. 2
      rust/chains/hyperlane-cosmos/src/providers/grpc.rs
  35. 15
      rust/chains/hyperlane-cosmos/src/providers/rpc.rs
  36. 31
      rust/chains/hyperlane-cosmos/src/utils.rs
  37. 294
      rust/config/mainnet_config.json
  38. 6
      rust/hyperlane-base/Cargo.toml
  39. 8
      rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs
  40. 2
      rust/hyperlane-base/src/metrics/core.rs
  41. 1
      rust/hyperlane-core/Cargo.toml
  42. 2
      rust/hyperlane-core/src/accumulator/incremental.rs
  43. 34
      rust/hyperlane-core/src/chain.rs
  44. 8
      rust/hyperlane-core/src/rpc_clients/retry.rs
  45. 111
      rust/hyperlane-core/src/traits/pending_operation.rs
  46. 8
      rust/hyperlane-core/src/types/indexing.rs
  47. 2
      rust/sealevel/libraries/account-utils/src/lib.rs
  48. 2
      rust/sealevel/libraries/ecdsa-signature/Cargo.toml
  49. 2
      rust/sealevel/libraries/message-recipient-interface/Cargo.toml
  50. 2
      rust/sealevel/programs/hyperlane-sealevel-igp/Cargo.toml
  51. 2
      rust/sealevel/programs/mailbox-test/src/functional.rs
  52. 2
      rust/sealevel/programs/mailbox/Cargo.toml
  53. 5
      rust/utils/run-locally/src/cosmos/types.rs
  54. 10
      rust/utils/run-locally/src/invariants.rs
  55. 31
      solidity/CHANGELOG.md
  56. 38
      solidity/contracts/client/GasRouter.sol
  57. 81
      solidity/contracts/client/MailboxClient.sol
  58. 65
      solidity/contracts/client/Router.sol
  59. 1
      solidity/contracts/interfaces/isms/IMultisigIsm.sol
  60. 5
      solidity/contracts/isms/multisig/AbstractMultisigIsm.sol
  61. 12
      solidity/contracts/test/ERC20Test.sol
  62. 2
      solidity/contracts/test/TestGasRouter.sol
  63. 16
      solidity/contracts/token/HypNative.sol
  64. 12
      solidity/contracts/token/extensions/HypNativeScaled.sol
  65. 4
      solidity/contracts/token/extensions/HypXERC20.sol
  66. 1
      solidity/contracts/token/extensions/HypXERC20Lockbox.sol
  67. 18
      solidity/contracts/token/interfaces/IXERC20.sol
  68. 6
      solidity/contracts/token/libs/FastTokenRouter.sol
  69. 71
      solidity/contracts/token/libs/TokenRouter.sol
  70. 7
      solidity/package.json
  71. 40
      solidity/script/avs/eigenlayer_addresses.json
  72. 7
      solidity/test/InterchainAccountRouter.t.sol
  73. 33
      solidity/test/token/HypERC20.t.sol
  74. 5
      typescript/.changeset/pink-mirrors-sneeze.md
  75. 5
      typescript/.changeset/tall-radios-grin.md
  76. 8
      typescript/ccip-server/CHANGELOG.md
  77. 2
      typescript/ccip-server/package.json
  78. 43
      typescript/cli/CHANGELOG.md
  79. 6
      typescript/cli/ci-test.sh
  80. 10
      typescript/cli/package.json
  81. 6
      typescript/cli/src/avs/config.ts
  82. 8
      typescript/cli/src/avs/stakeRegistry.ts
  83. 13
      typescript/cli/src/commands/avs.ts
  84. 32
      typescript/cli/src/deploy/warp.ts
  85. 2
      typescript/cli/src/version.ts
  86. 36
      typescript/helloworld/CHANGELOG.md
  87. 6
      typescript/helloworld/contracts/HelloWorld.sol
  88. 9
      typescript/helloworld/package.json
  89. 45
      typescript/infra/CHANGELOG.md
  90. 60
      typescript/infra/config/environments/mainnet3/agent.ts
  91. 12
      typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json
  92. 9
      typescript/infra/config/environments/mainnet3/aw-validators/rc.json
  93. 17
      typescript/infra/config/environments/mainnet3/chains.ts
  94. 4544
      typescript/infra/config/environments/mainnet3/core/verification.json
  95. 8
      typescript/infra/config/environments/mainnet3/funding.ts
  96. 62
      typescript/infra/config/environments/mainnet3/gasPrices.json
  97. 9
      typescript/infra/config/environments/mainnet3/index.ts
  98. 186
      typescript/infra/config/environments/mainnet3/ism/verification.json
  99. 3
      typescript/infra/config/environments/mainnet3/owners.ts
  100. 4
      typescript/infra/config/environments/mainnet3/supportedChainNames.ts
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,7 @@
---
'@hyperlane-xyz/helloworld': minor
'@hyperlane-xyz/infra': minor
'@hyperlane-xyz/cli': minor
---
Upgrade registry to 2.1.1

@ -1,5 +0,0 @@
---
"@hyperlane-xyz/core": patch
---
fix: make XERC20 and XERC20 Lockbox proxy-able

@ -0,0 +1,28 @@
name: 'Checkout Registry'
description: 'Checkout the hyperlane-registry repository and move it to the parent directory'
inputs:
registry_version:
description: 'Override the version of the hyperlane-registry to checkout'
runs:
using: 'composite'
steps:
- name: Read .registryrc if registry_version not provided
shell: bash
run: |
if [ -z "${{ inputs.registry_version }}" ]; then
REGISTRY_VERSION=$(cat .registryrc)
echo "REGISTRY_VERSION=$REGISTRY_VERSION" >> $GITHUB_ENV
else
echo "REGISTRY_VERSION=${{ inputs.registry_version }}" >> $GITHUB_ENV
fi
- name: Checkout hyperlane-registry
uses: actions/checkout@v4
with:
repository: hyperlane-xyz/hyperlane-registry
ref: ${{ env.REGISTRY_VERSION }}
path: ./hyperlane-registry
- name: Move hyperlane-registry to parent directory
shell: bash
run: mv ./hyperlane-registry ../

@ -43,7 +43,7 @@ jobs:
runs-on: ${{ matrix.OS }}
steps:
- name: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: ubuntu setup
if: ${{ matrix.OS == 'larger-runner' }}
run: |
@ -74,7 +74,7 @@ jobs:
run: chmod ug+x,-w relayer scraper validator
working-directory: rust/target/${{ matrix.TARGET }}/release
- name: upload binaries
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.TARGET }}-${{ needs.prepare.outputs.tag_sha }}-${{ needs.prepare.outputs.tag_date }}
path: |

@ -10,9 +10,12 @@ on:
- 'typescript/infra/**'
- 'Dockerfile'
- '.dockerignore'
- '.github/workflows/monorepo-docker.yml'
concurrency:
group: build-push-monorepo-${{ github.ref }}
cancel-in-progress: true
jobs:
check-env:
runs-on: ubuntu-latest
@ -36,7 +39,7 @@ jobs:
if: needs.check-env.outputs.gcloud-service-key == 'true'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
@ -45,10 +48,10 @@ jobs:
id: taggen
run: |
echo "TAG_DATE=$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT
echo "TAG_SHA=$(echo '${{ github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT
echo "TAG_SHA=$(echo '${{ github.event.pull_request.head.sha || github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v5
with:
# list of Docker images to use as base name for tags
images: |
@ -59,15 +62,22 @@ jobs:
type=ref,event=pr
type=raw,value=${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to GCR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: gcr.io
username: _json_key
password: ${{ secrets.GCLOUD_SERVICE_KEY }}
- name: Read .registryrc
shell: bash
run: |
REGISTRY_VERSION=$(cat .registryrc)
echo "REGISTRY_VERSION=$REGISTRY_VERSION" >> $GITHUB_ENV
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: ./
file: ./Dockerfile
@ -76,6 +86,5 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
# To always fetch the latest registry, we use the date as the cache key
build-args: |
REGISTRY_CACHE=${{ steps.taggen.outputs.TAG_DATE }}
REGISTRY_COMMIT=${{ env.REGISTRY_VERSION }}

@ -19,14 +19,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
# check out full history
fetch-depth: 0
submodules: recursive
- name: Setup Node.js 18.x
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18.x

@ -7,6 +7,7 @@ on:
pull_request:
paths:
- 'rust/**'
- '.github/workflows/rust-docker.yml'
concurrency:
group: build-push-agents-${{ github.ref }}
cancel-in-progress: true
@ -33,17 +34,17 @@ jobs:
if: needs.check-env.outputs.gcloud-service-key == 'true'
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Generate tag data
id: taggen
run: |
echo "TAG_DATE=$(date +'%Y%m%d-%H%M%S')" >> $GITHUB_OUTPUT
echo "TAG_SHA=$(echo '${{ github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT
echo "TAG_SHA=$(echo '${{ github.event.pull_request.head.sha || github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
uses: docker/metadata-action@v5
with:
# list of Docker images to use as base name for tags
images: |
@ -54,15 +55,15 @@ jobs:
type=ref,event=pr
type=raw,value=${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
uses: docker/setup-buildx-action@v3
- name: Login to GCR
uses: docker/login-action@v2
uses: docker/login-action@v3
with:
registry: gcr.io
username: _json_key
password: ${{ secrets.GCLOUD_SERVICE_KEY }}
- name: Build and push
uses: docker/build-push-action@v3
uses: docker/build-push-action@v5
with:
context: ./rust
file: ./rust/Dockerfile

@ -4,6 +4,9 @@ name: rust
on:
push:
branches: [main]
paths-ignore:
- 'rust/**'
- .github/workflows/rust.yml
pull_request:
branches: [main]
paths-ignore:

@ -1,6 +1,12 @@
name: rust
on:
push:
branches: [main]
paths:
- 'rust/**'
- .github/workflows/rust.yml
- '!*.md'
pull_request:
branches: [main]
paths:
@ -29,7 +35,7 @@ jobs:
runs-on: larger-runner
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- uses: actions-rs/toolchain@v1
@ -39,8 +45,7 @@ jobs:
- name: rust cache
uses: Swatinem/rust-cache@v2
with:
prefix-key: "v2-rust"
shared-key: "test"
prefix-key: "v3-rust"
workspaces: |
./rust
- name: Free disk space
@ -57,7 +62,7 @@ jobs:
runs-on: larger-runner
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- uses: actions-rs/toolchain@v1
@ -69,8 +74,7 @@ jobs:
- name: rust cache
uses: Swatinem/rust-cache@v2
with:
prefix-key: "v2-rust"
shared-key: "lint"
prefix-key: "v3-rust"
workspaces: |
./rust
- name: Free disk space

@ -18,13 +18,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -35,13 +35,13 @@ jobs:
run: yarn install
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
- name: forge-build
run: cd solidity && forge build --build-info
- name: Static analysis
uses: crytic/slither-action@v0.3.0
uses: crytic/slither-action@v0.4.0
id: slither
with:
target: 'solidity/'
@ -51,6 +51,6 @@ jobs:
ignore-compile: true
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v2
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.slither.outputs.sarif }}

@ -14,17 +14,17 @@ jobs:
steps:
# Checkout the PR branch
- name: Checkout PR branch
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -35,7 +35,7 @@ jobs:
run: yarn install
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
# Run the command on PR branch
- name: Run command on PR branch

@ -3,6 +3,9 @@ name: test
on:
push:
branches: [main]
paths:
- '*.md'
- '!**/*'
pull_request:
branches:
- '*'
@ -28,12 +31,6 @@ jobs:
- name: Instant pass
run: echo "yarn-build job passed"
checkout-registry:
runs-on: ubuntu-latest
steps:
- name: Instant pass
run: echo "checkout-registry job passed"
lint-prettier:
runs-on: ubuntu-latest
steps:

@ -4,6 +4,8 @@ on:
# Triggers the workflow on push or pull request against main
push:
branches: [main]
paths-ignore:
- '*.md'
pull_request:
branches:
- '*' # run against all branches
@ -23,24 +25,22 @@ env:
LOG_FORMAT: PRETTY
CARGO_TERM_COLOR: always
RUST_BACKTRACE: full
# Alongside the monorepo in the directory above the $GITHUB_WORKSPACE.
REGISTRY_URI: ${{ github.workspace }}/../hyperlane-registry
jobs:
yarn-install:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -61,14 +61,14 @@ jobs:
runs-on: ubuntu-latest
needs: [yarn-install]
steps:
- uses: actions/checkout@v3
- 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@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -76,7 +76,7 @@ jobs:
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }}
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
@ -86,43 +86,18 @@ jobs:
- name: build
run: yarn build
checkout-registry:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
repository: hyperlane-xyz/hyperlane-registry
ref: main
path: ./hyperlane-registry
# Put alongside the monorepo in the directory above the $GITHUB_WORKSPACE.
# actions/checkout doesn't allow you to checkout a repository outside of the workspace.
# See https://github.com/actions/checkout/issues/197.
- run: mv ./hyperlane-registry ../
# A workaround for relative paths not being supported by actions/cache.
# See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630.
- run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV
- name: registry-cache
uses: actions/cache@v3
with:
path: |
${{ env.REGISTRY_URI_ABSOLUTE }}
key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }}
lint-prettier:
runs-on: ubuntu-latest
needs: [yarn-install]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
# check out full history
fetch-depth: 0
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -143,54 +118,46 @@ jobs:
yarn-test:
runs-on: ubuntu-latest
needs: [yarn-build, checkout-registry]
needs: [yarn-build]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
fetch-depth: 0
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}
# A workaround for relative paths not being supported by actions/cache.
# See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630.
- run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV
- name: registry-cache
uses: actions/cache@v3
with:
path: |
${{ env.REGISTRY_URI_ABSOLUTE }}
key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }}
- name: Checkout registry
uses: ./.github/actions/checkout-registry
- name: Unit Tests
run: yarn test:ci
agent-configs:
runs-on: ubuntu-latest
needs: [yarn-build, checkout-registry]
needs: [yarn-build]
strategy:
fail-fast: false
matrix:
environment: [mainnet3, testnet4]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -198,23 +165,15 @@ jobs:
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }}
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}
# A workaround for relative paths not being supported by actions/cache.
# See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630.
- run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV
- name: registry-cache
uses: actions/cache@v3
with:
path: |
${{ env.REGISTRY_URI_ABSOLUTE }}
key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }}
- name: Checkout registry
uses: ./.github/actions/checkout-registry
- name: Generate ${{ matrix.environment }} agent config
run: |
@ -229,22 +188,22 @@ jobs:
e2e-matrix:
runs-on: larger-runner
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group'
needs: [yarn-build, checkout-registry]
needs: [yarn-build]
strategy:
matrix:
e2e-type: [cosmwasm, non-cosmwasm]
steps:
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
- name: setup rust
uses: actions-rs/toolchain@v1
@ -252,6 +211,14 @@ jobs:
toolchain: stable
profile: minimal
- name: rust cache
uses: Swatinem/rust-cache@v2
with:
prefix-key: "v1-${{ runner.os }}-rust-cache"
shared-key: ${{ matrix.e2e-type }}
workspaces: |
./rust
- name: Free disk space
run: |
# Based on https://github.com/actions/runner-images/issues/2840#issuecomment-790492173
@ -267,7 +234,7 @@ jobs:
make-default: true
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -275,40 +242,25 @@ jobs:
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }}
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}
# A workaround for relative paths not being supported by actions/cache.
# See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630.
- run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV
- name: registry-cache
uses: actions/cache@v3
with:
path: |
${{ env.REGISTRY_URI_ABSOLUTE }}
key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }}
- name: cargo-cache
uses: actions/cache@v3
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-cache-${{ hashFiles('./rust/Cargo.lock') }}
- name: Checkout registry
uses: ./.github/actions/checkout-registry
- name: agent tests with CosmWasm
run: cargo test --release --package run-locally --bin run-locally --features cosmos -- cosmos::test --nocapture
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
env:
RUST_BACKTRACE: 'full'
- name: agent tests excluding CosmWasm
run: cargo run --release --bin run-locally
run: cargo run --release --bin run-locally --features test-utils
if: matrix.e2e-type == 'non-cosmwasm'
working-directory: ./rust
env:
@ -327,10 +279,59 @@ jobs:
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.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-e2e:
runs-on: larger-runner
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group'
needs: [yarn-build, checkout-registry]
needs: [yarn-build, prebuild-cli-e2e]
strategy:
matrix:
include:
@ -338,17 +339,17 @@ jobs:
- test-type: configure_hook_enabled
- test-type: pi_with_core_chain
steps:
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
submodules: recursive
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
- name: setup rust
uses: actions-rs/toolchain@v1
@ -356,6 +357,14 @@ jobs:
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
@ -371,7 +380,7 @@ jobs:
make-default: true
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -379,37 +388,22 @@ jobs:
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }}
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}
# A workaround for relative paths not being supported by actions/cache.
# See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630.
- run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV
- name: registry-cache
uses: actions/cache@v3
with:
path: |
${{ env.REGISTRY_URI_ABSOLUTE }}
key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }}
- name: cargo-cache
uses: actions/cache@v3
with:
path: |
~/.cargo
key: ${{ runner.os }}-cargo-cache-${{ hashFiles('./rust/Cargo.lock') }}
- name: Checkout registry
uses: ./.github/actions/checkout-registry
- name: cli e2e tests
run: ./typescript/cli/ci-test.sh ${{ matrix.test-type }}
env-test:
runs-on: ubuntu-latest
needs: [yarn-build, checkout-registry]
needs: [yarn-build]
strategy:
fail-fast: false
matrix:
@ -422,31 +416,23 @@ jobs:
module: core
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
!./rust
key: ${{ github.event.pull_request.head.sha || github.sha }}
# A workaround for relative paths not being supported by actions/cache.
# See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630.
- run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV
- name: registry-cache
uses: actions/cache@v3
with:
path: |
${{ env.REGISTRY_URI_ABSOLUTE }}
key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }}
- name: Checkout registry
uses: ./.github/actions/checkout-registry
- name: Fork test ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} deployment
run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }}
@ -456,13 +442,13 @@ jobs:
needs: [yarn-test]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
- name: yarn-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
**/node_modules
@ -470,7 +456,7 @@ jobs:
key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }}
- name: build-cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: |
./*
@ -478,7 +464,7 @@ jobs:
key: ${{ github.event.pull_request.head.sha || github.sha }}
- name: foundry-install
uses: onbjerg/foundry-toolchain@v1
uses: foundry-rs/foundry-toolchain@v1
- name: Run tests with coverage
run: yarn coverage
@ -486,6 +472,6 @@ jobs:
NODE_OPTIONS: --max_old_space_size=4096
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}

@ -0,0 +1 @@
v2.1.1

@ -29,8 +29,7 @@ COPY solidity ./solidity
RUN yarn build
ENV REGISTRY_URI="/hyperlane-registry"
# To allow us to avoid caching the registry clone, we use a build-time arg to force
# the below steps to be re-run if this arg is changed.
ARG REGISTRY_CACHE="default"
RUN git clone https://github.com/hyperlane-xyz/hyperlane-registry.git "$REGISTRY_URI"
ARG REGISTRY_COMMIT="main"
RUN git clone https://github.com/hyperlane-xyz/hyperlane-registry.git "$REGISTRY_URI" \
&& cd "$REGISTRY_URI" \
&& git checkout "$REGISTRY_COMMIT"

@ -6,7 +6,7 @@
"@trivago/prettier-plugin-sort-imports": "^4.2.1",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"eslint": "^9.0.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jest": "^28.2.0",
"husky": "^8.0.0",

1
rust/Cargo.lock generated

@ -7037,6 +7037,7 @@ dependencies = [
"num-traits",
"once_cell",
"prometheus",
"rand 0.8.5",
"regex",
"reqwest",
"serde",

@ -119,6 +119,7 @@ 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"
@ -163,8 +164,8 @@ 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.25.0"
strum_macros = "0.25.2"
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"] }

@ -26,6 +26,7 @@ itertools.workspace = true
num-derive.workspace = true
num-traits.workspace = true
prometheus.workspace = true
rand.workspace = true
regex.workspace = true
reqwest = { workspace = true, features = ["json"] }
serde.workspace = true
@ -43,7 +44,7 @@ hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" }
[dev-dependencies]
once_cell.workspace = true
mockall.worksapce = true
mockall.workspace = true
tokio-test.workspace = true
hyperlane-test = { path = "../../hyperlane-test" }
hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] }

@ -0,0 +1,123 @@
use hyperlane_core::HyperlaneMessage;
#[derive(Debug, Clone, Default)]
pub struct AddressBlacklist {
// A list of addresses that are blocked from being relayed.
// Addresses are any length to support different address types.
pub blacklist: Vec<Vec<u8>>,
}
impl AddressBlacklist {
pub fn new(blacklist: Vec<Vec<u8>>) -> Self {
Self { blacklist }
}
/// Returns true if the message is blocked by the blacklist.
/// At the moment, this only checks if the sender, recipient, or body of the
/// message contains any of the blocked addresses.
pub fn find_blacklisted_address(&self, message: &HyperlaneMessage) -> Option<Vec<u8>> {
self.blacklist.iter().find_map(|address| {
if is_subsequence(message.sender.as_bytes(), address)
|| is_subsequence(message.recipient.as_bytes(), address)
|| is_subsequence(&message.body, address)
{
// Return the blocked address that was found.
Some(address.clone())
} else {
None
}
})
}
}
/// Returns true if `needle` is a subsequence of `haystack`.
fn is_subsequence<T: PartialEq>(mut haystack: &[T], needle: &[T]) -> bool {
if needle.is_empty() {
return true;
}
while !haystack.is_empty() {
if needle.len() > haystack.len() {
return false;
}
if haystack.starts_with(needle) {
return true;
}
haystack = &haystack[1..];
}
false
}
#[cfg(test)]
mod test {
use hyperlane_core::H256;
use super::*;
#[test]
fn test_is_subsequence() {
assert!(is_subsequence(b"hello", b"hello"));
assert!(is_subsequence(b"hello", b"he"));
assert!(is_subsequence(b"hello", b"lo"));
assert!(is_subsequence(b"hello", b""));
assert!(is_subsequence(b"hello", b"o"));
assert!(!is_subsequence(b"hello", b"hello world"));
assert!(!is_subsequence(b"hello", b"world"));
assert!(!is_subsequence(b"hello", b"world hello"));
}
#[test]
fn test_is_blocked() {
let blocked = b"blocked";
let blocklist = AddressBlacklist::new(vec![blocked.to_vec()]);
let bytes_with_subsequence = |subsequence: &[u8], index: usize, len: usize| {
let mut bytes = vec![0; len];
bytes[index..index + subsequence.len()].copy_from_slice(subsequence);
bytes
};
let h256_with_subsequence = |subsequence: &[u8], index: usize| {
let bytes = bytes_with_subsequence(subsequence, index, H256::len_bytes());
H256::from_slice(&bytes)
};
// Blocked - sender includes the blocked address
let message = HyperlaneMessage {
sender: h256_with_subsequence(blocked, 0),
..Default::default()
};
assert_eq!(
blocklist.find_blacklisted_address(&message),
Some(blocked.to_vec())
);
// Blocked - recipient includes the blocked address
let message = HyperlaneMessage {
recipient: h256_with_subsequence(blocked, 20),
..Default::default()
};
assert_eq!(
blocklist.find_blacklisted_address(&message),
Some(blocked.to_vec())
);
// Blocked - body includes the blocked address
let message = HyperlaneMessage {
body: bytes_with_subsequence(blocked, 100 - blocked.len(), 100),
..Default::default()
};
assert_eq!(
blocklist.find_blacklisted_address(&message),
Some(blocked.to_vec())
);
// Not blocked - sender, recipient, and body do not include the blocked address
let message = HyperlaneMessage {
body: vec![1; 100],
..Default::default()
};
assert!(blocklist.find_blacklisted_address(&message).is_none());
}
}

@ -278,8 +278,9 @@ pub struct BaseMetadataBuilder {
allow_local_checkpoint_syncers: bool,
metrics: Arc<CoreMetrics>,
db: HyperlaneRocksDB,
max_depth: u32,
app_context_classifier: IsmAwareAppContextClassifier,
#[new(value = "7")]
max_depth: u32,
}
impl Debug for BaseMetadataBuilder {

@ -25,6 +25,7 @@
//! - FallbackProviderSubmitter (Serialized, but if some RPC provider sucks,
//! switch everyone to new one)
pub(crate) mod blacklist;
pub(crate) mod gas_payment;
pub(crate) mod metadata;
pub(crate) mod op_queue;

@ -1,7 +1,7 @@
use std::{cmp::Reverse, collections::BinaryHeap, sync::Arc};
use derive_new::new;
use hyperlane_core::{PendingOperation, QueueOperation};
use hyperlane_core::{PendingOperation, PendingOperationStatus, QueueOperation};
use prometheus::{IntGauge, IntGaugeVec};
use tokio::sync::{broadcast::Receiver, Mutex};
use tracing::{debug, info, instrument};
@ -21,8 +21,15 @@ pub struct OpQueue {
impl OpQueue {
/// Push an element onto the queue and update metrics
/// Arguments:
/// - `op`: the operation to push onto the queue
/// - `new_status`: optional new status to set for the operation. When an operation is added to a queue,
/// 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 = "debug")]
pub async fn push(&self, op: QueueOperation) {
pub async fn push(&self, mut op: QueueOperation, new_status: Option<PendingOperationStatus>) {
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();
@ -98,8 +105,12 @@ impl OpQueue {
/// Get the metric associated with this operation
fn get_operation_metric(&self, operation: &dyn PendingOperation) -> IntGauge {
let (destination, app_context) = operation.get_operation_labels();
self.metrics
.with_label_values(&[&destination, &self.queue_metrics_label, &app_context])
self.metrics.with_label_values(&[
&destination,
&self.queue_metrics_label,
&operation.status().to_string(),
&app_context,
])
}
}
@ -141,6 +152,12 @@ mod test {
self.id
}
fn status(&self) -> PendingOperationStatus {
PendingOperationStatus::FirstPrepareAttempt
}
fn set_status(&mut self, _status: PendingOperationStatus) {}
fn reset_attempts(&mut self) {
self.seconds_to_next_attempt = 0;
}
@ -219,7 +236,12 @@ mod test {
(
IntGaugeVec::new(
prometheus::Opts::new("op_queue", "OpQueue metrics"),
&["destination", "queue_metrics_label", "app_context"],
&[
"destination",
"queue_metrics_label",
"operation_status",
"app_context",
],
)
.unwrap(),
"queue_metrics_label".to_string(),
@ -256,12 +278,22 @@ mod test {
// push to queue 1
for _ in 0..=2 {
op_queue_1.push(ops.pop_front().unwrap()).await;
op_queue_1
.push(
ops.pop_front().unwrap(),
Some(PendingOperationStatus::FirstPrepareAttempt),
)
.await;
}
// push to queue 2
for _ in 3..messages_to_send {
op_queue_2.push(ops.pop_front().unwrap()).await;
op_queue_2
.push(
ops.pop_front().unwrap(),
Some(PendingOperationStatus::FirstPrepareAttempt),
)
.await;
}
// Retry by message ids
@ -320,7 +352,9 @@ mod test {
// push to queue
for op in ops {
op_queue.push(op).await;
op_queue
.push(op, Some(PendingOperationStatus::FirstPrepareAttempt))
.await;
}
// Retry by domain

@ -5,6 +5,8 @@ use derive_new::new;
use futures::future::join_all;
use futures_util::future::try_join_all;
use hyperlane_core::total_estimated_cost;
use hyperlane_core::ConfirmReason::*;
use hyperlane_core::PendingOperationStatus;
use prometheus::{IntCounter, IntGaugeVec};
use tokio::sync::broadcast::Sender;
use tokio::sync::mpsc;
@ -184,7 +186,9 @@ async fn receive_task(
// make sure things are getting wired up correctly; if this works in testing it
// should also be valid in production.
debug_assert_eq!(*op.destination_domain(), domain);
prepare_queue.push(op).await;
prepare_queue
.push(op, Some(PendingOperationStatus::FirstPrepareAttempt))
.await;
}
}
@ -220,7 +224,7 @@ async fn prepare_task(
.filter(|r| {
matches!(
r,
PendingOperationResult::NotReady | PendingOperationResult::Reprepare
PendingOperationResult::NotReady | PendingOperationResult::Reprepare(_)
)
})
.count();
@ -231,20 +235,27 @@ async fn prepare_task(
debug!(?op, "Operation prepared");
metrics.ops_prepared.inc();
// TODO: push multiple messages at once
submit_queue.push(op).await;
submit_queue
.push(op, Some(PendingOperationStatus::ReadyToSubmit))
.await;
}
PendingOperationResult::NotReady => {
prepare_queue.push(op).await;
prepare_queue.push(op, None).await;
}
PendingOperationResult::Reprepare => {
PendingOperationResult::Reprepare(reason) => {
metrics.ops_failed.inc();
prepare_queue.push(op).await;
prepare_queue
.push(op, Some(PendingOperationStatus::Retry(reason)))
.await;
}
PendingOperationResult::Drop => {
metrics.ops_dropped.inc();
}
PendingOperationResult::Confirm => {
confirm_queue.push(op).await;
PendingOperationResult::Confirm(reason) => {
debug!(?op, "Pushing operation to confirm queue");
confirm_queue
.push(op, Some(PendingOperationStatus::Confirm(reason)))
.await;
}
}
}
@ -296,7 +307,9 @@ async fn submit_single_operation(
op.submit().await;
debug!(?op, "Operation submitted");
op.set_next_attempt_after(CONFIRM_DELAY);
confirm_queue.push(op).await;
confirm_queue
.push(op, Some(PendingOperationStatus::Confirm(SubmittedBySelf)))
.await;
metrics.ops_submitted.inc();
if matches!(
@ -342,7 +355,7 @@ async fn confirm_task(
if op_results.iter().all(|op| {
matches!(
op,
PendingOperationResult::NotReady | PendingOperationResult::Confirm
PendingOperationResult::NotReady | PendingOperationResult::Confirm(_)
)
}) {
// None of the operations are ready, so wait for a little bit
@ -363,18 +376,25 @@ async fn confirm_operation(
debug_assert_eq!(*op.destination_domain(), domain);
let operation_result = op.confirm().await;
match operation_result {
match &operation_result {
PendingOperationResult::Success => {
debug!(?op, "Operation confirmed");
metrics.ops_confirmed.inc();
}
PendingOperationResult::NotReady | PendingOperationResult::Confirm => {
PendingOperationResult::NotReady => {
confirm_queue.push(op, None).await;
}
PendingOperationResult::Confirm(reason) => {
// TODO: push multiple messages at once
confirm_queue.push(op).await;
confirm_queue
.push(op, Some(PendingOperationStatus::Confirm(reason.clone())))
.await;
}
PendingOperationResult::Reprepare => {
PendingOperationResult::Reprepare(reason) => {
metrics.ops_failed.inc();
prepare_queue.push(op).await;
prepare_queue
.push(op, Some(PendingOperationStatus::Retry(reason.clone())))
.await;
}
PendingOperationResult::Drop => {
metrics.ops_dropped.inc();
@ -433,7 +453,9 @@ impl OperationBatch {
for mut op in self.operations {
op.set_operation_outcome(outcome.clone(), total_estimated_cost);
op.set_next_attempt_after(CONFIRM_DELAY);
confirm_queue.push(op).await;
confirm_queue
.push(op, Some(PendingOperationStatus::Confirm(SubmittedBySelf)))
.await;
}
return;
}

@ -9,12 +9,13 @@ use derive_new::new;
use eyre::Result;
use hyperlane_base::{db::HyperlaneRocksDB, CoreMetrics};
use hyperlane_core::{
gas_used_by_operation, make_op_try, BatchItem, ChainCommunicationError, ChainResult,
gas_used_by_operation, BatchItem, ChainCommunicationError, ChainResult, ConfirmReason,
HyperlaneChain, HyperlaneDomain, HyperlaneMessage, Mailbox, MessageSubmissionData,
PendingOperation, PendingOperationResult, TryBatchAs, TxOutcome, H256, U256,
PendingOperation, PendingOperationResult, PendingOperationStatus, ReprepareReason, TryBatchAs,
TxOutcome, H256, U256,
};
use prometheus::{IntCounter, IntGauge};
use tracing::{debug, error, info, instrument, trace, warn};
use tracing::{debug, error, info, info_span, instrument, trace, warn, Instrument};
use super::{
gas_payment::GasPaymentEnforcer,
@ -53,6 +54,7 @@ pub struct MessageContext {
pub struct PendingMessage {
pub message: HyperlaneMessage,
ctx: Arc<MessageContext>,
status: PendingOperationStatus,
app_context: Option<String>,
#[new(default)]
submitted: bool,
@ -120,6 +122,14 @@ impl PendingOperation for PendingMessage {
self.message.id()
}
fn status(&self) -> PendingOperationStatus {
self.status.clone()
}
fn set_status(&mut self, status: PendingOperationStatus) {
self.status = status;
}
fn priority(&self) -> u32 {
self.message.nonce
}
@ -138,8 +148,6 @@ impl PendingOperation for PendingMessage {
#[instrument(skip(self), ret, fields(id=?self.id()), level = "debug")]
async fn prepare(&mut self) -> PendingOperationResult {
make_op_try!(|| self.on_reprepare());
if !self.is_ready() {
trace!("Message is not ready to be submitted yet");
return PendingOperationResult::NotReady;
@ -148,27 +156,36 @@ impl PendingOperation for PendingMessage {
// If the message has already been processed, e.g. due to another relayer having
// already processed, then mark it as already-processed, and move on to
// the next tick.
let is_already_delivered = op_try!(
self.ctx
.destination_mailbox
.delivered(self.message.id())
.await,
"checking message delivery status"
);
let is_already_delivered = match self
.ctx
.destination_mailbox
.delivered(self.message.id())
.await
{
Ok(is_delivered) => is_delivered,
Err(err) => {
return self.on_reprepare(Some(err), ReprepareReason::ErrorCheckingDeliveryStatus);
}
};
if is_already_delivered {
debug!("Message has already been delivered, marking as submitted.");
self.submitted = true;
self.set_next_attempt_after(CONFIRM_DELAY);
return PendingOperationResult::Confirm;
return PendingOperationResult::Confirm(ConfirmReason::AlreadySubmitted);
}
let provider = self.ctx.destination_mailbox.provider();
// We cannot deliver to an address that is not a contract so check and drop if it isn't.
let is_contract = op_try!(
provider.is_contract(&self.message.recipient).await,
"checking if message recipient is a contract"
);
let is_contract = match provider.is_contract(&self.message.recipient).await {
Ok(is_contract) => is_contract,
Err(err) => {
return self.on_reprepare(
Some(err),
ReprepareReason::ErrorCheckingIfRecipientIsContract,
);
}
};
if !is_contract {
info!(
recipient=?self.message.recipient,
@ -177,56 +194,76 @@ impl PendingOperation for PendingMessage {
return PendingOperationResult::Drop;
}
let ism_address = op_try!(
self.ctx
.destination_mailbox
.recipient_ism(self.message.recipient)
.await,
"fetching ISM address. Potentially malformed recipient ISM address."
);
let ism_address = match self
.ctx
.destination_mailbox
.recipient_ism(self.message.recipient)
.await
{
Ok(ism_address) => ism_address,
Err(err) => {
return self.on_reprepare(Some(err), ReprepareReason::ErrorFetchingIsmAddress);
}
};
let message_metadata_builder = op_try!(
MessageMetadataBuilder::new(
ism_address,
&self.message,
self.ctx.metadata_builder.clone()
)
.await,
"getting the message metadata builder"
);
let message_metadata_builder = match MessageMetadataBuilder::new(
ism_address,
&self.message,
self.ctx.metadata_builder.clone(),
)
.await
{
Ok(message_metadata_builder) => message_metadata_builder,
Err(err) => {
return self.on_reprepare(Some(err), ReprepareReason::ErrorGettingMetadataBuilder);
}
};
let Some(metadata) = op_try!(
message_metadata_builder
.build(ism_address, &self.message)
.await,
"building metadata"
) else {
info!("Could not fetch metadata");
return self.on_reprepare();
let metadata = match message_metadata_builder
.build(ism_address, &self.message)
.await
{
Ok(metadata) => metadata,
Err(err) => {
return self.on_reprepare(Some(err), ReprepareReason::ErrorBuildingMetadata);
}
};
let Some(metadata) = metadata else {
return self.on_reprepare::<String>(None, ReprepareReason::CouldNotFetchMetadata);
};
// Estimate transaction costs for the process call. If there are issues, it's
// likely that gas estimation has failed because the message is
// reverting. This is defined behavior, so we just log the error and
// move onto the next tick.
let tx_cost_estimate = op_try!(
self.ctx
.destination_mailbox
.process_estimate_costs(&self.message, &metadata)
.await,
"estimating costs for process call"
);
let tx_cost_estimate = match self
.ctx
.destination_mailbox
.process_estimate_costs(&self.message, &metadata)
.await
{
Ok(metadata) => metadata,
Err(err) => {
return self.on_reprepare(Some(err), ReprepareReason::ErrorEstimatingGas);
}
};
// If the gas payment requirement hasn't been met, move to the next tick.
let Some(gas_limit) = op_try!(
self.ctx
.origin_gas_payment_enforcer
.message_meets_gas_payment_requirement(&self.message, &tx_cost_estimate)
.await,
"checking if message meets gas payment requirement"
) else {
warn!(?tx_cost_estimate, "Gas payment requirement not met yet");
return self.on_reprepare();
let gas_limit = match self
.ctx
.origin_gas_payment_enforcer
.message_meets_gas_payment_requirement(&self.message, &tx_cost_estimate)
.await
{
Ok(gas_limit) => gas_limit,
Err(err) => {
return self.on_reprepare(Some(err), ReprepareReason::ErrorCheckingGasRequirement);
}
};
let Some(gas_limit) = gas_limit else {
return self.on_reprepare::<String>(None, ReprepareReason::GasPaymentRequirementNotMet);
};
// Go ahead and attempt processing of message to destination chain.
@ -238,8 +275,8 @@ impl PendingOperation for PendingMessage {
if let Some(max_limit) = self.ctx.transaction_gas_limit {
if gas_limit > max_limit {
info!("Message delivery estimated gas exceeds max gas limit");
return self.on_reprepare();
// TODO: consider dropping instead of repreparing in this case
return self.on_reprepare::<String>(None, ReprepareReason::ExceedsMaxGasLimit);
}
}
@ -288,41 +325,41 @@ impl PendingOperation for PendingMessage {
}
async fn confirm(&mut self) -> PendingOperationResult {
make_op_try!(|| {
// Provider error; just try again later
// Note: this means that we are using `NotReady` for a retryable error case
self.inc_attempts();
PendingOperationResult::NotReady
});
if !self.is_ready() {
return PendingOperationResult::NotReady;
}
let is_delivered = op_try!(
self.ctx
.destination_mailbox
.delivered(self.message.id())
.await,
"Confirming message delivery"
);
let is_delivered = match self
.ctx
.destination_mailbox
.delivered(self.message.id())
.await
{
Ok(is_delivered) => is_delivered,
Err(err) => {
return self.on_reconfirm(Some(err), "Error confirming message delivery");
}
};
if is_delivered {
op_try!(
critical: self.record_message_process_success(),
"recording message process success"
);
if let Err(err) = self.record_message_process_success() {
return self
.on_reconfirm(Some(err), "Error when recording message process success");
}
info!(
submission=?self.submission_outcome,
"Message successfully processed"
);
PendingOperationResult::Success
} else {
warn!(
let span = info_span!(
"Error: Transaction attempting to process message either reverted or was reorged",
tx_outcome=?self.submission_outcome,
message_id=?self.message.id(),
"Transaction attempting to process message either reverted or was reorged"
message_id=?self.message.id()
);
self.on_reprepare()
self.on_reprepare::<String>(None, ReprepareReason::RevertedOrReorged)
.instrument(span)
.into_inner()
}
}
@ -395,7 +432,13 @@ impl PendingMessage {
ctx: Arc<MessageContext>,
app_context: Option<String>,
) -> Self {
let mut pm = Self::new(message, ctx, app_context);
let mut pm = Self::new(
message,
ctx,
// Since we don't persist the message status for now, assume it's the first attempt
PendingOperationStatus::FirstPrepareAttempt,
app_context,
);
match pm
.ctx
.origin_db
@ -414,10 +457,29 @@ impl PendingMessage {
pm
}
fn on_reprepare(&mut self) -> PendingOperationResult {
fn on_reprepare<E: Debug>(
&mut self,
err: Option<E>,
reason: ReprepareReason,
) -> PendingOperationResult {
self.inc_attempts();
self.submitted = false;
PendingOperationResult::Reprepare
if let Some(e) = err {
warn!(error = ?e, "Repreparing message: {}", reason.clone());
} else {
warn!("Repreparing message: {}", reason.clone());
}
PendingOperationResult::Reprepare(reason)
}
fn on_reconfirm<E: Debug>(&mut self, err: Option<E>, reason: &str) -> PendingOperationResult {
self.inc_attempts();
if let Some(e) = err {
warn!(error = ?e, id = ?self.id(), "Reconfirming message: {}", reason.clone());
} else {
warn!(id = ?self.id(), "Reconfirming message: {}", reason.clone());
}
PendingOperationResult::NotReady
}
fn is_ready(&self) -> bool {
@ -443,7 +505,6 @@ impl PendingMessage {
}
fn reset_attempts(&mut self) {
self.set_retries(0);
self.next_attempt_after = None;
self.last_attempted_at = Instant::now();
}
@ -484,8 +545,17 @@ impl PendingMessage {
i if (24..36).contains(&i) => 60 * 30,
// wait 60min for the next 12 attempts
i if (36..48).contains(&i) => 60 * 60,
// wait 3h for the next 12 attempts,
_ => 60 * 60 * 3,
// linearly increase the backoff time after 48 attempts,
// adding 1h for each additional attempt
_ => {
let hour: u64 = 60 * 60;
// To be extra safe, `max` to make sure it's at least 1 hour.
let target = hour.max((num_retries - 47) as u64 * hour);
// Schedule it at some random point in the next hour to
// avoid scheduling messages with the same # of retries
// at the exact same time.
target + (rand::random::<u64>() % hour)
}
}))
}
}

@ -8,6 +8,7 @@ use std::{
use async_trait::async_trait;
use derive_new::new;
use ethers::utils::hex;
use eyre::Result;
use hyperlane_base::{
db::{HyperlaneRocksDB, ProcessMessage},
@ -18,15 +19,19 @@ use prometheus::IntGauge;
use tokio::sync::mpsc::UnboundedSender;
use tracing::{debug, instrument, trace};
use super::{metadata::AppContextClassifier, pending_message::*};
use super::{blacklist::AddressBlacklist, metadata::AppContextClassifier, pending_message::*};
use crate::{processor::ProcessorExt, settings::matching_list::MatchingList};
/// Finds unprocessed messages from an origin and submits then through a channel
/// for to the appropriate destination.
#[allow(clippy::too_many_arguments)]
pub struct MessageProcessor {
whitelist: Arc<MatchingList>,
blacklist: Arc<MatchingList>,
/// A matching list of messages that should be whitelisted.
message_whitelist: Arc<MatchingList>,
/// A matching list of messages that should be blacklisted.
message_blacklist: Arc<MatchingList>,
/// Addresses that messages may not interact with.
address_blacklist: Arc<AddressBlacklist>,
metrics: MessageProcessorMetrics,
/// channel for each destination chain to send operations (i.e. message
/// submissions) to
@ -138,7 +143,10 @@ impl DirectionalNonceIterator {
#[instrument]
fn iterate(&mut self) {
match self.direction {
NonceDirection::High => self.nonce = self.nonce.map(|n| n.saturating_add(1)),
NonceDirection::High => {
self.nonce = self.nonce.map(|n| n.saturating_add(1));
debug!(?self, "Iterating high nonce");
}
NonceDirection::Low => {
if let Some(nonce) = self.nonce {
// once the message with nonce zero is processed, we should stop going backwards
@ -155,6 +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");
return Ok(MessageStatus::Processable(message));
} else {
return Ok(MessageStatus::Processed);
@ -213,8 +222,8 @@ impl Debug for MessageProcessor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"MessageProcessor {{ whitelist: {:?}, blacklist: {:?}, nonce_iterator: {:?}}}",
self.whitelist, self.blacklist, self.nonce_iterator
"MessageProcessor {{ message_whitelist: {:?}, message_blacklist: {:?}, address_blacklist: {:?}, nonce_iterator: {:?}}}",
self.message_whitelist, self.message_blacklist, self.address_blacklist, self.nonce_iterator
)
}
}
@ -235,18 +244,33 @@ impl ProcessorExt for MessageProcessor {
// nonce.
// Scan until we find next nonce without delivery confirmation.
if let Some(msg) = self.try_get_unprocessed_message().await? {
debug!(?msg, "Processor working on message");
debug!(
?msg,
cursor = ?self.nonce_iterator,
"Processor working on message"
);
let destination = msg.destination;
// Skip if not whitelisted.
if !self.whitelist.msg_matches(&msg, true) {
debug!(?msg, whitelist=?self.whitelist, "Message not whitelisted, skipping");
if !self.message_whitelist.msg_matches(&msg, true) {
debug!(?msg, whitelist=?self.message_whitelist, "Message not whitelisted, skipping");
return Ok(());
}
// Skip if the message is blacklisted
if self.blacklist.msg_matches(&msg, false) {
debug!(?msg, blacklist=?self.blacklist, "Message blacklisted, skipping");
if self.message_whitelist.msg_matches(&msg, false) {
debug!(?msg, blacklist=?self.message_whitelist, "Message blacklisted, skipping");
return Ok(());
}
// Skip if the message involves a blacklisted address
if let Some(blacklisted_address) = self.address_blacklist.find_blacklisted_address(&msg)
{
debug!(
?msg,
blacklisted_address = hex::encode(blacklisted_address),
"Message involves blacklisted address, skipping"
);
return Ok(());
}
@ -283,18 +307,21 @@ impl ProcessorExt for MessageProcessor {
}
impl MessageProcessor {
#[allow(clippy::too_many_arguments)]
pub fn new(
db: HyperlaneRocksDB,
whitelist: Arc<MatchingList>,
blacklist: Arc<MatchingList>,
message_whitelist: Arc<MatchingList>,
message_blacklist: Arc<MatchingList>,
address_blacklist: Arc<AddressBlacklist>,
metrics: MessageProcessorMetrics,
send_channels: HashMap<u32, UnboundedSender<QueueOperation>>,
destination_ctxs: HashMap<u32, Arc<MessageContext>>,
metric_app_contexts: Vec<(MatchingList, String)>,
) -> Self {
Self {
whitelist,
blacklist,
message_whitelist,
message_blacklist,
address_blacklist,
metrics,
send_channels,
destination_ctxs,
@ -444,7 +471,6 @@ mod test {
false,
Arc::new(core_metrics),
db.clone(),
5,
IsmAwareAppContextClassifier::new(Arc::new(MockMailboxContract::default()), vec![]),
)
}
@ -470,6 +496,7 @@ mod test {
db.clone(),
Default::default(),
Default::default(),
Default::default(),
dummy_processor_metrics(origin_domain.id()),
HashMap::from([(destination_domain.id(), send_channel)]),
HashMap::from([(destination_domain.id(), message_context)]),

@ -33,6 +33,7 @@ use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument
use crate::{
merkle_tree::builder::MerkleTreeBuilder,
msg::{
blacklist::AddressBlacklist,
gas_payment::GasPaymentEnforcer,
metadata::{BaseMetadataBuilder, IsmAwareAppContextClassifier},
op_submitter::{SerialSubmitter, SerialSubmitterMetrics},
@ -70,8 +71,9 @@ pub struct Relayer {
prover_syncs: HashMap<HyperlaneDomain, Arc<RwLock<MerkleTreeBuilder>>>,
merkle_tree_hook_syncs: HashMap<HyperlaneDomain, Arc<dyn ContractSyncer<MerkleTreeInsertion>>>,
dbs: HashMap<HyperlaneDomain, HyperlaneRocksDB>,
whitelist: Arc<MatchingList>,
blacklist: Arc<MatchingList>,
message_whitelist: Arc<MatchingList>,
message_blacklist: Arc<MatchingList>,
address_blacklist: Arc<AddressBlacklist>,
transaction_gas_limit: Option<U256>,
skip_transaction_gas_limit_for: HashSet<u32>,
allow_local_checkpoint_syncers: bool,
@ -89,11 +91,12 @@ impl Debug for Relayer {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Relayer {{ origin_chains: {:?}, destination_chains: {:?}, whitelist: {:?}, blacklist: {:?}, transaction_gas_limit: {:?}, skip_transaction_gas_limit_for: {:?}, allow_local_checkpoint_syncers: {:?} }}",
"Relayer {{ origin_chains: {:?}, destination_chains: {:?}, message_whitelist: {:?}, message_blacklist: {:?}, address_blacklist: {:?}, transaction_gas_limit: {:?}, skip_transaction_gas_limit_for: {:?}, allow_local_checkpoint_syncers: {:?} }}",
self.origin_chains,
self.destination_chains,
self.whitelist,
self.blacklist,
self.message_whitelist,
self.message_blacklist,
self.address_blacklist,
self.transaction_gas_limit,
self.skip_transaction_gas_limit_for,
self.allow_local_checkpoint_syncers
@ -177,14 +180,16 @@ impl BaseAgent for Relayer {
.map(|(k, v)| (k, v as _))
.collect();
let whitelist = Arc::new(settings.whitelist);
let blacklist = Arc::new(settings.blacklist);
let message_whitelist = Arc::new(settings.whitelist);
let message_blacklist = Arc::new(settings.blacklist);
let address_blacklist = Arc::new(AddressBlacklist::new(settings.address_blacklist));
let skip_transaction_gas_limit_for = settings.skip_transaction_gas_limit_for;
let transaction_gas_limit = settings.transaction_gas_limit;
info!(
%whitelist,
%blacklist,
%message_whitelist,
%message_blacklist,
?address_blacklist,
?transaction_gas_limit,
?skip_transaction_gas_limit_for,
"Whitelist configuration"
@ -242,7 +247,6 @@ impl BaseAgent for Relayer {
settings.allow_local_checkpoint_syncers,
core.metrics.clone(),
db,
5,
IsmAwareAppContextClassifier::new(
mailboxes[destination].clone(),
settings.metric_app_contexts.clone(),
@ -276,8 +280,9 @@ impl BaseAgent for Relayer {
interchain_gas_payment_syncs,
prover_syncs,
merkle_tree_hook_syncs,
whitelist,
blacklist,
message_whitelist,
message_blacklist,
address_blacklist,
transaction_gas_limit,
skip_transaction_gas_limit_for,
allow_local_checkpoint_syncers: settings.allow_local_checkpoint_syncers,
@ -488,8 +493,9 @@ impl Relayer {
let message_processor = MessageProcessor::new(
self.dbs.get(origin).unwrap().clone(),
self.whitelist.clone(),
self.blacklist.clone(),
self.message_whitelist.clone(),
self.message_blacklist.clone(),
self.address_blacklist.clone(),
metrics,
send_channels,
destination_ctxs,

@ -8,6 +8,7 @@ use std::{collections::HashSet, path::PathBuf};
use convert_case::Case;
use derive_more::{AsMut, AsRef, Deref, DerefMut};
use ethers::utils::hex;
use eyre::{eyre, Context};
use hyperlane_base::{
impl_loadable_from_settings,
@ -46,6 +47,10 @@ pub struct RelayerSettings {
pub whitelist: MatchingList,
/// Filter for what messages to block.
pub blacklist: MatchingList,
/// Filter for what addresses to block interactions with.
/// This is intentionally not an H256 to allow for addresses of any length without
/// adding any padding.
pub address_blacklist: Vec<Vec<u8>>,
/// This is optional. If not specified, any amount of gas will be valid, otherwise this
/// is the max allowed gas in wei to relay a transaction.
pub transaction_gas_limit: Option<U256>,
@ -191,6 +196,14 @@ impl FromRawConf<RawRelayerSettings> for RelayerSettings {
.and_then(parse_matching_list)
.unwrap_or_default();
let address_blacklist = p
.chain(&mut err)
.get_opt_key("addressBlacklist")
.parse_string()
.end()
.map(|str| parse_address_list(str, &mut err, || &p.cwp + "address_blacklist"))
.unwrap_or_default();
let transaction_gas_limit = p
.chain(&mut err)
.get_opt_key("transactionGasLimit")
@ -268,6 +281,7 @@ impl FromRawConf<RawRelayerSettings> for RelayerSettings {
gas_payment_enforcement,
whitelist,
blacklist,
address_blacklist,
transaction_gas_limit,
skip_transaction_gas_limit_for,
allow_local_checkpoint_syncers,
@ -311,3 +325,53 @@ fn parse_matching_list(p: ValueParser) -> ConfigResult<MatchingList> {
err.into_result(ml)
}
fn parse_address_list(
str: &str,
err: &mut ConfigParsingError,
err_path: impl Fn() -> ConfigPath,
) -> Vec<Vec<u8>> {
str.split(',')
.filter_map(|s| {
let mut s = s.trim().to_owned();
if let Some(stripped) = s.strip_prefix("0x") {
s = stripped.to_owned();
}
hex::decode(s).take_err(err, &err_path)
})
.collect_vec()
}
#[cfg(test)]
mod test {
use super::*;
use hyperlane_core::H160;
#[test]
fn test_parse_address_blacklist() {
let valid_address1 = b"valid".to_vec();
let valid_address2 = H160::random().as_bytes().to_vec();
// Successful parsing
let input = format!(
"0x{}, {}",
hex::encode(&valid_address1),
hex::encode(&valid_address2)
);
let mut err = ConfigParsingError::default();
let res = parse_address_list(&input, &mut err, ConfigPath::default);
assert_eq!(res, vec![valid_address1.clone(), valid_address2.clone()]);
assert!(err.is_ok());
// An error in the final address provided
let input = format!(
"0x{}, {}, 0xaazz",
hex::encode(&valid_address1),
hex::encode(&valid_address2)
);
let mut err = ConfigParsingError::default();
let res = parse_address_list(&input, &mut err, ConfigPath::default);
assert_eq!(res, vec![valid_address1, valid_address2]);
assert!(!err.is_ok());
}
}

@ -94,6 +94,14 @@ const DOMAINS: &[RawDomain] = &[
is_test_net: false,
is_deprecated: false,
},
RawDomain {
name: "fraxtal",
token: "frxETH",
domain: 252,
chain_id: 252,
is_test_net: false,
is_deprecated: false,
},
RawDomain {
name: "fuji",
token: "AVAX",
@ -110,6 +118,14 @@ const DOMAINS: &[RawDomain] = &[
is_test_net: false,
is_deprecated: false,
},
RawDomain {
name: "linea",
token: "ETH",
domain: 59144,
chain_id: 59144,
is_test_net: false,
is_deprecated: false,
},
RawDomain {
name: "mantapacific",
token: "ETH",
@ -182,6 +198,14 @@ const DOMAINS: &[RawDomain] = &[
is_test_net: true,
is_deprecated: false,
},
RawDomain {
name: "sei",
token: "SEI",
domain: 1329,
chain_id: 1329,
is_test_net: false,
is_deprecated: false,
},
RawDomain {
name: "sepolia",
token: "ETH",

@ -4,7 +4,7 @@ use std::time::{Duration, Instant};
use std::vec;
use hyperlane_core::rpc_clients::call_and_retry_indefinitely;
use hyperlane_core::{ChainCommunicationError, ChainResult, MerkleTreeHook};
use hyperlane_core::{ChainResult, MerkleTreeHook};
use prometheus::IntGauge;
use tokio::time::sleep;
use tracing::{debug, error, info};
@ -61,17 +61,8 @@ impl ValidatorSubmitter {
/// 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();
call_and_retry_indefinitely(|| {
let target_checkpoint = target_checkpoint;
let self_clone = self.clone();
Box::pin(async move {
self_clone
.submit_checkpoints_until_correctness_checkpoint(&mut tree, &target_checkpoint)
.await?;
Ok(())
})
})
.await;
self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &target_checkpoint)
.await;
info!(
?target_checkpoint,
@ -132,21 +123,8 @@ impl ValidatorSubmitter {
sleep(self.interval).await;
continue;
}
tree = call_and_retry_indefinitely(|| {
let mut tree = tree;
let self_clone = self.clone();
Box::pin(async move {
self_clone
.submit_checkpoints_until_correctness_checkpoint(
&mut tree,
&latest_checkpoint,
)
.await?;
Ok(tree)
})
})
.await;
self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &latest_checkpoint)
.await;
self.metrics
.latest_checkpoint_processed
@ -162,7 +140,7 @@ impl ValidatorSubmitter {
&self,
tree: &mut IncrementalMerkle,
correctness_checkpoint: &Checkpoint,
) -> ChainResult<()> {
) {
// This should never be called with a tree that is ahead of the correctness checkpoint.
assert!(
!tree_exceeds_checkpoint(correctness_checkpoint, tree),
@ -182,7 +160,14 @@ impl ValidatorSubmitter {
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))?
.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(),
@ -225,9 +210,7 @@ impl ValidatorSubmitter {
?correctness_checkpoint,
"Incorrect tree root, something went wrong"
);
return Err(ChainCommunicationError::CustomError(
"Incorrect tree root, something went wrong".to_string(),
));
panic!("Incorrect tree root, something went wrong");
}
if !checkpoint_queue.is_empty() {
@ -236,57 +219,71 @@ impl ValidatorSubmitter {
queue_len = checkpoint_queue.len(),
"Reached tree consistency"
);
self.sign_and_submit_checkpoints(checkpoint_queue).await?;
self.sign_and_submit_checkpoints(checkpoint_queue).await;
info!(
index = checkpoint.index,
"Signed all queued checkpoints until index"
);
}
Ok(())
}
/// Signs and submits any previously unsubmitted checkpoints.
async fn sign_and_submit_checkpoints(
async fn sign_and_submit_checkpoint(
&self,
checkpoints: Vec<CheckpointWithMessageId>,
checkpoint: CheckpointWithMessageId,
) -> ChainResult<()> {
let last_checkpoint = checkpoints.as_slice()[checkpoints.len() - 1];
for queued_checkpoint in checkpoints {
let existing = self
.checkpoint_syncer
.fetch_checkpoint(queued_checkpoint.index)
.await?;
if existing.is_some() {
debug!(
index = queued_checkpoint.index,
"Checkpoint already submitted"
);
continue;
}
let signed_checkpoint = self.signer.sign(queued_checkpoint).await?;
self.checkpoint_syncer
.write_checkpoint(&signed_checkpoint)
.await?;
debug!(
index = queued_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;
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
.update_latest_index(last_checkpoint.index)
.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<CheckpointWithMessageId>) {
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.

@ -1,6 +1,5 @@
use async_trait::async_trait;
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use futures::future;
use hyperlane_core::{
ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract,
HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, InterchainGasPaymaster,
@ -9,12 +8,15 @@ use hyperlane_core::{
use once_cell::sync::Lazy;
use std::ops::RangeInclusive;
use tendermint::abci::EventAttribute;
use tracing::{instrument, warn};
use tracing::instrument;
use crate::{
rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer},
signers::Signer,
utils::{CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64},
utils::{
execute_and_parse_log_futures, CONTRACT_ADDRESS_ATTRIBUTE_KEY,
CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64,
},
ConnectionConf, CosmosProvider, HyperlaneCosmosError,
};
@ -223,26 +225,7 @@ impl Indexer<InterchainGasPayment> for CosmosInterchainGasPaymasterIndexer {
})
.collect();
// TODO: this can be refactored when we rework indexing, to be part of the block-by-block indexing
let result = future::join_all(logs_futures)
.await
.into_iter()
.flatten()
.map(|(logs, block_number)| {
if let Err(err) = &logs {
warn!(?err, ?block_number, "Failed to fetch logs for block");
}
logs
})
// Propagate errors from any of the queries. This will cause the entire range to be retried,
// including successful ones, but we don't have a way to handle partial failures in a range for now.
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.map(|(log, meta)| (Indexed::new(log), meta))
.collect();
Ok(result)
execute_and_parse_log_futures(logs_futures).await
}
async fn get_finalized_block_number(&self) -> ChainResult<u32> {

@ -1,5 +1,4 @@
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use futures::future;
use std::{
fmt::{Debug, Formatter},
io::Cursor,
@ -8,14 +7,15 @@ use std::{
str::FromStr,
};
use crate::payloads::mailbox::{
GeneralMailboxQuery, ProcessMessageRequest, ProcessMessageRequestInner,
};
use crate::payloads::{general, mailbox};
use crate::rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer};
use crate::CosmosProvider;
use crate::{address::CosmosAddress, types::tx_response_to_outcome};
use crate::{grpc::WasmProvider, HyperlaneCosmosError};
use crate::{
payloads::mailbox::{GeneralMailboxQuery, ProcessMessageRequest, ProcessMessageRequestInner},
utils::execute_and_parse_log_futures,
};
use crate::{signers::Signer, utils::get_block_height_for_lag, ConnectionConf};
use async_trait::async_trait;
use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse;
@ -371,23 +371,7 @@ impl Indexer<HyperlaneMessage> for CosmosMailboxIndexer {
})
.collect();
// TODO: this can be refactored when we rework indexing, to be part of the block-by-block indexing
let result = future::join_all(logs_futures)
.await
.into_iter()
.flatten()
.filter_map(|(logs_res, block_number)| match logs_res {
Ok(logs) => Some(logs),
Err(err) => {
warn!(?err, ?block_number, "Failed to fetch logs for block");
None
}
})
.flatten()
.map(|(log, meta)| (log.into(), meta))
.collect();
Ok(result)
execute_and_parse_log_futures(logs_futures).await
}
async fn get_finalized_block_number(&self) -> ChainResult<u32> {

@ -2,7 +2,6 @@ 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 futures::future;
use hyperlane_core::{
accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint,
ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider,
@ -10,17 +9,14 @@ use hyperlane_core::{
};
use once_cell::sync::Lazy;
use tendermint::abci::EventAttribute;
use tracing::{instrument, warn};
use tracing::instrument;
use crate::{
grpc::WasmProvider,
payloads::{
general::{self},
merkle_tree_hook,
},
payloads::{general, merkle_tree_hook},
rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer},
utils::{
get_block_height_for_lag, CONTRACT_ADDRESS_ATTRIBUTE_KEY,
execute_and_parse_log_futures, get_block_height_for_lag, CONTRACT_ADDRESS_ATTRIBUTE_KEY,
CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64,
},
ConnectionConf, CosmosProvider, HyperlaneCosmosError, Signer,
@ -304,23 +300,7 @@ impl Indexer<MerkleTreeInsertion> for CosmosMerkleTreeHookIndexer {
})
.collect();
// TODO: this can be refactored when we rework indexing, to be part of the block-by-block indexing
let result = future::join_all(logs_futures)
.await
.into_iter()
.flatten()
.filter_map(|(logs_res, block_number)| match logs_res {
Ok(logs) => Some(logs),
Err(err) => {
warn!(?err, ?block_number, "Failed to fetch logs for block");
None
}
})
.flatten()
.map(|(log, meta)| (log.into(), meta))
.collect();
Ok(result)
execute_and_parse_log_futures(logs_futures).await
}
/// Get the chain's latest block number that has reached finality

@ -277,7 +277,7 @@ impl WasmGrpcProvider {
let raw_tx = TxRaw {
body_bytes: sign_doc.body_bytes,
auth_info_bytes: sign_doc.auth_info_bytes,
// The poorly documented trick to simuluating a tx without a valid signature is to just pass
// The poorly documented trick to simulating a tx without a valid signature is to just pass
// in a single empty signature. Taken from cosmjs:
// https://github.com/cosmos/cosmjs/blob/44893af824f0712d1f406a8daa9fcae335422235/packages/stargate/src/modules/tx/queries.ts#L67
signatures: vec![vec![]],

@ -1,6 +1,5 @@
use async_trait::async_trait;
use cosmrs::rpc::client::Client;
use hyperlane_core::rpc_clients::call_with_retry;
use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator, LogMeta, H256, U256};
use sha256::digest;
use std::fmt::Debug;
@ -216,9 +215,7 @@ impl CosmosWasmIndexer {
impl WasmIndexer for CosmosWasmIndexer {
#[instrument(err, skip(self))]
async fn get_finalized_block_number(&self) -> ChainResult<u32> {
let latest_block =
call_with_retry(move || Box::pin(Self::get_latest_block(self.provider.rpc().clone())))
.await?;
let latest_block = Self::get_latest_block(self.provider.rpc().clone()).await?;
let latest_height: u32 = latest_block
.block
.header
@ -242,11 +239,11 @@ impl WasmIndexer for CosmosWasmIndexer {
let client = self.provider.rpc().clone();
debug!(?block_number, cursor_label, domain=?self.provider.domain, "Getting logs in block");
let (block, block_results) = tokio::join!(
call_with_retry(|| { Box::pin(Self::get_block(client.clone(), block_number)) }),
call_with_retry(|| { Box::pin(Self::get_block_results(client.clone(), block_number)) }),
);
// 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?;
Ok(self.handle_txs(block?, block_results?, parser, cursor_label))
Ok(self.handle_txs(block, block_results, parser, cursor_label))
}
}

@ -1,8 +1,11 @@
use std::num::NonZeroU64;
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use hyperlane_core::ChainResult;
use futures::future;
use hyperlane_core::{ChainCommunicationError, ChainResult, Indexed, LogMeta};
use once_cell::sync::Lazy;
use tokio::task::JoinHandle;
use tracing::warn;
use crate::grpc::{WasmGrpcProvider, WasmProvider};
@ -31,6 +34,32 @@ pub(crate) async fn get_block_height_for_lag(
Ok(block_height)
}
#[allow(clippy::type_complexity)]
pub(crate) async fn execute_and_parse_log_futures<T: Into<Indexed<T>>>(
logs_futures: Vec<JoinHandle<(Result<Vec<(T, LogMeta)>, ChainCommunicationError>, u32)>>,
) -> ChainResult<Vec<(Indexed<T>, LogMeta)>> {
// TODO: this can be refactored when we rework indexing, to be part of the block-by-block indexing
let result = future::join_all(logs_futures)
.await
.into_iter()
.flatten()
.map(|(logs, block_number)| {
if let Err(err) = &logs {
warn!(?err, ?block_number, "Failed to fetch logs for block");
}
logs
})
// Propagate errors from any of the queries. This will cause the entire range to be retried,
// including successful ones, but we don't have a way to handle partial failures in a range for now.
// This is also why cosmos indexing should be run with small chunks (currently set to 5).
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.map(|(log, meta)| (log.into(), meta))
.collect();
Ok(result)
}
#[cfg(test)]
/// Helper function to create a Vec<EventAttribute> from a JSON string -
/// crate::payloads::general::EventAttribute has a Deserialize impl while

@ -19,7 +19,7 @@
"chainId": 888888888,
"displayName": "Ancient8",
"domainId": 888888888,
"domainRoutingIsm": "0xB6F0f1267B01C27326F61a4B4fe2c73751802685",
"domainRoutingIsm": "0x477145b11E1a71fEb658d96A0E27F19495121504",
"domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"fallbackRoutingHook": "0x5E01d8F34b629E3f92d69546bbc4142A7Adee7e9",
"gasCurrencyCoinGeckoId": "ethereum",
@ -27,7 +27,7 @@
"from": 2507127
},
"interchainGasPaymaster": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA",
"interchainSecurityModule": "0xBd3C7253F08c040eDB9c54e7CD4f8a5fd1eb935D",
"interchainSecurityModule": "0xa10686c87d47C8b78c846E77BE2f96607C7c44F4",
"isTestnet": false,
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162",
@ -38,7 +38,7 @@
"symbol": "ETH"
},
"pausableHook": "0x66DC49405Ae2956f7E87FEAa9fE8f506C8987462",
"pausableIsm": "0xcf678903c003651DB0bb933820259A16ea9d95e4",
"pausableIsm": "0xF8DbA46fF9D8ef650052c89CA2Df793FaBc375F9",
"protocol": "ethereum",
"protocolFee": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0",
"proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
@ -48,7 +48,7 @@
}
],
"staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"staticAggregationIsm": "0xBd3C7253F08c040eDB9c54e7CD4f8a5fd1eb935D",
"staticAggregationIsm": "0xc6ec1364d1ce3E963Fa65A0bDF57eC722478e1FB",
"staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
@ -86,7 +86,7 @@
"interchainAccountIsm": "0xfa8bfcE55B3A0631dF38257615cEF7FCD3523A48",
"interchainAccountRouter": "0xCD0CFFf6eFD943b4b81f2c15847730dbcD30e3aE",
"interchainGasPaymaster": "0x3b6044acd6767f017e99318AA6Ef93b7B06A5a22",
"interchainSecurityModule": "0x96845a0469363f90779f6D5cd49D79bDDAc69429",
"interchainSecurityModule": "0xd12C017529BE32c23150313F1E473B76e6B19773",
"mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9",
"merkleTreeHook": "0x748040afB89B8FdBb992799808215419d36A0930",
"name": "arbitrum",
@ -152,7 +152,7 @@
"interchainAccountIsm": "0x786c26C1857032617c215f265509d6E44e44Bfe3",
"interchainAccountRouter": "0xA967A6CE0e73fAf672843DECaA372511996E8852",
"interchainGasPaymaster": "0x95519ba800BBd0d34eeAE026fEc620AD978176C0",
"interchainSecurityModule": "0xe7a61510EA7197281b49e5bdf1798608d5132595",
"interchainSecurityModule": "0xB7D96FcD923267640BcffC7c3F23530E6C7A4209",
"mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6",
"merkleTreeHook": "0x84eea61D679F42D92145fA052C89900CBAccE95A",
"name": "avalanche",
@ -218,7 +218,7 @@
"interchainAccountIsm": "0x861908E6c8F992537F557da5Fb5876836036b347",
"interchainAccountRouter": "0xa85F9e4fdA2FFF1c07f2726a630443af3faDF830",
"interchainGasPaymaster": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94",
"interchainSecurityModule": "0x77bE0b5aE400675063Ce2B2B0d692D9341f4b193",
"interchainSecurityModule": "0xe7aaFbA826B0bDC60aD09b7b8cA8175c9A89cE0b",
"mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D",
"merkleTreeHook": "0x19dc38aeae620380430C200a6E990D5Af5480117",
"name": "base",
@ -275,11 +275,12 @@
"domainRoutingIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"fallbackRoutingHook": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa",
"gasCurrencyCoinGeckoId": "ethereum",
"gnosisSafeTransactionServiceUrl": "https://transaction.blast-safe.io",
"index": {
"from": 2496427
},
"interchainGasPaymaster": "0xB3fCcD379ad66CED0c91028520C64226611A48c9",
"interchainSecurityModule": "0x208263bB303B2a737642fB13C765F106a2591be8",
"interchainSecurityModule": "0xdE516712D58166257E03254BD596CA726417a837",
"mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7",
"merkleTreeHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465",
"name": "blast",
@ -338,7 +339,7 @@
"interchainAccountIsm": "0xB274Bbbc1df5f1d1763216A93d473fde6f5de043",
"interchainAccountRouter": "0x4BBd67dC995572b40Dc6B3eB6CdE5185a5373868",
"interchainGasPaymaster": "0x78E25e7f84416e69b9339B0A6336EB6EFfF6b451",
"interchainSecurityModule": "0xfA360ff588623A026BF19A1801F2A8F1f045fa33",
"interchainSecurityModule": "0x4C23e778aF68a54cb59A44581b90E78b35763C83",
"mailbox": "0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4",
"merkleTreeHook": "0xFDb9Cd5f9daAA2E4474019405A328a88E7484f26",
"name": "bsc",
@ -411,7 +412,7 @@
"interchainAccountIsm": "0x30a8DEc5318e2aAa9ad5b069fC606c4CfF6f5676",
"interchainAccountRouter": "0x4ED23E3885e1651E62564F78817D91865beba575",
"interchainGasPaymaster": "0x571f1435613381208477ac5d6974310d88AC7cB7",
"interchainSecurityModule": "0x0dcb01D4ABfa73fadB17C4B0e8cd52A38BD52c66",
"interchainSecurityModule": "0x1b90cCF49e45a87F751b344864e4246D1a57a100",
"mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb",
"merkleTreeHook": "0x04dB778f05854f26E67e0a66b740BBbE9070D366",
"name": "celo",
@ -476,7 +477,7 @@
"interchainAccountIsm": "0x609707355a53d2aAb6366f48E2b607C599D26B29",
"interchainAccountRouter": "0x8dBae9B1616c46A20591fE0006Bf015E28ca5cC9",
"interchainGasPaymaster": "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611",
"interchainSecurityModule": "0x8CE0c6cAf18DbF5882b35F26E28412f3E9AbDeca",
"interchainSecurityModule": "0x355F2c5532521ca6Df8A908641Ed69eD0619466d",
"mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239",
"merkleTreeHook": "0x48e6c30B97748d1e2e03bf3e9FbE3890ca5f8CCA",
"name": "ethereum",
@ -513,6 +514,60 @@
},
"validatorAnnounce": "0xCe74905e51497b4adD3639366708b821dcBcff96"
},
"fraxtal": {
"aggregationHook": "0xD7ff06cDd83642D648baF0d36f77e79349120dA4",
"blockExplorers": [
{
"apiUrl": "https://api.fraxscan.com/api",
"family": "etherscan",
"name": "Fraxscan",
"url": "https://fraxscan.com"
}
],
"blocks": {
"confirmations": 1,
"estimateBlockTime": 2,
"reorgPeriod": 1
},
"chainId": 252,
"displayName": "Fraxtal",
"domainId": 252,
"domainRoutingIsm": "0x0CA20946c1b7367Bd47C0a35E8feD23a4Ff59B9a",
"domainRoutingIsmFactory": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E",
"fallbackRoutingHook": "0xC077A0Cc408173349b1c9870C667B40FE3C01dd7",
"gasCurrencyCoinGeckoId": "frax-ether",
"index": {
"from": 5350807
},
"interchainGasPaymaster": "0x2Fca7f6eC3d4A0408900f2BB30004d4616eE985E",
"interchainSecurityModule": "0xf1465DB845d0978e74d45EF195734b43bB739094",
"mailbox": "0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3",
"merkleTreeHook": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147",
"name": "fraxtal",
"nativeToken": {
"decimals": 18,
"name": "Frax Ether",
"symbol": "frxETH"
},
"pausableHook": "0x4E1c88DD261BEe2941e6c1814597e30F53330428",
"pausableIsm": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4",
"protocol": "ethereum",
"protocolFee": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807",
"proxyAdmin": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7",
"rpcUrls": [
{
"http": "https://rpc.frax.com"
}
],
"staticAggregationHookFactory": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D",
"staticAggregationIsm": "0xcA26D50602efA9d835b01A142Ae218f59aa60433",
"staticAggregationIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"staticMerkleRootMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"staticMessageIdMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1",
"storageGasOracle": "0x5060eCD5dFAD300A90592C04e504600A7cdcF70b",
"testRecipient": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05",
"validatorAnnounce": "0x1956848601549de5aa0c887892061fA5aB4f6fC4"
},
"gnosis": {
"aggregationHook": "0xdD1FA1C12496474c1dDC67a658Ba81437F818861",
"blockExplorers": [
@ -542,7 +597,7 @@
"interchainAccountIsm": "0x5a56dff3D92D635372718f86e6dF09C1129CFf53",
"interchainAccountRouter": "0x5E59EBAedeB691408EBAcF6C37218fa2cFcaC9f2",
"interchainGasPaymaster": "0xDd260B99d302f0A3fF885728c086f729c06f227f",
"interchainSecurityModule": "0x5DB7edF8C1CF91e34895dB2e4b28d8b9C68ddC7B",
"interchainSecurityModule": "0x613D9ab2dd6cCFa2763d86FdEE3d96C4Ef4C2E96",
"mailbox": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f",
"merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645",
"name": "gnosis",
@ -592,11 +647,13 @@
"reorgPeriod": 0
},
"chainId": 2525,
"customHook": "0xA376b27212D608324808923Add679A2c9FAFe9Da",
"displayName": "Injective EVM",
"displayNameShort": "inEVM",
"domainId": 2525,
"domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730",
"domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"fallbackRoutingHook": "0xA376b27212D608324808923Add679A2c9FAFe9Da",
"gasCurrencyCoinGeckoId": "injective-protocol",
"index": {
"from": 18972465
@ -604,7 +661,7 @@
"interchainAccountIsm": "0x31894E7a734540B343d67E491148EB4FC9f7A45B",
"interchainAccountRouter": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd",
"interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117",
"interchainSecurityModule": "0x440f7AD246F3e75df88a6338E8A33e91DA4B2B05",
"interchainSecurityModule": "0xC1a7a7b15d2B243f7c6Dbbdd6e8A2C2434cdf7BF",
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65",
"name": "inevm",
@ -629,6 +686,7 @@
"staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
"storageGasOracle": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76",
"testRecipient": "0x28291a7062afA569104bEd52F7AcCA3dD2FafD11",
"timelockController": "0x0000000000000000000000000000000000000000",
"validatorAnnounce": "0x15ab173bDB6832f9b64276bA128659b0eD77730B"
},
@ -657,7 +715,7 @@
}
],
"index": {
"chunk": 25,
"chunk": 5,
"from": 58419500
},
"interchainGasPaymaster": "0x27ae52298e5b53b34b7ae0ca63e05845c31e1f59",
@ -684,6 +742,61 @@
"slip44": 118,
"validatorAnnounce": "0x1fb225b2fcfbe75e614a1d627de97ff372242eed"
},
"linea": {
"aggregationHook": "0x43fF73dF1E170D076D9Ed30d4C6922A9D34322dE",
"blockExplorers": [
{
"apiUrl": "https://api.lineascan.build/api",
"family": "etherscan",
"name": "LineaScan",
"url": "https://lineascan.build"
}
],
"blocks": {
"confirmations": 1,
"estimateBlockTime": 3,
"reorgPeriod": 1
},
"chainId": 59144,
"displayName": "Linea",
"domainId": 59144,
"domainRoutingIsm": "0x6faCF71D804964Ca62f16e56DE74d7dF38FdC3F0",
"domainRoutingIsmFactory": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E",
"fallbackRoutingHook": "0x4E1c88DD261BEe2941e6c1814597e30F53330428",
"gasCurrencyCoinGeckoId": "ethereum",
"gnosisSafeTransactionServiceUrl": "https://transaction.safe.linea.build",
"index": {
"from": 5154574
},
"interchainGasPaymaster": "0x8105a095368f1a184CceA86cCe21318B5Ee5BE28",
"interchainSecurityModule": "0xF8aD4EB8aBA13ae546B8D01501c63e4543Ff0660",
"mailbox": "0x02d16BC51af6BfD153d67CA61754cF912E82C4d9",
"merkleTreeHook": "0xC077A0Cc408173349b1c9870C667B40FE3C01dd7",
"name": "linea",
"nativeToken": {
"decimals": 18,
"name": "Ether",
"symbol": "ETH"
},
"pausableHook": "0x5060eCD5dFAD300A90592C04e504600A7cdcF70b",
"pausableIsm": "0x01aA8200936B475762Ee28D38B43a6cFe9076E52",
"protocol": "ethereum",
"protocolFee": "0x7556a0E61d577D921Cba8Fca0d7D6299d36E607E",
"proxyAdmin": "0x7f50C5776722630a0024fAE05fDe8b47571D7B39",
"rpcUrls": [
{
"http": "https://rpc.linea.build"
}
],
"staticAggregationHookFactory": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D",
"staticAggregationIsm": "0xF8aD4EB8aBA13ae546B8D01501c63e4543Ff0660",
"staticAggregationIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"staticMerkleRootMultisigIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"staticMessageIdMultisigIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1",
"storageGasOracle": "0x781bE492F1232E66990d83a9D3AC3Ec26f56DAfB",
"testRecipient": "0x273Bc6b01D9E88c064b6E5e409BdF998246AEF42",
"validatorAnnounce": "0x62B7592C1B6D1E43f4630B8e37f4377097840C05"
},
"mantapacific": {
"aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F",
"blockExplorers": [
@ -714,7 +827,7 @@
"interchainAccountIsm": "0xA34ceDf9068C5deE726C67A4e1DCfCc2D6E2A7fD",
"interchainAccountRouter": "0x0f6fF770Eda6Ba1433C39cCf47d4059b254224Aa",
"interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4",
"interchainSecurityModule": "0xEda7cCD2A8CF717dc997D0002e363e4D10bF5c0d",
"interchainSecurityModule": "0x83319BD846D54E49C850027191D094D5E9114339",
"isTestnet": false,
"mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E",
"merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112",
@ -725,6 +838,7 @@
"symbol": "ETH"
},
"pausableHook": "0x7556a0E61d577D921Cba8Fca0d7D6299d36E607E",
"pausableIsm": "0x6119B76720CcfeB3D256EC1b91218EEfFD6756E1",
"protocol": "ethereum",
"protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638",
"proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
@ -734,6 +848,7 @@
}
],
"staticAggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"staticAggregationIsm": "0x845A3feB4BcdC32a457c4051F67d3950FC6Fd1d1",
"staticAggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"staticMerkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMessageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
@ -744,6 +859,7 @@
"validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9"
},
"mode": {
"aggregationHook": "0x80D80cfBa98dD2d456ECd43Dcc1f852D5C4EeD7a",
"blockExplorers": [
{
"apiUrl": "https://explorer.mode.network/api",
@ -760,14 +876,16 @@
"chainId": 34443,
"displayName": "Mode",
"domainId": 34443,
"domainRoutingIsm": "0xB6F0f1267B01C27326F61a4B4fe2c73751802685",
"domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"fallbackRoutingHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA",
"gasCurrencyCoinGeckoId": "ethereum",
"gnosisSafeTransactionServiceUrl": "https://transaction-mode.safe.optimism.io",
"index": {
"from": 6817759
},
"interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d",
"interchainSecurityModule": "0x8dfE6790DbB2Ecc1bEdb0eECfc1Ff467Ae5d8C89",
"interchainSecurityModule": "0xEe9443bc2Df7C10327077c605120CDd95e8Ca75F",
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6",
"name": "mode",
@ -777,6 +895,7 @@
"symbol": "ETH"
},
"pausableHook": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882",
"pausableIsm": "0xe243Fb51d91c5DE62afAbE44F7Ed2D4DC51668C6",
"protocol": "ethereum",
"protocolFee": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4",
"proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
@ -786,6 +905,7 @@
}
],
"staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"staticAggregationIsm": "0x6BdA2074b7edCE8c4e4cD3D35517267468Aed93F",
"staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
@ -812,6 +932,7 @@
"chainId": 1284,
"displayName": "Moonbeam",
"domainId": 1284,
"domainRoutingIsm": "0x7Faa23CEdA03364A79e05259e07D5E358E7400F7",
"domainRoutingIsmFactory": "0x8061Af3A459093540d17823D651BC5E2A92669a7",
"fallbackRoutingHook": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361",
"gnosisSafeTransactionServiceUrl": "https://transaction.multisig.moonbeam.network",
@ -821,7 +942,7 @@
"interchainAccountIsm": "0x799eA6f430f5CA901b59335fFC2fA10531106009",
"interchainAccountRouter": "0x6b142f596FFc761ac3fFaaC1ecaDe54f4EE09977",
"interchainGasPaymaster": "0x14760E32C0746094cF14D97124865BC7F0F7368F",
"interchainSecurityModule": "0x373836DFa82f2D27ec79Ca32A197Aa1665F0E1e9",
"interchainSecurityModule": "0x3db9D7c5cD0Aa312AB1262C711C10DeBA8303388",
"mailbox": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3",
"merkleTreeHook": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547",
"name": "moonbeam",
@ -831,6 +952,7 @@
"symbol": "GLMR"
},
"pausableHook": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f",
"pausableIsm": "0x58062b26193B28000Cd991Df767f3A2674502de8",
"protocol": "ethereum",
"protocolFee": "0xCd3e29A9D293DcC7341295996a118913F7c582c0",
"proxyAdmin": "0x6A9cdA3dd1F593983BFd142Eb35e6ce4137bd5ce",
@ -840,6 +962,7 @@
}
],
"staticAggregationHookFactory": "0x59cC3E7A49DdC4893eB8754c7908f96072A7DbE8",
"staticAggregationIsm": "0xDAAfa04d38d95f5B8418786AE0F7ee5B962ee92B",
"staticAggregationIsmFactory": "0x40c6Abcb6A2CdC8882d4bEcaC47927005c7Bb8c2",
"staticMerkleRootMultisigIsmFactory": "0xE2f485bc031Feb5a4C41C1967bf028653d75f0C3",
"staticMessageIdMultisigIsmFactory": "0x84Df48F8f241f11d0fA302d09d73030429Bd9C73",
@ -884,7 +1007,7 @@
}
],
"index": {
"chunk": 50,
"chunk": 5,
"from": 4000000
},
"interchainGasPaymaster": "0x504ee9ac43ec5814e00c7d21869a90ec52becb489636bdf893b7df9d606b5d67",
@ -935,6 +1058,7 @@
"chainId": 10,
"displayName": "Optimism",
"domainId": 10,
"domainRoutingIsm": "0xDFfFCA9320E2c7530c61c4946B4c2376A1901dF2",
"domainRoutingIsmFactory": "0xD2e905108c5e44dADA680274740f896Ea96Cf2Fb",
"fallbackRoutingHook": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d",
"gasCurrencyCoinGeckoId": "ethereum",
@ -945,7 +1069,7 @@
"interchainAccountIsm": "0x0389faCac114023C123E22F3E54394944cAbcb48",
"interchainAccountRouter": "0x33Ef006E7083BB38E0AFe3C3979F4e9b84415bf1",
"interchainGasPaymaster": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C",
"interchainSecurityModule": "0x04938856bE60c8e734ffDe5f720E2238302BE8D2",
"interchainSecurityModule": "0x32Ce76b9FfD0BbA0e43a2C27c2cAc90E82C48B95",
"mailbox": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D",
"merkleTreeHook": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f",
"name": "optimism",
@ -955,6 +1079,7 @@
"symbol": "ETH"
},
"pausableHook": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A",
"pausableIsm": "0xD84D8114cCfa5c2403E56aBf754da529430704F0",
"protocol": "ethereum",
"protocolFee": "0xD71Ff941120e8f935b8b1E2C1eD72F5d140FF458",
"proxyAdmin": "0xE047cb95FB3b7117989e911c6afb34771183fC35",
@ -964,6 +1089,7 @@
}
],
"staticAggregationHookFactory": "0x15DEeAB8dECDe553bb0B1F9C00984cbcae1af3D7",
"staticAggregationIsm": "0xdF6316DF574974110DCC94BB4E520B09Fe3CbEf9",
"staticAggregationIsmFactory": "0x7491843F3A5Ba24E0f17a22645bDa04A1Ae2c584",
"staticMerkleRootMultisigIsmFactory": "0xCA6Cb9Bc3cfF9E11003A06617cF934B684Bc78BC",
"staticMessageIdMultisigIsmFactory": "0xAa4Be20E9957fE21602c74d7C3cF5CB1112EA9Ef",
@ -975,13 +1101,25 @@
},
"osmosis": {
"bech32Prefix": "osmo",
"blockExplorers": [
{
"apiUrl": "https://www.mintscan.io/osmosis",
"family": "other",
"name": "Mintscan",
"url": "https://www.mintscan.io/osmosis"
}
],
"blocks": {
"confirmations": 1,
"estimateBlockTime": 3,
"reorgPeriod": 1
},
"canonicalAsset": "uosmo",
"chainId": "osmosis-1",
"contractAddressBytes": 32,
"domainId": "875",
"displayName": "Osmosis",
"domainId": 875,
"gasCurrencyCoinGeckoId": "osmosis",
"gasPrice": {
"amount": "0.025",
"denom": "uosmo"
@ -992,18 +1130,37 @@
}
],
"index": {
"chunk": 5,
"from": 14389169
},
"interchainGasPaymaster": "0xd20a9dcf61939fc2fe6ad501b9457b1029b3cc7ab12ed72675ea2e10d831ee5d",
"isTestnet": false,
"mailbox": "0x9493e39d85dd038022f97d88aba6bff98d98f9a016b4f2e498bf1d9898420172",
"merkleTreeHook": "0x8920e062ee5ed8afccbc155d13ea9049296399ee41403655864fcd243edc7388",
"name": "osmosis1",
"name": "osmosis",
"nativeToken": {
"decimals": 6,
"denom": "uosmo",
"name": "Osmosis",
"symbol": "OSMO"
},
"protocol": "cosmos",
"restUrls": [
{
"http": "https://osmosis-rest.publicnode.com"
}
],
"rpcUrls": [
{
"http": "https://osmosis-rpc.publicnode.com:443"
"http": "https://osmosis-rpc.publicnode.com"
}
],
"signer": {
"key": "0x5486418967eabc770b0fcb995f7ef6d9a72f7fc195531ef76c5109f44f51af26",
"prefix": "osmo",
"type": "cosmosKey"
},
"slip44": 118,
"validatorAnnounce": "0xaf867da5b09a20ee49161d57f99477c0c42d100f34eb53da0d2eb7fc6c257235"
},
"polygon": {
@ -1035,7 +1192,7 @@
"interchainAccountIsm": "0x90384bC552e3C48af51Ef7D9473A9bF87431f5c7",
"interchainAccountRouter": "0x5e80f3474825B61183c0F0f0726796F589082420",
"interchainGasPaymaster": "0x0071740Bf129b05C4684abfbBeD248D80971cce2",
"interchainSecurityModule": "0xe289bD204Dbb4F3aaFA27Dbe5751C71e101CFD80",
"interchainSecurityModule": "0x1c39BD73C933c6B1b14685ABD28F996A6a0e306f",
"mailbox": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB",
"merkleTreeHook": "0x73FbD25c3e817DC4B4Cd9d00eff6D83dcde2DfF6",
"name": "polygon",
@ -1094,6 +1251,7 @@
"displayName": "Polygon zkEVM",
"displayNameShort": "zkEVM",
"domainId": 1101,
"domainRoutingIsm": "0x8b6862a784f634F4C8E1cbb04c9DA3dB637B7EaA",
"domainRoutingIsmFactory": "0xe4057c5B0c43Dc18E36b08C39B419F190D29Ac2d",
"fallbackRoutingHook": "0x01aE937A7B05d187bBCBE80F44F41879D3D335a4",
"gasCurrencyCoinGeckoId": "ethereum",
@ -1104,7 +1262,7 @@
"interchainAccountIsm": "0xC49aF4965264FA7BB6424CE37aA06773ad177224",
"interchainAccountRouter": "0xF15D70941dE2Bf95A23d6488eBCbedE0a444137f",
"interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4",
"interchainSecurityModule": "0xf2BEE9D2c15Ba9D7e06799B5912dE1F05533c141",
"interchainSecurityModule": "0xb7556707f04BEF8888294C18c447bE1e4446bF7D",
"mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E",
"merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112",
"name": "polygonzkevm",
@ -1114,6 +1272,7 @@
"symbol": "ETH"
},
"pausableHook": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c",
"pausableIsm": "0x784b9D0f4eF9fb8444DfB5d24AB221C9D1A85395",
"protocol": "ethereum",
"protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638",
"proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
@ -1126,14 +1285,20 @@
}
],
"staticAggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"staticAggregationIsm": "0xAe7d2FA2aFc57Ce9F05930d403673A267b3efE50",
"staticAggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"staticMerkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMessageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"storageGasOracle": "0x19dc38aeae620380430C200a6E990D5Af5480117",
"testRecipient": "0xD127D4549cb4A5B2781303a4fE99a10EAd13263A",
"timelockController": "0x0000000000000000000000000000000000000000",
"transactionOverrides": {
"gasPrice": 1000000000
},
"validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9"
},
"redstone": {
"aggregationHook": "0x7bC13D23eD161E152a05c71D037b4642EA61B8eF",
"blockExplorers": [
{
"apiUrl": "https://explorer.redstone.xyz/api",
@ -1150,6 +1315,7 @@
"chainId": 690,
"displayName": "Redstone",
"domainId": 690,
"domainRoutingIsm": "0x5D1e7D7c5B9e6dDC8439F67F10c578f2A1084f6F",
"domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"fallbackRoutingHook": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882",
"gasCurrencyCoinGeckoId": "ethereum",
@ -1157,7 +1323,7 @@
"from": 1797579
},
"interchainGasPaymaster": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634",
"interchainSecurityModule": "0xF4689C7fA4920C91a6EEEd59630C9C8da7a77D40",
"interchainSecurityModule": "0xD10AD7d927817558468Ce0F0ab1edEF8D230E2d8",
"mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D",
"merkleTreeHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA",
"name": "redstone",
@ -1167,6 +1333,7 @@
"symbol": "ETH"
},
"pausableHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465",
"pausableIsm": "0xD53Fdbb7537aCa82bAf7cCA7204088f15b52796a",
"protocol": "ethereum",
"protocolFee": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4",
"proxyAdmin": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1",
@ -1176,6 +1343,7 @@
}
],
"staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"staticAggregationIsm": "0x3Bb9244a2aBBb710c933e8D82Ff8F0C200F3c036",
"staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
"staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
@ -1201,10 +1369,11 @@
"chainId": 534352,
"displayName": "Scroll",
"domainId": 534352,
"domainRoutingIsm": "0x4d02AfFc3F030c887e2f914B8B67E0B845e034fD",
"domainRoutingIsmFactory": "0xe03dad16074BC5EEA9A9311257BF02Eb0B6AAA2b",
"fallbackRoutingHook": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762",
"gasCurrencyCoinGeckoId": "ethereum",
"gnosisSafeTransactionServiceUrl": "https://transaction.safe.scroll.xyz",
"gnosisSafeTransactionServiceUrl": "https://safe-transaction-scroll.safe.global",
"index": {
"chunk": 999,
"from": 271840
@ -1212,7 +1381,7 @@
"interchainAccountIsm": "0xb89c6ED617f5F46175E41551350725A09110bbCE",
"interchainAccountRouter": "0x9629c28990F11c31735765A6FD59E1E1bC197DbD",
"interchainGasPaymaster": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2",
"interchainSecurityModule": "0xaDc0cB48E8DB81855A930C0C1165ea3dCe4Ba5C7",
"interchainSecurityModule": "0x0E6aFC8a7b5223cAD7a7c346da1B2e6ECBaA93f0",
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76",
"name": "scroll",
@ -1222,6 +1391,7 @@
"symbol": "ETH"
},
"pausableHook": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2",
"pausableIsm": "0x11Fa12DBaCe771E293e19743feA342e378C6341F",
"protocol": "ethereum",
"protocolFee": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94",
"proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
@ -1231,16 +1401,75 @@
}
],
"staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"staticAggregationIsm": "0xAC0F1820F1F3fEd26293B8714464ca431824f823",
"staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
"storageGasOracle": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13",
"testRecipient": "0x674f4698d063cE4C0d604c88dD7D542De72f327f",
"timelockController": "0x0000000000000000000000000000000000000000",
"transactionOverrides": {
"gasPrice": 2000000000
},
"validatorAnnounce": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638"
},
"sei": {
"aggregationHook": "0x40514BD46C57455933Be8BAedE96C4F0Ba3507D6",
"blockExplorers": [
{
"apiUrl": "https://seitrace.com/pacific-1/api",
"family": "etherscan",
"name": "Seitrace",
"url": "https://seitrace.com"
}
],
"blocks": {
"confirmations": 1,
"estimateBlockTime": 1,
"reorgPeriod": 1
},
"chainId": 1329,
"displayName": "Sei",
"domainId": 1329,
"domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730",
"domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"fallbackRoutingHook": "0xB3fCcD379ad66CED0c91028520C64226611A48c9",
"gasCurrencyCoinGeckoId": "sei-network",
"index": {
"from": 80809403
},
"interchainGasPaymaster": "0xFC62DeF1f08793aBf0E67f69257c6be258194F72",
"interchainSecurityModule": "0x10FF77061869714E92E574FcE9025a419f5b999d",
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0xca1b69fA4c4a7c7fD839bC50867c589592bcfe49",
"name": "sei",
"nativeToken": {
"decimals": 18,
"name": "Sei",
"symbol": "SEI"
},
"pausableHook": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4",
"pausableIsm": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d",
"protocol": "ethereum",
"protocolFee": "0x83c2DB237e93Ce52565AB110124f78fdf159E3f4",
"proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"rpcUrls": [
{
"http": "https://evm-rpc.sei-apis.com"
}
],
"staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"staticAggregationIsm": "0x596eCC936068AeBD836e79D530043b868569a61B",
"staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
"storageGasOracle": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4",
"testRecipient": "0xdB670e1a1e312BF17425b08cE55Bdf2cD8F8eD54",
"transactionOverrides": {
"gasPrice": 2000000000
},
"validatorAnnounce": "0x5332D1AC0A626D265298c14ff681c0A8D28dB86d"
},
"viction": {
"aggregationHook": "0x5c7890FAf9c99dC55926F00d624D7Bc6D7ac6834",
"blockExplorers": [
@ -1270,7 +1499,7 @@
"interchainAccountIsm": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807",
"interchainAccountRouter": "0x1956848601549de5aa0c887892061fA5aB4f6fC4",
"interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4",
"interchainSecurityModule": "0xf8F3AF5F6B8f319364c339c0b8cA5975481901eD",
"interchainSecurityModule": "0xA76F4620ac1e97d273B2C9Ca71805c8afD792098",
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112",
"name": "viction",
@ -1304,6 +1533,7 @@
"validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9"
},
"zetachain": {
"aggregationHook": "0x80D80cfBa98dD2d456ECd43Dcc1f852D5C4EeD7a",
"blockExplorers": [
{
"apiUrl": "https://explorer.zetachain.com",
@ -1320,6 +1550,7 @@
"chainId": 7000,
"displayName": "ZetaChain",
"domainId": 7000,
"domainRoutingIsm": "0xaDc0cB48E8DB81855A930C0C1165ea3dCe4Ba5C7",
"domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"fallbackRoutingHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA",
"gasCurrencyCoinGeckoId": "zetachain",
@ -1327,7 +1558,7 @@
"from": 3068132
},
"interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d",
"interchainSecurityModule": "0x8dfE6790DbB2Ecc1bEdb0eECfc1Ff467Ae5d8C89",
"interchainSecurityModule": "0x8fB4297373f1f11032856693Cf6eee5eC8d7FF4F",
"mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6",
"name": "zetachain",
@ -1337,6 +1568,7 @@
"symbol": "ZETA"
},
"pausableHook": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882",
"pausableIsm": "0x7b75b29caD47e10146e29BBf7BD9025e021a7023",
"protocol": "ethereum",
"protocolFee": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4",
"proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
@ -1346,12 +1578,10 @@
},
{
"http": "https://zetachain-athens-evm.blockpi.network/v1/rpc/public"
},
{
"http": "https://zetachain-mainnet-archive.allthatnode.com:8545"
}
],
"staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"staticAggregationIsm": "0x7CB2dbE36aF0C0893B1B3502358Bc3697343559c",
"staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",

@ -23,17 +23,17 @@ ed25519-dalek.workspace = true
ethers.workspace = true
eyre.workspace = true
fuels.workspace = true
futures.worksapce = true
futures.workspace = true
futures-util.workspace = true
itertools.workspace = true
maplit.workspace = true
mockall.worksapce = true
mockall.workspace = true
paste.workspace = true
prometheus.workspace = true
rocksdb.workspace = true
serde.workspace = true
serde_json.workspace = true
solana-sdk.worksapce = true
solana-sdk.workspace = true
static_assertions.workspace = true
tempfile = { workspace = true, optional = true }
thiserror.workspace = true

@ -546,10 +546,10 @@ pub(crate) mod test {
pub sequence: u32,
}
impl Into<Indexed<MockSequencedData>> for MockSequencedData {
fn into(self) -> Indexed<MockSequencedData> {
let sequence = self.sequence;
Indexed::new(self).with_sequence(sequence)
impl From<MockSequencedData> for Indexed<MockSequencedData> {
fn from(val: MockSequencedData) -> Self {
let sequence = val.sequence;
Indexed::new(val).with_sequence(sequence)
}
}

@ -132,7 +132,7 @@ impl CoreMetrics {
"Submitter queue length",
const_labels_ref
),
&["remote", "queue_name", "app_context"],
&["remote", "queue_name", "operation_status", "app_context"],
registry
)?;

@ -37,6 +37,7 @@ serde = { workspace = true }
serde_json = { workspace = true }
sha3 = { workspace = true }
strum = { workspace = true, optional = true, features = ["derive"] }
strum_macros = { workspace = true, optional = true }
thiserror = { workspace = true }
tokio = { workspace = true, optional = true, features = ["rt", "time"] }
tracing.workspace = true

@ -7,7 +7,7 @@ use crate::accumulator::{
H256, TREE_DEPTH, ZERO_HASHES,
};
#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, Copy, new, PartialEq, Eq)]
#[derive(BorshDeserialize, BorshSerialize, Debug, Clone, new, PartialEq, Eq)]
/// An incremental merkle tree, modeled on the eth2 deposit contract
pub struct IncrementalMerkle {
/// The branch of the tree

@ -80,11 +80,31 @@ pub enum KnownHyperlaneDomain {
Neutron = 1853125230,
Osmosis = 875,
Injective = 6909546,
InEvm = 2525,
Ancient8 = 888888888,
Blast = 81457,
Mode = 34443,
Redstone = 690,
Viction = 88,
Zetachain = 7000,
PlumeTestnet = 161221135,
Fraxtal = 252,
Linea = 59144,
Sei = 1329,
// -- Local test chains --
/// Test1 local chain
Test1 = 13371,
@ -216,7 +236,8 @@ impl KnownHyperlaneDomain {
many_to_one!(match self {
Mainnet: [
Ethereum, Avalanche, Arbitrum, Polygon, Optimism, BinanceSmartChain, Celo,
Moonbeam, Gnosis, MantaPacific, Neutron, Injective, InEvm
Moonbeam, Gnosis, MantaPacific, Neutron, Injective, InEvm, Ancient8, Blast,
Mode, Redstone, Viction, Zetachain, Fraxtal, Linea, Sei, Osmosis
],
Testnet: [
Alfajores, MoonbaseAlpha, Sepolia, ScrollSepolia, Chiado, PlumeTestnet, Fuji, BinanceSmartChainTestnet, Holesky
@ -232,12 +253,14 @@ impl KnownHyperlaneDomain {
HyperlaneDomainProtocol::Ethereum: [
Ethereum, Sepolia, Holesky, Polygon, Avalanche, Fuji, Arbitrum,
Optimism, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis,
Alfajores, Moonbeam, InEvm, MoonbaseAlpha, ScrollSepolia,
Chiado, MantaPacific, PlumeTestnet, Test1, Test2, Test3
Alfajores, Moonbeam, InEvm, Ancient8, Blast, Mode, Redstone, Viction,
Zetachain, MoonbaseAlpha, ScrollSepolia, Chiado, MantaPacific, PlumeTestnet,
Fraxtal, Linea, Sei,
Test1, Test2, Test3
],
HyperlaneDomainProtocol::Fuel: [FuelTest1],
HyperlaneDomainProtocol::Sealevel: [SealevelTest1, SealevelTest2],
HyperlaneDomainProtocol::Cosmos: [CosmosTest99990, CosmosTest99991, Neutron, Injective],
HyperlaneDomainProtocol::Cosmos: [CosmosTest99990, CosmosTest99991, Neutron, Osmosis, Injective],
})
}
@ -249,7 +272,8 @@ impl KnownHyperlaneDomain {
HyperlaneDomainTechnicalStack::Other: [
Ethereum, Sepolia, Holesky, Polygon, Avalanche, Fuji, Optimism,
BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, Alfajores, Moonbeam, MoonbaseAlpha,
ScrollSepolia, Chiado, MantaPacific, Neutron, Injective, InEvm,
ScrollSepolia, Chiado, MantaPacific, Neutron, Injective, InEvm, Ancient8, Blast, Mode, Redstone,
Viction, Zetachain, Fraxtal, Linea, Sei, Osmosis,
Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, CosmosTest99991
],
})

@ -34,14 +34,6 @@ pub async fn call_and_retry_n_times<T>(
))
}
/// Retry calling a fallible async function a predefined number of times
#[instrument(err, skip(f))]
pub async fn call_with_retry<T>(
f: impl FnMut() -> Pin<Box<dyn Future<Output = ChainResult<T>> + Send>>,
) -> ChainResult<T> {
call_and_retry_n_times(f, DEFAULT_MAX_RPC_RETRIES).await
}
/// Retry calling a fallible async function indefinitely, until it succeeds
pub async fn call_and_retry_indefinitely<T>(
f: impl FnMut() -> Pin<Box<dyn Future<Output = ChainResult<T>> + Send>>,

@ -10,6 +10,7 @@ use crate::{
};
use async_trait::async_trait;
use num::CheckedDiv;
use strum::Display;
use tracing::warn;
/// Boxed operation that can be stored in an operation queue
@ -55,6 +56,13 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs<HyperlaneMessage> {
/// Label to use for metrics granularity.
fn app_context(&self) -> Option<String>;
/// The status of the operation, which should explain why it is in the
/// queue.
fn status(&self) -> PendingOperationStatus;
/// Set the status of the operation.
fn set_status(&mut self, status: PendingOperationStatus);
/// Get tuple of labels for metrics.
fn get_operation_labels(&self) -> (String, String) {
let app_context = self.app_context().unwrap_or("Unknown".to_string());
@ -106,6 +114,74 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs<HyperlaneMessage> {
fn set_retries(&mut self, retries: u32);
}
#[derive(Debug, Display, Clone)]
/// Status of a pending operation
pub enum PendingOperationStatus {
/// The operation is ready to be prepared for the first time, or has just been loaded from storage
FirstPrepareAttempt,
/// The operation is ready to be prepared again, with the given reason
#[strum(to_string = "Retry({0})")]
Retry(ReprepareReason),
/// The operation is ready to be submitted
ReadyToSubmit,
/// The operation has been submitted and is awaiting confirmation
#[strum(to_string = "Confirm({0})")]
Confirm(ConfirmReason),
}
#[derive(Display, Debug, Clone)]
/// Reasons for repreparing an operation
pub enum ReprepareReason {
#[strum(to_string = "Error checking message delivery status")]
/// Error checking message delivery status
ErrorCheckingDeliveryStatus,
#[strum(to_string = "Error checking if message recipient is a contract")]
/// Error checking if message recipient is a contract
ErrorCheckingIfRecipientIsContract,
#[strum(to_string = "Error fetching ISM address")]
/// Error fetching ISM address
ErrorFetchingIsmAddress,
#[strum(to_string = "Error getting message metadata builder")]
/// Error getting message metadata builder
ErrorGettingMetadataBuilder,
#[strum(to_string = "Error building metadata")]
/// Error building metadata
ErrorBuildingMetadata,
#[strum(to_string = "Could not fetch metadata")]
/// Could not fetch metadata
CouldNotFetchMetadata,
#[strum(to_string = "Error estimating costs for process call")]
/// Error estimating costs for process call
ErrorEstimatingGas,
#[strum(to_string = "Error checking if message meets gas payment requirement")]
/// Error checking if message meets gas payment requirement
ErrorCheckingGasRequirement,
#[strum(to_string = "Gas payment requirement not met")]
/// Gas payment requirement not met
GasPaymentRequirementNotMet,
#[strum(to_string = "Message delivery estimated gas exceeds max gas limit")]
/// Message delivery estimated gas exceeds max gas limit
ExceedsMaxGasLimit,
#[strum(to_string = "Delivery transaction reverted or reorged")]
/// Delivery transaction reverted or reorged
RevertedOrReorged,
}
#[derive(Display, Debug, Clone)]
/// Reasons for repreparing an operation
pub enum ConfirmReason {
#[strum(to_string = "Submitted by this relayer")]
/// Operation was submitted by this relayer
SubmittedBySelf,
#[strum(to_string = "Already submitted, awaiting confirmation")]
/// Operation was already submitted (either by another relayer, or by a previous run of this relayer), awaiting confirmation
AlreadySubmitted,
/// Error checking message delivery status
ErrorConfirmingDelivery,
/// Error storing delivery outcome
ErrorRecordingProcessSuccess,
}
/// Utility fn to calculate the total estimated cost of an operation batch
pub fn total_estimated_cost(ops: &[Box<dyn PendingOperation>]) -> U256 {
ops.iter()
@ -192,40 +268,9 @@ pub enum PendingOperationResult {
/// This operation is not ready to be attempted again yet
NotReady,
/// Operation needs to be started from scratch again
Reprepare,
Reprepare(ReprepareReason),
/// Do not attempt to run the operation again, forget about it
Drop,
/// Send this message straight to the confirm queue
Confirm,
}
/// create a `op_try!` macro for the `on_retry` handler.
#[macro_export]
macro_rules! make_op_try {
($on_retry:expr) => {
/// Handle a result and either return early with retry or a critical failure on
/// error.
macro_rules! op_try {
(critical: $e:expr, $ctx:literal) => {
match $e {
Ok(v) => v,
Err(e) => {
error!(error=?e, concat!("Critical error when ", $ctx));
#[allow(clippy::redundant_closure_call)]
return $on_retry();
}
}
};
($e:expr, $ctx:literal) => {
match $e {
Ok(v) => v,
Err(e) => {
warn!(error=?e, concat!("Error when ", $ctx));
#[allow(clippy::redundant_closure_call)]
return $on_retry();
}
}
};
}
};
Confirm(ConfirmReason),
}

@ -1,6 +1,6 @@
use derive_new::new;
use crate::{HyperlaneMessage, MerkleTreeInsertion, Sequenced};
use crate::{HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, Sequenced};
/// Wrapper struct that adds indexing information to a type
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, new)]
@ -73,3 +73,9 @@ impl From<MerkleTreeInsertion> for Indexed<MerkleTreeInsertion> {
Indexed::new(value).with_sequence(sequence as _)
}
}
impl From<InterchainGasPayment> for Indexed<InterchainGasPayment> {
fn from(value: InterchainGasPayment) -> Self {
Indexed::new(value)
}
}

@ -93,7 +93,7 @@ where
Ok(Self::from(Self::fetch_data(buf)?.unwrap_or_default()))
}
// Optimisically write then realloc on failure.
// Optimistically write then realloc on failure.
// If we serialize and calculate len before realloc we will waste heap space as there is no
// free(). Tradeoff between heap usage and compute budget.
pub fn store(

@ -11,7 +11,7 @@ thiserror.workspace = true
hyperlane-core = { path = "../../../hyperlane-core" }
# Required to allow dependencies `getrandom` but to preserve determinism required by programs, see
# https://github.com/solana-labs/solana/blob/master/docs/src/developing/on-chain-programs/developing-rust.md#depending-on-rand
# https://github.com/solana-foundation/developer-content/blob/main/docs/programs/lang-rust.md#depending-on-rand
getrandom = { workspace = true, features = ["custom"] }
[lib]

@ -12,7 +12,7 @@ spl-type-length-value.workspace = true
hyperlane-core = { path = "../../../hyperlane-core" }
# Required to allow dependencies `getrandom` but to preserve determinism required by programs, see
# https://github.com/solana-labs/solana/blob/master/docs/src/developing/on-chain-programs/developing-rust.md#depending-on-rand
# https://github.com/solana-foundation/developer-content/blob/main/docs/programs/lang-rust.md#depending-on-rand
getrandom = { workspace = true, features = ["custom"] }
[lib]

@ -21,7 +21,7 @@ num-derive.workspace = true
num-traits.workspace = true
thiserror.workspace = true
# Required to allow dependencies `getrandom` but to preserve determinism required by programs, see
# https://github.com/solana-labs/solana/blob/master/docs/src/developing/on-chain-programs/developing-rust.md#depending-on-rand
# https://github.com/solana-foundation/developer-content/blob/main/docs/programs/lang-rust.md#depending-on-rand
getrandom = { workspace = true, features = ["custom"] }
serde = { workspace = true, optional = true }

@ -212,7 +212,7 @@ async fn test_dispatch_from_eoa() {
local_domain: LOCAL_DOMAIN,
outbox_bump_seed: mailbox_accounts.outbox_bump_seed,
owner: Some(payer.pubkey()),
tree: expected_tree,
tree: expected_tree.clone(),
},
)
.await;

@ -17,7 +17,7 @@ solana-program.workspace = true
thiserror.workspace = true
spl-noop.workspace = true
# Required to allow dependencies `getrandom` but to preserve determinism required by programs, see
# https://github.com/solana-labs/solana/blob/master/docs/src/developing/on-chain-programs/developing-rust.md#depending-on-rand
# https://github.com/solana-foundation/developer-content/blob/main/docs/programs/lang-rust.md#depending-on-rand
getrandom = { workspace = true, features = ["custom"] }
proc-macro-crate = "~1.2.1" # TODO: remove this dependency once solana supports rust >=1.64

@ -177,10 +177,7 @@ impl AgentConfig {
amount: "0.05".to_string(),
},
contract_address_bytes: 32,
index: AgentConfigIndex {
from: 1,
chunk: 100,
},
index: AgentConfigIndex { from: 1, chunk: 5 },
}
}
}

@ -69,8 +69,14 @@ pub fn termination_invariants_met(
.len();
// Zero insertion messages don't reach `submit` stage where gas is spent, so we only expect these logs for the other messages.
assert_eq!(
gas_expenditure_log_count as u32, total_messages_expected,
// TODO: Sometimes we find more logs than expected. This may either mean that gas is deducted twice for the same message due to a bug,
// or that submitting the message transaction fails for some messages. Figure out which is the case and convert this check to
// strict equality.
// EDIT: Having had a quick look, it seems like there are some legitimate reverts happening in the confirm step
// (`Transaction attempting to process message either reverted or was reorged`)
// in which case more gas expenditure logs than messages are expected.
assert!(
gas_expenditure_log_count as u32 >= total_messages_expected,
"Didn't record gas payment for all delivered messages"
);

@ -1,5 +1,36 @@
# @hyperlane-xyz/core
## 3.16.0
### Patch Changes
- @hyperlane-xyz/utils@3.16.0
## 3.15.1
### Patch Changes
- 6620fe636: fix: `TokenRouter.transferRemote` with hook overrides
- @hyperlane-xyz/utils@3.15.1
## 3.15.0
### Minor Changes
- 51bfff683: Mint/burn limit checking for xERC20 bridging
Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments
### Patch Changes
- @hyperlane-xyz/utils@3.15.0
## 3.14.0
### Patch Changes
- a8a68f6f6: fix: make XERC20 and XERC20 Lockbox proxy-able
- @hyperlane-xyz/utils@3.14.0
## 3.13.0
### Minor Changes

@ -43,13 +43,13 @@ abstract contract GasRouter is Router {
*/
function quoteGasPayment(
uint32 _destinationDomain
) external view returns (uint256 _gasPayment) {
return _quoteDispatch(_destinationDomain, "");
) external view returns (uint256) {
return _GasRouter_quoteDispatch(_destinationDomain, "", address(hook));
}
function _metadata(
function _GasRouter_hookMetadata(
uint32 _destination
) internal view virtual override returns (bytes memory) {
) internal view returns (bytes memory) {
return
StandardHookMetadata.overrideGasLimit(destinationGas[_destination]);
}
@ -57,4 +57,34 @@ abstract contract GasRouter is Router {
function _setDestinationGas(uint32 domain, uint256 gas) internal {
destinationGas[domain] = gas;
}
function _GasRouter_dispatch(
uint32 _destination,
uint256 _value,
bytes memory _messageBody,
address _hook
) internal returns (bytes32) {
return
_Router_dispatch(
_destination,
_value,
_messageBody,
_GasRouter_hookMetadata(_destination),
_hook
);
}
function _GasRouter_quoteDispatch(
uint32 _destination,
bytes memory _messageBody,
address _hook
) internal view returns (uint256) {
return
_Router_quoteDispatch(
_destination,
_messageBody,
_GasRouter_hookMetadata(_destination),
_hook
);
}
}

@ -95,85 +95,4 @@ abstract contract MailboxClient is OwnableUpgradeable {
function _isDelivered(bytes32 id) internal view returns (bool) {
return mailbox.delivered(id);
}
function _metadata(
uint32 /*_destinationDomain*/
) internal view virtual returns (bytes memory) {
return "";
}
function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return
_dispatch(_destinationDomain, _recipient, msg.value, _messageBody);
}
function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
uint256 _value,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return
mailbox.dispatch{value: _value}(
_destinationDomain,
_recipient,
_messageBody,
_metadata(_destinationDomain),
hook
);
}
function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
uint256 _value,
bytes memory _messageBody,
bytes memory _hookMetadata,
IPostDispatchHook _hook
) internal virtual returns (bytes32) {
return
mailbox.dispatch{value: _value}(
_destinationDomain,
_recipient,
_messageBody,
_hookMetadata,
_hook
);
}
function _quoteDispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody
) internal view virtual returns (uint256) {
return
mailbox.quoteDispatch(
_destinationDomain,
_recipient,
_messageBody,
_metadata(_destinationDomain),
hook
);
}
function _quoteDispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody,
bytes calldata _hookMetadata,
IPostDispatchHook _hook
) internal view virtual returns (uint256) {
return
mailbox.quoteDispatch(
_destinationDomain,
_recipient,
_messageBody,
_hookMetadata,
_hook
);
}
}

@ -167,28 +167,73 @@ abstract contract Router is MailboxClient, IMessageRecipient {
);
}
function _dispatch(
function _Router_dispatch(
uint32 _destinationDomain,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return _dispatch(_destinationDomain, msg.value, _messageBody);
uint256 _value,
bytes memory _messageBody,
bytes memory _hookMetadata,
address _hook
) internal returns (bytes32) {
bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
return
mailbox.dispatch{value: _value}(
_destinationDomain,
_router,
_messageBody,
_hookMetadata,
IPostDispatchHook(_hook)
);
}
/**
* DEPRECATED: Use `_Router_dispatch` instead
* @dev For backward compatibility with v2 client contracts
*/
function _dispatch(
uint32 _destinationDomain,
uint256 _value,
bytes memory _messageBody
) internal virtual returns (bytes32) {
) internal returns (bytes32) {
return
_Router_dispatch(
_destinationDomain,
msg.value,
_messageBody,
"",
address(hook)
);
}
function _Router_quoteDispatch(
uint32 _destinationDomain,
bytes memory _messageBody,
bytes memory _hookMetadata,
address _hook
) internal view returns (uint256) {
bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
return
super._dispatch(_destinationDomain, _router, _value, _messageBody);
mailbox.quoteDispatch(
_destinationDomain,
_router,
_messageBody,
_hookMetadata,
IPostDispatchHook(_hook)
);
}
/**
* DEPRECATED: Use `_Router_quoteDispatch` instead
* @dev For backward compatibility with v2 client contracts
*/
function _quoteDispatch(
uint32 _destinationDomain,
bytes memory _messageBody
) internal view virtual returns (uint256) {
bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
return super._quoteDispatch(_destinationDomain, _router, _messageBody);
) internal view returns (uint256) {
return
_Router_quoteDispatch(
_destinationDomain,
_messageBody,
"",
address(hook)
);
}
}

@ -8,6 +8,7 @@ interface IMultisigIsm is IInterchainSecurityModule {
* @notice Returns the set of validators responsible for verifying _message
* and the number of signatures required
* @dev Can change based on the content of _message
* @dev Signatures provided to `verify` must be consistent with validator ordering
* @param _message Hyperlane formatted interchain message
* @return validators The array of validator addresses
* @return threshold The number of validator signatures needed

@ -26,6 +26,7 @@ abstract contract AbstractMultisigIsm is IMultisigIsm {
* @notice Returns the set of validators responsible for verifying _message
* and the number of signatures required
* @dev Can change based on the content of _message
* @dev Signatures provided to `verify` must be consistent with validator ordering
* @param _message Hyperlane formatted interchain message
* @return validators The array of validator addresses
* @return threshold The number of validator signatures needed
@ -60,7 +61,9 @@ abstract contract AbstractMultisigIsm is IMultisigIsm {
/**
* @notice Requires that m-of-n validators verify a merkle root,
* and verifies a merkle proof of `_message` against that root.
* and verifies a merkle proof of `_message` against that root.
* @dev Optimization relies on the caller sorting signatures in the same order as validators.
* @dev Employs https://www.geeksforgeeks.org/two-pointers-technique/ to minimize gas usage.
* @param _metadata ABI encoded module metadata
* @param _message Formatted Hyperlane message (see Message.sol).
*/

@ -74,6 +74,18 @@ contract XERC20Test is ERC20Test, IXERC20 {
function owner() external pure returns (address) {
return address(0x0);
}
function burningCurrentLimitOf(
address _bridge
) external view returns (uint256) {
return type(uint256).max;
}
function mintingCurrentLimitOf(
address _bridge
) external view returns (uint256) {
return type(uint256).max;
}
}
contract XERC20LockboxTest is IXERC20Lockbox {

@ -7,7 +7,7 @@ contract TestGasRouter is GasRouter {
constructor(address _mailbox) GasRouter(_mailbox) {}
function dispatch(uint32 _destination, bytes memory _msg) external payable {
_dispatch(_destination, _msg);
_GasRouter_dispatch(_destination, msg.value, _msg, address(hook));
}
function _handle(uint32, bytes32, bytes calldata) internal pure override {}

@ -36,24 +36,16 @@ contract HypNative is TokenRouter {
/**
* @inheritdoc TokenRouter
* @dev uses (`msg.value` - `_amount`) as interchain gas payment and `msg.sender` as refund address.
* @dev uses (`msg.value` - `_amount`) as hook payment and `msg.sender` as refund address.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) public payable virtual override returns (bytes32 messageId) {
) external payable virtual override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
uint256 gasPayment = msg.value - _amount;
return
_transferRemote(
_destination,
_recipient,
_amount,
gasPayment,
bytes(""),
address(0)
);
uint256 _hookPayment = msg.value - _amount;
return _transferRemote(_destination, _recipient, _amount, _hookPayment);
}
function balanceOf(

@ -25,18 +25,16 @@ contract HypNativeScaled is HypNative {
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) public payable override returns (bytes32 messageId) {
) external payable override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
uint256 gasPayment = msg.value - _amount;
uint256 scaledAmount = _amount / scale;
uint256 _hookPayment = msg.value - _amount;
uint256 _scaledAmount = _amount / scale;
return
_transferRemote(
_destination,
_recipient,
scaledAmount,
gasPayment,
bytes(""),
address(0)
_scaledAmount,
_hookPayment
);
}

@ -8,7 +8,9 @@ contract HypXERC20 is HypERC20Collateral {
constructor(
address _xerc20,
address _mailbox
) HypERC20Collateral(_xerc20, _mailbox) {}
) HypERC20Collateral(_xerc20, _mailbox) {
_disableInitializers();
}
function _transferFromSender(
uint256 _amountOrId

@ -18,6 +18,7 @@ contract HypXERC20Lockbox is HypERC20Collateral {
lockbox = IXERC20Lockbox(_lockbox);
xERC20 = lockbox.XERC20();
approveLockbox();
_disableInitializers();
}
/**

@ -36,4 +36,22 @@ interface IXERC20 is IERC20 {
) external;
function owner() external returns (address);
/**
* @notice Returns the current limit of a bridge
* @param _bridge the bridge we are viewing the limits of
* @return _limit The limit the bridge has
*/
function burningCurrentLimitOf(
address _bridge
) external view returns (uint256 _limit);
/**
* @notice Returns the current limit of a bridge
* @param _bridge the bridge we are viewing the limits of
* @return _limit The limit the bridge has
*/
function mintingCurrentLimitOf(
address _bridge
) external view returns (uint256 _limit);
}

@ -109,9 +109,11 @@ abstract contract FastTokenRouter is TokenRouter {
_fastTransferId
);
messageId = _dispatch(
messageId = _GasRouter_dispatch(
_destination,
TokenMessage.format(_recipient, _amountOrId, metadata)
msg.value,
TokenMessage.format(_recipient, _amountOrId, metadata),
address(hook)
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}

@ -57,14 +57,7 @@ abstract contract TokenRouter is GasRouter {
uint256 _amountOrId
) external payable virtual returns (bytes32 messageId) {
return
_transferRemote(
_destination,
_recipient,
_amountOrId,
msg.value,
bytes(""),
address(0)
);
_transferRemote(_destination, _recipient, _amountOrId, msg.value);
}
/**
@ -97,45 +90,45 @@ abstract contract TokenRouter is GasRouter {
);
}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev The metadata is the token metadata, and is DIFFERENT than the hook metadata.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @param _gasPayment The amount of native token to pay for interchain gas.
* @param _hookMetadata The metadata passed into the hook
* @param _hook The post dispatch hook to be called by the Mailbox
* @return messageId The identifier of the dispatched message.
*/
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _gasPayment,
bytes memory _hookMetadata,
address _hook
uint256 _value
) internal returns (bytes32 messageId) {
bytes memory metadata = _transferFromSender(_amountOrId);
if (address(_hook) == address(0)) {
messageId = _dispatch(
_destination,
_gasPayment,
TokenMessage.format(_recipient, _amountOrId, metadata)
);
} else {
messageId = _dispatch(
return
_transferRemote(
_destination,
_recipient,
_gasPayment,
TokenMessage.format(_recipient, _amountOrId, metadata),
_hookMetadata,
IPostDispatchHook(_hook)
_amountOrId,
_value,
_GasRouter_hookMetadata(_destination),
address(hook)
);
}
}
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _value,
bytes memory _hookMetadata,
address _hook
) internal virtual returns (bytes32 messageId) {
bytes memory _tokenMetadata = _transferFromSender(_amountOrId);
bytes memory _tokenMessage = TokenMessage.format(
_recipient,
_amountOrId,
_tokenMetadata
);
messageId = _Router_dispatch(
_destination,
_value,
_tokenMessage,
_hookMetadata,
_hook
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}

@ -1,10 +1,10 @@
{
"name": "@hyperlane-xyz/core",
"description": "Core solidity contracts for Hyperlane",
"version": "3.13.0",
"version": "3.16.0",
"dependencies": {
"@eth-optimism/contracts": "^0.6.0",
"@hyperlane-xyz/utils": "3.13.0",
"@hyperlane-xyz/utils": "3.16.0",
"@layerzerolabs/lz-evm-oapp-v2": "2.0.2",
"@openzeppelin/contracts": "^4.9.3",
"@openzeppelin/contracts-upgradeable": "^v4.9.3",
@ -15,7 +15,9 @@
"@nomiclabs/hardhat-ethers": "^2.2.3",
"@nomiclabs/hardhat-waffle": "^2.0.6",
"@typechain/ethers-v5": "^11.1.2",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"@types/node": "^18.14.5",
"chai": "^4.3.6",
"ethereum-waffle": "^4.0.10",
"ethers": "^5.7.2",
@ -26,6 +28,7 @@
"prettier-plugin-solidity": "^1.1.3",
"solhint": "^4.5.4",
"solhint-plugin-prettier": "^0.0.5",
"solidity-bytes-utils": "^0.8.0",
"solidity-coverage": "^0.8.3",
"ts-generator": "^0.1.1",
"ts-node": "^10.8.0",

@ -5,14 +5,54 @@
"avsDirectory": "0x135DDa560e946695d6f155dACaFC6f1F25C1F5AF",
"paymentCoordinator": "",
"strategies": [
{
"name": "swETH",
"strategy": "0x0Fe4F44beE93503346A3Ac9EE5A26b130a5796d6"
},
{
"name": "oETH",
"strategy": "0x13760F50a9d7377e4F20CB8CF9e4c26586c658ff"
},
{
"name": "rETH",
"strategy": "0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2"
},
{
"name": "mETH",
"strategy": "0x298aFB19A105D59E74658C4C334Ff360BadE6dd2"
},
{
"name": "cbETH",
"strategy": "0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc"
},
{
"name": "osETH",
"strategy": "0x57ba429517c3473B6d34CA9aCd56c0e735b94c02"
},
{
"name": "wBETH",
"strategy": "0x7CA911E83dabf90C90dD3De5411a10F1A6112184"
},
{
"name": "sfrxETH",
"strategy": "0x8CA7A5d6f3acd3A7A8bC468a8CD0FB14B6BD28b6"
},
{
"name": "stETH",
"strategy": "0x93c4b944D05dfe6df7645A86cd2206016c51564D"
},
{
"name": "ETHx",
"strategy": "0x9d7eD45EE2E8FC5482fa2428f15C971e6369011d"
},
{
"name": "ankrETH",
"strategy": "0xa4C637e0F704745D182e4D38cAb7E7485321d059"
},
{
"name": "lsETH",
"strategy": "0xAe60d8180437b5C34bB956822ac2710972584473"
},
{
"name": "Beacon Chain ETH",
"strategy": "0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0"

@ -479,6 +479,7 @@ contract InterchainAccountRouterTest is Test {
uint64 payment,
bytes32 data
) public {
CallLib.Call[] memory calls = getCalls(data);
vm.assume(payment < gasLimit * igp.gasPrice());
// arrange
bytes memory metadata = StandardHookMetadata.formatMetadata(
@ -495,11 +496,7 @@ contract InterchainAccountRouterTest is Test {
// act
vm.expectRevert("IGP: insufficient interchain gas payment");
originRouter.callRemote{value: payment}(
destination,
getCalls(data),
metadata
);
originRouter.callRemote{value: payment}(destination, calls, metadata);
}
function testFuzz_callRemoteWithOverrides_default(bytes32 data) public {

@ -23,6 +23,7 @@ import {XERC20LockboxTest, XERC20Test, FiatTokenTest, ERC20Test} from "../../con
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol";
import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol";
import {GasRouter} from "../../contracts/client/GasRouter.sol";
import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol";
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {HypERC20Collateral} from "../../contracts/token/HypERC20Collateral.sol";
@ -198,38 +199,38 @@ abstract contract HypTokenTest is Test {
function _performRemoteTransferWithHook(
uint256 _msgValue,
uint256 _amount
uint256 _amount,
address _hook,
bytes memory _hookMetadata
) internal returns (bytes32 messageId) {
vm.prank(ALICE);
messageId = localToken.transferRemote{value: _msgValue}(
DESTINATION,
BOB.addressToBytes32(),
_amount,
bytes(""),
address(noopHook)
_hookMetadata,
address(_hook)
);
_processTransfers(BOB, _amount);
assertEq(remoteToken.balanceOf(BOB), _amount);
}
function testTransfer_withHookSpecified() public {
function testTransfer_withHookSpecified(
uint256 fee,
bytes calldata metadata
) public {
TestPostDispatchHook hook = new TestPostDispatchHook();
hook.setFee(fee);
vm.prank(ALICE);
primaryToken.approve(address(localToken), TRANSFER_AMT);
bytes32 messageId = _performRemoteTransferWithHook(
REQUIRED_VALUE,
TRANSFER_AMT
TRANSFER_AMT,
address(hook),
metadata
);
assertTrue(noopHook.messageDispatched(messageId));
/// @dev Using this test would be ideal, but vm.expectCall with nested functions more than 1 level deep is broken
/// In other words, the call graph of Route.transferRemote() -> Mailbox.dispatch() -> Hook.postDispatch() does not work with expectCall
// vm.expectCall(
// address(noopHook),
// abi.encodeCall(
// IPostDispatchHook.postDispatch,
// (bytes(""), outboundMessage)
// )
// );
/// @dev Also, using expectedCall with Mailbox.dispatch() won't work either because overloaded function selection is broken, see https://github.com/ethereum/solidity/issues/13815
assertTrue(hook.messageDispatched(messageId));
}
function testBenchmark_overheadGasUsage() public virtual {

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': major
---
Refactor testIgpConfig function for clarity and maintainability

@ -0,0 +1,5 @@
---
'@hyperlane-xyz/sdk': major
---
fix(sdk): refactor `addVerificationArtifacts` for enhanced Artifact Deduplication in HyperlaneDeployer

@ -1,5 +1,13 @@
# @hyperlane-xyz/ccip-server
## 3.16.0
## 3.15.1
## 3.15.0
## 3.14.0
## 3.13.0
## 3.12.0

@ -1,6 +1,6 @@
{
"name": "@hyperlane-xyz/ccip-server",
"version": "3.13.0",
"version": "3.16.0",
"description": "CCIP server",
"typings": "dist/index.d.ts",
"typedocMain": "src/index.ts",

@ -1,5 +1,48 @@
# @hyperlane-xyz/cli
## 3.16.0
### Patch Changes
- Updated dependencies [f9bbdde76]
- Updated dependencies [5cc64eb09]
- @hyperlane-xyz/sdk@3.16.0
- @hyperlane-xyz/utils@3.16.0
## 3.15.1
### Patch Changes
- 921e449b4: Support priorityFee fetching from RPC and some better logging
- Updated dependencies [acaa22cd9]
- Updated dependencies [921e449b4]
- @hyperlane-xyz/sdk@3.15.1
- @hyperlane-xyz/utils@3.15.1
## 3.15.0
### Minor Changes
- 51bfff683: Mint/burn limit checking for xERC20 bridging
Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments
### Patch Changes
- Updated dependencies [51bfff683]
- @hyperlane-xyz/sdk@3.15.0
- @hyperlane-xyz/utils@3.15.0
## 3.14.0
### Minor Changes
- f4bbfcf08: AVS deployment on mainnet
### Patch Changes
- @hyperlane-xyz/sdk@3.14.0
- @hyperlane-xyz/utils@3.14.0
## 3.13.0
### Minor Changes

@ -254,7 +254,7 @@ run_hyperlane_send_message() {
run_validator() {
echo -e "\nPre-building validator with cargo"
cargo build --bin validator
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
@ -290,7 +290,7 @@ run_validator() {
echo "Validator running, sleeping to let it sync"
# This needs to be long to allow time for the cargo build to finish
sleep 15
sleep 20
echo "Done sleeping"
for CHAIN in ${CHAIN1} ${CHAIN2}
@ -308,7 +308,7 @@ run_validator() {
run_relayer() {
echo -e "\nPre-building relayer with cargo"
cargo build --bin relayer
cargo build --bin relayer --features test-utils
echo "Running relayer"
export CONFIG_FILES=/tmp/agent-config.json

@ -1,13 +1,13 @@
{
"name": "@hyperlane-xyz/cli",
"version": "3.13.0",
"version": "3.16.0",
"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": "1.3.0",
"@hyperlane-xyz/sdk": "3.13.0",
"@hyperlane-xyz/utils": "3.13.0",
"@hyperlane-xyz/registry": "2.1.1",
"@hyperlane-xyz/sdk": "3.16.0",
"@hyperlane-xyz/utils": "3.16.0",
"@inquirer/prompts": "^3.0.0",
"asn1.js": "^5.4.1",
"bignumber.js": "^9.1.1",
@ -21,6 +21,8 @@
"zod": "^3.21.2"
},
"devDependencies": {
"@ethersproject/abi": "*",
"@ethersproject/providers": "*",
"@types/mocha": "^10.0.1",
"@types/node": "^18.14.5",
"@types/yargs": "^17.0.24",

@ -16,4 +16,10 @@ export const avsAddresses: ChainMap<AVSContracts> = {
ecdsaStakeRegistry: '0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72',
hyperlaneServiceManager: '0xc76E477437065093D353b7d56c81ff54D167B0Ab',
},
ethereum: {
avsDirectory: '0x135dda560e946695d6f155dacafc6f1f25c1f5af',
proxyAdmin: '0x75EE15Ee1B4A75Fa3e2fDF5DF3253c25599cc659',
ecdsaStakeRegistry: '0x272CF0BB70D3B4f79414E0823B426d2EaFd48910',
hyperlaneServiceManager: '0xe8E59c6C8B56F2c178f63BCFC4ce5e5e2359c8fc',
},
};

@ -24,12 +24,12 @@ export async function registerOperatorWithSignature({
context,
chain,
operatorKeyPath,
avsSigningKey,
avsSigningKeyAddress,
}: {
context: WriteCommandContext;
chain: ChainName;
operatorKeyPath: string;
avsSigningKey: Address;
avsSigningKeyAddress: Address;
}) {
const { multiProvider } = context;
@ -67,13 +67,13 @@ export async function registerOperatorWithSignature({
}
log(
`Registering operator ${operatorAsSigner.address} attesting ${avsSigningKey} with signature on ${chain}...`,
`Registering operator ${operatorAsSigner.address} attesting ${avsSigningKeyAddress} with signature on ${chain}...`,
);
await multiProvider.handleTx(
chain,
ecdsaStakeRegistry.registerOperatorWithSignature(
operatorSignature,
avsSigningKey,
avsSigningKeyAddress,
),
);
logBlue(`Operator ${operatorAsSigner.address} registered to Hyperlane AVS`);

@ -40,7 +40,7 @@ export const registrationOptions: { [k: string]: Options } = {
description: 'Path to the operator key file',
demandOption: true,
},
avsSigningKey: {
avsSigningKeyAddress: {
type: 'string',
description: 'Address of the AVS signing key',
demandOption: true,
@ -50,17 +50,22 @@ export const registrationOptions: { [k: string]: Options } = {
const registerCommand: CommandModuleWithWriteContext<{
chain: ChainName;
operatorKeyPath: string;
avsSigningKey: Address;
avsSigningKeyAddress: Address;
}> = {
command: 'register',
describe: 'Register operator with the AVS',
builder: registrationOptions,
handler: async ({ context, chain, operatorKeyPath, avsSigningKey }) => {
handler: async ({
context,
chain,
operatorKeyPath,
avsSigningKeyAddress,
}) => {
await registerOperatorWithSignature({
context,
chain,
operatorKeyPath,
avsSigningKey,
avsSigningKeyAddress,
});
process.exit(0);
},

@ -1,5 +1,9 @@
import { confirm } from '@inquirer/prompts';
import {
HypXERC20Lockbox__factory,
HypXERC20__factory,
} from '@hyperlane-xyz/core';
import {
HypERC20Deployer,
HypERC721Deployer,
@ -121,7 +125,7 @@ async function executeDeploy(params: DeployParams) {
const deployedContracts = await deployer.deploy(config);
logGreen('✅ Hyp token deployments complete');
logGreen('✅ Warp contract deployments complete');
const warpCoreConfig = await getWarpCoreConfig(params, deployedContracts);
if (!isDryRun) {
@ -161,8 +165,30 @@ async function getWarpCoreConfig(
throw new Error('Missing decimals on token metadata');
}
const collateralAddressOrDenom =
config.type === TokenType.collateral ? config.token : undefined;
const collateralAddressOrDenom = await (async () => {
if (config.type === TokenType.XERC20Lockbox) {
const provider = context.multiProvider.tryGetProvider(chainName);
if (!provider) {
throw new Error(`Unable to pull provider for ${chainName}`);
}
const xERC20 = await HypXERC20Lockbox__factory.connect(
config.token,
provider,
).xERC20();
const wrappedToken = await HypXERC20__factory.connect(
xERC20,
provider,
).wrappedToken();
return wrappedToken;
}
if (config.type === TokenType.collateral) {
return config.token;
}
return undefined;
})();
warpCoreConfig.tokens.push({
chainName,
standard: TOKEN_TYPE_TO_STANDARD[config.type],

@ -1 +1 @@
export const VERSION = '3.13.0';
export const VERSION = '3.16.0';

@ -1,5 +1,41 @@
# @hyperlane-xyz/helloworld
## 3.16.0
### Patch Changes
- Updated dependencies [f9bbdde76]
- Updated dependencies [5cc64eb09]
- @hyperlane-xyz/sdk@3.16.0
- @hyperlane-xyz/core@3.16.0
## 3.15.1
### Patch Changes
- 6620fe636: fix: `TokenRouter.transferRemote` with hook overrides
- Updated dependencies [6620fe636]
- Updated dependencies [acaa22cd9]
- Updated dependencies [921e449b4]
- @hyperlane-xyz/core@3.15.1
- @hyperlane-xyz/sdk@3.15.1
## 3.15.0
### Patch Changes
- Updated dependencies [51bfff683]
- @hyperlane-xyz/sdk@3.15.0
- @hyperlane-xyz/core@3.15.0
## 3.14.0
### Patch Changes
- Updated dependencies [a8a68f6f6]
- @hyperlane-xyz/core@3.14.0
- @hyperlane-xyz/sdk@3.14.0
## 3.13.0
### Patch Changes

@ -83,12 +83,6 @@ contract HelloWorld is Router {
}
// ============ Internal functions ============
function _metadata(
uint32 /*_destinationDomain*/
) internal view override returns (bytes memory) {
return StandardHookMetadata.overrideGasLimit(HANDLE_GAS_AMOUNT);
}
/**
* @notice Handles a message from a remote router.
* @dev Only called for messages sent from a remote router, as enforced by Router.sol.

@ -1,11 +1,11 @@
{
"name": "@hyperlane-xyz/helloworld",
"description": "A basic skeleton of an Hyperlane app",
"version": "3.13.0",
"version": "3.16.0",
"dependencies": {
"@hyperlane-xyz/core": "3.13.0",
"@hyperlane-xyz/registry": "1.3.0",
"@hyperlane-xyz/sdk": "3.13.0",
"@hyperlane-xyz/core": "3.16.0",
"@hyperlane-xyz/registry": "2.1.1",
"@hyperlane-xyz/sdk": "3.16.0",
"@openzeppelin/contracts-upgradeable": "^4.9.3",
"ethers": "^5.7.2"
},
@ -14,6 +14,7 @@
"@nomiclabs/hardhat-waffle": "^2.0.6",
"@trivago/prettier-plugin-sort-imports": "^4.2.1",
"@typechain/ethers-v5": "^11.1.2",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",

@ -1,5 +1,50 @@
# @hyperlane-xyz/infra
## 3.16.0
### Minor Changes
- 5cc64eb09: Add support for new chains: linea, fraxtal, sei.
Support osmosis remote.
Drive-by fix to always fetch explorer API keys when running deploy script.
### Patch Changes
- 5cc64eb09: Allow selecting a specific chain to govern in check-deploy script
- Updated dependencies [f9bbdde76]
- Updated dependencies [5cc64eb09]
- @hyperlane-xyz/sdk@3.16.0
- @hyperlane-xyz/helloworld@3.16.0
- @hyperlane-xyz/utils@3.16.0
## 3.15.1
### Patch Changes
- Updated dependencies [6620fe636]
- Updated dependencies [acaa22cd9]
- Updated dependencies [921e449b4]
- @hyperlane-xyz/helloworld@3.15.1
- @hyperlane-xyz/sdk@3.15.1
- @hyperlane-xyz/utils@3.15.1
## 3.15.0
### Patch Changes
- Updated dependencies [51bfff683]
- @hyperlane-xyz/sdk@3.15.0
- @hyperlane-xyz/helloworld@3.15.0
- @hyperlane-xyz/utils@3.15.0
## 3.14.0
### Patch Changes
- @hyperlane-xyz/helloworld@3.14.0
- @hyperlane-xyz/sdk@3.14.0
- @hyperlane-xyz/utils@3.14.0
## 3.13.0
### Minor Changes

@ -27,6 +27,7 @@ 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 renzoEzEthAddresses from './warp/renzo-ezETH-addresses.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';
@ -53,18 +54,22 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = {
bsc: true,
celo: true,
ethereum: true,
fraxtal: true,
gnosis: true,
injective: true,
inevm: true,
linea: true,
mantapacific: true,
mode: true,
moonbeam: true,
neutron: true,
optimism: true,
osmosis: true,
polygon: true,
polygonzkevm: true,
redstone: true,
scroll: true,
sei: true,
viction: true,
zetachain: true,
},
@ -77,19 +82,23 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = {
bsc: true,
celo: true,
ethereum: true,
fraxtal: true,
gnosis: true,
injective: true,
inevm: true,
linea: true,
mantapacific: true,
mode: true,
moonbeam: true,
// At the moment, we only relay between Neutron and Manta Pacific on the neutron context.
neutron: false,
optimism: true,
osmosis: true,
polygon: true,
polygonzkevm: true,
redstone: true,
scroll: true,
sei: true,
viction: true,
zetachain: true,
},
@ -102,19 +111,26 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = {
bsc: true,
celo: true,
ethereum: true,
fraxtal: true,
gnosis: true,
// Cannot scrape non-EVM chains
injective: false,
inevm: true,
linea: true,
mantapacific: true,
mode: true,
moonbeam: true,
// Cannot scrape non-EVM chains
neutron: false,
optimism: true,
// Cannot scrape non-EVM chains
osmosis: false,
polygon: true,
polygonzkevm: true,
redstone: 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,
scroll: true,
// Has RPC non-compliance that breaks scraping.
viction: false,
@ -198,8 +214,34 @@ const metricAppContexts = [
name: 'ancient8_ethereum_usdc',
matchingList: routerMatchingList(ancient8EthereumUsdcAddresses),
},
{
name: 'renzo_ezeth',
matchingList: routerMatchingList(renzoEzEthAddresses),
},
];
// Resource requests are based on observed usage found in https://abacusworks.grafana.net/d/FSR9YWr7k
const relayerResources = {
requests: {
cpu: '14000m',
memory: '12Gi',
},
};
const validatorResources = {
requests: {
cpu: '250m',
memory: '256Mi',
},
};
const scraperResources = {
requests: {
cpu: '100m',
memory: '4Gi',
},
};
const hyperlane: RootAgentConfig = {
...contextBase,
context: Contexts.Hyperlane,
@ -209,25 +251,28 @@ const hyperlane: RootAgentConfig = {
rpcConsensusType: RpcConsensusType.Fallback,
docker: {
repo,
tag: 'd6bb976-20240520-164138',
tag: '9535087-20240623-174819',
},
gasPaymentEnforcement: gasPaymentEnforcement,
metricAppContexts,
resources: relayerResources,
},
validators: {
docker: {
repo,
tag: 'de8c2a7-20240515-135254',
tag: '0d12ff3-20240620-173353',
},
rpcConsensusType: RpcConsensusType.Quorum,
chains: validatorChainConfig(Contexts.Hyperlane),
resources: validatorResources,
},
scraper: {
rpcConsensusType: RpcConsensusType.Fallback,
docker: {
repo,
tag: 'd6bb976-20240520-164138',
tag: '0d12ff3-20240620-173353',
},
resources: scraperResources,
},
};
@ -240,21 +285,23 @@ const releaseCandidate: RootAgentConfig = {
rpcConsensusType: RpcConsensusType.Fallback,
docker: {
repo,
tag: 'c9c5d37-20240510-014327',
tag: '9535087-20240623-174819',
},
// We're temporarily (ab)using the RC relayer as a way to increase
// message throughput.
// whitelist: releaseCandidateHelloworldMatchingList,
gasPaymentEnforcement,
metricAppContexts,
resources: relayerResources,
},
validators: {
docker: {
repo,
tag: 'c9c5d37-20240510-014327',
tag: '0d12ff3-20240620-173353',
},
rpcConsensusType: RpcConsensusType.Quorum,
chains: validatorChainConfig(Contexts.ReleaseCandidate),
resources: validatorResources,
},
};
@ -271,7 +318,7 @@ const neutron: RootAgentConfig = {
rpcConsensusType: RpcConsensusType.Fallback,
docker: {
repo,
tag: 'c9c5d37-20240510-014327',
tag: '0d12ff3-20240620-173353',
},
gasPaymentEnforcement: [
{
@ -298,6 +345,7 @@ const neutron: RootAgentConfig = {
matchingList: routerMatchingList(arbitrumNeutronEclipAddresses),
},
],
resources: relayerResources,
},
};

@ -47,6 +47,9 @@
"0x749d6e7ad949e522c92181dc77f7bbc1c5d71506"
]
},
"fraxtal": {
"validators": ["0x4bce180dac6da60d0f3a2bdf036ffe9004f944c1"]
},
"gnosis": {
"validators": [
"0xd4df66a859585678f2ea8357161d896be19cc1ca",
@ -64,6 +67,9 @@
"injective": {
"validators": ["0xbfb8911b72cfb138c7ce517c57d9c691535dc517"]
},
"linea": {
"validators": ["0xf2d5409a59e0f5ae7635aff73685624904a77d94"]
},
"mantapacific": {
"validators": [
"0x8e668c97ad76d0e28375275c41ece4972ab8a5bc",
@ -95,6 +101,9 @@
"0x779a17e035018396724a6dec8a59bda1b5adf738"
]
},
"osmosis": {
"validators": ["0xea483af11c19fa41b16c31d1534c2a486a92bcac"]
},
"polygon": {
"validators": [
"0x12ecb319c7f4e8ac5eb5226662aeb8528c5cefac",
@ -119,6 +128,9 @@
"0x7210fa0a6be39a75cb14d682ebfb37e2b53ecbe5"
]
},
"sei": {
"validators": ["0x9920d2dbf6c85ffc228fdc2e810bf895732c6aa5"]
},
"viction": {
"validators": ["0x1f87c368f8e05a85ef9126d984a980a20930cb9c"]
},

@ -47,6 +47,9 @@
"0x87cf8a85465118aff9ec728ca157798201b1e368"
]
},
"fraxtal": {
"validators": ["0x8c772b730c8deb333dded14cb462e577a06283da"]
},
"gnosis": {
"validators": [
"0xd5122daa0c3dfc94a825ae928f3ea138cdb6a2e1",
@ -61,6 +64,9 @@
"0xe83d36fd00d9ef86243d9f7147b29e98d11df0ee"
]
},
"linea": {
"validators": ["0xad4886b6f5f5088c7ae53b69d1ff5cfc2a17bec4"]
},
"mantapacific": {
"validators": [
"0x84fcb05e6e5961df2dfd9f36e8f2b3e87ede7d76",
@ -116,6 +122,9 @@
"0x07c2f32a402543badc3141f6b98969d75ef2ac28"
]
},
"sei": {
"validators": ["0x846e48a7e85e5403cc690a347e1ad3c3dca11b6e"]
},
"viction": {
"validators": [
"0xe858971cd865b11d3e8fb6b6af72db0d85881baf",

@ -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';
@ -44,6 +46,13 @@ export const chainMetadataOverrides: ChainMap<Partial<ChainMetadata>> = {
gasPrice: 2 * 10 ** 9, // 2 gwei
},
},
sei: {
// Sei's `eth_feeHistory` is not to spec and incompatible with ethers-rs,
// so we force legacy transactions by setting a gas price.
transactionOverrides: {
gasPrice: 2 * 10 ** 9, // 2 gwei
},
},
moonbeam: {
transactionOverrides: {
maxFeePerGas: 350 * 10 ** 9, // 350 gwei
@ -51,3 +60,11 @@ export const chainMetadataOverrides: ChainMap<Partial<ChainMetadata>> = {
},
},
};
export const getRegistry = async (useSecrets = true): Promise<IRegistry> =>
getRegistryForEnvironment(
environment,
supportedChainNames,
chainMetadataOverrides,
useSecrets,
);

@ -7,7 +7,7 @@ import { environment } from './chains.js';
export const keyFunderConfig: KeyFunderConfig = {
docker: {
repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo',
tag: '7720875-20240531-072251',
tag: 'b134b04-20240605-133031',
},
// We're currently using the same deployer/key funder key as mainnet2.
// To minimize nonce clobbering we offset the key funder cron
@ -31,8 +31,10 @@ export const keyFunderConfig: KeyFunderConfig = {
bsc: '5',
celo: '3',
ethereum: '0.5',
fraxtal: '0.2',
gnosis: '5',
inevm: '3',
linea: '0.2',
mantapacific: '0.2',
mode: '0.2',
moonbeam: '5',
@ -41,6 +43,7 @@ export const keyFunderConfig: KeyFunderConfig = {
polygonzkevm: '0.5',
redstone: '0.2',
scroll: '0.5',
sei: '10',
viction: '3',
zetachain: '20',
},
@ -53,8 +56,10 @@ export const keyFunderConfig: KeyFunderConfig = {
bsc: '0.35',
celo: '150',
ethereum: '0.4',
fraxtal: '0',
gnosis: '100',
inevm: '0.05',
linea: '0',
mantapacific: '0',
mode: '0',
moonbeam: '250',
@ -63,6 +68,7 @@ export const keyFunderConfig: KeyFunderConfig = {
polygonzkevm: '0.05',
redstone: '0',
scroll: '0.05',
sei: '0',
viction: '0.05',
zetachain: '0',
},

@ -1,6 +1,6 @@
{
"arbitrum": {
"amount": "0.01",
"amount": "0.5",
"decimals": 9
},
"ancient8": {
@ -16,7 +16,7 @@
"decimals": 9
},
"blast": {
"amount": "0.1",
"amount": "0.5",
"decimals": 9
},
"bsc": {
@ -31,8 +31,28 @@
"amount": "20",
"decimals": 9
},
"fraxtal": {
"amount": "0.001000253",
"decimals": 9
},
"gnosis": {
"amount": "5.877696928",
"decimals": 9
},
"inevm": {
"amount": "0.1",
"decimals": 9
},
"injective": {
"amount": "700000000",
"decimals": 1
},
"linea": {
"amount": "0.110844655",
"decimals": 9
},
"mantapacific": {
"amount": "0.100163166",
"amount": "0.101967574",
"decimals": 9
},
"mode": {
@ -43,22 +63,22 @@
"amount": "125.0",
"decimals": 9
},
"neutron": {
"amount": "0.0053",
"decimals": 1
},
"optimism": {
"amount": "0.061126811",
"amount": "0.25",
"decimals": 9
},
"osmosis": {
"amount": "0.025",
"decimals": 1
},
"polygon": {
"amount": "101.76455238",
"decimals": 9
},
"gnosis": {
"amount": "3.236596353",
"decimals": 9
},
"scroll": {
"amount": "1.3231",
"decimals": 9
},
"polygonzkevm": {
"amount": "3.95",
"decimals": 9
@ -67,24 +87,20 @@
"amount": "0.0003",
"decimals": 9
},
"inevm": {
"amount": "0.1",
"scroll": {
"amount": "1.3231",
"decimals": 9
},
"sei": {
"amount": "1.0",
"decimals": 9
},
"viction": {
"amount": "0.25",
"decimals": 9
},
"neutron": {
"amount": "0.0053",
"decimals": 1
},
"injective": {
"amount": "700000000",
"decimals": 1
},
"zetachain": {
"amount": "0.0001",
"amount": "10.1",
"decimals": 9
}
}

@ -14,6 +14,7 @@ import { agents } from './agent.js';
import {
chainMetadataOverrides,
environment as environmentName,
getRegistry,
} from './chains.js';
import { core } from './core.js';
import { keyFunderConfig } from './funding.js';
@ -24,14 +25,6 @@ import { bridgeAdapterConfigs, relayerConfig } from './liquidityLayer.js';
import { owners } from './owners.js';
import { supportedChainNames } from './supportedChainNames.js';
const getRegistry = async (useSecrets = true): Promise<IRegistry> =>
getRegistryForEnvironment(
environmentName,
supportedChainNames,
chainMetadataOverrides,
useSecrets,
);
export const environment: EnvironmentConfig = {
environment: environmentName,
supportedChainNames,

@ -1767,6 +1767,68 @@
"name": "DomaingRoutingIsm"
}
],
"fraxtal": [
{
"address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"constructorArguments": "",
"isProxy": false,
"name": "StaticMerkleRootMultisigIsmFactory"
},
{
"address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7",
"constructorArguments": "",
"isProxy": true,
"name": "StaticMerkleRootMultisigIsm"
},
{
"address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1",
"constructorArguments": "",
"isProxy": false,
"name": "StaticMessageIdMultisigIsmFactory"
},
{
"address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354",
"constructorArguments": "",
"isProxy": true,
"name": "StaticMessageIdMultisigIsm"
},
{
"address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"constructorArguments": "",
"isProxy": false,
"name": "StaticAggregationIsmFactory"
},
{
"address": "0x7f51A658837A315134A97ff8B586d71B726B7e61",
"constructorArguments": "",
"isProxy": true,
"name": "StaticAggregationIsm"
},
{
"address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D",
"constructorArguments": "",
"isProxy": false,
"name": "StaticAggregationHookFactory"
},
{
"address": "0xDFF18Bf286c9cDd0fC653a28616460Cf7443F8EF",
"constructorArguments": "",
"isProxy": true,
"name": "StaticAggregationHook"
},
{
"address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E",
"constructorArguments": "",
"isProxy": false,
"name": "DomainRoutingIsmFactory"
},
{
"address": "0x3a49EcAC1031612D66fa20D6F40f214aCeAc2B4B",
"constructorArguments": "",
"isProxy": true,
"name": "DomaingRoutingIsm"
}
],
"gnosis": [
{
"address": "0x8E273260EAd8B72A085B19346A676d355740e875",
@ -2173,6 +2235,68 @@
"name": "DomaingRoutingIsm"
}
],
"linea": [
{
"address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004",
"constructorArguments": "",
"isProxy": false,
"name": "StaticMerkleRootMultisigIsmFactory"
},
{
"address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7",
"constructorArguments": "",
"isProxy": true,
"name": "StaticMerkleRootMultisigIsm"
},
{
"address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1",
"constructorArguments": "",
"isProxy": false,
"name": "StaticMessageIdMultisigIsmFactory"
},
{
"address": "0x71DCcD21B912F7d4f636af0C9eA5DC0C10617354",
"constructorArguments": "",
"isProxy": true,
"name": "StaticMessageIdMultisigIsm"
},
{
"address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7",
"constructorArguments": "",
"isProxy": false,
"name": "StaticAggregationIsmFactory"
},
{
"address": "0x7f51A658837A315134A97ff8B586d71B726B7e61",
"constructorArguments": "",
"isProxy": true,
"name": "StaticAggregationIsm"
},
{
"address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D",
"constructorArguments": "",
"isProxy": false,
"name": "StaticAggregationHookFactory"
},
{
"address": "0xDFF18Bf286c9cDd0fC653a28616460Cf7443F8EF",
"constructorArguments": "",
"isProxy": true,
"name": "StaticAggregationHook"
},
{
"address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E",
"constructorArguments": "",
"isProxy": false,
"name": "DomainRoutingIsmFactory"
},
{
"address": "0x3a49EcAC1031612D66fa20D6F40f214aCeAc2B4B",
"constructorArguments": "",
"isProxy": true,
"name": "DomaingRoutingIsm"
}
],
"mantapacific": [
{
"address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2",
@ -3323,6 +3447,68 @@
"name": "DomaingRoutingIsm"
}
],
"sei": [
{
"address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",
"constructorArguments": "",
"isProxy": false,
"name": "StaticMerkleRootMultisigIsmFactory"
},
{
"address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc",
"constructorArguments": "",
"isProxy": true,
"name": "StaticMerkleRootMultisigIsm"
},
{
"address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE",
"constructorArguments": "",
"isProxy": false,
"name": "StaticMessageIdMultisigIsmFactory"
},
{
"address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F",
"constructorArguments": "",
"isProxy": true,
"name": "StaticMessageIdMultisigIsm"
},
{
"address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A",
"constructorArguments": "",
"isProxy": false,
"name": "StaticAggregationIsmFactory"
},
{
"address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2",
"constructorArguments": "",
"isProxy": true,
"name": "StaticAggregationIsm"
},
{
"address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6",
"constructorArguments": "",
"isProxy": false,
"name": "StaticAggregationHookFactory"
},
{
"address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0",
"constructorArguments": "",
"isProxy": true,
"name": "StaticAggregationHook"
},
{
"address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908",
"constructorArguments": "",
"isProxy": false,
"name": "DomainRoutingIsmFactory"
},
{
"address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874",
"constructorArguments": "",
"isProxy": true,
"name": "DomaingRoutingIsm"
}
],
"viction": [
{
"address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC",

@ -39,6 +39,9 @@ export const safes: ChainMap<Address | undefined> = {
polygonzkevm: '0x1610f578D4d77Fc4ae7ce2DD9AA0b98A5Cd0a9b2',
// injective: 'inj1632x8j35kenryam3mkrsez064sqg2y2fr0frzt',
// solana: 'EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3',
blast: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5',
linea: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5',
mode: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5',
};
export const DEPLOYER = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba';

@ -9,18 +9,22 @@ export const supportedChainNames = [
'bsc',
'celo',
'ethereum',
'fraxtal',
'gnosis',
'inevm',
'injective',
'linea',
'mantapacific',
'mode',
'moonbeam',
'neutron',
'optimism',
'osmosis',
'polygon',
'polygonzkevm',
'redstone',
'scroll',
'sei',
'viction',
'zetachain',
];

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save