diff --git a/.changeset/fast-pillows-wash.md b/.changeset/fast-pillows-wash.md deleted file mode 100644 index 7db9c9ec0b..0000000000 --- a/.changeset/fast-pillows-wash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/utils': patch ---- - -Add isAddress function to check if string matches EVM, Cosmos, or Solana address formats diff --git a/.changeset/thirty-pandas-design.md b/.changeset/thirty-pandas-design.md deleted file mode 100644 index 14a9698d23..0000000000 --- a/.changeset/thirty-pandas-design.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/core': patch -'@hyperlane-xyz/infra': patch -'@hyperlane-xyz/sdk': patch ---- - -Support pausable ISM in deployer and checker diff --git a/.github/workflows/agent-release-artifacts.yml b/.github/workflows/agent-release-artifacts.yml index 79c421f22a..2827120989 100644 --- a/.github/workflows/agent-release-artifacts.yml +++ b/.github/workflows/agent-release-artifacts.yml @@ -49,7 +49,7 @@ jobs: run: | sudo apt-get update -qq sudo apt-get install -qq crossbuild-essential-arm64 crossbuild-essential-armhf - + # some additional configuration for cross-compilation on linux cat >>~/.cargo/config < layout.diff - name: Comment PR with layout diff diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2a4ea99c65..2761190839 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,7 @@ jobs: - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - name: yarn-cache @@ -54,6 +55,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive fetch-depth: 0 @@ -71,7 +73,7 @@ jobs: path: | ./* !./rust - key: ${{ github.sha }} + key: ${{ github.event.pull_request.head.sha || github.sha }} - name: build run: yarn build @@ -82,6 +84,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} # check out full history fetch-depth: 0 @@ -111,6 +114,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive fetch-depth: 0 @@ -123,19 +127,49 @@ jobs: path: | ./* !./rust - key: ${{ github.sha }} + key: ${{ github.event.pull_request.head.sha || github.sha }} - name: Unit Tests run: yarn test:ci - metadata-check: - runs-on: ubuntu-latest + e2e-matrix: + runs-on: larger-runner needs: [yarn-build] + strategy: + matrix: + e2e-type: [cosmwasm, non-cosmwasm] steps: + - uses: actions/setup-node@v3 + with: + node-version: 18 + - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - fetch-depth: 0 + + - name: foundry-install + uses: onbjerg/foundry-toolchain@v1 + + - name: setup rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + + - name: Free disk space + run: | + # Based on https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + + - name: Install mold linker + uses: rui314/setup-mold@v1 + with: + mold-version: 2.0.0 + make-default: true - name: yarn-cache uses: actions/cache@v3 @@ -151,14 +185,50 @@ jobs: path: | ./* !./rust - key: ${{ github.sha }} + key: ${{ 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: Metadata Health Check - run: yarn workspace @hyperlane-xyz/sdk run test:metadata + - name: agent tests with CosmWasm + run: cargo test --release --package run-locally --bin run-locally --features cosmos -- cosmos::test --nocapture + if: matrix.e2e-type == 'cosmwasm' + working-directory: ./rust + env: + RUST_BACKTRACE: 1 + + - name: agent tests excluding CosmWasm + run: cargo run --release --bin run-locally + if: matrix.e2e-type == 'non-cosmwasm' + working-directory: ./rust + env: + E2E_CI_MODE: 'true' + E2E_CI_TIMEOUT_SEC: '600' + E2E_KATHY_MESSAGES: '20' e2e: + runs-on: ubuntu-latest + needs: [e2e-matrix] + if: always() # This ensures that the job runs even if the e2e jobs fail + steps: + - name: Report Matrix Result + run: | + echo "All e2e-matrix jobs have completed." + # You can add additional commands here to report the result as needed + + cli-e2e: runs-on: larger-runner needs: [yarn-build] + strategy: + matrix: + include: + - test-type: preset_hook_enabled + - test-type: configure_hook_enabled + - test-type: pi_with_core_chain steps: - uses: actions/setup-node@v3 with: @@ -166,6 +236,7 @@ jobs: - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - name: foundry-install @@ -205,7 +276,7 @@ jobs: path: | ./* !./rust - key: ${{ github.sha }} + key: ${{ github.event.pull_request.head.sha || github.sha }} - name: cargo-cache uses: actions/cache@v3 @@ -214,35 +285,30 @@ jobs: ~/.cargo key: ${{ runner.os }}-cargo-cache-${{ hashFiles('./rust/Cargo.lock') }} - - name: agent build - run: cargo build --release --bin run-locally - working-directory: ./rust - - - name: agent tests with CosmWasm - run: RUST_BACKTRACE=1 cargo test --release --package run-locally --bin run-locally --features cosmos -- cosmos::test --nocapture - working-directory: ./rust - - - name: agent tests excluding CosmWasm - run: ./target/release/run-locally - working-directory: ./rust - env: - E2E_CI_MODE: 'true' - E2E_CI_TIMEOUT_SEC: '600' - E2E_KATHY_MESSAGES: '20' - - name: cli e2e tests - run: ./typescript/cli/ci-test.sh + run: ./typescript/cli/ci-test.sh ${{ matrix.test-type }} env-test: runs-on: ubuntu-latest needs: [yarn-build] strategy: + fail-fast: false matrix: - environment: [testnet4, mainnet3] - module: [ism, core, helloworld] + environment: [mainnet3] + chain: [ethereum, arbitrum, optimism, inevm, viction] + module: [core, igp] + include: + - environment: testnet4 + chain: sepolia + module: core + - environment: mainnet3 + chain: inevm + module: warp steps: - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: foundry-install uses: onbjerg/foundry-toolchain@v1 @@ -253,10 +319,10 @@ jobs: path: | ./* !./rust - key: ${{ github.sha }} + key: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Test ${{ matrix.environment }} ${{ matrix.module }} deployment (check, deploy, govern, check again) - run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} + - name: Fork test ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} deployment + run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} coverage: runs-on: ubuntu-latest @@ -265,6 +331,7 @@ jobs: steps: - uses: actions/checkout@v3 with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 - name: yarn-cache @@ -281,7 +348,7 @@ jobs: path: | ./* !./rust - key: ${{ github.sha }} + key: ${{ github.event.pull_request.head.sha || github.sha }} - name: foundry-install uses: onbjerg/foundry-toolchain@v1 diff --git a/Dockerfile b/Dockerfile index a3e6049616..f40cc6e499 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM node:18-alpine WORKDIR /hyperlane-monorepo -RUN apk add --update --no-cache git g++ make py3-pip +RUN apk add --update --no-cache git g++ make py3-pip jq RUN yarn set version 4.0.1 @@ -15,6 +15,7 @@ COPY typescript/sdk/package.json ./typescript/sdk/ COPY typescript/helloworld/package.json ./typescript/helloworld/ COPY typescript/cli/package.json ./typescript/cli/ COPY typescript/infra/package.json ./typescript/infra/ +COPY typescript/ccip-server/package.json ./typescript/ccip-server/ COPY solidity/package.json ./solidity/ RUN yarn install && yarn cache clean diff --git a/package.json b/package.json index 246a43a8d9..38f5720097 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "build": "yarn workspaces foreach --all --parallel --topological run build", "clean": "yarn workspaces foreach --all --parallel run clean", "prettier": "yarn workspaces foreach --since --parallel run prettier", - "lint": "yarn workspaces foreach --since --parallel run lint", + "lint": "yarn workspaces foreach --all --parallel run lint", "test": "yarn workspaces foreach --all --parallel run test", "test:ci": "yarn workspaces foreach --all --parallel run test:ci", "coverage": "yarn workspaces foreach --all --parallel run coverage", diff --git a/rust/.gitignore b/rust/.gitignore index 1d73887c2d..ffe12328db 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -2,4 +2,5 @@ target validatordb relayerdb kathydb +hyperlane_db config/test_config.json diff --git a/rust/Cargo.lock b/rust/Cargo.lock index c20fa554bb..43350301df 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -108,16 +108,16 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", "once_cell", "version_check", ] [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "once_cell", @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" dependencies = [ "anstyle", "anstyle-parse", @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arrayref" @@ -279,8 +279,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", "synstructure", ] @@ -291,8 +291,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -325,6 +325,16 @@ dependencies = [ "event-listener", ] +[[package]] +name = "async-rwlock" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261803dcc39ba9e72760ba6e16d0199b1eef9fc44e81bffabbebb9f5aea3906c" +dependencies = [ + "async-mutex", + "event-listener", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -342,20 +352,20 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -405,8 +415,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -417,8 +427,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -450,7 +460,11 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", "sync_wrapper", + "tokio", "tower", "tower-layer", "tower-service", @@ -504,8 +518,8 @@ checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" dependencies = [ "heck 0.3.3", "proc-macro-error", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -551,9 +565,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64ct" @@ -619,12 +633,12 @@ dependencies = [ "lazycell", "peeking_take_while", "prettyplease", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "regex", "rustc-hash", "shlex", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -763,11 +777,11 @@ dependencies = [ [[package]] name = "borsh" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d4d6dafc1a3bb54687538972158f07b2c948bc57d5890df22c0739098b3028" +checksum = "f58b559fd6448c6e2fd0adb5720cd98a2506594cafa4737ff98c396f3e82f667" dependencies = [ - "borsh-derive 1.3.0", + "borsh-derive 1.3.1", "cfg_aliases", ] @@ -780,21 +794,21 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.70", + "proc-macro2 1.0.76", "syn 1.0.109", ] [[package]] name = "borsh-derive" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4918709cc4dd777ad2b6303ed03cb37f3ca0ccede8c1b0d28ac6db8f4710e0" +checksum = "7aadb5b6ccbd078890f6d7003694e33816e6b784358f18e15e7e6d9f065a57cd" dependencies = [ "once_cell", - "proc-macro-crate 2.0.0", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro-crate 3.0.0", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", "syn_derive", ] @@ -804,8 +818,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -815,8 +829,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -901,8 +915,8 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -921,9 +935,9 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -983,9 +997,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff" +checksum = "ceed8ef69d8518a5dda55c07425450b58a4e1946f4951eab6d7191ee86c2443d" dependencies = [ "serde", ] @@ -1080,13 +1094,13 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", - "libloading", + "libloading 0.8.1", ] [[package]] @@ -1123,9 +1137,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.11" +version = "4.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "80932e03c33999b9235edb8655bc9df3204adc9887c2f95b50cb1deb9fd54253" dependencies = [ "clap_builder", "clap_derive 4.4.7", @@ -1133,9 +1147,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "d6c0db58c659eef1c73e444d298c27322a1b52f6927d2ad470c0c0f96fa7b8fa" dependencies = [ "anstream", "anstyle", @@ -1151,8 +1165,8 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -1163,9 +1177,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -1183,6 +1197,15 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "cobs" version = "0.2.3" @@ -1199,7 +1222,7 @@ dependencies = [ "bs58 0.4.0", "coins-core", "digest 0.10.7", - "getrandom 0.2.11", + "getrandom 0.2.12", "hmac 0.12.1", "k256 0.11.6", "lazy_static", @@ -1216,7 +1239,7 @@ checksum = "2a11892bcac83b4c6e95ab84b5b06c76d9d70ad73548dd07418269c5c7977171" dependencies = [ "bitvec 0.17.4", "coins-bip32", - "getrandom 0.2.11", + "getrandom 0.2.12", "hex 0.4.3", "hmac 0.12.1", "pbkdf2 0.11.0", @@ -1313,15 +1336,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -1427,10 +1450,10 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ - "prost", + "prost 0.11.9", "prost-types", "tendermint-proto 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tonic", + "tonic 0.9.2", ] [[package]] @@ -1442,8 +1465,8 @@ dependencies = [ "cosmos-sdk-proto", "ecdsa 0.16.9", "eyre", - "getrandom 0.2.11", - "k256 0.13.2", + "getrandom 0.2.12", + "k256 0.13.3", "rand_core 0.6.4", "serde", "serde_json", @@ -1456,32 +1479,32 @@ dependencies = [ [[package]] name = "cosmwasm-crypto" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bb3c77c3b7ce472056968c745eb501c440fbc07be5004eba02782c35bfbbe3" +checksum = "8ed6aa9f904de106fa16443ad14ec2abe75e94ba003bb61c681c0e43d4c58d2a" dependencies = [ "digest 0.10.7", "ecdsa 0.16.9", "ed25519-zebra", - "k256 0.13.2", + "k256 0.13.3", "rand_core 0.6.4", "thiserror", ] [[package]] name = "cosmwasm-derive" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea73e9162e6efde00018d55ed0061e93a108b5d6ec4548b4f8ce3c706249687" +checksum = "40abec852f3d4abec6d44ead9a58b78325021a1ead1e7229c3471414e57b2e49" dependencies = [ "syn 1.0.109", ] [[package]] name = "cosmwasm-schema" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df41ea55f2946b6b43579659eec048cc2f66e8c8e2e3652fc5e5e476f673856" +checksum = "b166215fbfe93dc5575bae062aa57ae7bb41121cffe53bac33b033257949d2a9" dependencies = [ "cosmwasm-schema-derive", "schemars", @@ -1492,22 +1515,22 @@ dependencies = [ [[package]] name = "cosmwasm-schema-derive" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43609e92ce1b9368aa951b334dd354a2d0dd4d484931a5f83ae10e12a26c8ba9" +checksum = "8bf12f8e20bb29d1db66b7ca590bc2f670b548d21e9be92499bc0f9022a994a8" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "cosmwasm-std" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d6864742e3a7662d024b51a94ea81c9af21db6faea2f9a6d2232bb97c6e53e" +checksum = "ad011ae7447188e26e4a7dbca2fcd0fc186aa21ae5c86df0503ea44c78f9e469" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bech32 0.9.1", "bnum", "cosmwasm-crypto", @@ -1519,15 +1542,15 @@ dependencies = [ "serde", "serde-json-wasm", "sha2 0.10.8", - "static_assertions", + "static_assertions 1.1.0", "thiserror", ] [[package]] name = "cosmwasm-storage" -version = "1.5.0" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2b4ae72a03e8f56c85df59d172d51d2d7dc9cec6e2bc811e3fb60c588032a4" +checksum = "66de2ab9db04757bcedef2b5984fbe536903ada4a8a9766717a4a71197ef34f6" dependencies = [ "cosmwasm-std", "serde", @@ -1544,9 +1567,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -1568,55 +1591,46 @@ checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" [[package]] name = "crossbeam-channel" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.16" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.9.0", ] [[package]] name = "crossbeam-queue" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.17" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" @@ -1720,12 +1734,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" +checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b" dependencies = [ "nix 0.27.1", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1835,7 +1849,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "static_assertions", + "static_assertions 1.1.0", "thiserror", ] @@ -1849,8 +1863,8 @@ dependencies = [ "darling 0.13.4", "graphql-parser", "once_cell", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", ] @@ -1893,8 +1907,8 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", ] @@ -1907,8 +1921,8 @@ checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "strsim 0.10.0", "syn 1.0.109", ] @@ -1920,7 +1934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.33", + "quote 1.0.35", "syn 1.0.109", ] @@ -1931,7 +1945,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core 0.14.4", - "quote 1.0.33", + "quote 1.0.35", "syn 1.0.109", ] @@ -1997,9 +2011,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -2017,8 +2031,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -2028,8 +2042,8 @@ version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -2049,8 +2063,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ "darling 0.14.4", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -2071,8 +2085,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rustc_version", "syn 1.0.109", ] @@ -2127,9 +2141,9 @@ dependencies = [ [[package]] name = "dir-diff" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2860407d7d7e2e004bb2128510ad9e8d669e76fa005ccf567977b5d71b8b4a0b" +checksum = "a7ad16bf5f84253b50d6557681c58c3ab67c47c77d39fed9aeb56e947290bd10" dependencies = [ "walkdir", ] @@ -2181,9 +2195,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -2275,7 +2289,7 @@ dependencies = [ name = "ecdsa-signature" version = "0.1.0" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", "hyperlane-core", "solana-program", "thiserror", @@ -2360,8 +2374,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -2461,8 +2475,8 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -2474,9 +2488,9 @@ checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" dependencies = [ "num-bigint 0.4.4", "num-traits", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -2486,9 +2500,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e" dependencies = [ "once_cell", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -2583,7 +2597,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ - "ethereum-types", + "ethereum-types 0.14.1", "hex 0.4.3", "once_cell", "regex", @@ -2591,7 +2605,20 @@ dependencies = [ "serde_json", "sha3 0.10.8", "thiserror", - "uint", + "uint 0.9.5", +] + +[[package]] +name = "ethbloom" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3932e82d64d347a045208924002930dc105a138995ccdc1479d0f05f0359f17c" +dependencies = [ + "crunchy", + "fixed-hash 0.3.2", + "impl-rlp 0.2.1", + "impl-serde 0.2.3", + "tiny-keccak 1.5.0", ] [[package]] @@ -2606,7 +2633,21 @@ dependencies = [ "impl-rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "scale-info", - "tiny-keccak", + "tiny-keccak 2.0.2", +] + +[[package]] +name = "ethereum-types" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b054df51e53f253837ea422681215b42823c02824bde982699d0dceecf6165a1" +dependencies = [ + "crunchy", + "ethbloom 0.6.4", + "ethereum-types-serialize", + "fixed-hash 0.3.2", + "serde", + "uint 0.5.0", ] [[package]] @@ -2615,14 +2656,23 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "ethbloom", + "ethbloom 0.13.0", "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-codec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-rlp 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "impl-serde 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "primitive-types", "scale-info", - "uint", + "uint 0.9.5", +] + +[[package]] +name = "ethereum-types-serialize" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d77b32bc1891a79dad925f2acbc318ee942b38b9110f9dbc5fbeffcea350" +dependencies = [ + "serde", ] [[package]] @@ -2678,10 +2728,10 @@ dependencies = [ "dunce", "ethers-core", "eyre", - "getrandom 0.2.11", + "getrandom 0.2.12", "hex 0.4.3", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "regex", "reqwest", "serde", @@ -2700,8 +2750,8 @@ dependencies = [ "ethers-contract-abigen", "ethers-core", "hex 0.4.3", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "serde_json", "syn 1.0.109", ] @@ -2723,16 +2773,16 @@ dependencies = [ "k256 0.11.6", "once_cell", "open-fastrlp", - "proc-macro2 1.0.70", + "proc-macro2 1.0.76", "rand 0.8.5", - "rlp", + "rlp 0.5.2", "rlp-derive", "serde", "serde_json", "strum 0.24.1", "syn 1.0.109", "thiserror", - "tiny-keccak", + "tiny-keccak 2.0.2", "unicode-xid 0.2.4", ] @@ -2742,7 +2792,7 @@ version = "1.0.2" source = "git+https://github.com/hyperlane-xyz/ethers-rs?tag=2023-11-29-02#c9ced035628da59376c369be035facda1648577a" dependencies = [ "ethers-core", - "getrandom 0.2.11", + "getrandom 0.2.12", "reqwest", "semver", "serde", @@ -2786,6 +2836,7 @@ dependencies = [ "derive-new", "derive_builder", "ethers", + "ethers-core", "futures", "hyperlane-core", "log", @@ -2795,7 +2846,7 @@ dependencies = [ "prometheus", "serde", "serde_json", - "static_assertions", + "static_assertions 1.1.0", "tokio", ] @@ -2812,7 +2863,7 @@ dependencies = [ "futures-core", "futures-timer", "futures-util", - "getrandom 0.2.11", + "getrandom 0.2.12", "hashers", "hex 0.4.3", "http", @@ -2943,6 +2994,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" +[[package]] +name = "fixed-hash" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1a683d1234507e4f3bf2736eeddf0de1dc65996dc0164d57eba0a74bcf29489" +dependencies = [ + "byteorder", + "heapsize", + "rand 0.5.6", + "rustc-hex", + "static_assertions 0.2.5", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -2952,7 +3016,7 @@ dependencies = [ "byteorder", "rand 0.8.5", "rustc-hex", - "static_assertions", + "static_assertions 1.1.0", ] [[package]] @@ -2963,7 +3027,7 @@ dependencies = [ "byteorder", "rand 0.8.5", "rustc-hex", - "static_assertions", + "static_assertions 1.1.0", ] [[package]] @@ -3037,6 +3101,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + [[package]] name = "fuel-abi-types" version = "0.2.1" @@ -3140,7 +3210,7 @@ dependencies = [ "coins-bip32", "coins-bip39", "fuel-types", - "getrandom 0.2.11", + "getrandom 0.2.12", "lazy_static", "rand 0.8.5", "secp256k1", @@ -3246,8 +3316,8 @@ dependencies = [ "fuel-abi-types", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "regex", "serde_json", "syn 1.0.109", @@ -3279,8 +3349,8 @@ dependencies = [ "fuels-code-gen", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rand 0.8.5", "regex", "serde_json", @@ -3304,7 +3374,7 @@ dependencies = [ "futures", "hex 0.4.3", "itertools 0.10.5", - "proc-macro2 1.0.70", + "proc-macro2 1.0.76", "rand 0.8.5", "regex", "serde", @@ -3388,7 +3458,7 @@ dependencies = [ "hex 0.4.3", "itertools 0.10.5", "lazy_static", - "proc-macro2 1.0.70", + "proc-macro2 1.0.76", "regex", "serde", "serde_json", @@ -3405,9 +3475,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -3420,9 +3490,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -3430,15 +3500,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -3458,9 +3528,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-locks" @@ -3474,26 +3544,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" @@ -3503,9 +3573,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -3574,9 +3644,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "js-sys", @@ -3642,9 +3712,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" dependencies = [ "bytes", "fnv", @@ -3692,7 +3762,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.7", ] [[package]] @@ -3701,7 +3771,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.7", "allocator-api2", ] @@ -3729,7 +3799,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "headers-core", "http", @@ -3761,6 +3831,15 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + [[package]] name = "heck" version = "0.3.3" @@ -3874,28 +3953,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "hpl-interface" -version = "0.0.6-rc3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9124bd88d88aba044ae790007bcbf25493b93424156e8759523c9c5d02229b0c" -dependencies = [ - "bech32 0.9.1", - "cosmwasm-schema", - "cosmwasm-std", - "cosmwasm-storage", - "cw-storage-plus", - "cw2", - "cw20", - "cw20-base", - "ripemd", - "schemars", - "serde", - "sha2 0.10.8", - "sha3 0.10.8", - "thiserror", -] - [[package]] name = "http" version = "0.2.11" @@ -3936,6 +3993,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "0.14.28" @@ -4006,7 +4073,9 @@ dependencies = [ "futures-util", "http", "hyper", + "log", "rustls 0.21.10", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -4041,6 +4110,7 @@ name = "hyperlane-base" version = "0.1.0" dependencies = [ "async-trait", + "axum", "backtrace", "backtrace-oneline", "bs58 0.5.0", @@ -4054,6 +4124,7 @@ dependencies = [ "ethers-prometheus", "eyre", "fuels", + "futures", "futures-util", "hyperlane-core", "hyperlane-cosmos", @@ -4061,10 +4132,11 @@ dependencies = [ "hyperlane-fuel", "hyperlane-sealevel", "hyperlane-test", - "itertools 0.11.0", + "itertools 0.12.0", "maplit", "paste", "prometheus", + "reqwest", "rocksdb", "rusoto_core", "rusoto_kms", @@ -4073,7 +4145,7 @@ dependencies = [ "serde", "serde_json", "solana-sdk", - "static_assertions", + "static_assertions 1.1.0", "tempfile", "thiserror", "tokio", @@ -4081,15 +4153,18 @@ dependencies = [ "tracing-error", "tracing-futures", "tracing-subscriber", + "tracing-test", "url", "walkdir", "warp", + "ya-gcp", ] [[package]] name = "hyperlane-core" version = "0.1.0" dependencies = [ + "async-rwlock", "async-trait", "auto_impl 1.1.0", "bigdecimal 0.4.2", @@ -4105,9 +4180,10 @@ dependencies = [ "ethers-providers", "eyre", "fixed-hash 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "getrandom 0.2.11", + "futures", + "getrandom 0.2.12", "hex 0.4.3", - "itertools 0.11.0", + "itertools 0.12.0", "num 0.4.1", "num-derive 0.4.1", "num-traits", @@ -4118,9 +4194,10 @@ dependencies = [ "solana-sdk", "strum 0.25.0", "thiserror", - "tiny-keccak", + "tiny-keccak 2.0.2", "tokio", - "uint", + "tracing", + "uint 0.9.5", ] [[package]] @@ -4128,16 +4205,23 @@ name = "hyperlane-cosmos" version = "0.1.0" dependencies = [ "async-trait", - "base64 0.21.5", + "base64 0.21.7", "bech32 0.9.1", "cosmrs", + "cosmwasm-std", "derive-new", + "futures", "hex 0.4.3", - "hpl-interface", + "http", "hyper", "hyper-tls", "hyperlane-core", + "hyperlane-cosmwasm-interface", + "injective-protobuf", + "injective-std", + "itertools 0.12.0", "once_cell", + "protobuf", "ripemd", "serde", "serde_json", @@ -4147,12 +4231,34 @@ dependencies = [ "tendermint-rpc", "thiserror", "tokio", - "tonic", + "tonic 0.9.2", "tracing", "tracing-futures", "url", ] +[[package]] +name = "hyperlane-cosmwasm-interface" +version = "0.0.6-rc6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e622014ab94f1e7f0acbe71df7c1384224261e2c76115807aaf24215970942" +dependencies = [ + "bech32 0.9.1", + "cosmwasm-schema", + "cosmwasm-std", + "cosmwasm-storage", + "cw-storage-plus", + "cw2", + "cw20", + "cw20-base", + "ripemd", + "schemars", + "serde", + "sha2 0.10.8", + "sha3 0.10.8", + "thiserror", +] + [[package]] name = "hyperlane-ethereum" version = "0.1.0" @@ -4203,7 +4309,7 @@ dependencies = [ "account-utils", "anyhow", "async-trait", - "base64 0.21.5", + "base64 0.21.7", "borsh 0.9.3", "derive-new", "hyperlane-core", @@ -4236,7 +4342,8 @@ dependencies = [ "bincode", "borsh 0.9.3", "bs58 0.5.0", - "clap 4.4.11", + "clap 4.4.17", + "ethers", "hex 0.4.3", "hyperlane-core", "hyperlane-sealevel-connection-client", @@ -4299,7 +4406,7 @@ dependencies = [ "access-control", "account-utils", "borsh 0.9.3", - "getrandom 0.2.11", + "getrandom 0.2.12", "hyperlane-core", "num-derive 0.4.1", "num-traits", @@ -4340,14 +4447,14 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.5", + "base64 0.21.7", "blake3", "borsh 0.9.3", - "getrandom 0.2.11", + "getrandom 0.2.12", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", "hyperlane-sealevel-message-recipient-interface", - "itertools 0.11.0", + "itertools 0.12.0", "log", "num-derive 0.4.1", "num-traits", @@ -4364,7 +4471,7 @@ version = "0.1.0" dependencies = [ "access-control", "account-utils", - "base64 0.21.5", + "base64 0.21.7", "borsh 0.9.3", "hyperlane-core", "hyperlane-sealevel-interchain-security-module-interface", @@ -4373,7 +4480,7 @@ dependencies = [ "hyperlane-sealevel-test-ism", "hyperlane-sealevel-test-send-receiver", "hyperlane-test-utils", - "itertools 0.11.0", + "itertools 0.12.0", "log", "num-derive 0.4.1", "num-traits", @@ -4390,7 +4497,7 @@ name = "hyperlane-sealevel-message-recipient-interface" version = "0.1.0" dependencies = [ "borsh 0.9.3", - "getrandom 0.2.11", + "getrandom 0.2.12", "hyperlane-core", "solana-program", "spl-type-length-value", @@ -4613,9 +4720,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -4704,13 +4811,22 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-rlp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f7a72f11830b52333f36e3b09a288333888bf54380fd0ac0790a3c31ab0f3c5" +dependencies = [ + "rlp 0.4.6", +] + [[package]] name = "impl-rlp" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" dependencies = [ - "rlp", + "rlp 0.5.2", ] [[package]] @@ -4718,7 +4834,16 @@ name = "impl-rlp" version = "0.3.0" source = "git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane#3c2a89084ccfc27b82fda29007b4e27215a75cb1" dependencies = [ - "rlp", + "rlp 0.5.2", +] + +[[package]] +name = "impl-serde" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e3cae7e99c7ff5a995da2cf78dd0a5383740eda71d98cf7b1910c301ac69b8" +dependencies = [ + "serde", ] [[package]] @@ -4744,8 +4869,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -4793,6 +4918,38 @@ dependencies = [ "regex", ] +[[package]] +name = "injective-protobuf" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52219a08aba8c17846fd23d472d1d69c817fe5b427d135273e4c7311edd6972" +dependencies = [ + "cosmwasm-std", + "ethereum-types 0.5.2", + "num 0.4.1", + "protobuf", + "protobuf-codegen-pure", + "schemars", + "serde", + "subtle-encoding", +] + +[[package]] +name = "injective-std" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7a5b52d19dca05823c7e4b481d41b49c04a0e56f66a5c92396a6fdd3314710" +dependencies = [ + "chrono", + "cosmwasm-std", + "osmosis-std-derive", + "prost 0.11.9", + "prost-types", + "schemars", + "serde", + "serde-cw-value", +] + [[package]] name = "inout" version = "0.1.3" @@ -4822,13 +4979,13 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "is-terminal" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi 0.3.3", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -4840,15 +4997,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.12.0" @@ -4875,9 +5023,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -4923,9 +5071,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f01b677d82ef7a676aa37e099defd83a28e15687112cafdd112d60236b6115b" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" dependencies = [ "cfg-if", "ecdsa 0.16.9", @@ -4937,9 +5085,9 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -4958,9 +5106,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libloading" @@ -4972,6 +5120,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "libm" version = "0.2.8" @@ -5055,9 +5213,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "295c17e837573c8c821dbaeb3cceb3d745ad082f7572191409e69cbc1b3fd050" dependencies = [ "cc", "pkg-config", @@ -5187,9 +5345,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" @@ -5209,15 +5367,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - [[package]] name = "merlin" version = "3.0.0" @@ -5307,8 +5456,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb" dependencies = [ "cfg-if", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -5319,7 +5468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" dependencies = [ "modular-bitfield-impl", - "static_assertions", + "static_assertions 1.1.0", ] [[package]] @@ -5328,8 +5477,8 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -5391,7 +5540,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset", ] [[package]] @@ -5519,8 +5668,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -5530,9 +5679,9 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb77679af88f8b125209d354a202862602672222e7f2313fdd6dc349bad4712" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -5625,8 +5774,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -5637,9 +5786,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", ] [[package]] @@ -5650,9 +5808,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -5693,7 +5851,7 @@ dependencies = [ "arrayvec", "auto_impl 1.1.0", "bytes", - "ethereum-types", + "ethereum-types 0.14.1", "open-fastrlp-derive", ] @@ -5704,16 +5862,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -5730,9 +5888,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -5743,9 +5901,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -5788,6 +5946,18 @@ version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +[[package]] +name = "osmosis-std-derive" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4d482a16be198ee04e0f94e10dd9b8d02332dcf33bc5ea4b255e7e25eedc5df" +dependencies = [ + "itertools 0.10.5", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 1.0.109", +] + [[package]] name = "ouroboros" version = "0.15.6" @@ -5806,8 +5976,8 @@ checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -5844,8 +6014,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ "proc-macro-crate 2.0.0", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -5988,8 +6158,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", ] [[package]] @@ -6024,9 +6194,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" +checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06" dependencies = [ "memchr", "thiserror", @@ -6035,9 +6205,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2" +checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde" dependencies = [ "pest", "pest_generator", @@ -6045,22 +6215,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227" +checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] name = "pest_meta" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6" +checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d" dependencies = [ "once_cell", "pest", @@ -6092,9 +6262,9 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -6142,9 +6312,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "plain" @@ -6239,12 +6409,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ - "proc-macro2 1.0.70", - "syn 2.0.41", + "proc-macro2 1.0.76", + "syn 2.0.48", ] [[package]] @@ -6257,7 +6427,7 @@ dependencies = [ "impl-rlp 0.3.0 (git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane)", "impl-serde 0.4.0 (git+https://github.com/hyperlane-xyz/parity-common.git?branch=hyperlane)", "scale-info", - "uint", + "uint 0.9.5", ] [[package]] @@ -6289,6 +6459,15 @@ dependencies = [ "toml_edit 0.20.7", ] +[[package]] +name = "proc-macro-crate" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd" +dependencies = [ + "toml_edit 0.21.0", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -6296,8 +6475,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", "version_check", ] @@ -6308,8 +6487,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "version_check", ] @@ -6324,9 +6503,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] @@ -6356,6 +6535,15 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +dependencies = [ + "bytes", +] + [[package]] name = "prost-derive" version = "0.11.9" @@ -6364,8 +6552,8 @@ checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" dependencies = [ "anyhow", "itertools 0.10.5", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -6375,7 +6563,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13" dependencies = [ - "prost", + "prost 0.11.9", ] [[package]] @@ -6383,6 +6571,28 @@ name = "protobuf" version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" +dependencies = [ + "bytes", +] + +[[package]] +name = "protobuf-codegen" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +dependencies = [ + "protobuf", +] + +[[package]] +name = "protobuf-codegen-pure" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a29399fc94bcd3eeaa951c715f7bea69409b2445356b00519740bcd6ddd865" +dependencies = [ + "protobuf", + "protobuf-codegen", +] [[package]] name = "psl-types" @@ -6405,8 +6615,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -6493,11 +6703,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "proc-macro2 1.0.70", + "proc-macro2 1.0.76", ] [[package]] @@ -6512,6 +6722,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi", +] + [[package]] name = "rand" version = "0.7.3" @@ -6556,6 +6779,21 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + [[package]] name = "rand_core" version = "0.5.1" @@ -6571,7 +6809,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", ] [[package]] @@ -6648,7 +6886,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", "libredox", "thiserror", ] @@ -6710,12 +6948,13 @@ dependencies = [ "ethers", "ethers-contract", "eyre", + "futures", "futures-util", "hyperlane-base", "hyperlane-core", "hyperlane-ethereum", "hyperlane-test", - "itertools 0.11.0", + "itertools 0.12.0", "num-derive 0.4.1", "num-traits", "once_cell", @@ -6748,7 +6987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "async-compression", - "base64 0.21.5", + "base64 0.21.7", "bytes", "cookie", "cookie_store", @@ -6831,7 +7070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", - "getrandom 0.2.11", + "getrandom 0.2.12", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -6871,11 +7110,20 @@ version = "0.7.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] +[[package]] +name = "rlp" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1190dcc8c3a512f1eef5d09bb8c84c7f39e1054e174d1795482e18f5272f2e73" +dependencies = [ + "rustc-hex", +] + [[package]] name = "rlp" version = "0.5.2" @@ -6891,8 +7139,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -6937,11 +7185,11 @@ dependencies = [ "ctrlc", "eyre", "hex 0.4.3", - "hpl-interface", "hyperlane-core", "hyperlane-cosmos", + "hyperlane-cosmwasm-interface", "jobserver", - "k256 0.13.2", + "k256 0.13.3", "macro_rules_attribute", "maplit", "nix 0.26.4", @@ -7084,7 +7332,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" dependencies = [ "arrayvec", - "borsh 1.3.0", + "borsh 1.3.1", "bytes", "num-traits", "rand 0.8.5", @@ -7131,9 +7379,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.4.1", "errno", @@ -7175,10 +7423,24 @@ checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", "ring 0.17.7", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct 0.7.1", ] +[[package]] +name = "rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41" +dependencies = [ + "log", + "ring 0.17.7", + "rustls-pki-types", + "rustls-webpki 0.102.1", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.5.0" @@ -7218,9 +7480,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -7231,6 +7499,17 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustls-webpki" +version = "0.102.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b" +dependencies = [ + "ring 0.17.7", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -7289,18 +7568,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ "proc-macro-crate 1.2.1", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -7321,8 +7600,8 @@ version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c767fd6fa65d9ccf9cf026122c1b555f2ef9a4f0cea69da4d7dbc3e258d30967" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "serde_derive_internals", "syn 1.0.109", ] @@ -7353,9 +7632,10 @@ dependencies = [ "hyperlane-base", "hyperlane-core", "hyperlane-test", - "itertools 0.11.0", + "itertools 0.12.0", "migration", "num-bigint 0.4.4", + "num-traits", "prometheus", "sea-orm", "serde", @@ -7383,9 +7663,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -7486,8 +7766,8 @@ checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" dependencies = [ "bae", "heck 0.3.3", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -7546,8 +7826,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", "thiserror", ] @@ -7570,8 +7850,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -7591,8 +7871,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rustversion", "syn 1.0.109", ] @@ -7684,9 +7964,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" dependencies = [ "serde", ] @@ -7699,23 +7979,32 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde-aux" -version = "4.3.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "184eba62ebddb71658697c8b08822edee89970bf318c5362189f0de27f85b498" +checksum = "a86348501c129f3ad50c2f4635a01971f76974cd8a3f335988a0f1581c082765" dependencies = [ "serde", "serde_json", ] +[[package]] +name = "serde-cw-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75d32da6b8ed758b7d850b6c3c08f1d7df51a4df3cb201296e63e34a78e99d4" +dependencies = [ + "serde", +] + [[package]] name = "serde-json-wasm" version = "0.5.1" @@ -7727,22 +8016,22 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -7751,31 +8040,41 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_repr" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3081f5ffbb02284dda55132aa26daecedd7372a42417bbbab6f14ab7d6bb9145" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -7808,8 +8107,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling 0.13.4", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -7893,9 +8192,9 @@ dependencies = [ [[package]] name = "sha256" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" dependencies = [ "async-trait", "bytes", @@ -8003,9 +8302,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" [[package]] name = "socket2" @@ -8316,8 +8615,8 @@ name = "solana-frozen-abi-macro" version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rustc_version", "syn 1.0.109", ] @@ -8419,14 +8718,14 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.11", + "getrandom 0.2.12", "itertools 0.10.5", "js-sys", "lazy_static", "libc", "libsecp256k1", "log", - "memoffset 0.6.5", + "memoffset", "num-derive 0.3.3", "num-traits", "parking_lot 0.12.1", @@ -8460,7 +8759,7 @@ dependencies = [ "enum-iterator", "itertools 0.10.5", "libc", - "libloading", + "libloading 0.7.4", "log", "num-derive 0.3.3", "num-traits", @@ -8642,8 +8941,8 @@ version = "1.14.13" source = "git+https://github.com/hyperlane-xyz/solana.git?tag=hyperlane-1.14.13-2023-07-04#62a6421cab862c77b9ac7a8d93f54f8b5b223af7" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rustversion", "syn 1.0.109", ] @@ -9053,8 +9352,8 @@ dependencies = [ "either", "heck 0.4.1", "once_cell", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "serde_json", "sqlx-core", "sqlx-rt", @@ -9080,6 +9379,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" + [[package]] name = "static_assertions" version = "1.1.0" @@ -9140,8 +9445,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" dependencies = [ "heck 0.3.3", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] @@ -9152,8 +9457,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rustversion", "syn 1.0.109", ] @@ -9165,10 +9470,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "rustversion", - "syn 2.0.41", + "syn 2.0.48", ] [[package]] @@ -9215,19 +9520,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.41" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "unicode-ident", ] @@ -9238,9 +9543,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -9255,8 +9560,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", "unicode-xid 0.2.4", ] @@ -9291,6 +9596,24 @@ dependencies = [ "serde", ] +[[package]] +name = "tame-gcs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20ec2d6525a66afebdff9e1d8ef143c9deae9a3b040c61d3cfa9ae6fda80060" +dependencies = [ + "base64 0.13.1", + "bytes", + "chrono", + "http", + "percent-encoding", + "serde", + "serde_json", + "serde_urlencoded", + "thiserror", + "url", +] + [[package]] name = "tap" version = "1.0.1" @@ -9322,7 +9645,7 @@ dependencies = [ "pin-project", "rand 0.8.5", "serde", - "static_assertions", + "static_assertions 1.1.0", "tarpc-plugins", "thiserror", "tokio", @@ -9338,22 +9661,22 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", + "proc-macro2 1.0.76", + "quote 1.0.35", "syn 1.0.109", ] [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.4.1", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -9367,10 +9690,10 @@ dependencies = [ "ed25519-consensus", "flex-error", "futures", - "k256 0.13.2", + "k256 0.13.3", "num-traits", "once_cell", - "prost", + "prost 0.11.9", "prost-types", "ripemd", "serde", @@ -9409,7 +9732,7 @@ dependencies = [ "flex-error", "num-derive 0.3.3", "num-traits", - "prost", + "prost 0.11.9", "prost-types", "serde", "serde_bytes", @@ -9426,7 +9749,7 @@ dependencies = [ "flex-error", "num-derive 0.3.3", "num-traits", - "prost", + "prost 0.11.9", "prost-types", "serde", "serde_bytes", @@ -9443,7 +9766,7 @@ dependencies = [ "bytes", "flex-error", "futures", - "getrandom 0.2.11", + "getrandom 0.2.12", "http", "hyper", "hyper-proxy", @@ -9470,9 +9793,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -9500,22 +9823,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -9536,6 +9859,8 @@ checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" dependencies = [ "deranged", "itoa", + "libc", + "num_threads", "powerfmt", "serde", "time-core", @@ -9576,6 +9901,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tiny-keccak" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d8a021c69bb74a44ccedb824a046447e2c84a01df9e5c20779750acb38e11b2" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -9602,9 +9936,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -9635,9 +9969,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -9816,6 +10150,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + [[package]] name = "tonic" version = "0.9.2" @@ -9825,7 +10170,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.5", + "base64 0.21.7", "bytes", "futures-core", "futures-util", @@ -9836,7 +10181,38 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", + "prost 0.11.9", + "rustls-native-certs 0.6.3", + "rustls-pemfile 1.0.4", + "tokio", + "tokio-rustls 0.24.1", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.21.7", + "bytes", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.12.3", + "rustls 0.21.10", "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", "tokio", @@ -9898,9 +10274,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -9933,6 +10309,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-opentelemetry" version = "0.17.4" @@ -9969,12 +10356,37 @@ dependencies = [ "serde", "serde_json", "sharded-slab", + "smallvec", "thread_local", "tracing", "tracing-core", + "tracing-log", "tracing-serde", ] +[[package]] +name = "tracing-test" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" +dependencies = [ + "lazy_static", + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08" +dependencies = [ + "lazy_static", + "quote 1.0.35", + "syn 1.0.109", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -10034,6 +10446,18 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +[[package]] +name = "uint" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "082df6964410f6aa929a61ddfafc997e4f32c62c22490e439ac351cec827f436" +dependencies = [ + "byteorder", + "crunchy", + "heapsize", + "rustc-hex", +] + [[package]] name = "uint" version = "0.9.5" @@ -10043,7 +10467,7 @@ dependencies = [ "byteorder", "crunchy", "hex 0.4.3", - "static_assertions", + "static_assertions 1.1.0", ] [[package]] @@ -10143,7 +10567,7 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "log", "once_cell", "url", @@ -10188,7 +10612,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.11", + "getrandom 0.2.12", "serde", ] @@ -10206,18 +10630,22 @@ name = "validator" version = "0.1.0" dependencies = [ "async-trait", + "axum", "config", + "derive-new", "derive_more", "ethers", "eyre", + "futures", "futures-util", "hyperlane-base", "hyperlane-core", "hyperlane-cosmos", "hyperlane-ethereum", "hyperlane-test", - "k256 0.13.2", + "k256 0.13.3", "prometheus", + "reqwest", "serde", "serde_json", "thiserror", @@ -10321,9 +10749,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -10331,24 +10759,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -10358,32 +10786,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ - "quote 1.0.33", + "quote 1.0.35", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "wasm-timer" @@ -10402,9 +10830,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", @@ -10509,20 +10937,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.52.0", ] [[package]] @@ -10543,21 +10962,6 @@ dependencies = [ "windows-targets 0.52.0", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -10588,12 +10992,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.0", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -10606,12 +11004,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -10624,12 +11016,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -10642,12 +11028,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -10660,12 +11040,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -10678,12 +11052,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -10696,12 +11064,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -10716,9 +11078,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.30" +version = "0.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" dependencies = [ "memchr", ] @@ -10781,9 +11143,9 @@ dependencies = [ [[package]] name = "xattr" -version = "1.1.3" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7dae5072fe1f8db8f8d29059189ac175196e410e40ba42d5d4684ae2f750995" +checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", "linux-raw-sys", @@ -10796,6 +11158,32 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" +[[package]] +name = "ya-gcp" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186a4237c7bddb8a13c1056f45bdfb5c548020327203a66878d74fa7cf31286e" +dependencies = [ + "cfg-if", + "futures", + "http", + "humantime-serde", + "hyper", + "hyper-rustls 0.24.2", + "paste", + "rand 0.8.5", + "rustls 0.21.10", + "rustls-native-certs 0.6.3", + "serde", + "tame-gcs", + "thiserror", + "tokio", + "tonic 0.10.2", + "tower", + "tracing", + "yup-oauth2", +] + [[package]] name = "yaml-rust" version = "0.4.5" @@ -10814,24 +11202,51 @@ dependencies = [ "time", ] +[[package]] +name = "yup-oauth2" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b61da40aeb0907a65f7fb5c1de83c5a224d6a9ebb83bf918588a2bb744d636b8" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.7", + "futures", + "http", + "hyper", + "hyper-rustls 0.24.2", + "itertools 0.12.0", + "log", + "percent-encoding", + "rustls 0.22.2", + "rustls-pemfile 1.0.4", + "seahash", + "serde", + "serde_json", + "time", + "tokio", + "tower-service", + "url", +] + [[package]] name = "zerocopy" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.31" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] @@ -10849,9 +11264,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.70", - "quote 1.0.33", - "syn 2.0.41", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", ] [[package]] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index ae5fd5e37e..51c0f0f964 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -52,7 +52,9 @@ version = "0.1.0" Inflector = "0.11.4" anyhow = "1.0" async-trait = "0.1" +async-rwlock = "1.3" auto_impl = "1.0" +axum = "0.6.1" backtrace = "0.3" base64 = "0.21.2" bigdecimal = "0.4.2" @@ -70,6 +72,7 @@ cosmrs = { version = "0.14", default-features = false, features = [ "tokio", "grpc", ] } +cosmwasm-std = "*" crunchy = "0.2" ctrlc = "3.2" curve25519-dalek = { version = "~3.2", features = ["serde"] } @@ -90,10 +93,13 @@ bech32 = "0.9.1" elliptic-curve = "0.12.3" getrandom = { version = "0.2", features = ["js"] } hex = "0.4.3" -hpl-interface = "=0.0.6-rc3" +http = "*" hyper = "0.14" hyper-tls = "0.5.0" -itertools = "0.11.0" +hyperlane-cosmwasm-interface = "=0.0.6-rc6" +injective-protobuf = "0.2.2" +injective-std = "0.1.5" +itertools = "*" jobserver = "=0.1.26" jsonrpc-core = "18.0" k256 = { version = "0.13.1", features = ["std", "ecdsa"] } @@ -112,6 +118,7 @@ paste = "1.0" pretty_env_logger = "0.5.0" primitive-types = "=0.12.1" prometheus = "0.13" +protobuf = "*" regex = "1.5" reqwest = "0.11" ripemd = "0.1.3" @@ -172,12 +179,14 @@ tracing = { version = "0.1", features = ["release_max_level_debug"] } tracing-error = "0.2" tracing-futures = "0.2" tracing-subscriber = { version = "0.3", default-features = false } +tracing-test = "0.2.2" uint = "0.9.5" ureq = { version = "2.4", default-features = false } url = "2.3" walkdir = "2" warp = "0.3" which = "4.3" +ya-gcp = { version = "0.11.1", features = ["storage"] } ## TODO: remove this cosmwasm-schema = "1.2.7" diff --git a/rust/README.md b/rust/README.md index 51bba96136..f2b26247df 100644 --- a/rust/README.md +++ b/rust/README.md @@ -51,7 +51,7 @@ cargo build --release --bin relayer ./target/release/relayer ``` -### Running local binary against cloud resources (AWS KMS, S3, Postgresql etc) +### Running local binary against cloud resources (AWS KMS, S3, Postgresql, Google Cloud Storage, etc) Building the docker image and upgrading the pod is a **slow** process. To speed up the development cycle, you can run a local binary against cloud resources. This workflow is useful for testing local changes against cloud resources. It is also useful for debugging issues in production. @@ -74,6 +74,9 @@ Configure additional env variables appropriately: HYP_DB=/tmp/fuji-validator-db CONFIG_FILES=./config/testnet_config.json HYP_TRACING_FMT=pretty +GCS_USER_SECRET=./path/to/file +# or if service account used +GCS_SERVICE_ACCOUNT_KEY=./path/to/file DATABASE_URL= # for scraper ``` @@ -94,6 +97,12 @@ cargo run --release --bin run-locally This will automatically build the agents, start a local node, build and deploy the contracts, and run a relayer and validator. By default, this test will run indefinitely, but can be stopped with `ctrl-c`. +To run the tests for a specific VM, use the `--features` flag. + +```bash +cargo test --release --package run-locally --bin run-locally --features cosmos -- cosmos::test --nocapture +``` + ### Building Agent Docker Images There exists a docker build for the agent binaries. These docker images are used for deploying the agents in a diff --git a/rust/agents/relayer/Cargo.toml b/rust/agents/relayer/Cargo.toml index 0bd5697971..8f95fc8431 100644 --- a/rust/agents/relayer/Cargo.toml +++ b/rust/agents/relayer/Cargo.toml @@ -19,6 +19,7 @@ enum_dispatch.workspace = true ethers-contract.workspace = true ethers.workspace = true eyre.workspace = true +futures.workspace = true futures-util.workspace = true itertools.workspace = true num-derive.workspace = true @@ -34,7 +35,7 @@ tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] } tracing-futures.workspace = true tracing.workspace = true -hyperlane-core = { path = "../../hyperlane-core", features = ["agent"] } +hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async"] } hyperlane-base = { path = "../../hyperlane-base" } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } diff --git a/rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs b/rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs index af71d63306..f9665019a7 100644 --- a/rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs +++ b/rust/agents/relayer/src/msg/gas_payment/policies/on_chain_fee_quoting.rs @@ -204,7 +204,7 @@ mod test { let tx_cost_estimate = TxCostEstimate { gas_limit: MIN * 100, // Large gas limit - gas_price: COST_ESTIMATE.gas_price.clone().try_into().unwrap(), + gas_price: COST_ESTIMATE.gas_price.clone(), l2_gas_limit: Some(MIN * 2), }; diff --git a/rust/agents/relayer/src/msg/metadata/base.rs b/rust/agents/relayer/src/msg/metadata/base.rs index 65dda6181f..950723e74f 100644 --- a/rust/agents/relayer/src/msg/metadata/base.rs +++ b/rust/agents/relayer/src/msg/metadata/base.rs @@ -381,7 +381,7 @@ impl BaseMetadataBuilder { continue; } - match config.build(None) { + match config.build(None).await { Ok(checkpoint_syncer) => { // found the syncer for this validator checkpoint_syncers.insert(validator.into(), checkpoint_syncer.into()); diff --git a/rust/agents/relayer/src/msg/metadata/ccip_read.rs b/rust/agents/relayer/src/msg/metadata/ccip_read.rs index ea4cef3472..1ca3fbe1b6 100644 --- a/rust/agents/relayer/src/msg/metadata/ccip_read.rs +++ b/rust/agents/relayer/src/msg/metadata/ccip_read.rs @@ -3,7 +3,7 @@ use derive_more::Deref; use derive_new::new; use ethers::{abi::AbiDecode, core::utils::hex::decode as hex_decode}; use eyre::Context; -use hyperlane_core::{HyperlaneMessage, RawHyperlaneMessage, H256}; +use hyperlane_core::{utils::bytes_to_hex, HyperlaneMessage, RawHyperlaneMessage, H256}; use hyperlane_ethereum::OffchainLookup; use regex::Regex; use reqwest::Client; @@ -54,13 +54,18 @@ impl MetadataBuilder for CcipReadIsmMetadataBuilder { }; for url in info.urls.iter() { + // Need to explicitly convert the sender H160 the hex because the `ToString` implementation + // for `H160` truncates the output. (e.g. `0xc66a…7b6f` instead of returning + // the full address) + let sender_as_bytes = &bytes_to_hex(info.sender.as_bytes()); + let data_as_bytes = &info.call_data.to_string(); let interpolated_url = url - .replace("{sender}", &info.sender.to_string()) - .replace("{data}", &info.call_data.to_string()); + .replace("{sender}", sender_as_bytes) + .replace("{data}", data_as_bytes); let res = if !url.contains("{data}") { let body = json!({ - "data": info.call_data.to_string(), - "sender": info.sender.to_string(), + "sender": sender_as_bytes, + "data": data_as_bytes }); Client::new() .post(interpolated_url) diff --git a/rust/agents/relayer/src/msg/pending_message.rs b/rust/agents/relayer/src/msg/pending_message.rs index dfd4d12ccf..1294d45922 100644 --- a/rust/agents/relayer/src/msg/pending_message.rs +++ b/rust/agents/relayer/src/msg/pending_message.rs @@ -6,7 +6,7 @@ use std::{ use async_trait::async_trait; use derive_new::new; -use eyre::{Context, Result}; +use eyre::Result; use hyperlane_base::{db::HyperlaneRocksDB, CoreMetrics}; use hyperlane_core::{HyperlaneChain, HyperlaneDomain, HyperlaneMessage, Mailbox, U256}; use prometheus::{IntCounter, IntGauge}; @@ -193,7 +193,7 @@ impl PendingOperation for PendingMessage { .await, "checking if message meets gas payment requirement" ) else { - info!(?tx_cost_estimate, "Gas payment requirement not met yet"); + warn!(?tx_cost_estimate, "Gas payment requirement not met yet"); return self.on_reprepare(); }; diff --git a/rust/agents/relayer/src/msg/pending_operation.rs b/rust/agents/relayer/src/msg/pending_operation.rs index a1219a98b0..6192cd7cd3 100644 --- a/rust/agents/relayer/src/msg/pending_operation.rs +++ b/rust/agents/relayer/src/msg/pending_operation.rs @@ -2,7 +2,6 @@ use std::{cmp::Ordering, time::Instant}; use async_trait::async_trait; use enum_dispatch::enum_dispatch; -use eyre::Report; use hyperlane_core::HyperlaneDomain; #[allow(unused_imports)] // required for enum_dispatch @@ -110,9 +109,6 @@ pub enum PendingOperationResult { Reprepare, /// Do not attempt to run the operation again, forget about it Drop, - /// Pass the error up the chain, this is non-recoverable and indicates a - /// system failure. - CriticalFailure(Report), } /// create a `op_try!` macro for the `on_retry` handler. @@ -121,30 +117,27 @@ macro_rules! make_op_try { /// 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!("Error when ", $ctx)); - return PendingOperationResult::CriticalFailure( - Err::<(), _>(e) - .context(concat!("When ", $ctx)) - .unwrap_err() - ); - } - } - }; - ($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(); - } - } - }; + (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(); + } + } + }; + } }; } diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/agents/relayer/src/msg/processor.rs index d06bc6b192..76777181c9 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/agents/relayer/src/msg/processor.rs @@ -329,6 +329,7 @@ mod test { domain_name: name.to_owned(), domain_type: test_domain.domain_type(), domain_protocol: test_domain.domain_protocol(), + domain_technical_stack: test_domain.domain_technical_stack(), } } @@ -421,7 +422,7 @@ mod test { .iter() .zip(msg_retries_to_set.iter()) .for_each(|(pm, expected_retries)| { - // Round up the actuall backoff because it was calculated with an `Instant::now()` that was a fraction of a second ago + // Round up the actual backoff because it was calculated with an `Instant::now()` that was a fraction of a second ago let expected_backoff = PendingMessage::calculate_msg_backoff(*expected_retries) .map(|b| b.as_secs_f32().round()); let actual_backoff = pm._next_attempt_after().map(|instant| { diff --git a/rust/agents/relayer/src/msg/serial_submitter.rs b/rust/agents/relayer/src/msg/serial_submitter.rs index b677006dfc..a4fbb13ce0 100644 --- a/rust/agents/relayer/src/msg/serial_submitter.rs +++ b/rust/agents/relayer/src/msg/serial_submitter.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use std::time::Duration; use derive_new::new; -use eyre::{bail, Result}; use futures_util::future::try_join_all; use prometheus::{IntCounter, IntGauge}; use tokio::spawn; @@ -81,12 +80,12 @@ pub struct SerialSubmitter { } impl SerialSubmitter { - pub fn spawn(self) -> Instrumented>> { + pub fn spawn(self) -> Instrumented> { let span = info_span!("SerialSubmitter", destination=%self.domain); spawn(async move { self.run().await }).instrument(span) } - async fn run(self) -> Result<()> { + async fn run(self) { let Self { domain, metrics, @@ -128,10 +127,13 @@ impl SerialSubmitter { )), ]; - for i in try_join_all(tasks).await? { - i? + if let Err(err) = try_join_all(tasks).await { + tracing::error!( + error=?err, + ?domain, + "SerialSubmitter task panicked for domain" + ); } - Ok(()) } } @@ -140,7 +142,7 @@ async fn receive_task( domain: HyperlaneDomain, mut rx: mpsc::UnboundedReceiver>, prepare_queue: OpQueue, -) -> Result<()> { +) { // Pull any messages sent to this submitter while let Some(op) = rx.recv().await { trace!(?op, "Received new operation"); @@ -149,7 +151,6 @@ async fn receive_task( debug_assert_eq!(*op.domain(), domain); prepare_queue.lock().await.push(Reverse(op)); } - bail!("Submitter receive channel was closed") } #[instrument(skip_all, fields(%domain))] @@ -158,7 +159,7 @@ async fn prepare_task( prepare_queue: OpQueue, tx_submit: mpsc::Sender>, metrics: SerialSubmitterMetrics, -) -> Result<()> { +) { loop { // Pick the next message to try preparing. let next = { @@ -179,7 +180,9 @@ async fn prepare_task( debug!(?op, "Operation prepared"); metrics.ops_prepared.inc(); // this send will pause this task if the submitter is not ready to accept yet - tx_submit.send(op).await?; + if let Err(err) = tx_submit.send(op).await { + tracing::error!(error=?err, "Failed to send prepared operation to submitter"); + } } PendingOperationResult::NotReady => { // none of the operations are ready yet, so wait for a little bit @@ -193,9 +196,6 @@ async fn prepare_task( PendingOperationResult::Drop => { metrics.ops_dropped.inc(); } - PendingOperationResult::CriticalFailure(e) => { - return Err(e); - } } } } @@ -207,7 +207,7 @@ async fn submit_task( prepare_queue: OpQueue, confirm_queue: OpQueue, metrics: SerialSubmitterMetrics, -) -> Result<()> { +) { while let Some(mut op) = rx_submit.recv().await { trace!(?op, "Submitting operation"); debug_assert_eq!(*op.domain(), domain); @@ -228,10 +228,8 @@ async fn submit_task( PendingOperationResult::Drop => { metrics.ops_dropped.inc(); } - PendingOperationResult::CriticalFailure(e) => return Err(e), } } - bail!("Internal submitter channel was closed"); } #[instrument(skip_all, fields(%domain))] @@ -240,7 +238,7 @@ async fn confirm_task( prepare_queue: OpQueue, confirm_queue: OpQueue, metrics: SerialSubmitterMetrics, -) -> Result<()> { +) { loop { // Pick the next message to try confirming. let next = { @@ -272,7 +270,6 @@ async fn confirm_task( PendingOperationResult::Drop => { metrics.ops_dropped.inc(); } - PendingOperationResult::CriticalFailure(e) => return Err(e), } } } diff --git a/rust/agents/relayer/src/processor.rs b/rust/agents/relayer/src/processor.rs index 122d96a5b1..56dbe3eb8d 100644 --- a/rust/agents/relayer/src/processor.rs +++ b/rust/agents/relayer/src/processor.rs @@ -5,7 +5,7 @@ use derive_new::new; use eyre::Result; use hyperlane_core::HyperlaneDomain; use tokio::task::JoinHandle; -use tracing::instrument; +use tracing::{instrument, warn}; #[async_trait] pub trait ProcessorExt: Send + Debug { @@ -23,14 +23,17 @@ pub struct Processor { } impl Processor { - pub fn spawn(self) -> JoinHandle> { + pub fn spawn(self) -> JoinHandle<()> { tokio::spawn(async move { self.main_loop().await }) } - #[instrument(ret, err, skip(self), level = "info", fields(domain=%self.ticker.domain()))] - async fn main_loop(mut self) -> Result<()> { + #[instrument(ret, skip(self), level = "info", fields(domain=%self.ticker.domain()))] + async fn main_loop(mut self) { loop { - self.ticker.tick().await?; + if let Err(err) = self.ticker.tick().await { + warn!(error=%err, "Error in processor tick"); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } } } } diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index c26c0b57a4..035ab66752 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -7,17 +7,16 @@ use std::{ use async_trait::async_trait; use derive_more::AsRef; use eyre::Result; +use futures_util::future::try_join_all; use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, - metrics::{AgentMetrics, AgentMetricsUpdater}, - run_all, + metrics::{AgentMetrics, MetricsUpdater}, settings::ChainConf, - BaseAgent, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, SequencedDataContractSync, - WatermarkContractSync, + BaseAgent, ChainMetrics, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, + SequencedDataContractSync, WatermarkContractSync, }; use hyperlane_core::{ - metrics::agent::METRICS_SCRAPE_INTERVAL, HyperlaneDomain, HyperlaneMessage, - InterchainGasPayment, MerkleTreeInsertion, U256, + HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, U256, }; use tokio::{ sync::{ @@ -26,10 +25,9 @@ use tokio::{ }, task::JoinHandle, }; -use tracing::{info, info_span, instrument::Instrumented, Instrument}; +use tracing::{info, info_span, instrument::Instrumented, warn, Instrument}; -use crate::merkle_tree::processor::{MerkleTreeProcessor, MerkleTreeProcessorMetrics}; -use crate::processor::{Processor, ProcessorExt}; +use crate::processor::Processor; use crate::{ merkle_tree::builder::MerkleTreeBuilder, msg::{ @@ -42,6 +40,10 @@ use crate::{ }, settings::{matching_list::MatchingList, RelayerSettings}, }; +use crate::{ + merkle_tree::processor::{MerkleTreeProcessor, MerkleTreeProcessorMetrics}, + processor::ProcessorExt, +}; #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] struct ContextKey { @@ -72,7 +74,10 @@ pub struct Relayer { skip_transaction_gas_limit_for: HashSet, allow_local_checkpoint_syncers: bool, core_metrics: Arc, + // TODO: decide whether to consolidate `agent_metrics` and `chain_metrics` into a single struct + // or move them in `core_metrics`, like the validator metrics agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, } impl Debug for Relayer { @@ -102,6 +107,7 @@ impl BaseAgent for Relayer { settings: Self::Settings, core_metrics: Arc, agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, ) -> Result where Self: Sized, @@ -260,13 +266,23 @@ impl BaseAgent for Relayer { allow_local_checkpoint_syncers: settings.allow_local_checkpoint_syncers, core_metrics, agent_metrics, + chain_metrics, }) } #[allow(clippy::async_yields_async)] - async fn run(self) -> Instrumented>> { + async fn run(self) { let mut tasks = vec![]; + // running http server + let server = self + .core + .settings + .server(self.core_metrics.clone()) + .expect("Failed to create server"); + let server_task = server.run(vec![]).instrument(info_span!("Relayer server")); + tasks.push(server_task); + // send channels by destination chain let mut send_channels = HashMap::with_capacity(self.destination_chains.len()); for (dest_domain, dest_conf) in &self.destination_chains { @@ -276,25 +292,16 @@ impl BaseAgent for Relayer { tasks.push(self.run_destination_submitter(dest_domain, receive_channel)); - let agent_metrics_conf = dest_conf - .agent_metrics_conf(Self::AGENT_NAME.to_string()) - .await - .unwrap(); - let agent_metrics_fetcher = dest_conf.build_provider(&self.core_metrics).await.unwrap(); - let agent_metrics = AgentMetricsUpdater::new( + let metrics_updater = MetricsUpdater::new( + dest_conf, + self.core_metrics.clone(), self.agent_metrics.clone(), - agent_metrics_conf, - agent_metrics_fetcher, - ); - - let fetcher_task = tokio::spawn(async move { - agent_metrics - .start_updating_on_interval(METRICS_SCRAPE_INTERVAL) - .await; - Ok(()) - }) - .instrument(info_span!("AgentMetrics")); - tasks.push(fetcher_task); + self.chain_metrics.clone(), + Self::AGENT_NAME.to_string(), + ) + .await + .unwrap(); + tasks.push(metrics_updater.spawn()); } for origin in &self.origin_chains { @@ -309,15 +316,17 @@ impl BaseAgent for Relayer { tasks.push(self.run_merkle_tree_processor(origin)); } - run_all(tasks) + if let Err(err) = try_join_all(tasks).await { + tracing::error!( + error=?err, + "Relayer task panicked" + ); + } } } impl Relayer { - async fn run_message_sync( - &self, - origin: &HyperlaneDomain, - ) -> Instrumented>> { + async fn run_message_sync(&self, origin: &HyperlaneDomain) -> Instrumented> { let index_settings = self.as_ref().settings.chains[origin.name()].index_settings(); let contract_sync = self.message_syncs.get(origin).unwrap().clone(); let cursor = contract_sync @@ -335,7 +344,7 @@ impl Relayer { async fn run_interchain_gas_payment_sync( &self, origin: &HyperlaneDomain, - ) -> Instrumented>> { + ) -> Instrumented> { let index_settings = self.as_ref().settings.chains[origin.name()].index_settings(); let contract_sync = self .interchain_gas_payment_syncs @@ -350,7 +359,7 @@ impl Relayer { async fn run_merkle_tree_hook_syncs( &self, origin: &HyperlaneDomain, - ) -> Instrumented>> { + ) -> Instrumented> { let index_settings = self.as_ref().settings.chains[origin.name()].index.clone(); let contract_sync = self.merkle_tree_hook_syncs.get(origin).unwrap().clone(); let cursor = contract_sync @@ -364,13 +373,13 @@ impl Relayer { &self, origin: &HyperlaneDomain, send_channels: HashMap>>, - ) -> Instrumented>> { + ) -> Instrumented> { let metrics = MessageProcessorMetrics::new( &self.core.metrics, origin, self.destination_chains.keys(), ); - let destination_ctxs = self + let destination_ctxs: HashMap<_, _> = self .destination_chains .keys() .filter(|&destination| destination != origin) @@ -385,6 +394,7 @@ impl Relayer { ) }) .collect(); + let message_processor = MessageProcessor::new( self.dbs.get(origin).unwrap().clone(), self.whitelist.clone(), @@ -396,18 +406,11 @@ impl Relayer { let span = info_span!("MessageProcessor", origin=%message_processor.domain()); let processor = Processor::new(Box::new(message_processor)); - tokio::spawn(async move { - let res = tokio::try_join!(processor.spawn())?; - info!(?res, "try_join finished for message processor"); - Ok(()) - }) - .instrument(span) + + processor.spawn().instrument(span) } - fn run_merkle_tree_processor( - &self, - origin: &HyperlaneDomain, - ) -> Instrumented>> { + fn run_merkle_tree_processor(&self, origin: &HyperlaneDomain) -> Instrumented> { let metrics = MerkleTreeProcessorMetrics::new(); let merkle_tree_processor = MerkleTreeProcessor::new( self.dbs.get(origin).unwrap().clone(), @@ -417,12 +420,7 @@ impl Relayer { let span = info_span!("MerkleTreeProcessor", origin=%merkle_tree_processor.domain()); let processor = Processor::new(Box::new(merkle_tree_processor)); - tokio::spawn(async move { - let res = tokio::try_join!(processor.spawn())?; - info!(?res, "try_join finished for merkle tree processor"); - Ok(()) - }) - .instrument(span) + processor.spawn().instrument(span) } #[allow(clippy::too_many_arguments)] @@ -431,19 +429,22 @@ impl Relayer { &self, destination: &HyperlaneDomain, receiver: UnboundedReceiver>, - ) -> Instrumented>> { + ) -> Instrumented> { let serial_submitter = SerialSubmitter::new( destination.clone(), receiver, SerialSubmitterMetrics::new(&self.core.metrics, destination), ); let span = info_span!("SerialSubmitter", destination=%destination); - let submit_fut = serial_submitter.spawn(); - + let destination = destination.clone(); tokio::spawn(async move { - let res = tokio::try_join!(submit_fut)?; - info!(?res, "try_join finished for submitter"); - Ok(()) + // Propagate task panics + serial_submitter.spawn().await.unwrap_or_else(|err| { + panic!( + "destination submitter panicked for destination {}: {:?}", + destination, err + ) + }); }) .instrument(span) } diff --git a/rust/agents/relayer/src/settings/matching_list.rs b/rust/agents/relayer/src/settings/matching_list.rs index 483b65ed9f..da037f19bc 100644 --- a/rust/agents/relayer/src/settings/matching_list.rs +++ b/rust/agents/relayer/src/settings/matching_list.rs @@ -186,8 +186,8 @@ impl<'de> Visitor<'de> for FilterVisitor { A: SeqAccess<'de>, { let mut values = Vec::new(); - while let Some(i) = seq.next_element::<&str>()? { - values.push(parse_addr(i)?) + while let Some(i) = seq.next_element::()? { + values.push(parse_addr(&i)?) } Ok(Self::Value::Enumerated(values)) } @@ -454,4 +454,19 @@ mod test { r#"[{"origindomain":1399811151,"senderaddress":"DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7","destinationdomain":11155111,"recipientaddress":"0x6AD4DEBA8A147d000C09de6465267a9047d1c217"}]"#, ).unwrap(); } + + #[test] + fn supports_sequence_h256s() { + let json_str = r#"[{"origindomain":1399811151,"senderaddress":["0x6AD4DEBA8A147d000C09de6465267a9047d1c217","0x6AD4DEBA8A147d000C09de6465267a9047d1c218"],"destinationdomain":11155111,"recipientaddress":["0x6AD4DEBA8A147d000C09de6465267a9047d1c217","0x6AD4DEBA8A147d000C09de6465267a9047d1c218"]}]"#; + + // Test parsing directly into MatchingList + serde_json::from_str::(json_str).unwrap(); + + // Test parsing into a Value and then into MatchingList, which is the path used + // by the agent config parser. + let val: serde_json::Value = serde_json::from_str(json_str).unwrap(); + let value_parser = + hyperlane_base::settings::parser::ValueParser::new(Default::default(), &val); + crate::settings::parse_matching_list(value_parser).unwrap(); + } } diff --git a/rust/agents/scraper/Cargo.toml b/rust/agents/scraper/Cargo.toml index 0daeea7e50..587a56b8dd 100644 --- a/rust/agents/scraper/Cargo.toml +++ b/rust/agents/scraper/Cargo.toml @@ -18,6 +18,7 @@ eyre.workspace = true futures.workspace = true itertools.workspace = true num-bigint.workspace = true +num-traits.workspace = true prometheus.workspace = true sea-orm = { workspace = true } serde.workspace = true diff --git a/rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs b/rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs index be7d938e6a..444447024a 100644 --- a/rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs +++ b/rust/agents/scraper/migration/src/m20230309_000001_create_table_domain.rs @@ -30,14 +30,6 @@ const DOMAINS: &[RawDomain] = &[ is_test_net: false, is_deprecated: false, }, - RawDomain { - name: "arbitrumgoerli", - token: "ETH", - domain: 421613, - chain_id: 421613, - is_test_net: true, - is_deprecated: false, - }, RawDomain { name: "avalanche", token: "AVAX", @@ -47,10 +39,10 @@ const DOMAINS: &[RawDomain] = &[ is_deprecated: false, }, RawDomain { - name: "basegoerli", + name: "base", token: "ETH", - domain: 84531, - chain_id: 84531, + domain: 8453, + chain_id: 8453, is_test_net: false, is_deprecated: false, }, @@ -94,14 +86,6 @@ const DOMAINS: &[RawDomain] = &[ is_test_net: true, is_deprecated: false, }, - RawDomain { - name: "goerli", - token: "ETH", - domain: 5, - chain_id: 5, - is_test_net: true, - is_deprecated: false, - }, RawDomain { name: "gnosis", token: "xDAI", @@ -110,6 +94,14 @@ const DOMAINS: &[RawDomain] = &[ is_test_net: false, is_deprecated: false, }, + RawDomain { + name: "mantapacific", + token: "ETH", + domain: 169, + chain_id: 169, + is_test_net: false, + is_deprecated: false, + }, RawDomain { name: "moonbasealpha", token: "DEV", @@ -142,14 +134,6 @@ const DOMAINS: &[RawDomain] = &[ is_test_net: false, is_deprecated: false, }, - RawDomain { - name: "optimismgoerli", - token: "ETH", - domain: 420, - chain_id: 420, - is_test_net: true, - is_deprecated: false, - }, RawDomain { name: "polygon", token: "MATIC", @@ -159,50 +143,50 @@ const DOMAINS: &[RawDomain] = &[ is_deprecated: false, }, RawDomain { - name: "scrollsepolia", + name: "polygonzkevm", token: "ETH", - domain: 534351, - chain_id: 534351, - is_test_net: true, + domain: 1101, + chain_id: 1101, + is_test_net: false, is_deprecated: false, }, RawDomain { - name: "sepolia", + name: "scroll", token: "ETH", - domain: 11155111, - chain_id: 11155111, - is_test_net: true, + domain: 534352, + chain_id: 534352, + is_test_net: false, is_deprecated: false, }, RawDomain { - name: "polygonzkevmtestnet", + name: "scrollsepolia", token: "ETH", - domain: 1442, - chain_id: 1442, + domain: 534351, + chain_id: 534351, is_test_net: true, is_deprecated: false, }, RawDomain { - name: "polygonzkevm", + name: "sepolia", token: "ETH", - domain: 1101, - chain_id: 1101, - is_test_net: false, + domain: 11155111, + chain_id: 11155111, + is_test_net: true, is_deprecated: false, }, RawDomain { - name: "base", - token: "ETH", - domain: 8453, - chain_id: 8453, + name: "viction", + token: "VIC", + domain: 88, + chain_id: 88, is_test_net: false, is_deprecated: false, }, RawDomain { - name: "scroll", - token: "ETH", - domain: 534352, - chain_id: 534352, + name: "inevm", + token: "INJ", + domain: 2525, + chain_id: 2525, is_test_net: false, is_deprecated: false, }, diff --git a/rust/agents/scraper/src/agent.rs b/rust/agents/scraper/src/agent.rs index 9941c6a806..6d414e90bb 100644 --- a/rust/agents/scraper/src/agent.rs +++ b/rust/agents/scraper/src/agent.rs @@ -2,9 +2,10 @@ use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; use derive_more::AsRef; +use futures::future::try_join_all; use hyperlane_base::{ - metrics::AgentMetrics, run_all, settings::IndexSettings, BaseAgent, ContractSyncMetrics, - CoreMetrics, HyperlaneAgentCore, + metrics::AgentMetrics, settings::IndexSettings, BaseAgent, ChainMetrics, ContractSyncMetrics, + CoreMetrics, HyperlaneAgentCore, MetricsUpdater, }; use hyperlane_core::HyperlaneDomain; use tokio::task::JoinHandle; @@ -19,8 +20,11 @@ pub struct Scraper { #[as_ref] core: HyperlaneAgentCore, contract_sync_metrics: Arc, - metrics: Arc, scrapers: HashMap, + settings: ScraperSettings, + core_metrics: Arc, + agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, } #[derive(Debug)] @@ -38,7 +42,8 @@ impl BaseAgent for Scraper { async fn from_settings( settings: Self::Settings, metrics: Arc, - _agent_metrics: AgentMetrics, + agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, ) -> eyre::Result where Self: Sized, @@ -76,26 +81,53 @@ impl BaseAgent for Scraper { Ok(Self { core, - metrics, contract_sync_metrics, scrapers, + settings, + core_metrics: metrics, + agent_metrics, + chain_metrics, }) } #[allow(clippy::async_yields_async)] - async fn run(self) -> Instrumented>> { + async fn run(self) { let mut tasks = Vec::with_capacity(self.scrapers.len()); - for domain in self.scrapers.keys() { + + // running http server + let server = self + .core + .settings + .server(self.core_metrics.clone()) + .expect("Failed to create server"); + let server_task = server.run(vec![]).instrument(info_span!("Relayer server")); + tasks.push(server_task); + + for (domain, scraper) in self.scrapers.iter() { tasks.push(self.scrape(*domain).await); + + let chain_conf = self.settings.chain_setup(&scraper.domain).unwrap(); + let metrics_updater = MetricsUpdater::new( + chain_conf, + self.core_metrics.clone(), + self.agent_metrics.clone(), + self.chain_metrics.clone(), + Self::AGENT_NAME.to_string(), + ) + .await + .unwrap(); + tasks.push(metrics_updater.spawn()); + } + if let Err(err) = try_join_all(tasks).await { + tracing::error!(error = ?err, "Scraper task panicked"); } - run_all(tasks) } } impl Scraper { /// Sync contract data and other blockchain with the current chain state. /// This will spawn long-running contract sync tasks - async fn scrape(&self, domain_id: u32) -> Instrumented>> { + async fn scrape(&self, domain_id: u32) -> Instrumented> { let scraper = self.scrapers.get(&domain_id).unwrap(); let db = scraper.db.clone(); let index_settings = scraper.index_settings.clone(); @@ -105,7 +137,7 @@ impl Scraper { tasks.push( self.build_message_indexer( domain.clone(), - self.metrics.clone(), + self.core_metrics.clone(), self.contract_sync_metrics.clone(), db.clone(), index_settings.clone(), @@ -115,7 +147,7 @@ impl Scraper { tasks.push( self.build_delivery_indexer( domain.clone(), - self.metrics.clone(), + self.core_metrics.clone(), self.contract_sync_metrics.clone(), db.clone(), index_settings.clone(), @@ -125,14 +157,19 @@ impl Scraper { tasks.push( self.build_interchain_gas_payment_indexer( domain, - self.metrics.clone(), + self.core_metrics.clone(), self.contract_sync_metrics.clone(), db, index_settings.clone(), ) .await, ); - run_all(tasks) + + tokio::spawn(async move { + // If any of the tasks panic, we want to propagate it, so we unwrap + try_join_all(tasks).await.unwrap(); + }) + .instrument(info_span!("Scraper Tasks")) } } @@ -146,7 +183,7 @@ macro_rules! spawn_sync_task { contract_sync_metrics: Arc, db: HyperlaneSqlDb, index_settings: IndexSettings, - ) -> Instrumented>> { + ) -> Instrumented> { let sync = self .as_ref() .settings @@ -170,6 +207,7 @@ macro_rules! spawn_sync_task { } } } + impl Scraper { async fn build_message_indexer( &self, @@ -178,7 +216,7 @@ impl Scraper { contract_sync_metrics: Arc, db: HyperlaneSqlDb, index_settings: IndexSettings, - ) -> Instrumented>> { + ) -> Instrumented> { let sync = self .as_ref() .settings diff --git a/rust/agents/scraper/src/chain_scraper/mod.rs b/rust/agents/scraper/src/chain_scraper/mod.rs index 4d91fed5a8..4240115d53 100644 --- a/rust/agents/scraper/src/chain_scraper/mod.rs +++ b/rust/agents/scraper/src/chain_scraper/mod.rs @@ -8,7 +8,7 @@ use eyre::Result; use hyperlane_base::settings::IndexSettings; use hyperlane_core::{ unwrap_or_none_result, BlockInfo, Delivery, HyperlaneDomain, HyperlaneLogStore, - HyperlaneMessage, HyperlaneProvider, HyperlaneSequenceIndexerStore, + HyperlaneMessage, HyperlaneProvider, HyperlaneSequenceAwareIndexerStoreReader, HyperlaneWatermarkedLogStore, InterchainGasPayment, LogMeta, H256, }; use itertools::Itertools; @@ -370,7 +370,7 @@ impl HyperlaneLogStore for HyperlaneSqlDb { } #[async_trait] -impl HyperlaneSequenceIndexerStore for HyperlaneSqlDb { +impl HyperlaneSequenceAwareIndexerStoreReader for HyperlaneSqlDb { /// Gets a message by its nonce. async fn retrieve_by_sequence(&self, sequence: u32) -> Result> { let message = self @@ -381,7 +381,7 @@ impl HyperlaneSequenceIndexerStore for HyperlaneSqlDb { } /// Gets the block number at which the log occurred. - async fn retrieve_log_block_number(&self, sequence: u32) -> Result> { + async fn retrieve_log_block_number_by_sequence(&self, sequence: u32) -> Result> { let tx_id = unwrap_or_none_result!( self.db .retrieve_dispatched_tx_id(self.domain().id(), &self.mailbox_address, sequence) diff --git a/rust/agents/validator/Cargo.toml b/rust/agents/validator/Cargo.toml index f562938db2..98a5fe6f83 100644 --- a/rust/agents/validator/Cargo.toml +++ b/rust/agents/validator/Cargo.toml @@ -11,10 +11,13 @@ version.workspace = true [dependencies] async-trait.workspace = true +axum.workspace = true config.workspace = true derive_more.workspace = true +derive-new.workspace = true ethers.workspace = true eyre.workspace = true +futures.workspace = true futures-util.workspace = true prometheus.workspace = true serde.workspace = true @@ -24,13 +27,14 @@ tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] } tracing-futures.workspace = true tracing.workspace = true -hyperlane-core = { path = "../../hyperlane-core", features = ["agent"] } +hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async"] } hyperlane-base = { path = "../../hyperlane-base" } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } hyperlane-cosmos = { path = "../../chains/hyperlane-cosmos" } [dev-dependencies] tokio-test.workspace = true +reqwest.workspace = true hyperlane-test = { path = "../../hyperlane-test" } k256.workspace = true diff --git a/rust/agents/validator/src/main.rs b/rust/agents/validator/src/main.rs index a5cc31a4ee..ebc24974c2 100644 --- a/rust/agents/validator/src/main.rs +++ b/rust/agents/validator/src/main.rs @@ -9,6 +9,7 @@ use hyperlane_base::agent_main; use crate::validator::Validator; +mod server; mod settings; mod submit; mod validator; diff --git a/rust/agents/validator/src/server/eigen_node.rs b/rust/agents/validator/src/server/eigen_node.rs new file mode 100644 index 0000000000..ef69e148ef --- /dev/null +++ b/rust/agents/validator/src/server/eigen_node.rs @@ -0,0 +1,275 @@ +//! A server that serves EigenLayer specific routes +//! compliant with the spec here https://eigen.nethermind.io/docs/spec/api/ +//! +//! Base URL /eigen +//! Routes +//! - /node - Node Info +//! eg. response {"node_name":"Hyperlane Validator","spec_version":"0.1.0","node_version":"0.1.0"} +//! - /node/health - Node Health +//! eg. response 200 - healthy, 206 - partially healthy, 503 - unhealthy +//! - /node/services - List of Services +//! eg. response [{"id":"hyperlane-validator-indexer","name":"indexer","description":"indexes the messages from the origin chain mailbox","status":"up"},{"id":"hyperlane-validator-submitter","name":"submitter","description":"signs messages indexed from the indexer","status":"up"}] +//! - /node/services/:service_id/health - Service Health +//! eg. response 200 - healthy, 503 - unhealthy + +use axum::{ + http::StatusCode, + response::IntoResponse, + routing::{get, Router}, + Json, +}; +use derive_new::new; +use hyperlane_base::CoreMetrics; +use hyperlane_core::HyperlaneDomain; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +enum ServiceStatus { + Up, + Down, + Initializing, +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct NodeInfo { + node_name: String, + spec_version: String, + node_version: String, +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] +struct Service { + id: String, + name: String, + description: String, + status: ServiceStatus, +} + +#[derive(new)] +pub struct EigenNodeAPI { + origin_chain: HyperlaneDomain, + core_metrics: Arc, +} + +impl EigenNodeAPI { + pub fn router(&self) -> Router { + let core_metrics_clone = self.core_metrics.clone(); + let origin_chain = self.origin_chain.clone(); + + tracing::info!("Serving the EigenNodeAPI routes..."); + + let health_route = get(move || { + Self::node_health_handler(origin_chain.clone(), core_metrics_clone.clone()) + }); + let services_route = Router::new() + .route("/", get(Self::node_services_handler)) + .route("/:service_id/health", get(Self::service_health_handler)); + + let node_route = Router::new() + .route("/health", health_route) + .nest("/services", services_route) + .route("/", get(Self::node_info_handler)); + + Router::new().nest("/node", node_route) + } + + pub async fn node_info_handler() -> impl IntoResponse { + let node_info = NodeInfo { + node_name: "Hyperlane Validator".to_string(), + spec_version: "0.1.0".to_string(), + node_version: "0.1.0".to_string(), + }; + Json(node_info) + } + + /// Method to return the NodeInfo data + /// if signed_checkpoint - observed_checkpoint <= 1 return 200 - healthy + /// else if observed_checkpoint - signed_checkpoint <= 10 return 203 - partially healthy + /// else return 503 - unhealthy + pub async fn node_health_handler( + origin_chain: HyperlaneDomain, + core_metrics: Arc, + ) -> impl IntoResponse { + let checkpoint_delta = core_metrics.get_latest_checkpoint_validator_delta(origin_chain); + + // logic to check if the node is healthy + if checkpoint_delta <= 1 { + // 200 - healthy + StatusCode::OK + } else if checkpoint_delta <= 10 { + // 206 - partially healthy + StatusCode::PARTIAL_CONTENT + } else { + // 503 - unhealthy + StatusCode::SERVICE_UNAVAILABLE + } + } + + /// Method to return a list of services + /// NB: hardcoded for now + pub async fn node_services_handler() -> impl IntoResponse { + let services = vec![ + Service { + id: "hyperlane-validator-indexer".to_string(), + name: "indexer".to_string(), + description: "indexes the messages from the origin chain mailbox".to_string(), + status: ServiceStatus::Up, + }, + Service { + id: "hyperlane-validator-submitter".to_string(), + name: "submitter".to_string(), + description: "signs messages indexed from the indexer".to_string(), + status: ServiceStatus::Up, + }, + ]; + Json(services) + } + + /// Method to return the health of a service + pub async fn service_health_handler(_service_id: String) -> impl IntoResponse { + // TODO: implement logic to check if the service is healthy + // now just return 200 + StatusCode::OK + } +} + +#[cfg(test)] +mod tests { + use std::net::SocketAddr; + + use super::*; + use axum::http::StatusCode; + use prometheus::Registry; + + const PARTIALLY_HEALTHY_OBSERVED_CHECKPOINT: i64 = 34; + const HEALTHY_OBSERVED_CHECKPOINT: i64 = 42; + + async fn setup_test_server() -> (reqwest::Client, SocketAddr, Arc) { + let core_metrics = + Arc::new(CoreMetrics::new("dummy_validator", 37582, Registry::new()).unwrap()); + // Initialize the Prometheus registry + core_metrics + .latest_checkpoint() + .with_label_values(&["validator_observed", "ethereum"]) + .set(HEALTHY_OBSERVED_CHECKPOINT); + + let node_api = EigenNodeAPI::new( + HyperlaneDomain::new_test_domain("ethereum"), + Arc::clone(&core_metrics), + ); + let app = node_api.router(); + + // Running the app in the background using a test server + let server = + axum::Server::bind(&"127.0.0.1:0".parse().unwrap()).serve(app.into_make_service()); + let addr = server.local_addr(); + tokio::spawn(server); + + // Create a client + let client = reqwest::Client::new(); + + (client, addr, core_metrics) + } + + #[tokio::test] + async fn test_eigen_node_api() { + let (client, addr, _) = setup_test_server().await; + let res = client + .get(format!("http://{}/node", addr)) + .send() + .await + .expect("Failed to send request"); + + // Check that the response status is OK + assert_eq!(res.status(), StatusCode::OK); + + // what to expect when you're expecting + let expected = NodeInfo { + node_name: "Hyperlane Validator".to_string(), + spec_version: "0.1.0".to_string(), + node_version: "0.1.0".to_string(), + }; + + // check the response body if needed + let json: NodeInfo = res.json().await.expect("Failed to parse json"); + assert_eq!(json, expected); + } + + #[tokio::test] + async fn test_eigen_node_health_api() { + let (client, addr, core_metrics) = setup_test_server().await; + let res = client + .get(format!("http://{}/node/health", addr)) + .send() + .await + .expect("Failed to send request"); + assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE); + + core_metrics + .latest_checkpoint() + .with_label_values(&["validator_processed", "ethereum"]) + .set(PARTIALLY_HEALTHY_OBSERVED_CHECKPOINT); + let res = client + .get(format!("http://{}/node/health", addr)) + .send() + .await + .expect("Failed to send request"); + assert_eq!(res.status(), StatusCode::PARTIAL_CONTENT); + + core_metrics + .latest_checkpoint() + .with_label_values(&["validator_processed", "ethereum"]) + .set(HEALTHY_OBSERVED_CHECKPOINT); + let res = client + .get(format!("http://{}/node/health", addr)) + .send() + .await + .expect("Failed to send request"); + assert_eq!(res.status(), StatusCode::OK); + } + + #[tokio::test] + async fn test_eigen_node_services_handler() { + let (client, addr, _) = setup_test_server().await; + let res = client + .get(format!("http://{}/node/services", addr)) + .send() + .await + .expect("Failed to send request"); + assert_eq!(res.status(), StatusCode::OK); + + let expected_services = vec![ + Service { + id: "hyperlane-validator-indexer".to_string(), + name: "indexer".to_string(), + description: "indexes the messages from the origin chain mailbox".to_string(), + status: ServiceStatus::Up, + }, + Service { + id: "hyperlane-validator-submitter".to_string(), + name: "submitter".to_string(), + description: "signs messages indexed from the indexer".to_string(), + status: ServiceStatus::Up, + }, + ]; + let services: Vec = res.json().await.expect("Failed to parse json"); + assert_eq!(services, expected_services); + } + + #[tokio::test] + async fn test_service_health_handler() { + let (client, addr, _) = setup_test_server().await; + let res = client + .get(format!( + "http://{}/node/services/hyperlane-validator-indexer/health", + addr + )) + .send() + .await + .expect("Failed to send request"); + + // Check that the response status is OK + assert_eq!(res.status(), StatusCode::OK); + } +} diff --git a/rust/agents/validator/src/server/mod.rs b/rust/agents/validator/src/server/mod.rs new file mode 100644 index 0000000000..a269d69e95 --- /dev/null +++ b/rust/agents/validator/src/server/mod.rs @@ -0,0 +1,5 @@ +pub mod eigen_node; +pub use eigen_node::EigenNodeAPI; + +pub mod validator_server; +pub use validator_server::ValidatorServer; diff --git a/rust/agents/validator/src/server/validator_server.rs b/rust/agents/validator/src/server/validator_server.rs new file mode 100644 index 0000000000..772559ac37 --- /dev/null +++ b/rust/agents/validator/src/server/validator_server.rs @@ -0,0 +1,20 @@ +use crate::server::eigen_node::EigenNodeAPI; +use axum::routing::Router; +use hyperlane_base::CoreMetrics; // Add missing import statement +use hyperlane_core::HyperlaneDomain; +use std::sync::Arc; + +pub struct ValidatorServer { + pub routes: Vec<(&'static str, Router)>, +} + +impl ValidatorServer { + // add routes for servering EigenLayer specific routes compliant with the spec here https://eigen.nethermind.io/docs/spec/api/ + pub fn new(origin_chain: HyperlaneDomain, metrics: Arc) -> Self { + let mut routes = vec![]; + let eigen_node_api = EigenNodeAPI::new(origin_chain, metrics); + routes.push(("/eigen", eigen_node_api.router())); + + Self { routes } + } +} diff --git a/rust/agents/validator/src/submit.rs b/rust/agents/validator/src/submit.rs index 3de0a798af..bc040ed56c 100644 --- a/rust/agents/validator/src/submit.rs +++ b/rust/agents/validator/src/submit.rs @@ -3,12 +3,11 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use std::vec; -use eyre::{bail, Result}; -use hyperlane_core::MerkleTreeHook; +use hyperlane_core::rpc_clients::call_and_retry_indefinitely; +use hyperlane_core::{ChainCommunicationError, ChainResult, MerkleTreeHook}; use prometheus::IntGauge; use tokio::time::sleep; -use tracing::{debug, info}; -use tracing::{error, instrument}; +use tracing::{debug, error, info}; use hyperlane_base::{db::HyperlaneRocksDB, CheckpointSyncer, CoreMetrics}; use hyperlane_core::{ @@ -60,29 +59,28 @@ impl ValidatorSubmitter { /// Submits signed checkpoints from index 0 until the target checkpoint (inclusive). /// Runs idly forever once the target checkpoint is reached to avoid exiting the task. - #[instrument(err, skip(self), fields(domain=%self.merkle_tree_hook.domain()))] - pub(crate) async fn backfill_checkpoint_submitter( - self, - target_checkpoint: Checkpoint, - ) -> Result<()> { + pub(crate) async fn backfill_checkpoint_submitter(self, target_checkpoint: Checkpoint) { let mut tree = IncrementalMerkle::default(); - self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &target_checkpoint) - .await?; + 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; info!( ?target_checkpoint, "Backfill checkpoint submitter successfully reached target checkpoint" ); - - // TODO: remove this once validator is tolerant of tasks exiting. - loop { - sleep(Duration::from_secs(u64::MAX)).await; - } } /// Submits signed checkpoints indefinitely, starting from the `tree`. - #[instrument(err, skip(self, tree), fields(domain=%self.merkle_tree_hook.domain()))] - pub(crate) async fn checkpoint_submitter(self, mut tree: IncrementalMerkle) -> Result<()> { + pub(crate) async fn checkpoint_submitter(self, mut tree: IncrementalMerkle) { // How often to log checkpoint info - once every minute let checkpoint_info_log_period = Duration::from_secs(60); // The instant in which we last logged checkpoint info, if at all @@ -102,10 +100,12 @@ impl ValidatorSubmitter { loop { // Lag by reorg period because this is our correctness checkpoint. - let latest_checkpoint = self - .merkle_tree_hook - .latest_checkpoint(self.reorg_period) - .await?; + let latest_checkpoint = call_and_retry_indefinitely(|| { + let merkle_tree_hook = self.merkle_tree_hook.clone(); + Box::pin(async move { merkle_tree_hook.latest_checkpoint(self.reorg_period).await }) + }) + .await; + self.metrics .latest_checkpoint_observed .set(latest_checkpoint.index as i64); @@ -133,8 +133,20 @@ impl ValidatorSubmitter { continue; } - self.submit_checkpoints_until_correctness_checkpoint(&mut tree, &latest_checkpoint) - .await?; + 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.metrics .latest_checkpoint_processed @@ -150,7 +162,7 @@ impl ValidatorSubmitter { &self, tree: &mut IncrementalMerkle, correctness_checkpoint: &Checkpoint, - ) -> Result<()> { + ) -> ChainResult<()> { // This should never be called with a tree that is ahead of the correctness checkpoint. assert!( !tree_exceeds_checkpoint(correctness_checkpoint, tree), @@ -213,7 +225,9 @@ impl ValidatorSubmitter { ?correctness_checkpoint, "Incorrect tree root, something went wrong" ); - bail!("Incorrect tree root, something went wrong"); + return Err(ChainCommunicationError::CustomError( + "Incorrect tree root, something went wrong".to_string(), + )); } if !checkpoint_queue.is_empty() { @@ -238,7 +252,7 @@ impl ValidatorSubmitter { async fn sign_and_submit_checkpoints( &self, checkpoints: Vec, - ) -> Result<()> { + ) -> ChainResult<()> { let last_checkpoint = checkpoints.as_slice()[checkpoints.len() - 1]; for queued_checkpoint in checkpoints { diff --git a/rust/agents/validator/src/validator.rs b/rust/agents/validator/src/validator.rs index 42474030ed..1f9b86effa 100644 --- a/rust/agents/validator/src/validator.rs +++ b/rust/agents/validator/src/validator.rs @@ -1,18 +1,20 @@ use std::{num::NonZeroU64, sync::Arc, time::Duration}; +use crate::server::validator_server::ValidatorServer; use async_trait::async_trait; use derive_more::AsRef; use eyre::Result; -use futures_util::future::ready; +use futures_util::future::try_join_all; use tokio::{task::JoinHandle, time::sleep}; use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument}; use hyperlane_base::{ db::{HyperlaneRocksDB, DB}, metrics::AgentMetrics, - run_all, BaseAgent, CheckpointSyncer, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, - SequencedDataContractSync, + settings::ChainConf, + BaseAgent, ChainMetrics, CheckpointSyncer, ContractSyncMetrics, CoreMetrics, + HyperlaneAgentCore, MetricsUpdater, SequencedDataContractSync, }; use hyperlane_core::{ @@ -31,6 +33,7 @@ use crate::{ #[derive(Debug, AsRef)] pub struct Validator { origin_chain: HyperlaneDomain, + origin_chain_conf: ChainConf, #[as_ref] core: HyperlaneAgentCore, db: HyperlaneRocksDB, @@ -44,6 +47,9 @@ pub struct Validator { reorg_period: u64, interval: Duration, checkpoint_syncer: Arc, + core_metrics: Arc, + agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, } #[async_trait] @@ -55,7 +61,8 @@ impl BaseAgent for Validator { async fn from_settings( settings: Self::Settings, metrics: Arc, - _agent_metrics: AgentMetrics, + agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, ) -> Result where Self: Sized, @@ -67,7 +74,7 @@ impl BaseAgent for Validator { let (signer_instance, signer) = SingletonSigner::new(settings.validator.build().await?); let core = settings.build_hyperlane_core(metrics.clone()); - let checkpoint_syncer = settings.checkpoint_syncer.build(None)?.into(); + let checkpoint_syncer = settings.checkpoint_syncer.build(None).await?.into(); let mailbox = settings .build_mailbox(&settings.origin_chain, &metrics) @@ -81,6 +88,12 @@ impl BaseAgent for Validator { .build_validator_announce(&settings.origin_chain, &metrics) .await?; + let origin_chain_conf = core + .settings + .chain_setup(&settings.origin_chain) + .unwrap() + .clone(); + let contract_sync_metrics = Arc::new(ContractSyncMetrics::new(&metrics)); let merkle_tree_hook_sync = settings @@ -95,6 +108,7 @@ impl BaseAgent for Validator { Ok(Self { origin_chain: settings.origin_chain, + origin_chain_conf, core, db: msg_db, mailbox: mailbox.into(), @@ -106,23 +120,56 @@ impl BaseAgent for Validator { reorg_period: settings.reorg_period, interval: settings.interval, checkpoint_syncer, + agent_metrics, + chain_metrics, + core_metrics: metrics, }) } #[allow(clippy::async_yields_async)] - async fn run(mut self) -> Instrumented>> { + async fn run(mut self) { let mut tasks = vec![]; + let routes = + ValidatorServer::new(self.origin_chain.clone(), self.core.metrics.clone()).routes; + + // run server + let server = self + .core + .settings + .server(self.core_metrics.clone()) + .expect("Failed to create server"); + let server_task = tokio::spawn(async move { + server.run(routes); + }) + .instrument(info_span!("Validator server")); + tasks.push(server_task); + if let Some(signer_instance) = self.signer_instance.take() { tasks.push( tokio::spawn(async move { signer_instance.run().await; - Ok(()) }) .instrument(info_span!("SingletonSigner")), ); } + let metrics_updater = MetricsUpdater::new( + &self.origin_chain_conf, + self.core_metrics.clone(), + self.agent_metrics.clone(), + self.chain_metrics.clone(), + Self::AGENT_NAME.to_string(), + ) + .await + .unwrap(); + tasks.push( + tokio::spawn(async move { + metrics_updater.spawn().await.unwrap(); + }) + .instrument(info_span!("MetricsUpdater")), + ); + // announce the validator after spawning the signer task self.announce().await.expect("Failed to announce validator"); @@ -145,28 +192,33 @@ impl BaseAgent for Validator { } _ => { // Future that immediately resolves - return tokio::spawn(ready(Ok(()))).instrument(info_span!("Validator")); + return; } } } - run_all(tasks) + // Note that this only returns an error if one of the tasks panics + if let Err(err) = try_join_all(tasks).await { + error!(?err, "One of the validator tasks returned an error"); + } } } impl Validator { - async fn run_merkle_tree_hook_sync(&self) -> Instrumented>> { + async fn run_merkle_tree_hook_sync(&self) -> Instrumented> { let index_settings = self.as_ref().settings.chains[self.origin_chain.name()].index_settings(); let contract_sync = self.merkle_tree_hook_sync.clone(); let cursor = contract_sync .forward_backward_message_sync_cursor(index_settings) .await; - tokio::spawn(async move { contract_sync.clone().sync("merkle_tree_hook", cursor).await }) - .instrument(info_span!("MerkleTreeHookSyncer")) + tokio::spawn(async move { + contract_sync.clone().sync("merkle_tree_hook", cursor).await; + }) + .instrument(info_span!("MerkleTreeHookSyncer")) } - async fn run_checkpoint_submitters(&self) -> Vec>>> { + async fn run_checkpoint_submitters(&self) -> Vec>> { let submitter = ValidatorSubmitter::new( self.interval, self.reorg_period, diff --git a/rust/chains/hyperlane-cosmos/Cargo.toml b/rust/chains/hyperlane-cosmos/Cargo.toml index 40b4ae2f65..d02327111f 100644 --- a/rust/chains/hyperlane-cosmos/Cargo.toml +++ b/rust/chains/hyperlane-cosmos/Cargo.toml @@ -14,12 +14,19 @@ async-trait = { workspace = true } base64 = { workspace = true } bech32 = { workspace = true } cosmrs = { workspace = true, features = ["cosmwasm", "tokio", "grpc", "rpc"] } +cosmwasm-std = { workspace = true } derive-new = { workspace = true } +futures = { workspace = true } hex = { workspace = true } -hpl-interface.workspace = true +http = { workspace = true } +hyperlane-cosmwasm-interface.workspace = true hyper = { workspace = true } hyper-tls = { workspace = true } +injective-protobuf = { workspace = true } +injective-std = { workspace = true } +itertools = { workspace = true } once_cell = { workspace = true } +protobuf = { workspace = true } ripemd = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } @@ -34,4 +41,4 @@ tracing = { workspace = true } tracing-futures = { workspace = true } url = { workspace = true } -hyperlane-core = { path = "../../hyperlane-core" } +hyperlane-core = { path = "../../hyperlane-core", features = ["async"]} diff --git a/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs b/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs index d18a6577cd..df41c440b3 100644 --- a/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs +++ b/rust/chains/hyperlane-cosmos/src/aggregation_ism.rs @@ -11,7 +11,7 @@ use crate::{ use async_trait::async_trait; use hyperlane_core::{ AggregationIsm, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, - HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, RawHyperlaneMessage, H256, + HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, RawHyperlaneMessage, H160, H256, }; use tracing::instrument; @@ -85,10 +85,16 @@ impl AggregationIsm for CosmosAggregationIsm { let modules: ChainResult> = response .validators .iter() - // The returned values are Bech32-decoded Cosmos addresses. - // Since they are not EOAs but rather contracts, they are 32 bytes long and - // need to be parsed directly as an `H256`. - .map(|module| H256::from_str(module).map_err(Into::into)) + .map(|module| { + // The returned values are Bech32-decoded Cosmos addresses. + // Since they are not EOAs but rather contracts, they can be 32 bytes long and + // need to be parsed directly as an `H256`. + if let Ok(res) = H256::from_str(module) { + return Ok(res); + } + // If the address is not 32 bytes long, it is a 20-byte address + H160::from_str(module).map(H256::from).map_err(Into::into) + }) .collect(); Ok((modules?, response.threshold)) diff --git a/rust/chains/hyperlane-cosmos/src/error.rs b/rust/chains/hyperlane-cosmos/src/error.rs index 92af0bce05..d266d317c4 100644 --- a/rust/chains/hyperlane-cosmos/src/error.rs +++ b/rust/chains/hyperlane-cosmos/src/error.rs @@ -1,5 +1,6 @@ use cosmrs::proto::prost; use hyperlane_core::ChainCommunicationError; +use std::fmt::Debug; /// Errors from the crates specific to the hyperlane-cosmos /// implementation. @@ -25,15 +26,27 @@ pub enum HyperlaneCosmosError { #[error("{0}")] /// Cosmrs Tendermint Error CosmrsTendermintError(#[from] cosmrs::tendermint::Error), + #[error("{0}")] + /// CosmWasm Error + CosmWasmError(#[from] cosmwasm_std::StdError), /// Tonic error #[error("{0}")] Tonic(#[from] tonic::transport::Error), + /// Tonic codegen error + #[error("{0}")] + TonicGenError(#[from] tonic::codegen::StdError), /// Tendermint RPC Error #[error(transparent)] TendermintError(#[from] tendermint_rpc::error::Error), - /// protobuf error + /// Prost error + #[error("{0}")] + Prost(#[from] prost::DecodeError), + /// Protobuf error #[error("{0}")] - Protobuf(#[from] prost::DecodeError), + Protobuf(#[from] protobuf::ProtobufError), + /// Fallback providers failed + #[error("Fallback providers failed. (Errors: {0:?})")] + FallbackProvidersFailed(Vec), } impl From for ChainCommunicationError { diff --git a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs b/rust/chains/hyperlane-cosmos/src/interchain_gas.rs index 491274c846..60e0e1bb1f 100644 --- a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs +++ b/rust/chains/hyperlane-cosmos/src/interchain_gas.rs @@ -1,13 +1,15 @@ use async_trait::async_trait; use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; -use cosmrs::tendermint::abci::EventAttribute; +use futures::future; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexer, InterchainGasPaymaster, InterchainGasPayment, - LogMeta, SequenceIndexer, H256, U256, + LogMeta, SequenceAwareIndexer, H256, U256, }; use once_cell::sync::Lazy; use std::ops::RangeInclusive; +use tendermint::abci::EventAttribute; +use tracing::{instrument, warn}; use crate::{ rpc::{CosmosWasmIndexer, ParsedEvent, WasmIndexer}, @@ -83,7 +85,7 @@ static DESTINATION_ATTRIBUTE_KEY_BASE64: Lazy = Lazy::new(|| BASE64.encode(DESTINATION_ATTRIBUTE_KEY)); /// A reference to a InterchainGasPaymasterIndexer contract on some Cosmos chain -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CosmosInterchainGasPaymasterIndexer { indexer: Box, } @@ -110,6 +112,7 @@ impl CosmosInterchainGasPaymasterIndexer { }) } + #[instrument(err)] fn interchain_gas_payment_parser( attrs: &Vec, ) -> ChainResult> { @@ -203,10 +206,38 @@ impl Indexer for CosmosInterchainGasPaymasterIndexer { &self, range: RangeInclusive, ) -> ChainResult> { - let result = self - .indexer - .get_range_event_logs(range, Self::interchain_gas_payment_parser) - .await?; + let logs_futures: Vec<_> = range + .map(|block_number| { + let self_clone = self.clone(); + tokio::spawn(async move { + let logs = self_clone + .indexer + .get_logs_in_block( + block_number, + Self::interchain_gas_payment_parser, + "InterchainGasPaymentCursor", + ) + .await; + (logs, block_number) + }) + }) + .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(|(res, block_number)| match res { + Ok(logs) => Some(logs), + Err(err) => { + warn!(?err, ?block_number, "Failed to fetch logs for block"); + None + } + }) + .flatten() + .collect(); + Ok(result) } @@ -216,8 +247,8 @@ impl Indexer for CosmosInterchainGasPaymasterIndexer { } #[async_trait] -impl SequenceIndexer for CosmosInterchainGasPaymasterIndexer { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { +impl SequenceAwareIndexer for CosmosInterchainGasPaymasterIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { // TODO: implement when cosmwasm scraper support is implemented let tip = self.get_finalized_block_number().await?; Ok((None, tip)) @@ -260,7 +291,6 @@ impl TryInto for IncompleteInterchainGasPayment { #[cfg(test)] mod tests { - use cosmrs::tendermint::abci::EventAttribute; use hyperlane_core::{InterchainGasPayment, H256, U256}; use std::str::FromStr; diff --git a/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs b/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs index bfde29f298..dd495be89a 100644 --- a/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs +++ b/rust/chains/hyperlane-cosmos/src/interchain_security_module.rs @@ -82,7 +82,7 @@ impl InterchainSecurityModule for CosmosInterchainSecurityModule { .await?; let module_type_response = - serde_json::from_slice::(&data)?; + serde_json::from_slice::(&data)?; Ok(IsmType(module_type_response.typ).into()) } diff --git a/rust/chains/hyperlane-cosmos/src/lib.rs b/rust/chains/hyperlane-cosmos/src/lib.rs index 82a4a0ece1..c0ce3ad549 100644 --- a/rust/chains/hyperlane-cosmos/src/lib.rs +++ b/rust/chains/hyperlane-cosmos/src/lib.rs @@ -16,6 +16,7 @@ mod multisig_ism; mod payloads; mod providers; mod routing_ism; +mod rpc_clients; mod signers; mod trait_builder; mod types; diff --git a/rust/chains/hyperlane-cosmos/src/libs/address.rs b/rust/chains/hyperlane-cosmos/src/libs/address.rs index 11e15ff9e3..e594cb4603 100644 --- a/rust/chains/hyperlane-cosmos/src/libs/address.rs +++ b/rust/chains/hyperlane-cosmos/src/libs/address.rs @@ -36,7 +36,7 @@ impl CosmosAddress { Ok(CosmosAddress::new(account_id, digest)) } - /// Creates a wrapper arround a cosmrs AccountId from a private key byte array + /// Creates a wrapper around a cosmrs AccountId from a private key byte array pub fn from_privkey(priv_key: &[u8], prefix: &str) -> ChainResult { let pubkey = SigningKey::from_slice(priv_key) .map_err(Into::::into)? @@ -44,13 +44,25 @@ impl CosmosAddress { Self::from_pubkey(pubkey, prefix) } - /// Creates a wrapper arround a cosmrs AccountId from a H256 digest + /// Creates a wrapper around a cosmrs AccountId from a H256 digest /// /// - digest: H256 digest (hex representation of address) /// - prefix: Bech32 prefix - pub fn from_h256(digest: H256, prefix: &str) -> ChainResult { + /// - byte_count: Number of bytes to truncate the digest to. Cosmos addresses can sometimes + /// be less than 32 bytes, so this helps to serialize it in bech32 with the appropriate + /// length. + pub fn from_h256(digest: H256, prefix: &str, byte_count: usize) -> ChainResult { // This is the hex-encoded version of the address - let bytes = digest.as_bytes(); + let untruncated_bytes = digest.as_bytes(); + + if byte_count > untruncated_bytes.len() { + return Err(Overflow.into()); + } + + let remainder_bytes_start = untruncated_bytes.len() - byte_count; + // Left-truncate the digest to the desired length + let bytes = &untruncated_bytes[remainder_bytes_start..]; + // Bech32 encode it let account_id = AccountId::new(prefix, bytes).map_err(Into::::into)?; @@ -132,11 +144,13 @@ pub mod test { addr.address(), "neutron1kknekjxg0ear00dky5ykzs8wwp2gz62z9s6aaj" ); - // TODO: watch out for this edge case. This check will fail unless - // the first 12 bytes are removed from the digest. - // let digest = addr.digest(); - // let addr2 = CosmosAddress::from_h256(digest, prefix).expect("Cosmos address creation failed"); - // assert_eq!(addr.address(), addr2.address()); + + // Create an address with the same digest & explicitly set the byte count to 20, + // which should have the same result as the above. + let digest = addr.digest(); + let addr2 = + CosmosAddress::from_h256(digest, prefix, 20).expect("Cosmos address creation failed"); + assert_eq!(addr.address(), addr2.address()); } #[test] @@ -144,10 +158,19 @@ pub mod test { let hex_key = "0x1b16866227825a5166eb44031cdcf6568b3e80b52f2806e01b89a34dc90ae616"; let key = hex_or_base58_to_h256(hex_key).unwrap(); let prefix = "dual"; - let addr = CosmosAddress::from_h256(key, prefix).expect("Cosmos address creation failed"); + let addr = + CosmosAddress::from_h256(key, prefix, 32).expect("Cosmos address creation failed"); assert_eq!( addr.address(), "dual1rvtgvc38sfd9zehtgsp3eh8k269naq949u5qdcqm3x35mjg2uctqfdn3yq" ); + + // Last 20 bytes only, which is 0x1cdcf6568b3e80b52f2806e01b89a34dc90ae616 + let addr = + CosmosAddress::from_h256(key, prefix, 20).expect("Cosmos address creation failed"); + assert_eq!( + addr.address(), + "dual1rnw0v45t86qt2tegqmsphzdrfhys4esk9ktul7" + ); } } diff --git a/rust/chains/hyperlane-cosmos/src/mailbox.rs b/rust/chains/hyperlane-cosmos/src/mailbox.rs index bb94bc7feb..fcda4e78af 100644 --- a/rust/chains/hyperlane-cosmos/src/mailbox.rs +++ b/rust/chains/hyperlane-cosmos/src/mailbox.rs @@ -1,4 +1,5 @@ use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; +use futures::future; use std::{ fmt::{Debug, Formatter}, io::Cursor, @@ -18,20 +19,21 @@ use crate::{grpc::WasmProvider, HyperlaneCosmosError}; use crate::{signers::Signer, utils::get_block_height_for_lag, ConnectionConf}; use async_trait::async_trait; use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; -use cosmrs::tendermint::abci::EventAttribute; use once_cell::sync::Lazy; +use tendermint::abci::EventAttribute; use crate::utils::{CONTRACT_ADDRESS_ATTRIBUTE_KEY, CONTRACT_ADDRESS_ATTRIBUTE_KEY_BASE64}; use hyperlane_core::{ - utils::fmt_bytes, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, + utils::bytes_to_hex, ChainResult, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexer, LogMeta, Mailbox, TxCostEstimate, TxOutcome, H256, U256, }; use hyperlane_core::{ - ChainCommunicationError, ContractLocator, Decode, RawHyperlaneMessage, SequenceIndexer, + ChainCommunicationError, ContractLocator, Decode, RawHyperlaneMessage, SequenceAwareIndexer, }; use tracing::{instrument, warn}; +#[derive(Clone)] /// A reference to a Mailbox contract on some Cosmos chain pub struct CosmosMailbox { config: ConnectionConf, @@ -64,8 +66,12 @@ impl CosmosMailbox { } /// Prefix used in the bech32 address encoding - pub fn prefix(&self) -> String { - self.config.get_prefix() + pub fn bech32_prefix(&self) -> String { + self.config.get_bech32_prefix() + } + + fn contract_address_bytes(&self) -> usize { + self.config.get_contract_address_bytes() } } @@ -151,7 +157,12 @@ impl Mailbox for CosmosMailbox { #[instrument(err, ret, skip(self))] async fn recipient_ism(&self, recipient: H256) -> ChainResult { - let address = CosmosAddress::from_h256(recipient, &self.prefix())?.address(); + let address = CosmosAddress::from_h256( + recipient, + &self.bech32_prefix(), + self.contract_address_bytes(), + )? + .address(); let payload = mailbox::RecipientIsmRequest { recipient_ism: mailbox::RecipientIsmRequestInner { @@ -194,7 +205,7 @@ impl Mailbox for CosmosMailbox { Ok(tx_response_to_outcome(response)?) } - #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%fmt_bytes(metadata)))] + #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] async fn process_estimate_costs( &self, message: &HyperlaneMessage, @@ -253,7 +264,7 @@ static MESSAGE_ATTRIBUTE_KEY_BASE64: Lazy = Lazy::new(|| BASE64.encode(MESSAGE_ATTRIBUTE_KEY)); /// Struct that retrieves event data for a Cosmos Mailbox contract -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CosmosMailboxIndexer { mailbox: CosmosMailbox, indexer: Box, @@ -285,6 +296,7 @@ impl CosmosMailboxIndexer { }) } + #[instrument(err)] fn hyperlane_message_parser( attrs: &Vec, ) -> ChainResult> { @@ -343,10 +355,37 @@ impl Indexer for CosmosMailboxIndexer { &self, range: RangeInclusive, ) -> ChainResult> { - let result = self - .indexer - .get_range_event_logs(range, Self::hyperlane_message_parser) - .await?; + let logs_futures: Vec<_> = range + .map(|block_number| { + let self_clone = self.clone(); + tokio::spawn(async move { + let logs = self_clone + .indexer + .get_logs_in_block( + block_number, + Self::hyperlane_message_parser, + "HyperlaneMessageCursor", + ) + .await; + (logs, block_number) + }) + }) + .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() + .collect(); Ok(result) } @@ -369,8 +408,8 @@ impl Indexer for CosmosMailboxIndexer { } #[async_trait] -impl SequenceIndexer for CosmosMailboxIndexer { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { +impl SequenceAwareIndexer for CosmosMailboxIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = Indexer::::get_finalized_block_number(&self).await?; // No sequence for message deliveries. @@ -379,8 +418,8 @@ impl SequenceIndexer for CosmosMailboxIndexer { } #[async_trait] -impl SequenceIndexer for CosmosMailboxIndexer { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { +impl SequenceAwareIndexer for CosmosMailboxIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = Indexer::::get_finalized_block_number(&self).await?; let sequence = self.mailbox.nonce_at_block(Some(tip.into())).await?; @@ -391,7 +430,6 @@ impl SequenceIndexer for CosmosMailboxIndexer { #[cfg(test)] mod tests { - use cosmrs::tendermint::abci::EventAttribute; use hyperlane_core::HyperlaneMessage; use crate::{rpc::ParsedEvent, utils::event_attributes_from_str}; diff --git a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs index 3de0821dff..9cab0e9f90 100644 --- a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs @@ -2,14 +2,15 @@ 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 cosmrs::tendermint::abci::EventAttribute; +use futures::future; use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, - Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, SequenceIndexer, H256, + Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, }; use once_cell::sync::Lazy; -use tracing::instrument; +use tendermint::abci::EventAttribute; +use tracing::{instrument, warn}; use crate::{ grpc::WasmProvider, @@ -25,7 +26,7 @@ use crate::{ ConnectionConf, CosmosProvider, HyperlaneCosmosError, Signer, }; -#[derive(Debug)] +#[derive(Debug, Clone)] /// A reference to a MerkleTreeHook contract on some Cosmos chain pub struct CosmosMerkleTreeHook { /// Domain @@ -184,7 +185,7 @@ const MESSAGE_ID_ATTRIBUTE_KEY: &str = "message_id"; pub(crate) static MESSAGE_ID_ATTRIBUTE_KEY_BASE64: Lazy = Lazy::new(|| BASE64.encode(MESSAGE_ID_ATTRIBUTE_KEY)); -#[derive(Debug)] +#[derive(Debug, Clone)] /// A reference to a MerkleTreeHookIndexer contract on some Cosmos chain pub struct CosmosMerkleTreeHookIndexer { /// The CosmosMerkleTreeHook @@ -217,6 +218,7 @@ impl CosmosMerkleTreeHookIndexer { }) } + #[instrument(err)] fn merkle_tree_insertion_parser( attrs: &Vec, ) -> ChainResult> { @@ -285,10 +287,37 @@ impl Indexer for CosmosMerkleTreeHookIndexer { &self, range: RangeInclusive, ) -> ChainResult> { - let result = self - .indexer - .get_range_event_logs(range, Self::merkle_tree_insertion_parser) - .await?; + let logs_futures: Vec<_> = range + .map(|block_number| { + let self_clone = self.clone(); + tokio::spawn(async move { + let logs = self_clone + .indexer + .get_logs_in_block( + block_number, + Self::merkle_tree_insertion_parser, + "MerkleTreeInsertionCursor", + ) + .await; + (logs, block_number) + }) + }) + .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() + .collect(); Ok(result) } @@ -300,8 +329,8 @@ impl Indexer for CosmosMerkleTreeHookIndexer { } #[async_trait] -impl SequenceIndexer for CosmosMerkleTreeHookIndexer { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { +impl SequenceAwareIndexer for CosmosMerkleTreeHookIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = self.get_finalized_block_number().await?; let sequence = self .merkle_tree_hook @@ -335,7 +364,6 @@ impl TryInto for IncompleteMerkleTreeInsertion { #[cfg(test)] mod tests { - use cosmrs::tendermint::abci::EventAttribute; use hyperlane_core::H256; use std::str::FromStr; diff --git a/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs b/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs index 8276675ff7..23bb35a8f8 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/aggregate_ism.rs @@ -1,11 +1,11 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct VerifyRequest { pub verify: VerifyRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct VerifyRequestInner { pub metadata: String, pub message: String, diff --git a/rust/chains/hyperlane-cosmos/src/payloads/general.rs b/rust/chains/hyperlane-cosmos/src/payloads/general.rs index 488cae2d37..bf0931220d 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/general.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/general.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct EmptyStruct {} #[derive(Serialize, Deserialize, Debug, Clone)] @@ -9,6 +9,11 @@ pub struct Log { pub events: Vec, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Events { + pub events: Vec, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Event { #[serde(rename = "type")] diff --git a/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs b/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs index 052a1cc48b..4a0563945f 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/ism_routes.rs @@ -1,12 +1,12 @@ use super::general::EmptyStruct; use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct IsmRouteRequest { pub route: IsmRouteRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct IsmRouteRequestInner { pub message: String, // hexbinary } @@ -16,22 +16,22 @@ pub struct IsmRouteRespnose { pub ism: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct QueryRoutingIsmGeneralRequest { pub routing_ism: T, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct QueryRoutingIsmRouteResponse { pub ism: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct QueryIsmGeneralRequest { pub ism: T, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct QueryIsmModuleTypeRequest { pub module_type: EmptyStruct, } @@ -39,5 +39,5 @@ pub struct QueryIsmModuleTypeRequest { #[derive(Serialize, Deserialize, Debug)] pub struct QueryIsmModuleTypeResponse { #[serde(rename = "type")] - pub typ: hpl_interface::ism::IsmType, + pub typ: hyperlane_cosmwasm_interface::ism::IsmType, } diff --git a/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs b/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs index 145ba5b16c..75eef04595 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/mailbox.rs @@ -3,52 +3,52 @@ use serde::{Deserialize, Serialize}; use super::general::EmptyStruct; // Requests -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct GeneralMailboxQuery { pub mailbox: T, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct CountRequest { pub count: EmptyStruct, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct NonceRequest { pub nonce: EmptyStruct, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct RecipientIsmRequest { pub recipient_ism: RecipientIsmRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct RecipientIsmRequestInner { pub recipient_addr: String, // hexbinary } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct DefaultIsmRequest { pub default_ism: EmptyStruct, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeliveredRequest { pub message_delivered: DeliveredRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct DeliveredRequestInner { pub id: String, // hexbinary } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ProcessMessageRequest { pub process: ProcessMessageRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct ProcessMessageRequestInner { pub metadata: String, pub message: String, diff --git a/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs b/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs index 7635f0ef72..e960628771 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/merkle_tree_hook.rs @@ -4,24 +4,24 @@ use super::general::EmptyStruct; const TREE_DEPTH: usize = 32; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct MerkleTreeGenericRequest { pub merkle_hook: T, } // --------- Requests --------- -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct MerkleTreeRequest { pub tree: EmptyStruct, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct MerkleTreeCountRequest { pub count: EmptyStruct, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct CheckPointRequest { pub check_point: EmptyStruct, } diff --git a/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs b/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs index 204e726dc7..c56588d1d6 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/multisig_ism.rs @@ -1,11 +1,11 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct VerifyInfoRequest { pub verify_info: VerifyInfoRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct VerifyInfoRequestInner { pub message: String, // hexbinary } diff --git a/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs b/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs index fdf449c7c4..cf4e5eb1f8 100644 --- a/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs +++ b/rust/chains/hyperlane-cosmos/src/payloads/validator_announce.rs @@ -2,17 +2,17 @@ use serde::{Deserialize, Serialize}; use super::general::EmptyStruct; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct GetAnnouncedValidatorsRequest { pub get_announced_validators: EmptyStruct, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct GetAnnounceStorageLocationsRequest { pub get_announce_storage_locations: GetAnnounceStorageLocationsRequestInner, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct GetAnnounceStorageLocationsRequestInner { pub validators: Vec, } diff --git a/rust/chains/hyperlane-cosmos/src/providers/grpc.rs b/rust/chains/hyperlane-cosmos/src/providers/grpc.rs index 9653020ad3..f91be32bb5 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/grpc.rs +++ b/rust/chains/hyperlane-cosmos/src/providers/grpc.rs @@ -22,16 +22,23 @@ use cosmrs::{ traits::Message, }, tx::{self, Fee, MessageExt, SignDoc, SignerInfo}, - Coin, + Any, Coin, }; +use derive_new::new; use hyperlane_core::{ - ChainCommunicationError, ChainResult, ContractLocator, FixedPointNumber, U256, + rpc_clients::{BlockNumberGetter, FallbackProvider}, + ChainCommunicationError, ChainResult, ContractLocator, FixedPointNumber, HyperlaneDomain, U256, }; +use protobuf::Message as _; use serde::Serialize; -use tonic::transport::{Channel, Endpoint}; +use tonic::{ + transport::{Channel, Endpoint}, + GrpcMethod, IntoRequest, +}; +use url::Url; -use crate::HyperlaneCosmosError; use crate::{address::CosmosAddress, CosmosAmount}; +use crate::{rpc_clients::CosmosFallbackProvider, HyperlaneCosmosError}; use crate::{signers::Signer, ConnectionConf}; /// A multiplier applied to a simulated transaction's gas usage to @@ -41,6 +48,36 @@ const GAS_ESTIMATE_MULTIPLIER: f64 = 1.25; /// be valid for. const TIMEOUT_BLOCKS: u64 = 1000; +#[derive(Debug, Clone, new)] +struct CosmosChannel { + channel: Channel, + /// The url that this channel is connected to. + /// Not explicitly used, but useful for debugging. + _url: Url, +} + +#[async_trait] +impl BlockNumberGetter for CosmosChannel { + async fn get_block_number(&self) -> Result { + let mut client = ServiceClient::new(self.channel.clone()); + let request = tonic::Request::new(GetLatestBlockRequest {}); + + let response = client + .get_latest_block(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + let height = response + .block + .ok_or_else(|| ChainCommunicationError::from_other_str("block not present"))? + .header + .ok_or_else(|| ChainCommunicationError::from_other_str("header not present"))? + .height; + + Ok(height as u64) + } +} + #[async_trait] /// Cosmwasm GRPC Provider pub trait WasmProvider: Send + Sync { @@ -52,14 +89,14 @@ pub trait WasmProvider: Send + Sync { async fn latest_block_height(&self) -> ChainResult; /// Perform a wasm query against the stored contract address. - async fn wasm_query( + async fn wasm_query( &self, payload: T, block_height: Option, ) -> ChainResult>; /// Perform a wasm query against a specified contract address. - async fn wasm_query_to( + async fn wasm_query_to( &self, to: String, payload: T, @@ -67,19 +104,24 @@ pub trait WasmProvider: Send + Sync { ) -> ChainResult>; /// Send a wasm tx. - async fn wasm_send( + async fn wasm_send( &self, payload: T, gas_limit: Option, ) -> ChainResult; /// Estimate gas for a wasm tx. - async fn wasm_estimate_gas(&self, payload: T) -> ChainResult; + async fn wasm_estimate_gas( + &self, + payload: T, + ) -> ChainResult; } #[derive(Debug, Clone)] /// CosmWasm GRPC provider. pub struct WasmGrpcProvider { + /// Hyperlane domain, used for special cases depending on the chain. + domain: HyperlaneDomain, /// Connection configuration. conf: ConnectionConf, /// A contract address that can be used as the default @@ -89,30 +131,50 @@ pub struct WasmGrpcProvider { signer: Option, /// GRPC Channel that can be cheaply cloned. /// See `` - channel: Channel, + provider: CosmosFallbackProvider, gas_price: CosmosAmount, } impl WasmGrpcProvider { /// Create new CosmWasm GRPC Provider. pub fn new( + domain: HyperlaneDomain, conf: ConnectionConf, gas_price: CosmosAmount, locator: Option, signer: Option, ) -> ChainResult { - let endpoint = - Endpoint::new(conf.get_grpc_url()).map_err(Into::::into)?; - let channel = endpoint.connect_lazy(); + // get all the configured grpc urls and convert them to a Vec + let channels: Result, _> = conf + .get_grpc_urls() + .into_iter() + .map(|url| { + Endpoint::new(url.to_string()) + .map(|e| CosmosChannel::new(e.connect_lazy(), url)) + .map_err(Into::::into) + }) + .collect(); + let mut builder = FallbackProvider::builder(); + builder = builder.add_providers(channels?); + let fallback_provider = builder.build(); + let provider = CosmosFallbackProvider::new(fallback_provider); + let contract_address = locator - .map(|l| CosmosAddress::from_h256(l.address, &conf.get_prefix())) + .map(|l| { + CosmosAddress::from_h256( + l.address, + &conf.get_bech32_prefix(), + conf.get_contract_address_bytes(), + ) + }) .transpose()?; Ok(Self { + domain, conf, contract_address, signer, - channel, + provider, gas_price, }) } @@ -129,12 +191,13 @@ impl WasmGrpcProvider { self.gas_price.amount.clone() } - /// Generates an unsigned SignDoc for a transaction. - async fn generate_unsigned_sign_doc( + /// Generates an unsigned SignDoc for a transaction and the Coin amount + /// required to pay for tx fees. + async fn generate_unsigned_sign_doc_and_fee( &self, msgs: Vec, gas_limit: u64, - ) -> ChainResult { + ) -> ChainResult<(SignDoc, Coin)> { // As this function is only used for estimating gas or sending transactions, // we can reasonably expect to have a signer. let signer = self.get_signer()?; @@ -153,15 +216,14 @@ impl WasmGrpcProvider { let amount: u128 = (FixedPointNumber::from(gas_limit) * self.gas_price()) .ceil_to_integer() .try_into()?; - let auth_info = signer_info.auth_info(Fee::from_amount_and_gas( - Coin::new( - // The fee to pay is the gas limit * the gas price - amount, - self.conf.get_canonical_asset().as_str(), - ) - .map_err(Into::::into)?, - gas_limit, - )); + let fee_coin = Coin::new( + // The fee to pay is the gas limit * the gas price + amount, + self.conf.get_canonical_asset().as_str(), + ) + .map_err(Into::::into)?; + let auth_info = + signer_info.auth_info(Fee::from_amount_and_gas(fee_coin.clone(), gas_limit)); let chain_id = self .conf @@ -169,39 +231,46 @@ impl WasmGrpcProvider { .parse() .map_err(Into::::into)?; - Ok( + Ok(( SignDoc::new(&tx_body, &auth_info, &chain_id, account_info.account_number) .map_err(Into::::into)?, - ) + fee_coin, + )) } - /// Generates a raw signed transaction including `msgs`, estimating gas if a limit is not provided. - async fn generate_raw_signed_tx( + /// Generates a raw signed transaction including `msgs`, estimating gas if a limit is not provided, + /// and the Coin amount required to pay for tx fees. + async fn generate_raw_signed_tx_and_fee( &self, msgs: Vec, gas_limit: Option, - ) -> ChainResult> { + ) -> ChainResult<(Vec, Coin)> { let gas_limit = if let Some(l) = gas_limit { l } else { self.estimate_gas(msgs.clone()).await? }; - let sign_doc = self.generate_unsigned_sign_doc(msgs, gas_limit).await?; + let (sign_doc, fee) = self + .generate_unsigned_sign_doc_and_fee(msgs, gas_limit) + .await?; let signer = self.get_signer()?; let tx_signed = sign_doc .sign(&signer.signing_key()?) .map_err(Into::::into)?; - Ok(tx_signed - .to_bytes() - .map_err(Into::::into)?) + Ok(( + tx_signed + .to_bytes() + .map_err(Into::::into)?, + fee, + )) } /// Estimates gas for a transaction containing `msgs`. async fn estimate_gas(&self, msgs: Vec) -> ChainResult { // Get a sign doc with 0 gas, because we plan to simulate - let sign_doc = self.generate_unsigned_sign_doc(msgs, 0).await?; + let (sign_doc, _) = self.generate_unsigned_sign_doc_and_fee(msgs, 0).await?; let raw_tx = TxRaw { body_bytes: sign_doc.body_bytes, @@ -211,21 +280,36 @@ impl WasmGrpcProvider { // https://github.com/cosmos/cosmjs/blob/44893af824f0712d1f406a8daa9fcae335422235/packages/stargate/src/modules/tx/queries.ts#L67 signatures: vec![vec![]], }; - - let mut client = TxServiceClient::new(self.channel.clone()); let tx_bytes = raw_tx .to_bytes() .map_err(ChainCommunicationError::from_other)?; - #[allow(deprecated)] - let sim_req = tonic::Request::new(SimulateRequest { tx: None, tx_bytes }); - let gas_used = client - .simulate(sim_req) - .await - .map_err(ChainCommunicationError::from_other)? - .into_inner() - .gas_info - .ok_or_else(|| ChainCommunicationError::from_other_str("gas info not present"))? - .gas_used; + let gas_used = self + .provider + .call(move |provider| { + let tx_bytes_clone = tx_bytes.clone(); + let future = async move { + let mut client = TxServiceClient::new(provider.channel.clone()); + #[allow(deprecated)] + let sim_req = tonic::Request::new(SimulateRequest { + tx: None, + tx_bytes: tx_bytes_clone, + }); + let gas_used = client + .simulate(sim_req) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner() + .gas_info + .ok_or_else(|| { + ChainCommunicationError::from_other_str("gas info not present") + })? + .gas_used; + + Ok(gas_used) + }; + Box::pin(future) + }) + .await?; let gas_estimate = (gas_used as f64 * GAS_ESTIMATE_MULTIPLIER) as u64; @@ -234,14 +318,25 @@ impl WasmGrpcProvider { /// Fetches balance for a given `address` and `denom` pub async fn get_balance(&self, address: String, denom: String) -> ChainResult { - let mut client = QueryBalanceClient::new(self.channel.clone()); - - let balance_request = tonic::Request::new(QueryBalanceRequest { address, denom }); - let response = client - .balance(balance_request) - .await - .map_err(ChainCommunicationError::from_other)? - .into_inner(); + let response = self + .provider + .call(move |provider| { + let address = address.clone(); + let denom = denom.clone(); + let future = async move { + let mut client = QueryBalanceClient::new(provider.channel.clone()); + let balance_request = + tonic::Request::new(QueryBalanceRequest { address, denom }); + let response = client + .balance(balance_request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + Ok(response) + }; + Box::pin(future) + }) + .await?; let balance = response .balance @@ -251,15 +346,30 @@ impl WasmGrpcProvider { } /// Queries an account. - async fn account_query(&self, account: String) -> ChainResult { - let mut client = QueryAccountClient::new(self.channel.clone()); + pub async fn account_query(&self, account: String) -> ChainResult { + // Injective is a special case where their account query requires + // the use of different protobuf types. + if self.domain.is_injective() { + return self.account_query_injective(account).await; + } - let request = tonic::Request::new(QueryAccountRequest { address: account }); - let response = client - .account(request) - .await - .map_err(ChainCommunicationError::from_other)? - .into_inner(); + let response = self + .provider + .call(move |provider| { + let address = account.clone(); + let future = async move { + let mut client = QueryAccountClient::new(provider.channel.clone()); + let request = tonic::Request::new(QueryAccountRequest { address }); + let response = client + .account(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + Ok(response) + }; + Box::pin(future) + }) + .await?; let account = BaseAccount::decode( response @@ -271,19 +381,95 @@ impl WasmGrpcProvider { .map_err(Into::::into)?; Ok(account) } + + /// Injective-specific logic for querying an account. + async fn account_query_injective(&self, account: String) -> ChainResult { + let response = self + .provider + .call(move |provider| { + let address = account.clone(); + let future = async move { + let request = tonic::Request::new( + injective_std::types::cosmos::auth::v1beta1::QueryAccountRequest { + address, + }, + ); + + // Borrowed from the logic of `QueryAccountClient` in `cosmrs`, but using injective types. + + let mut grpc_client = tonic::client::Grpc::new(provider.channel.clone()); + grpc_client + .ready() + .await + .map_err(Into::::into)?; + + let codec = tonic::codec::ProstCodec::default(); + let path = + http::uri::PathAndQuery::from_static("/cosmos.auth.v1beta1.Query/Account"); + let mut req: tonic::Request< + injective_std::types::cosmos::auth::v1beta1::QueryAccountRequest, + > = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("cosmos.auth.v1beta1.Query", "Account")); + + let response: tonic::Response< + injective_std::types::cosmos::auth::v1beta1::QueryAccountResponse, + > = grpc_client + .unary(req, path, codec) + .await + .map_err(Into::::into)?; + + Ok(response) + }; + Box::pin(future) + }) + .await?; + + let mut eth_account = injective_protobuf::proto::account::EthAccount::parse_from_bytes( + response + .into_inner() + .account + .ok_or_else(|| ChainCommunicationError::from_other_str("account not present"))? + .value + .as_slice(), + ) + .map_err(Into::::into)?; + + let base_account = eth_account.take_base_account(); + let pub_key = base_account.pub_key.into_option(); + + Ok(BaseAccount { + address: base_account.address, + pub_key: pub_key.map(|pub_key| Any { + type_url: pub_key.type_url, + value: pub_key.value, + }), + account_number: base_account.account_number, + sequence: base_account.sequence, + }) + } } #[async_trait] impl WasmProvider for WasmGrpcProvider { async fn latest_block_height(&self) -> ChainResult { - let mut client = ServiceClient::new(self.channel.clone()); - let request = tonic::Request::new(GetLatestBlockRequest {}); + let response = self + .provider + .call(move |provider| { + let future = async move { + let mut client = ServiceClient::new(provider.channel.clone()); + let request = tonic::Request::new(GetLatestBlockRequest {}); + let response = client + .get_latest_block(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + Ok(response) + }; + Box::pin(future) + }) + .await?; - let response = client - .get_latest_block(request) - .await - .map_err(ChainCommunicationError::from_other)? - .into_inner(); let height = response .block .ok_or_else(|| ChainCommunicationError::from_other_str("block not present"))? @@ -296,7 +482,7 @@ impl WasmProvider for WasmGrpcProvider { async fn wasm_query(&self, payload: T, block_height: Option) -> ChainResult> where - T: Serialize + Send + Sync, + T: Serialize + Send + Sync + Clone, { let contract_address = self.contract_address.as_ref().ok_or_else(|| { ChainCommunicationError::from_other_str("No contract address available") @@ -312,39 +498,48 @@ impl WasmProvider for WasmGrpcProvider { block_height: Option, ) -> ChainResult> where - T: Serialize + Send + Sync, + T: Serialize + Send + Sync + Clone, { - let mut client = WasmQueryClient::new(self.channel.clone()); - let mut request = tonic::Request::new(QuerySmartContractStateRequest { - address: to, - query_data: serde_json::to_string(&payload)?.as_bytes().to_vec(), - }); - - if let Some(block_height) = block_height { - request - .metadata_mut() - .insert("x-cosmos-block-height", block_height.into()); - } - - let response = client - .smart_contract_state(request) - .await - .map_err(ChainCommunicationError::from_other)? - .into_inner(); + let query_data = serde_json::to_string(&payload)?.as_bytes().to_vec(); + let response = self + .provider + .call(move |provider| { + let to = to.clone(); + let query_data = query_data.clone(); + let future = async move { + let mut client = WasmQueryClient::new(provider.channel.clone()); + + let mut request = tonic::Request::new(QuerySmartContractStateRequest { + address: to, + query_data, + }); + if let Some(block_height) = block_height { + request + .metadata_mut() + .insert("x-cosmos-block-height", block_height.into()); + } + let response = client + .smart_contract_state(request) + .await + .map_err(ChainCommunicationError::from_other)? + .into_inner(); + Ok(response) + }; + Box::pin(future) + }) + .await?; Ok(response.data) } async fn wasm_send(&self, payload: T, gas_limit: Option) -> ChainResult where - T: Serialize + Send + Sync, + T: Serialize + Send + Sync + Clone, { let signer = self.get_signer()?; - let mut client = TxServiceClient::new(self.channel.clone()); let contract_address = self.contract_address.as_ref().ok_or_else(|| { ChainCommunicationError::from_other_str("No contract address available") })?; - let msgs = vec![MsgExecuteContract { sender: signer.address.clone(), contract: contract_address.address(), @@ -353,9 +548,6 @@ impl WasmProvider for WasmGrpcProvider { } .to_any() .map_err(ChainCommunicationError::from_other)?]; - - // We often use U256s to represent gas limits, but Cosmos expects u64s. Try to convert, - // and if it fails, just fallback to None which will result in gas estimation. let gas_limit: Option = gas_limit.and_then(|limit| match limit.try_into() { Ok(limit) => Some(limit), Err(err) => { @@ -366,20 +558,44 @@ impl WasmProvider for WasmGrpcProvider { None } }); + let (tx_bytes, fee) = self.generate_raw_signed_tx_and_fee(msgs, gas_limit).await?; - let tx_req = BroadcastTxRequest { - tx_bytes: self.generate_raw_signed_tx(msgs, gas_limit).await?, - mode: BroadcastMode::Sync as i32, - }; - - let tx_res = client - .broadcast_tx(tx_req) - .await - .map_err(Into::::into)? - .into_inner() - .tx_response - .ok_or_else(|| ChainCommunicationError::from_other_str("Empty tx_response"))?; + // Check if the signer has enough funds to pay for the fee so we can get + // a more informative error. + let signer_balance = self + .get_balance(signer.address.clone(), fee.denom.to_string()) + .await?; + let fee_amount: U256 = fee.amount.into(); + if signer_balance < fee_amount { + return Err(ChainCommunicationError::InsufficientFunds { + required: fee_amount, + available: signer_balance, + }); + } + let tx_res = self + .provider + .call(move |provider| { + let tx_bytes = tx_bytes.clone(); + let future = async move { + let mut client = TxServiceClient::new(provider.channel.clone()); + // We often use U256s to represent gas limits, but Cosmos expects u64s. Try to convert, + // and if it fails, just fallback to None which will result in gas estimation. + let tx_req = BroadcastTxRequest { + tx_bytes, + mode: BroadcastMode::Sync as i32, + }; + client + .broadcast_tx(tx_req) + .await + .map_err(Into::::into)? + .into_inner() + .tx_response + .ok_or_else(|| ChainCommunicationError::from_other_str("Empty tx_response")) + }; + Box::pin(future) + }) + .await?; Ok(tx_res) } @@ -409,3 +625,10 @@ impl WasmProvider for WasmGrpcProvider { Ok(response) } } + +#[async_trait] +impl BlockNumberGetter for WasmGrpcProvider { + async fn get_block_number(&self) -> Result { + self.latest_block_height().await + } +} diff --git a/rust/chains/hyperlane-cosmos/src/providers/mod.rs b/rust/chains/hyperlane-cosmos/src/providers/mod.rs index 21216f087c..2d1e121e88 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/mod.rs +++ b/rust/chains/hyperlane-cosmos/src/providers/mod.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use hyperlane_core::{ - BlockInfo, ChainResult, ContractLocator, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, - TxnInfo, H256, U256, + BlockInfo, ChainInfo, ChainResult, ContractLocator, HyperlaneChain, HyperlaneDomain, + HyperlaneProvider, TxnInfo, H256, U256, }; use tendermint_rpc::{client::CompatMode, HttpClient}; @@ -32,7 +32,13 @@ impl CosmosProvider { signer: Option, ) -> ChainResult { let gas_price = CosmosAmount::try_from(conf.get_minimum_gas_price().clone())?; - let grpc_client = WasmGrpcProvider::new(conf.clone(), gas_price.clone(), locator, signer)?; + let grpc_client = WasmGrpcProvider::new( + domain.clone(), + conf.clone(), + gas_price.clone(), + locator, + signer, + )?; let rpc_client = HttpClient::builder( conf.get_rpc_url() .parse() @@ -93,4 +99,8 @@ impl HyperlaneProvider for CosmosProvider { .get_balance(address, self.canonical_asset.clone()) .await?) } + + async fn get_chain_metrics(&self) -> ChainResult> { + Ok(None) + } } diff --git a/rust/chains/hyperlane-cosmos/src/providers/rpc.rs b/rust/chains/hyperlane-cosmos/src/providers/rpc.rs index 1f0d2a24a1..7648e879e0 100644 --- a/rust/chains/hyperlane-cosmos/src/providers/rpc.rs +++ b/rust/chains/hyperlane-cosmos/src/providers/rpc.rs @@ -1,33 +1,35 @@ -use std::ops::RangeInclusive; - use async_trait::async_trait; use cosmrs::rpc::client::Client; -use cosmrs::rpc::endpoint::{tx, tx_search::Response as TxSearchResponse}; -use cosmrs::rpc::query::Query; -use cosmrs::rpc::Order; -use cosmrs::tendermint::abci::EventAttribute; +use hyperlane_core::rpc_clients::call_with_retry; use hyperlane_core::{ChainCommunicationError, ChainResult, ContractLocator, LogMeta, H256, U256}; -use tracing::{instrument, trace}; +use sha256::digest; +use std::fmt::Debug; +use tendermint::abci::{Event, EventAttribute}; +use tendermint::hash::Algorithm; +use tendermint::Hash; +use tendermint_rpc::endpoint::block::Response as BlockResponse; +use tendermint_rpc::endpoint::block_results::Response as BlockResultsResponse; +use tendermint_rpc::HttpClient; +use tracing::{debug, instrument, trace}; use crate::address::CosmosAddress; use crate::{ConnectionConf, CosmosProvider, HyperlaneCosmosError}; -const PAGINATION_LIMIT: u8 = 100; - #[async_trait] /// Trait for wasm indexer. Use rpc provider pub trait WasmIndexer: Send + Sync { /// Get the finalized block height. async fn get_finalized_block_number(&self) -> ChainResult; - /// Get logs for the given range using the given parser. - async fn get_range_event_logs( + /// Get logs for the given block using the given parser. + async fn get_logs_in_block( &self, - range: RangeInclusive, + block_number: u32, parser: for<'a> fn(&'a Vec) -> ChainResult>, + cursor_label: &'static str, ) -> ChainResult> where - T: Send + Sync + PartialEq + 'static; + T: Send + Sync + PartialEq + Debug + 'static; } #[derive(Debug, Eq, PartialEq)] @@ -45,9 +47,14 @@ impl ParsedEvent { event, } } + + /// Get the inner event + pub fn inner(self) -> T { + self.event + } } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Cosmwasm RPC Provider pub struct CosmosWasmIndexer { provider: CosmosProvider, @@ -76,64 +83,101 @@ impl CosmosWasmIndexer { provider, contract_address: CosmosAddress::from_h256( locator.address, - conf.get_prefix().as_str(), + conf.get_bech32_prefix().as_str(), + conf.get_contract_address_bytes(), )?, target_event_kind: format!("{}-{}", Self::WASM_TYPE, event_type), reorg_period, }) } -} -impl CosmosWasmIndexer { - #[instrument(level = "trace", err, skip(self))] - async fn tx_search(&self, query: Query, page: u32) -> ChainResult { - Ok(self - .provider - .rpc() - .tx_search(query, false, page, PAGINATION_LIMIT, Order::Ascending) + async fn get_block(client: HttpClient, block_number: u32) -> ChainResult { + Ok(client + .block(block_number) .await .map_err(Into::::into)?) } + async fn get_block_results( + client: HttpClient, + block_number: u32, + ) -> ChainResult { + Ok(client + .block_results(block_number) + .await + .map_err(Into::::into)?) + } + + async fn get_latest_block(client: HttpClient) -> ChainResult { + Ok(client + .latest_block() + .await + .map_err(Into::::into)?) + } +} + +impl CosmosWasmIndexer { // Iterate through all txs, filter out failed txs, find target events // in successful txs, and parse them. fn handle_txs( &self, - txs: Vec, + block: BlockResponse, + block_results: BlockResultsResponse, parser: for<'a> fn(&'a Vec) -> ChainResult>, - ) -> ChainResult + '_> + cursor_label: &'static str, + ) -> Vec<(T, LogMeta)> where - T: PartialEq + 'static, + T: PartialEq + Debug + 'static, { - let logs_iter = txs + let Some(tx_results) = block_results.txs_results else { + return vec![]; + }; + + let tx_hashes: Vec = block + .clone() + .block + .data .into_iter() - .filter(|tx| { - // Filter out failed txs - let tx_failed = tx.tx_result.code.is_err(); - if tx_failed { - trace!(tx_hash=?tx.hash, "Indexed tx has failed, skipping"); - } - !tx_failed + .filter_map(|tx| hex::decode(digest(tx.as_slice())).ok()) + .filter_map(|hash| { + Hash::from_bytes(Algorithm::Sha256, hash.as_slice()) + .ok() + .map(|hash| H256::from_slice(hash.as_bytes())) }) - .flat_map(move |tx| { - // Find target events in successful txs - self.handle_tx(tx, parser) - }); + .collect(); - Ok(logs_iter) + tx_results + .into_iter() + .enumerate() + .filter_map(move |(idx, tx)| { + let Some(tx_hash) = tx_hashes.get(idx) else { + debug!(?tx, "No tx hash found for tx"); + return None; + }; + if tx.code.is_err() { + debug!(?tx_hash, "Not indexing failed transaction"); + return None; + } + Some(self.handle_tx(block.clone(), tx.events, *tx_hash, idx, parser)) + }) + .flatten() + .collect() } // Iter through all events in the tx, looking for any target events // made by the contract we are indexing. fn handle_tx( &self, - tx: tx::Response, + block: BlockResponse, + tx_events: Vec, + tx_hash: H256, + transaction_index: usize, parser: for<'a> fn(&'a Vec) -> ChainResult>, ) -> impl Iterator + '_ where T: PartialEq + 'static, { - tx.tx_result.events.into_iter().enumerate().filter_map(move |(log_idx, event)| { + tx_events.into_iter().enumerate().filter_map(move |(log_idx, event)| { if event.kind.as_str() != self.target_event_kind { return None; } @@ -142,7 +186,7 @@ impl CosmosWasmIndexer { .map_err(|err| { // This can happen if we attempt to parse an event that just happens // to have the same name but a different structure. - tracing::trace!(?err, tx_hash=?tx.hash, log_idx, ?event, "Failed to parse event attributes"); + tracing::trace!(?err, tx_hash=?tx_hash, log_idx, ?event, "Failed to parse event attributes"); }) .ok() .and_then(|parsed_event| { @@ -151,18 +195,16 @@ impl CosmosWasmIndexer { // Otherwise, we might index events from other contracts that happen // to have the same target event name. if parsed_event.contract_address != self.contract_address.address() { - trace!(tx_hash=?tx.hash, log_idx, ?event, "Event contract address does not match indexer contract address"); + trace!(tx_hash=?tx_hash, log_idx, ?event, "Event contract address does not match indexer contract address"); return None; } Some((parsed_event.event, LogMeta { address: self.contract_address.digest(), - block_number: tx.height.value(), - // FIXME: block_hash is not available in tx_search. - // This isn't strictly required atm. - block_hash: H256::zero(), - transaction_id: H256::from_slice(tx.hash.as_bytes()).into(), - transaction_index: tx.index.into(), + block_number: block.block.header.height.into(), + block_hash: H256::from_slice(block.block_id.hash.as_bytes()), + transaction_id: H256::from_slice(tx_hash.as_bytes()).into(), + transaction_index: transaction_index as u64, log_index: U256::from(log_idx), })) }) @@ -172,13 +214,12 @@ impl CosmosWasmIndexer { #[async_trait] impl WasmIndexer for CosmosWasmIndexer { + #[instrument(err, skip(self))] async fn get_finalized_block_number(&self) -> ChainResult { - let latest_height: u32 = self - .provider - .rpc() - .latest_block() - .await - .map_err(Into::::into)? + let latest_block = + call_with_retry(move || Box::pin(Self::get_latest_block(self.provider.rpc().clone()))) + .await?; + let latest_height: u32 = latest_block .block .header .height @@ -189,47 +230,23 @@ impl WasmIndexer for CosmosWasmIndexer { } #[instrument(err, skip(self, parser))] - async fn get_range_event_logs( + async fn get_logs_in_block( &self, - range: RangeInclusive, + block_number: u32, parser: for<'a> fn(&'a Vec) -> ChainResult>, + cursor_label: &'static str, ) -> ChainResult> where - T: PartialEq + Send + Sync + 'static, + T: Send + Sync + PartialEq + Debug + 'static, { - // Page starts from 1 - let query = Query::default() - .and_gte("tx.height", *range.start() as u64) - .and_lte("tx.height", *range.end() as u64) - .and_eq( - format!("{}._contract_address", self.target_event_kind), - self.contract_address.address(), - ); + let client = self.provider.rpc().clone(); + debug!(?block_number, cursor_label, domain=?self.provider.domain, "Getting logs in block"); - let tx_search_result = self.tx_search(query.clone(), 1).await?; + 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)) }), + ); - // Using the first tx_search_result, we can calculate the total number of pages. - let total_count = tx_search_result.total_count; - let last_page = div_ceil(total_count, PAGINATION_LIMIT.into()); - - let mut logs = self - .handle_txs(tx_search_result.txs, parser)? - .collect::>(); - - // If there are any more pages, fetch them and append to the result. - for page in 2..=last_page { - trace!(page, "Performing tx search"); - - let tx_search_result = self.tx_search(query.clone(), page).await?; - - logs.extend(self.handle_txs(tx_search_result.txs, parser)?); - } - - Ok(logs) + Ok(self.handle_txs(block?, block_results?, parser, cursor_label)) } } - -// TODO: just use div_ceil when upgrading from 1.72.1 to 1.73.0 or above -fn div_ceil(numerator: u32, denominator: u32) -> u32 { - (numerator as f32 / denominator as f32).ceil() as u32 -} diff --git a/rust/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs b/rust/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs new file mode 100644 index 0000000000..dc979ea784 --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/rpc_clients/fallback.rs @@ -0,0 +1,134 @@ +use std::{ + fmt::{Debug, Formatter}, + ops::Deref, +}; + +use derive_new::new; +use hyperlane_core::rpc_clients::FallbackProvider; + +/// Wrapper of `FallbackProvider` for use in `hyperlane-cosmos` +#[derive(new, Clone)] +pub struct CosmosFallbackProvider { + fallback_provider: FallbackProvider, +} + +impl Deref for CosmosFallbackProvider { + type Target = FallbackProvider; + + fn deref(&self) -> &Self::Target { + &self.fallback_provider + } +} + +impl Debug for CosmosFallbackProvider +where + C: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.fallback_provider.fmt(f) + } +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use async_trait::async_trait; + use hyperlane_core::rpc_clients::test::ProviderMock; + use hyperlane_core::rpc_clients::{BlockNumberGetter, FallbackProviderBuilder}; + use hyperlane_core::ChainCommunicationError; + use tokio::time::sleep; + + use super::*; + + #[derive(Debug, Clone, Default)] + struct CosmosProviderMock(ProviderMock); + + impl Deref for CosmosProviderMock { + type Target = ProviderMock; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl CosmosProviderMock { + fn new(request_sleep: Option) -> Self { + Self(ProviderMock::new(request_sleep)) + } + } + + #[async_trait] + impl BlockNumberGetter for CosmosProviderMock { + async fn get_block_number(&self) -> Result { + Ok(0) + } + } + + impl From for Box { + fn from(val: CosmosProviderMock) -> Self { + Box::new(val) + } + } + + impl CosmosFallbackProvider { + async fn low_level_test_call(&mut self) -> Result<(), ChainCommunicationError> { + self.call(|provider| { + provider.push("GET", "http://localhost:1234"); + let future = async move { + let body = tonic::body::BoxBody::default(); + let response = http::Response::builder().status(200).body(body).unwrap(); + if let Some(sleep_duration) = provider.request_sleep() { + sleep(sleep_duration).await; + } + Ok(response) + }; + Box::pin(future) + }) + .await?; + Ok(()) + } + } + + #[tokio::test] + async fn test_first_provider_is_attempted() { + let fallback_provider_builder = FallbackProviderBuilder::default(); + let providers = vec![ + CosmosProviderMock::default(), + CosmosProviderMock::default(), + CosmosProviderMock::default(), + ]; + let fallback_provider = fallback_provider_builder.add_providers(providers).build(); + let mut cosmos_fallback_provider = CosmosFallbackProvider::new(fallback_provider); + cosmos_fallback_provider + .low_level_test_call() + .await + .unwrap(); + let provider_call_count: Vec<_> = + ProviderMock::get_call_counts(&cosmos_fallback_provider).await; + assert_eq!(provider_call_count, vec![1, 0, 0]); + } + + #[tokio::test] + async fn test_one_stalled_provider() { + let fallback_provider_builder = FallbackProviderBuilder::default(); + let providers = vec![ + CosmosProviderMock::new(Some(Duration::from_millis(10))), + CosmosProviderMock::default(), + CosmosProviderMock::default(), + ]; + let fallback_provider = fallback_provider_builder + .add_providers(providers) + .with_max_block_time(Duration::from_secs(0)) + .build(); + let mut cosmos_fallback_provider = CosmosFallbackProvider::new(fallback_provider); + cosmos_fallback_provider + .low_level_test_call() + .await + .unwrap(); + + let provider_call_count: Vec<_> = + ProviderMock::get_call_counts(&cosmos_fallback_provider).await; + assert_eq!(provider_call_count, vec![0, 0, 1]); + } +} diff --git a/rust/chains/hyperlane-cosmos/src/rpc_clients/mod.rs b/rust/chains/hyperlane-cosmos/src/rpc_clients/mod.rs new file mode 100644 index 0000000000..536845688d --- /dev/null +++ b/rust/chains/hyperlane-cosmos/src/rpc_clients/mod.rs @@ -0,0 +1,3 @@ +pub use self::fallback::*; + +mod fallback; diff --git a/rust/chains/hyperlane-cosmos/src/trait_builder.rs b/rust/chains/hyperlane-cosmos/src/trait_builder.rs index 81c16b7846..1bb3627b9d 100644 --- a/rust/chains/hyperlane-cosmos/src/trait_builder.rs +++ b/rust/chains/hyperlane-cosmos/src/trait_builder.rs @@ -2,24 +2,29 @@ use std::str::FromStr; use derive_new::new; use hyperlane_core::{ChainCommunicationError, FixedPointNumber}; +use url::Url; /// Cosmos connection configuration #[derive(Debug, Clone)] pub struct ConnectionConf { /// The GRPC url to connect to - grpc_url: String, + grpc_urls: Vec, /// The RPC url to connect to rpc_url: String, /// The chain ID chain_id: String, - /// The prefix for the account address - prefix: String, + /// The human readable address prefix for the chains using bech32. + bech32_prefix: String, /// Canoncial Assets Denom canonical_asset: String, /// The gas price set by the cosmos-sdk validator. Note that this represents the /// minimum price set by the validator. /// More details here: https://docs.cosmos.network/main/learn/beginner/gas-fees#antehandler gas_price: RawCosmosAmount, + /// The number of bytes used to represent a contract address. + /// Cosmos address lengths are sometimes less than 32 bytes, so this helps to serialize it in + /// bech32 with the appropriate length. + contract_address_bytes: usize, } /// Untyped cosmos amount @@ -72,8 +77,8 @@ pub enum ConnectionConfError { impl ConnectionConf { /// Get the GRPC url - pub fn get_grpc_url(&self) -> String { - self.grpc_url.clone() + pub fn get_grpc_urls(&self) -> Vec { + self.grpc_urls.clone() } /// Get the RPC url @@ -86,9 +91,9 @@ impl ConnectionConf { self.chain_id.clone() } - /// Get the prefix - pub fn get_prefix(&self) -> String { - self.prefix.clone() + /// Get the bech32 prefix + pub fn get_bech32_prefix(&self) -> String { + self.bech32_prefix.clone() } /// Get the asset @@ -101,22 +106,29 @@ impl ConnectionConf { self.gas_price.clone() } + /// Get the number of bytes used to represent a contract address + pub fn get_contract_address_bytes(&self) -> usize { + self.contract_address_bytes + } + /// Create a new connection configuration pub fn new( - grpc_url: String, + grpc_urls: Vec, rpc_url: String, chain_id: String, - prefix: String, + bech32_prefix: String, canonical_asset: String, minimum_gas_price: RawCosmosAmount, + contract_address_bytes: usize, ) -> Self { Self { - grpc_url, + grpc_urls, rpc_url, chain_id, - prefix, + bech32_prefix, canonical_asset, gas_price: minimum_gas_price, + contract_address_bytes, } } } diff --git a/rust/chains/hyperlane-cosmos/src/types.rs b/rust/chains/hyperlane-cosmos/src/types.rs index d7647e7ddf..aa5a954650 100644 --- a/rust/chains/hyperlane-cosmos/src/types.rs +++ b/rust/chains/hyperlane-cosmos/src/types.rs @@ -1,10 +1,10 @@ use cosmrs::proto::cosmos::base::abci::v1beta1::TxResponse; use hyperlane_core::{ChainResult, ModuleType, TxOutcome, H256, U256}; -pub struct IsmType(pub hpl_interface::ism::IsmType); +pub struct IsmType(pub hyperlane_cosmwasm_interface::ism::IsmType); -impl From for IsmType { - fn from(value: hpl_interface::ism::IsmType) -> Self { +impl From for IsmType { + fn from(value: hyperlane_cosmwasm_interface::ism::IsmType) -> Self { IsmType(value) } } @@ -12,14 +12,20 @@ impl From for IsmType { impl From for ModuleType { fn from(value: IsmType) -> Self { match value.0 { - hpl_interface::ism::IsmType::Unused => ModuleType::Unused, - hpl_interface::ism::IsmType::Routing => ModuleType::Routing, - hpl_interface::ism::IsmType::Aggregation => ModuleType::Aggregation, - hpl_interface::ism::IsmType::LegacyMultisig => ModuleType::MessageIdMultisig, - hpl_interface::ism::IsmType::MerkleRootMultisig => ModuleType::MerkleRootMultisig, - hpl_interface::ism::IsmType::MessageIdMultisig => ModuleType::MessageIdMultisig, - hpl_interface::ism::IsmType::Null => ModuleType::Null, - hpl_interface::ism::IsmType::CcipRead => ModuleType::CcipRead, + hyperlane_cosmwasm_interface::ism::IsmType::Unused => ModuleType::Unused, + hyperlane_cosmwasm_interface::ism::IsmType::Routing => ModuleType::Routing, + hyperlane_cosmwasm_interface::ism::IsmType::Aggregation => ModuleType::Aggregation, + hyperlane_cosmwasm_interface::ism::IsmType::LegacyMultisig => { + ModuleType::MessageIdMultisig + } + hyperlane_cosmwasm_interface::ism::IsmType::MerkleRootMultisig => { + ModuleType::MerkleRootMultisig + } + hyperlane_cosmwasm_interface::ism::IsmType::MessageIdMultisig => { + ModuleType::MessageIdMultisig + } + hyperlane_cosmwasm_interface::ism::IsmType::Null => ModuleType::Null, + hyperlane_cosmwasm_interface::ism::IsmType::CcipRead => ModuleType::CcipRead, } } } diff --git a/rust/chains/hyperlane-ethereum/Cargo.toml b/rust/chains/hyperlane-ethereum/Cargo.toml index 8d6db17f45..82b5ff82b0 100644 --- a/rust/chains/hyperlane-ethereum/Cargo.toml +++ b/rust/chains/hyperlane-ethereum/Cargo.toml @@ -30,7 +30,7 @@ tracing-futures.workspace = true tracing.workspace = true url.workspace = true -hyperlane-core = { path = "../../hyperlane-core" } +hyperlane-core = { path = "../../hyperlane-core", features = ["async"]} ethers-prometheus = { path = "../../ethers-prometheus", features = ["serde"] } [build-dependencies] diff --git a/rust/chains/hyperlane-ethereum/src/error.rs b/rust/chains/hyperlane-ethereum/src/error.rs new file mode 100644 index 0000000000..597703cf85 --- /dev/null +++ b/rust/chains/hyperlane-ethereum/src/error.rs @@ -0,0 +1,23 @@ +use ethers::providers::ProviderError; +use hyperlane_core::ChainCommunicationError; + +/// Errors from the crates specific to the hyperlane-ethereum +/// implementation. +/// This error can then be converted into the broader error type +/// in hyperlane-core using the `From` trait impl +#[derive(Debug, thiserror::Error)] +pub enum HyperlaneEthereumError { + /// provider Error + #[error("{0}")] + ProviderError(#[from] ProviderError), + + /// Some details from a queried block are missing + #[error("Some details from a queried block are missing")] + MissingBlockDetails, +} + +impl From for ChainCommunicationError { + fn from(value: HyperlaneEthereumError) -> Self { + ChainCommunicationError::from_other(value) + } +} diff --git a/rust/chains/hyperlane-ethereum/src/interchain_gas.rs b/rust/chains/hyperlane-ethereum/src/interchain_gas.rs index b4a93fd541..53b5d80fcd 100644 --- a/rust/chains/hyperlane-ethereum/src/interchain_gas.rs +++ b/rust/chains/hyperlane-ethereum/src/interchain_gas.rs @@ -10,7 +10,7 @@ use ethers::prelude::Middleware; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexer, InterchainGasPaymaster, - InterchainGasPayment, LogMeta, SequenceIndexer, H160, H256, + InterchainGasPayment, LogMeta, SequenceAwareIndexer, H160, H256, }; use tracing::instrument; @@ -36,7 +36,7 @@ pub struct InterchainGasPaymasterIndexerBuilder { #[async_trait] impl BuildableWithProvider for InterchainGasPaymasterIndexerBuilder { - type Output = Box>; + type Output = Box>; async fn build_with_provider( &self, @@ -127,17 +127,17 @@ where } #[async_trait] -impl SequenceIndexer for EthereumInterchainGasPaymasterIndexer +impl SequenceAwareIndexer for EthereumInterchainGasPaymasterIndexer where M: Middleware + 'static, { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { - // The InterchainGasPaymasterIndexerBuilder must return a `SequenceIndexer` type. + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { + // The InterchainGasPaymasterIndexerBuilder must return a `SequenceAwareIndexer` type. // It's fine if only a blanket implementation is provided for EVM chains, since their - // indexing only uses the `Index` trait, which is a supertrait of `SequenceIndexer`. - // TODO: if `SequenceIndexer` turns out to not depend on `Indexer` at all, then the supertrait + // indexing only uses the `Index` trait, which is a supertrait of `SequenceAwareIndexer`. + // TODO: if `SequenceAwareIndexer` turns out to not depend on `Indexer` at all, then the supertrait // dependency could be removed, even if the builder would still need to return a type that is both - // ``SequenceIndexer` and `Indexer`. + // ``SequenceAwareIndexer` and `Indexer`. let tip = self.get_finalized_block_number().await?; Ok((None, tip)) } diff --git a/rust/chains/hyperlane-ethereum/src/lib.rs b/rust/chains/hyperlane-ethereum/src/lib.rs index 2d42850bc4..90ec70c019 100644 --- a/rust/chains/hyperlane-ethereum/src/lib.rs +++ b/rust/chains/hyperlane-ethereum/src/lib.rs @@ -75,6 +75,7 @@ mod signers; mod singleton_signer; mod config; +mod error; fn extract_fn_map(abi: &'static Lazy) -> HashMap, &'static str> { abi.functions() diff --git a/rust/chains/hyperlane-ethereum/src/mailbox.rs b/rust/chains/hyperlane-ethereum/src/mailbox.rs index 02c5bb51da..f92338f6be 100644 --- a/rust/chains/hyperlane-ethereum/src/mailbox.rs +++ b/rust/chains/hyperlane-ethereum/src/mailbox.rs @@ -13,9 +13,9 @@ use ethers_contract::builders::ContractCall; use tracing::instrument; use hyperlane_core::{ - utils::fmt_bytes, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, + utils::bytes_to_hex, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProtocolError, - HyperlaneProvider, Indexer, LogMeta, Mailbox, RawHyperlaneMessage, SequenceIndexer, + HyperlaneProvider, Indexer, LogMeta, Mailbox, RawHyperlaneMessage, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H160, H256, U256, }; @@ -40,7 +40,7 @@ pub struct SequenceIndexerBuilder { #[async_trait] impl BuildableWithProvider for SequenceIndexerBuilder { - type Output = Box>; + type Output = Box>; async fn build_with_provider( &self, @@ -61,7 +61,7 @@ pub struct DeliveryIndexerBuilder { #[async_trait] impl BuildableWithProvider for DeliveryIndexerBuilder { - type Output = Box>; + type Output = Box>; async fn build_with_provider( &self, @@ -148,12 +148,12 @@ where } #[async_trait] -impl SequenceIndexer for EthereumMailboxIndexer +impl SequenceAwareIndexer for EthereumMailboxIndexer where M: Middleware + 'static, { #[instrument(err, skip(self))] - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = Indexer::::get_finalized_block_number(self).await?; let sequence = self.contract.nonce().block(u64::from(tip)).call().await?; Ok((Some(sequence), tip)) @@ -186,13 +186,13 @@ where } #[async_trait] -impl SequenceIndexer for EthereumMailboxIndexer +impl SequenceAwareIndexer for EthereumMailboxIndexer where M: Middleware + 'static, { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { // A blanket implementation for this trait is fine for the EVM. - // TODO: Consider removing `Indexer` as a supertrait of `SequenceIndexer` + // TODO: Consider removing `Indexer` as a supertrait of `SequenceAwareIndexer` let tip = Indexer::::get_finalized_block_number(self).await?; Ok((None, tip)) } @@ -327,7 +327,7 @@ where .into()) } - #[instrument(skip(self), fields(metadata=%fmt_bytes(metadata)))] + #[instrument(skip(self), fields(metadata=%bytes_to_hex(metadata)))] async fn process( &self, message: &HyperlaneMessage, @@ -341,7 +341,7 @@ where Ok(receipt.into()) } - #[instrument(skip(self), fields(msg=%message, metadata=%fmt_bytes(metadata)))] + #[instrument(skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] async fn process_estimate_costs( &self, message: &HyperlaneMessage, @@ -437,7 +437,7 @@ mod test { provider.clone(), &ContractLocator { // An Arbitrum Nitro chain - domain: &HyperlaneDomain::Known(KnownHyperlaneDomain::ArbitrumGoerli), + domain: &HyperlaneDomain::Known(KnownHyperlaneDomain::PlumeTestnet), // Address doesn't matter because we're using a MockProvider address: H256::default(), }, diff --git a/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs b/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs index 90b0f73314..4bf7cea618 100644 --- a/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-ethereum/src/merkle_tree_hook.rs @@ -11,7 +11,7 @@ use tracing::instrument; use hyperlane_core::{ ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexer, LogMeta, MerkleTreeHook, - MerkleTreeInsertion, SequenceIndexer, H256, + MerkleTreeInsertion, SequenceAwareIndexer, H256, }; use crate::contracts::merkle_tree_hook::{MerkleTreeHook as MerkleTreeHookContract, Tree}; @@ -57,7 +57,7 @@ pub struct MerkleTreeHookIndexerBuilder { #[async_trait] impl BuildableWithProvider for MerkleTreeHookIndexerBuilder { - type Output = Box>; + type Output = Box>; async fn build_with_provider( &self, @@ -144,14 +144,14 @@ where } #[async_trait] -impl SequenceIndexer for EthereumMerkleTreeHookIndexer +impl SequenceAwareIndexer for EthereumMerkleTreeHookIndexer where M: Middleware + 'static, { - // TODO: if `SequenceIndexer` turns out to not depend on `Indexer` at all, then the supertrait + // TODO: if `SequenceAwareIndexer` turns out to not depend on `Indexer` at all, then the supertrait // dependency could be removed, even if the builder would still need to return a type that is both - // `SequenceIndexer` and `Indexer`. - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + // `SequenceAwareIndexer` and `Indexer`. + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = self.get_finalized_block_number().await?; let sequence = self.contract.count().block(u64::from(tip)).call().await?; Ok((Some(sequence), tip)) diff --git a/rust/chains/hyperlane-ethereum/src/provider.rs b/rust/chains/hyperlane-ethereum/src/provider.rs index 5fced1aaf4..54b119bb27 100644 --- a/rust/chains/hyperlane-ethereum/src/provider.rs +++ b/rust/chains/hyperlane-ethereum/src/provider.rs @@ -6,8 +6,8 @@ use std::time::Duration; use async_trait::async_trait; use derive_new::new; use ethers::prelude::Middleware; -use ethers_core::abi::Address; -use hyperlane_core::{ethers_core_types, U256}; +use ethers_core::{abi::Address, types::BlockNumber}; +use hyperlane_core::{ethers_core_types, ChainInfo, HyperlaneCustomErrorWrapper, U256}; use tokio::time::sleep; use tracing::instrument; @@ -21,10 +21,7 @@ use crate::BuildableWithProvider; /// Connection to an ethereum provider. Useful for querying information about /// the blockchain. #[derive(Debug, Clone, new)] -pub struct EthereumProvider -where - M: Middleware, -{ +pub struct EthereumProvider { provider: Arc, domain: HyperlaneDomain, } @@ -119,6 +116,33 @@ where .map_err(ChainCommunicationError::from_other)?; Ok(balance.into()) } + + async fn get_chain_metrics(&self) -> ChainResult> { + let Some(block) = self + .provider + .get_block(BlockNumber::Latest) + .await + .map_err(|e| { + ChainCommunicationError::Other(HyperlaneCustomErrorWrapper::new(Box::new(e))) + })? + else { + return Ok(None); + }; + + // Given the block is queried with `BlockNumber::Latest` rather than `BlockNumber::Pending`, + // if `block` is Some at this point, we're guaranteed to have its `hash` and `number` defined, + // so it's safe to unwrap below + // more info at + let chain_metrics = ChainInfo::new( + BlockInfo { + hash: block.hash.unwrap().into(), + timestamp: block.timestamp.as_u64(), + number: block.number.unwrap().as_u64(), + }, + block.base_fee_per_gas.map(Into::into), + ); + Ok(Some(chain_metrics)) + } } impl EthereumProvider diff --git a/rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs b/rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs index af1dd28ce2..6d57a10e2e 100644 --- a/rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs +++ b/rust/chains/hyperlane-ethereum/src/rpc_clients/fallback.rs @@ -1,67 +1,34 @@ use derive_new::new; +use hyperlane_core::rpc_clients::{BlockNumberGetter, FallbackProvider}; use std::fmt::{Debug, Formatter}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio::sync::RwLock; +use std::ops::Deref; +use std::time::Duration; +use thiserror::Error; use async_trait::async_trait; use ethers::providers::{HttpClientError, JsonRpcClient, ProviderError}; -use ethers_core::types::U64; use serde::{de::DeserializeOwned, Serialize}; use serde_json::Value; -use thiserror::Error; use tokio::time::sleep; -use tracing::{info, instrument, warn_span}; +use tracing::{instrument, warn_span}; -use ethers_prometheus::json_rpc_client::PrometheusJsonRpcClientConfigExt; +use ethers_prometheus::json_rpc_client::{JsonRpcBlockGetter, PrometheusJsonRpcClientConfigExt}; use crate::rpc_clients::{categorize_client_response, CategorizedResponse}; -const MAX_BLOCK_TIME: Duration = Duration::from_secs(2 * 60); -const BLOCK_NUMBER_RPC: &str = "eth_blockNumber"; +/// Wrapper of `FallbackProvider` for use in `hyperlane-ethereum` +#[derive(new)] +pub struct EthereumFallbackProvider(FallbackProvider); -#[derive(Clone, Copy, new)] -struct PrioritizedProviderInner { - // Index into the `providers` field of `PrioritizedProviders` - index: usize, - // Tuple of the block number and the time when it was queried - #[new(value = "(0, Instant::now())")] - last_block_height: (u64, Instant), -} +impl Deref for EthereumFallbackProvider { + type Target = FallbackProvider; -impl PrioritizedProviderInner { - fn from_block_height(index: usize, block_height: u64) -> Self { - Self { - index, - last_block_height: (block_height, Instant::now()), - } + fn deref(&self) -> &Self::Target { + &self.0 } } -struct PrioritizedProviders { - /// Sorted list of providers this provider calls in order of most primary to - /// most fallback. - providers: Vec, - priorities: RwLock>, -} - -/// A provider that bundles multiple providers and attempts to call the first, -/// then the second, and so on until a response is received. -pub struct FallbackProvider { - inner: Arc>, - max_block_time: Duration, -} - -impl Clone for FallbackProvider { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - max_block_time: self.max_block_time, - } - } -} - -impl Debug for FallbackProvider +impl Debug for EthereumFallbackProvider where C: JsonRpcClient + PrometheusJsonRpcClientConfigExt, { @@ -90,134 +57,6 @@ where } } -impl FallbackProvider { - /// Convenience method for creating a `FallbackProviderBuilder` with same - /// `JsonRpcClient` types - pub fn builder() -> FallbackProviderBuilder { - FallbackProviderBuilder::default() - } - - /// Create a new fallback provider - pub fn new(providers: impl IntoIterator) -> Self { - Self::builder().add_providers(providers).build() - } -} - -impl FallbackProvider -where - C: JsonRpcClient, -{ - async fn handle_stalled_provider( - &self, - priority: &PrioritizedProviderInner, - provider: &C, - ) -> Result<(), ProviderError> { - let now = Instant::now(); - if now - .duration_since(priority.last_block_height.1) - .le(&self.max_block_time) - { - // Do nothing, it's too early to tell if the provider has stalled - return Ok(()); - } - - let current_block_height: u64 = provider - .request(BLOCK_NUMBER_RPC, ()) - .await - .map(|r: U64| r.as_u64()) - .unwrap_or(priority.last_block_height.0); - if current_block_height <= priority.last_block_height.0 { - // The `max_block_time` elapsed but the block number returned by the provider has not increased - self.deprioritize_provider(*priority).await; - info!( - provider_index=%priority.index, - ?provider, - "Deprioritizing an inner provider in FallbackProvider", - ); - } else { - self.update_last_seen_block(priority.index, current_block_height) - .await; - } - Ok(()) - } - - async fn deprioritize_provider(&self, priority: PrioritizedProviderInner) { - // De-prioritize the current provider by moving it to the end of the queue - let mut priorities = self.inner.priorities.write().await; - priorities.retain(|&p| p.index != priority.index); - priorities.push(priority); - } - - async fn update_last_seen_block(&self, provider_index: usize, current_block_height: u64) { - let mut priorities = self.inner.priorities.write().await; - // Get provider position in the up-to-date priorities vec - if let Some(position) = priorities.iter().position(|p| p.index == provider_index) { - priorities[position] = - PrioritizedProviderInner::from_block_height(provider_index, current_block_height); - } - } - - async fn take_priorities_snapshot(&self) -> Vec { - let read_lock = self.inner.priorities.read().await; - (*read_lock).clone() - } -} - -/// Builder to create a new fallback provider. -#[derive(Debug, Clone)] -pub struct FallbackProviderBuilder { - providers: Vec, - max_block_time: Duration, -} - -impl Default for FallbackProviderBuilder { - fn default() -> Self { - Self { - providers: Vec::new(), - max_block_time: MAX_BLOCK_TIME, - } - } -} - -impl FallbackProviderBuilder { - /// Add a new provider to the set. Each new provider will be a lower - /// priority than the previous. - pub fn add_provider(mut self, provider: T) -> Self { - self.providers.push(provider); - self - } - - /// Add many providers sorted by highest priority to lowest. - pub fn add_providers(mut self, providers: impl IntoIterator) -> Self { - self.providers.extend(providers); - self - } - - #[cfg(test)] - pub fn with_max_block_time(mut self, max_block_time: Duration) -> Self { - self.max_block_time = max_block_time; - self - } - - /// Create a fallback provider. - pub fn build(self) -> FallbackProvider { - let provider_count = self.providers.len(); - let prioritized_providers = PrioritizedProviders { - providers: self.providers, - // The order of `self.providers` gives the initial priority. - priorities: RwLock::new( - (0..provider_count) - .map(PrioritizedProviderInner::new) - .collect(), - ), - }; - FallbackProvider { - inner: Arc::new(prioritized_providers), - max_block_time: self.max_block_time, - } - } -} - /// Errors specific to fallback provider. #[derive(Error, Debug)] pub enum FallbackError { @@ -234,12 +73,17 @@ impl From for ProviderError { #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl JsonRpcClient for FallbackProvider +impl JsonRpcClient for EthereumFallbackProvider> where - C: JsonRpcClient + PrometheusJsonRpcClientConfigExt, + C: JsonRpcClient + + Into> + + PrometheusJsonRpcClientConfigExt + + Clone, + JsonRpcBlockGetter: BlockNumberGetter, { type Error = ProviderError; + // TODO: Refactor to use `FallbackProvider::call` #[instrument] async fn request(&self, method: &str, params: T) -> Result where @@ -263,9 +107,9 @@ where _ => provider.request(method, ¶ms), }; let resp = fut.await; - self.handle_stalled_provider(priority, provider).await?; + self.handle_stalled_provider(priority, provider).await; let _span = - warn_span!("request_with_fallback", fallback_count=%idx, provider_index=%priority.index, ?provider).entered(); + warn_span!("request", fallback_count=%idx, provider_index=%priority.index, ?provider).entered(); match categorize_client_response(method, resp) { IsOk(v) => return Ok(serde_json::from_value(v)?), @@ -281,33 +125,32 @@ where #[cfg(test)] mod tests { + use ethers_prometheus::json_rpc_client::{JsonRpcBlockGetter, BLOCK_NUMBER_RPC}; + use hyperlane_core::rpc_clients::test::ProviderMock; + use hyperlane_core::rpc_clients::FallbackProviderBuilder; + use super::*; - use std::sync::Mutex; - #[derive(Debug)] - struct ProviderMock { - // Store requests as tuples of (method, params) - // Even if the tests were single-threaded, need the arc-mutex - // for interior mutability in `JsonRpcClient::request` - requests: Arc>>, - } + #[derive(Debug, Clone, Default)] + struct EthereumProviderMock(ProviderMock); - impl ProviderMock { - fn new() -> Self { - Self { - requests: Arc::new(Mutex::new(vec![])), - } + impl Deref for EthereumProviderMock { + type Target = ProviderMock; + + fn deref(&self) -> &Self::Target { + &self.0 } + } - fn push(&self, method: &str, params: T) { - self.requests - .lock() - .unwrap() - .push((method.to_owned(), format!("{:?}", params))); + impl EthereumProviderMock { + fn new(request_sleep: Option) -> Self { + Self(ProviderMock::new(request_sleep)) } + } - fn requests(&self) -> Vec<(String, String)> { - self.requests.lock().unwrap().clone() + impl From for JsonRpcBlockGetter { + fn from(val: EthereumProviderMock) -> Self { + JsonRpcBlockGetter::new(val) } } @@ -319,7 +162,7 @@ mod tests { } #[async_trait] - impl JsonRpcClient for ProviderMock { + impl JsonRpcClient for EthereumProviderMock { type Error = HttpClientError; /// Pushes the `(method, params)` to the back of the `requests` queue, @@ -330,12 +173,14 @@ mod tests { params: T, ) -> Result { self.push(method, params); - sleep(Duration::from_millis(10)).await; + if let Some(sleep_duration) = self.request_sleep() { + sleep(sleep_duration).await; + } dummy_return_value() } } - impl PrometheusJsonRpcClientConfigExt for ProviderMock { + impl PrometheusJsonRpcClientConfigExt for EthereumProviderMock { fn node_host(&self) -> &str { todo!() } @@ -345,34 +190,32 @@ mod tests { } } - async fn get_call_counts(fallback_provider: &FallbackProvider) -> Vec { - fallback_provider - .inner - .priorities - .read() - .await - .iter() - .map(|p| { - let provider = &fallback_provider.inner.providers[p.index]; - provider.requests().len() - }) - .collect() + impl EthereumFallbackProvider> + where + C: JsonRpcClient + + PrometheusJsonRpcClientConfigExt + + Into> + + Clone, + JsonRpcBlockGetter: BlockNumberGetter, + { + async fn low_level_test_call(&self) { + self.request::<_, u64>(BLOCK_NUMBER_RPC, ()).await.unwrap(); + } } #[tokio::test] async fn test_first_provider_is_attempted() { let fallback_provider_builder = FallbackProviderBuilder::default(); let providers = vec![ - ProviderMock::new(), - ProviderMock::new(), - ProviderMock::new(), + EthereumProviderMock::default(), + EthereumProviderMock::default(), + EthereumProviderMock::default(), ]; let fallback_provider = fallback_provider_builder.add_providers(providers).build(); - fallback_provider - .request::<_, u64>(BLOCK_NUMBER_RPC, ()) - .await - .unwrap(); - let provider_call_count: Vec<_> = get_call_counts(&fallback_provider).await; + let ethereum_fallback_provider = EthereumFallbackProvider::new(fallback_provider); + ethereum_fallback_provider.low_level_test_call().await; + let provider_call_count: Vec<_> = + ProviderMock::get_call_counts(ðereum_fallback_provider).await; assert_eq!(provider_call_count, vec![1, 0, 0]); } @@ -380,20 +223,18 @@ mod tests { async fn test_one_stalled_provider() { let fallback_provider_builder = FallbackProviderBuilder::default(); let providers = vec![ - ProviderMock::new(), - ProviderMock::new(), - ProviderMock::new(), + EthereumProviderMock::new(Some(Duration::from_millis(10))), + EthereumProviderMock::default(), + EthereumProviderMock::default(), ]; let fallback_provider = fallback_provider_builder .add_providers(providers) .with_max_block_time(Duration::from_secs(0)) .build(); - fallback_provider - .request::<_, u64>(BLOCK_NUMBER_RPC, ()) - .await - .unwrap(); - - let provider_call_count: Vec<_> = get_call_counts(&fallback_provider).await; + let ethereum_fallback_provider = EthereumFallbackProvider::new(fallback_provider); + ethereum_fallback_provider.low_level_test_call().await; + let provider_call_count: Vec<_> = + ProviderMock::get_call_counts(ðereum_fallback_provider).await; assert_eq!(provider_call_count, vec![0, 0, 2]); } diff --git a/rust/chains/hyperlane-ethereum/src/trait_builder.rs b/rust/chains/hyperlane-ethereum/src/trait_builder.rs index 89e4f31d4f..7fed53ca55 100644 --- a/rust/chains/hyperlane-ethereum/src/trait_builder.rs +++ b/rust/chains/hyperlane-ethereum/src/trait_builder.rs @@ -1,4 +1,4 @@ -use std::fmt::Write; +use std::fmt::{Debug, Write}; use std::sync::Arc; use std::time::Duration; @@ -10,22 +10,21 @@ use ethers::prelude::{ Http, JsonRpcClient, Middleware, NonceManagerMiddleware, Provider, Quorum, QuorumProvider, SignerMiddleware, WeightedProvider, Ws, WsClientError, }; -use hyperlane_core::metrics::agent::METRICS_SCRAPE_INTERVAL; +use hyperlane_core::rpc_clients::FallbackProvider; use reqwest::{Client, Url}; use thiserror::Error; use ethers_prometheus::json_rpc_client::{ - JsonRpcClientMetrics, JsonRpcClientMetricsBuilder, NodeInfo, PrometheusJsonRpcClient, - PrometheusJsonRpcClientConfig, -}; -use ethers_prometheus::middleware::{ - MiddlewareMetrics, PrometheusMiddleware, PrometheusMiddlewareConf, + JsonRpcBlockGetter, JsonRpcClientMetrics, JsonRpcClientMetricsBuilder, NodeInfo, + PrometheusJsonRpcClient, PrometheusJsonRpcClientConfig, }; +use ethers_prometheus::middleware::{MiddlewareMetrics, PrometheusMiddlewareConf}; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneDomain, KnownHyperlaneDomain, }; -use crate::{signers::Signers, ConnectionConf, FallbackProvider, RetryingProvider}; +use crate::EthereumFallbackProvider; +use crate::{signers::Signers, ConnectionConf, RetryingProvider}; // This should be whatever the prometheus scrape interval is const HTTP_CLIENT_TIMEOUT: Duration = Duration::from_secs(60); @@ -94,8 +93,7 @@ pub trait BuildableWithProvider { builder = builder.add_provider(weighted_provider); } let quorum_provider = builder.build(); - self.build(quorum_provider, locator, signer, middleware_metrics) - .await? + self.build(quorum_provider, locator, signer).await? } ConnectionConf::HttpFallback { urls } => { let mut builder = FallbackProvider::builder(); @@ -114,7 +112,11 @@ pub trait BuildableWithProvider { builder = builder.add_provider(metrics_provider); } let fallback_provider = builder.build(); - self.build(fallback_provider, locator, signer, middleware_metrics) + let ethereum_fallback_provider = EthereumFallbackProvider::< + _, + JsonRpcBlockGetter>, + >::new(fallback_provider); + self.build(ethereum_fallback_provider, locator, signer) .await? } ConnectionConf::Http { url } => { @@ -130,14 +132,13 @@ pub trait BuildableWithProvider { &middleware_metrics, ); let retrying_http_provider = RetryingProvider::new(metrics_provider, None, None); - self.build(retrying_http_provider, locator, signer, middleware_metrics) - .await? + self.build(retrying_http_provider, locator, signer).await? } ConnectionConf::Ws { url } => { let ws = Ws::connect(url) .await .map_err(EthereumProviderConnectionError::from)?; - self.build(ws, locator, signer, middleware_metrics).await? + self.build(ws, locator, signer).await? } }) } @@ -178,30 +179,19 @@ pub trait BuildableWithProvider { ) } - /// Create the provider, applying any middlewares (e.g. gas oracle, signer, metrics) as needed, + /// Create the provider, applying any middlewares (e.g. gas oracle, signer) as needed, /// and then create the associated trait. async fn build

( &self, client: P, locator: &ContractLocator, signer: Option, - metrics: Option<(MiddlewareMetrics, PrometheusMiddlewareConf)>, ) -> ChainResult where P: JsonRpcClient + 'static, { let provider = wrap_with_gas_oracle(Provider::new(client), locator.domain)?; - - Ok(if let Some(metrics) = metrics { - let provider = Arc::new(PrometheusMiddleware::new(provider, metrics.0, metrics.1)); - // TODO: This task is spawned each time `.build_ethereum(...)` is called, which is about 15 times, - // in spite of it doing the same thing, wasting resources. - // Only spawn this once along with the other agent tasks. - tokio::spawn(provider.start_updating_on_interval(METRICS_SCRAPE_INTERVAL)); - self.build_with_signer(provider, locator, signer).await? - } else { - self.build_with_signer(provider, locator, signer).await? - }) + self.build_with_signer(provider, locator, signer).await } /// Wrap the provider creation with a signing provider if signers were diff --git a/rust/chains/hyperlane-ethereum/src/tx.rs b/rust/chains/hyperlane-ethereum/src/tx.rs index 6919b4a03e..85e0edef03 100644 --- a/rust/chains/hyperlane-ethereum/src/tx.rs +++ b/rust/chains/hyperlane-ethereum/src/tx.rs @@ -9,7 +9,7 @@ use ethers::{ }; use ethers_contract::builders::ContractCall; use ethers_core::types::BlockNumber; -use hyperlane_core::{utils::fmt_bytes, ChainCommunicationError, ChainResult, H256, U256}; +use hyperlane_core::{utils::bytes_to_hex, ChainCommunicationError, ChainResult, H256, U256}; use tracing::{error, info}; use crate::Middleware; @@ -26,7 +26,7 @@ where let data = tx .tx .data() - .map(|b| fmt_bytes(b)) + .map(|b| bytes_to_hex(b)) .unwrap_or_else(|| "None".into()); let to = tx diff --git a/rust/chains/hyperlane-fuel/Cargo.toml b/rust/chains/hyperlane-fuel/Cargo.toml index 7dabcdd514..d2a711feb2 100644 --- a/rust/chains/hyperlane-fuel/Cargo.toml +++ b/rust/chains/hyperlane-fuel/Cargo.toml @@ -19,7 +19,7 @@ tracing-futures.workspace = true tracing.workspace = true url.workspace = true -hyperlane-core = { path = "../../hyperlane-core" } +hyperlane-core = { path = "../../hyperlane-core", features = ["async"]} [build-dependencies] abigen = { path = "../../utils/abigen", features = ["fuels"] } diff --git a/rust/chains/hyperlane-fuel/src/mailbox.rs b/rust/chains/hyperlane-fuel/src/mailbox.rs index 7402ac2783..dbf130235f 100644 --- a/rust/chains/hyperlane-fuel/src/mailbox.rs +++ b/rust/chains/hyperlane-fuel/src/mailbox.rs @@ -8,7 +8,7 @@ use fuels::prelude::{Bech32ContractId, WalletUnlocked}; use tracing::instrument; use hyperlane_core::{ - utils::fmt_bytes, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, + utils::bytes_to_hex, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexer, LogMeta, Mailbox, TxCostEstimate, TxOutcome, H256, U256, }; @@ -105,7 +105,7 @@ impl Mailbox for FuelMailbox { todo!() } - #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%fmt_bytes(metadata)))] + #[instrument(err, ret, skip(self), fields(msg=%message, metadata=%bytes_to_hex(metadata)))] async fn process_estimate_costs( &self, message: &HyperlaneMessage, diff --git a/rust/chains/hyperlane-fuel/src/provider.rs b/rust/chains/hyperlane-fuel/src/provider.rs index 8048076e04..992608fac8 100644 --- a/rust/chains/hyperlane-fuel/src/provider.rs +++ b/rust/chains/hyperlane-fuel/src/provider.rs @@ -1,7 +1,8 @@ use async_trait::async_trait; use hyperlane_core::{ - BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, H256, U256, + BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, + H256, U256, }; /// A wrapper around a fuel provider to get generic blockchain information. @@ -35,4 +36,8 @@ impl HyperlaneProvider for FuelProvider { async fn get_balance(&self, address: String) -> ChainResult { todo!() } + + async fn get_chain_metrics(&self) -> ChainResult> { + Ok(None) + } } diff --git a/rust/chains/hyperlane-sealevel/Cargo.toml b/rust/chains/hyperlane-sealevel/Cargo.toml index 248e3dfac1..2a3eed634f 100644 --- a/rust/chains/hyperlane-sealevel/Cargo.toml +++ b/rust/chains/hyperlane-sealevel/Cargo.toml @@ -24,7 +24,7 @@ tracing.workspace = true url.workspace = true account-utils = { path = "../../sealevel/libraries/account-utils" } -hyperlane-core = { path = "../../hyperlane-core", features = ["solana"] } +hyperlane-core = { path = "../../hyperlane-core", features = ["solana", "async"] } hyperlane-sealevel-interchain-security-module-interface = { path = "../../sealevel/libraries/interchain-security-module-interface" } hyperlane-sealevel-mailbox = { path = "../../sealevel/programs/mailbox", features = ["no-entrypoint"] } hyperlane-sealevel-igp = { path = "../../sealevel/programs/hyperlane-sealevel-igp", features = ["no-entrypoint"] } diff --git a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs b/rust/chains/hyperlane-sealevel/src/interchain_gas.rs index 411505aae2..cb4a3543e9 100644 --- a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs +++ b/rust/chains/hyperlane-sealevel/src/interchain_gas.rs @@ -2,7 +2,7 @@ use async_trait::async_trait; use hyperlane_core::{ config::StrOrIntParseError, ChainCommunicationError, ChainResult, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexer, - InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceIndexer, H256, H512, + InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H256, H512, }; use hyperlane_sealevel_igp::{ accounts::{GasPaymentAccount, ProgramDataAccount}, @@ -273,9 +273,9 @@ impl Indexer for SealevelInterchainGasPaymasterIndexer { } #[async_trait] -impl SequenceIndexer for SealevelInterchainGasPaymasterIndexer { +impl SequenceAwareIndexer for SealevelInterchainGasPaymasterIndexer { #[instrument(err, skip(self))] - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let program_data_account = self .rpc_client .get_account_with_commitment(&self.igp.data_pda_pubkey, CommitmentConfig::finalized()) diff --git a/rust/chains/hyperlane-sealevel/src/mailbox.rs b/rust/chains/hyperlane-sealevel/src/mailbox.rs index f7a22d89ed..bcf79be867 100644 --- a/rust/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/chains/hyperlane-sealevel/src/mailbox.rs @@ -11,7 +11,7 @@ use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, Decode as _, Encode as _, FixedPointNumber, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, Indexer, LogMeta, - Mailbox, MerkleTreeHook, SequenceIndexer, TxCostEstimate, TxOutcome, H256, H512, U256, + Mailbox, MerkleTreeHook, SequenceAwareIndexer, TxCostEstimate, TxOutcome, H256, H512, U256, }; use hyperlane_sealevel_interchain_security_module_interface::{ InterchainSecurityModuleInstruction, VerifyInstruction, @@ -113,7 +113,7 @@ impl SealevelMailbox { /// Simulates an instruction, and attempts to deserialize it into a T. /// If no return data at all was returned, returns Ok(None). - /// If some return data was returned but deserialization was unsuccesful, + /// If some return data was returned but deserialization was unsuccessful, /// an Err is returned. pub async fn simulate_instruction( &self, @@ -630,9 +630,9 @@ impl SealevelMailboxIndexer { } #[async_trait] -impl SequenceIndexer for SealevelMailboxIndexer { +impl SequenceAwareIndexer for SealevelMailboxIndexer { #[instrument(err, skip(self))] - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { let tip = Indexer::::get_finalized_block_number(self).await?; // TODO: need to make sure the call and tip are at the same height? let count = Mailbox::count(&self.mailbox, None).await?; @@ -676,8 +676,8 @@ impl Indexer for SealevelMailboxIndexer { } #[async_trait] -impl SequenceIndexer for SealevelMailboxIndexer { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { +impl SequenceAwareIndexer for SealevelMailboxIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { // TODO: implement when sealevel scraper support is implemented info!("Message delivery indexing not implemented"); let tip = Indexer::::get_finalized_block_number(self).await?; diff --git a/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs b/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs index b7ed6ba1e2..30dea8c535 100644 --- a/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs @@ -5,7 +5,7 @@ use derive_new::new; use hyperlane_core::{ accumulator::incremental::IncrementalMerkle, ChainCommunicationError, ChainResult, Checkpoint, HyperlaneChain, HyperlaneMessage, Indexer, LogMeta, MerkleTreeHook, MerkleTreeInsertion, - SequenceIndexer, + SequenceAwareIndexer, }; use hyperlane_sealevel_mailbox::accounts::OutboxAccount; use solana_sdk::commitment_config::CommitmentConfig; @@ -101,9 +101,9 @@ impl Indexer for SealevelMerkleTreeHookIndexer { } #[async_trait] -impl SequenceIndexer for SealevelMerkleTreeHookIndexer { - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)> { - SequenceIndexer::::sequence_and_tip(&self.0).await +impl SequenceAwareIndexer for SealevelMerkleTreeHookIndexer { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { + SequenceAwareIndexer::::latest_sequence_count_and_tip(&self.0).await } } diff --git a/rust/chains/hyperlane-sealevel/src/provider.rs b/rust/chains/hyperlane-sealevel/src/provider.rs index 91908f9168..42b2665501 100644 --- a/rust/chains/hyperlane-sealevel/src/provider.rs +++ b/rust/chains/hyperlane-sealevel/src/provider.rs @@ -3,7 +3,8 @@ use std::{str::FromStr, sync::Arc}; use async_trait::async_trait; use hyperlane_core::{ - BlockInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, H256, U256, + BlockInfo, ChainInfo, ChainResult, HyperlaneChain, HyperlaneDomain, HyperlaneProvider, TxnInfo, + H256, U256, }; use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; @@ -76,4 +77,8 @@ impl HyperlaneProvider for SealevelProvider { async fn get_balance(&self, address: String) -> ChainResult { self.get_balance(address).await } + + async fn get_chain_metrics(&self) -> ChainResult> { + Ok(None) + } } diff --git a/rust/config/mainnet3_config.json b/rust/config/mainnet3_config.json index c144174d61..f7683cd30c 100644 --- a/rust/config/mainnet3_config.json +++ b/rust/config/mainnet3_config.json @@ -1,64 +1,85 @@ { "chains": { "arbitrum": { + "blockExplorers": [ + { + "apiUrl": "https://api.arbiscan.io/api", + "family": "etherscan", + "name": "Arbiscan", + "url": "https://arbiscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 0 + }, "chainId": 42161, + "displayName": "Arbitrum", "domainId": 42161, + "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-arbitrum.safe.global/", "name": "arbitrum", - "protocol": "ethereum", - "displayName": "Arbitrum", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", + "technicalStack": "arbitrumnitro", "rpcUrls": [ { "http": "https://arb1.arbitrum.io/rpc" } ], - "blockExplorers": [ - { - "name": "Arbiscan", - "url": "https://arbiscan.io", - "apiUrl": "https://api.arbiscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 3 - }, - "gasCurrencyCoinGeckoId": "ethereum", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-arbitrum.safe.global/", "storageGasOracle": "0xD3805207b65d99C075ceA938Fa7c0587026a5DF5", "proxyAdmin": "0x80Cebd56A65e46c474a1A101e89E76C4c51D179c", "merkleRootMultisigIsmFactory": "0x3C330D4A2e2b8443AFaB8E326E64ab4251B7Eae0", "messageIdMultisigIsmFactory": "0x12Df53079d399a47e9E730df095b712B0FDFA791", "aggregationIsmFactory": "0xD4883084389fC1Eeb4dAfB2ADcFc36B711c310EB", "aggregationHookFactory": "0x9B5f440bBb64Fee337F37e03362b628711Ea09C7", - "routingIsmFactory": "0xC020F8A7b00178dFA0fcC75C159e14b79F8e5c63", "merkleTreeHook": "0x748040afB89B8FdBb992799808215419d36A0930", "interchainGasPaymaster": "0x3b6044acd6767f017e99318AA6Ef93b7B06A5a22", "aggregationHook": "0xe0cb37cFc47296f1c4eD77EFf92Aed478644d10c", "protocolFee": "0xD0199067DACb8526e7dc524a9a7DCBb57Cd25421", "mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9", "validatorAnnounce": "0x1df063280C4166AF9a725e3828b4dAC6c7113B08", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0xa2931C37957f3079d3B21b877d56E1db930e02a5", + "pausableHook": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "fallbackRoutingHook": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", + "interchainSecurityModule": "0xD0DBBF922076352cC50B285A0023536561F00EEa", "index": { - "from": 143699718 + "from": 18422579 } }, "avalanche": { + "blockExplorers": [ + { + "apiUrl": "https://api.snowtrace.io/api", + "family": "other", + "name": "SnowTrace", + "url": "https://snowtrace.io" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 2, + "reorgPeriod": 3 + }, "chainId": 43114, + "displayName": "Avalanche", "domainId": 43114, + "gasCurrencyCoinGeckoId": "avalanche-2", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-avalanche.safe.global/", "name": "avalanche", - "protocol": "ethereum", - "displayName": "Avalanche", "nativeToken": { "decimals": 18, "name": "Avalanche", "symbol": "AVAX" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://api.avax.network/ext/bc/C/rpc", @@ -68,49 +89,54 @@ } } ], - "blockExplorers": [ - { - "name": "SnowTrace", - "url": "https://snowtrace.io", - "apiUrl": "https://api.snowtrace.io/api", - "family": "other" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 3, - "estimateBlockTime": 2 - }, - "gasCurrencyCoinGeckoId": "avalanche-2", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-avalanche.safe.global/", "storageGasOracle": "0x175821F30AdCAA4bbB72Ce98eF76C2E0De2C3f21", "proxyAdmin": "0xd7CF8c05fd81b8cA7CfF8E6C49B08a9D63265c9B", "merkleRootMultisigIsmFactory": "0x896cF1D1B66cD211633eDd589fF158E8Cfaf9B54", "messageIdMultisigIsmFactory": "0x8819D653DF5b1FC0DdB32189a2704E471AF8483c", "aggregationIsmFactory": "0xa5E13796eB7d2EDCc88012c8cfF90D69B51FcF9f", "aggregationHookFactory": "0x3bF6Ac986C7Af9A9Ac356C0e99C0041EFd8D96e7", - "routingIsmFactory": "0xA9Ddc70f50009aF8bDB312aA757B4304b0F7BbB3", "merkleTreeHook": "0x84eea61D679F42D92145fA052C89900CBAccE95A", "interchainGasPaymaster": "0x95519ba800BBd0d34eeAE026fEc620AD978176C0", "aggregationHook": "0x0165a22BA489F7DA37DAf6397781777D9FCB5708", "protocolFee": "0xEc4AdA26E51f2685279F37C8aE62BeAd8212D597", "mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6", "validatorAnnounce": "0x9Cad0eC82328CEE2386Ec14a12E81d070a27712f", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0x28F7907911C7E321c596686AE6D1F20516450037", + "pausableHook": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "fallbackRoutingHook": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", + "interchainSecurityModule": "0xA36B02a83564f52d9244310Ea439ee6F6AfeFb60", "index": { - "from": 36881761 + "from": 36874693 } }, "base": { + "blockExplorers": [ + { + "apiUrl": "https://api.basescan.org/api", + "family": "etherscan", + "name": "BaseScan", + "url": "https://basescan.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 1 + }, "chainId": 8453, + "displayName": "Base", "domainId": 8453, + "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-base.safe.global/", "name": "base", - "protocol": "ethereum", - "displayName": "Base", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://base.publicnode.com/" @@ -122,25 +148,10 @@ "http": "https://base.blockpi.network/v1/rpc/public" } ], - "blockExplorers": [ - { - "name": "BaseScan", - "url": "https://basescan.org", - "apiUrl": "https://api.basescan.org/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 2 - }, - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-base.safe.global/", "merkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "messageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "aggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", "aggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", - "routingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "proxyAdmin": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x19dc38aeae620380430C200a6E990D5Af5480117", @@ -149,45 +160,52 @@ "aggregationHook": "0x13f3d4B0Ee0a713430fded9E18f7fb6c91A6E41F", "protocolFee": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a", "validatorAnnounce": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", + "routingIsmFactory": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", + "pausableHook": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "fallbackRoutingHook": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "interchainSecurityModule": "0x5D1e7D7c5B9e6dDC8439F67F10c578f2A1084f6F", "index": { - "from": 5702757 + "from": 5695475 } }, "bsc": { + "blockExplorers": [ + { + "apiUrl": "https://api.bscscan.com/api", + "family": "etherscan", + "name": "BscScan", + "url": "https://bscscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 15 + }, "chainId": 56, - "domainId": 56, - "name": "bsc", - "protocol": "ethereum", "displayName": "Binance Smart Chain", "displayNameShort": "Binance", + "domainId": 56, + "gasCurrencyCoinGeckoId": "binancecoin", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-bsc.safe.global/", + "name": "bsc", "nativeToken": { "decimals": 18, "name": "BNB", "symbol": "BNB" }, + "protocol": "ethereum", "rpcUrls": [ { - "http": "https://bsc-dataseed.binance.org" + "http": "https://rpc.ankr.com/bsc" }, { - "http": "https://rpc.ankr.com/bsc" - } - ], - "blockExplorers": [ + "http": "https://bsc.drpc.org" + }, { - "name": "BscScan", - "url": "https://bscscan.com", - "apiUrl": "https://api.bscscan.com/api", - "family": "etherscan" + "http": "https://bscrpc.com" } ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 15, - "estimateBlockTime": 3 - }, - "gasCurrencyCoinGeckoId": "binancecoin", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-bsc.safe.global/", "transactionOverrides": { "gasPrice": 7000000000 }, @@ -197,81 +215,114 @@ "messageIdMultisigIsmFactory": "0x4B1d8352E35e3BDE36dF5ED2e73C24E35c4a96b7", "aggregationIsmFactory": "0x38B3878c4fb44d201DA924c4a04bae3EE728c065", "aggregationHookFactory": "0xe70E86a7D1e001D419D71F960Cb6CaD59b6A3dB6", - "routingIsmFactory": "0xc40481D13419BC8090e6AD07074Ef39E538c09CE", "mailbox": "0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4", "merkleTreeHook": "0xFDb9Cd5f9daAA2E4474019405A328a88E7484f26", "interchainGasPaymaster": "0x78E25e7f84416e69b9339B0A6336EB6EFfF6b451", "aggregationHook": "0x402Fc106576462a892355d69ACF03D46A888ae88", "protocolFee": "0xA8Aa5f14a5463a78E45CC068F11c867949F3E367", "validatorAnnounce": "0x7024078130D9c2100fEA474DAD009C2d1703aCcd", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0xe6Af5720d34213C805C08e2470aea979e3F72F75", + "interchainSecurityModule": "0xab3df354baBee6c2B88E2CeD3b2e030e31aA5e61", + "fallbackRoutingHook": "0x237E81f87F57Badad9e09f13CC676D986cA852e7", + "pausableHook": "0x7DBdAd1b4A922B65d37d7258a4227b6658344b7f", "index": { - "from": 32897848 + "from": 32893043 } }, "celo": { + "blockExplorers": [ + { + "apiUrl": "https://api.celoscan.io/api", + "family": "etherscan", + "name": "CeloScan", + "url": "https://celoscan.io" + }, + { + "apiUrl": "https://explorer.celo.org/mainnet/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://explorer.celo.org" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, "chainId": 42220, + "displayName": "Celo", "domainId": 42220, + "gnosisSafeTransactionServiceUrl": "https://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/", "name": "celo", - "protocol": "ethereum", - "displayName": "Celo", "nativeToken": { "decimals": 18, "name": "CELO", "symbol": "CELO" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://forno.celo.org" } ], - "blockExplorers": [ - { - "name": "CeloScan", - "url": "https://celoscan.io", - "apiUrl": "https://api.celoscan.io/api", - "family": "etherscan" - }, - { - "name": "Blockscout", - "url": "https://explorer.celo.org", - "apiUrl": "https://explorer.celo.org/mainnet/api", - "family": "blockscout" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 5 - }, - "gnosisSafeTransactionServiceUrl": "https://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/", "storageGasOracle": "0xD9A9966E7dA9a7f0032bF449FB12696a638E673C", "proxyAdmin": "0x90f9a2E9eCe93516d65FdaB726a3c62F5960a1b9", "merkleRootMultisigIsmFactory": "0x4C96a1abc44dc846775CE702C9E9BE821D3b487c", "messageIdMultisigIsmFactory": "0xaB402f227e892Ef37C105bf06619c0fa106a1fB2", "aggregationIsmFactory": "0x1722dd970a1F56040712129f5Eeb76B003fd7500", "aggregationHookFactory": "0xc3745652EFB8555A8b064A0EA78d295133d326D2", - "routingIsmFactory": "0xec748b5623f0B50E4c5eB1CFa7Bd46C3213608b6", "merkleTreeHook": "0x04dB778f05854f26E67e0a66b740BBbE9070D366", "interchainGasPaymaster": "0x571f1435613381208477ac5d6974310d88AC7cB7", "aggregationHook": "0xc65890329066FB20c339Bc5C22f1756e9D3a4fF5", "protocolFee": "0x89886d431f9c3eEE64DCD6dAbA3f7D689D98D899", "mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb", "validatorAnnounce": "0xCeF677b65FDaA6804d4403083bb12B8dB3991FE1", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0x2A2c22B0a8615ad24839fA6Af302E896Af32d1a3", + "domainRoutingIsm": "0xf18E32428dad0802C5D6F723cB80A6Da889777c4", + "pausableIsm": "0x6Bc4437ce69696C9461Cbc89582c259AC8847A58", + "staticAggregationIsm": "0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E", + "interchainSecurityModule": "0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E", + "fallbackRoutingHook": "0xDC98a856fb9112894c2fE32267DA8bF35645FAF3", + "pausableHook": "0x80672c5D9Fd26B235654C24adc1CFcDeb8d15115", "index": { - "from": 22105253 + "from": 22102340 } }, "ethereum": { + "blockExplorers": [ + { + "apiUrl": "https://api.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://etherscan.io" + }, + { + "apiUrl": "https://blockscout.com/eth/mainnet/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://blockscout.com/eth/mainnet" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 13, + "reorgPeriod": 14 + }, "chainId": 1, + "displayName": "Ethereum", "domainId": 1, + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-mainnet.safe.global/", "name": "ethereum", - "protocol": "ethereum", - "displayName": "Ethereum", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" @@ -280,26 +331,6 @@ "http": "https://cloudflare-eth.com" } ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://etherscan.io", - "apiUrl": "https://api.etherscan.io/api", - "family": "etherscan" - }, - { - "name": "Blockscout", - "url": "https://blockscout.com/eth/mainnet", - "apiUrl": "https://blockscout.com/eth/mainnet/api", - "family": "blockscout" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 14, - "estimateBlockTime": 13 - }, - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-mainnet.safe.global/", "transactionOverrides": { "maxFeePerGas": 150000000000, "maxPriorityFeePerGas": 5000000000 @@ -310,28 +341,48 @@ "messageIdMultisigIsmFactory": "0xfA21D9628ADce86531854C2B7ef00F07394B0B69", "aggregationIsmFactory": "0x46FA191Ad972D9674Ed752B69f9659A0d7b22846", "aggregationHookFactory": "0x6D2555A8ba483CcF4409C39013F5e9a3285D3C9E", - "routingIsmFactory": "0xCb74c6aE411236CEE6803619916694BE86cF5987", "merkleTreeHook": "0x48e6c30B97748d1e2e03bf3e9FbE3890ca5f8CCA", "interchainGasPaymaster": "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611", "aggregationHook": "0xb87AC8EA4533AE017604E44470F7c1E550AC6F10", "protocolFee": "0x8B05BF30F6247a90006c5837eA63C7905D79e6d8", "mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239", "validatorAnnounce": "0xCe74905e51497b4adD3639366708b821dcBcff96", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0x28fA9552F19039b450498B0d8e5DEAe0d0aAc559", + "pausableHook": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "fallbackRoutingHook": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "interchainSecurityModule": "0x43Ce4Eb4aE3585dDe9Ac6967Db5b06f7f6764C8a", "index": { - "from": 18423787 + "from": 18422581 } }, "gnosis": { + "blockExplorers": [ + { + "apiUrl": "https://api.gnosisscan.io/api", + "family": "etherscan", + "name": "GnosisScan", + "url": "https://gnosisscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 14 + }, "chainId": 100, + "displayName": "Gnosis", "domainId": 100, + "gasCurrencyCoinGeckoId": "xdai", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-gnosis-chain.safe.global/", "name": "gnosis", - "protocol": "ethereum", - "displayName": "Gnosis", "nativeToken": { + "decimals": 18, "name": "xDai", - "symbol": "xDai", - "decimals": 18 + "symbol": "xDai" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://rpc.gnosischain.com", @@ -341,21 +392,6 @@ } } ], - "blockExplorers": [ - { - "name": "GnosisScan", - "url": "https://gnosisscan.io", - "apiUrl": "https://api.gnosisscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 14, - "estimateBlockTime": 5 - }, - "gasCurrencyCoinGeckoId": "xdai", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-gnosis-chain.safe.global/", "storageGasOracle": "0x5E01d8F34b629E3f92d69546bbc4142A7Adee7e9", "proxyAdmin": "0x81a92A1a272cb09d7b4970b07548463dC7aE0cB7", "merkleRootMultisigIsmFactory": "0x8E273260EAd8B72A085B19346A676d355740e875", @@ -363,44 +399,56 @@ "aggregationIsmFactory": "0x11EF91d17c5ad3330DbCa709a8841743d3Af6819", "aggregationHookFactory": "0xbC8AA096dabDf4A0200BB9f8D4Cbb644C3D86d7B", "mailbox": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", - "routingIsmFactory": "0xd9Cc2e652A162bb93173d1c44d46cd2c0bbDA59D", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "interchainGasPaymaster": "0xDd260B99d302f0A3fF885728c086f729c06f227f", "aggregationHook": "0xdD1FA1C12496474c1dDC67a658Ba81437F818861", "protocolFee": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", "validatorAnnounce": "0x87ED6926abc9E38b9C7C19f835B41943b622663c", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0xbB5Df000113e767dE11343A16f83De733e5bCC0F", + "pausableHook": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "fallbackRoutingHook": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", + "interchainSecurityModule": "0x8e1aa0687B6d939D5a44304D13B7c922ebB012f1", "index": { - "from": 30623434 + "from": 30620793 } }, "mantapacific": { - "protocol": "ethereum", - "domainId": 169, + "blockExplorers": [ + { + "apiUrl": "https://pacific-explorer.manta.network/api", + "family": "blockscout", + "name": "Manta Pacific Explorer", + "url": "https://pacific-explorer.manta.network" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 1 + }, "chainId": 169, - "name": "mantapacific", "displayName": "Manta Pacific", "displayNameShort": "Manta", + "domainId": 169, + "isTestnet": false, + "name": "mantapacific", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 3 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://pacific-rpc.manta.network/http" } ], - "isTestnet": false, "merkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "messageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", "aggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "aggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", - "routingIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "domainRoutingIsm": "0xDEed16fe4b1c9b2a93483EDFf34C77A9b57D31Ff", @@ -410,8 +458,12 @@ "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "routingIsmFactory": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", + "pausableHook": "0x7556a0E61d577D921Cba8Fca0d7D6299d36E607E", + "fallbackRoutingHook": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807", + "interchainSecurityModule": "0xDEed16fe4b1c9b2a93483EDFf34C77A9b57D31Ff", "index": { - "from": 437384 + "from": 437300 } }, "neutron": { @@ -423,22 +475,26 @@ "validatorAnnounce": "0xf3aa0d652226e21ae35cd9035c492ae41725edc9036edf0d6a48701b153b90a0", "merkleTreeHook": "0xcd30a0001cc1f436c41ef764a712ebabc5a144140e3fd03eafe64a9a24e4e27c", "protocol": "cosmos", - "finalityBlocks": 1, "rpcUrls": [ { "http": "https://rpc-kralum.neutron-1.neutron.org" } ], - "grpcUrl": "https://grpc-kralum.neutron-1.neutron.org:80", + "grpcUrls": [ + { + "http": "https://grpc-kralum.neutron-1.neutron.org:80" + } + ], "canonicalAsset": "untrn", - "prefix": "neutron", + "bech32Prefix": "neutron", "gasPrice": { "amount": "0.57", "denom": "untrn" }, + "contractAddressBytes": 32, "index": { "from": 4000000, - "chunk": 100000 + "chunk": 50 }, "blocks": { "reorgPeriod": 1 @@ -449,112 +505,220 @@ "prefix": "neutron" } }, + "injective": { + "name": "injective", + "domainId": "6909546", + "chainId": "injective-1", + "mailbox": "0x0f7fb53961d70687e352aa55cb329ca76edc0c19", + "interchainGasPaymaster": "0x27ae52298e5b53b34b7ae0ca63e05845c31e1f59", + "validatorAnnounce": "0x1fb225b2fcfbe75e614a1d627de97ff372242eed", + "merkleTreeHook": "0x568ad3638447f07def384969f4ea39fae3802962", + "protocol": "cosmos", + "rpcUrls": [ + { + "http": "https://injective-rpc.polkachu.com" + } + ], + "grpcUrls": [ + { + "http": "https://injective-grpc.goldenratiostaking.net:443" + } + ], + "canonicalAsset": "inj", + "bech32Prefix": "inj", + "gasPrice": { + "amount": "700000000", + "denom": "inj" + }, + "contractAddressBytes": 20, + "index": { + "from": 58419500, + "chunk": 50 + }, + "blocks": { + "reorgPeriod": 10 + } + }, "moonbeam": { + "blockExplorers": [ + { + "apiUrl": "https://api-moonbeam.moonscan.io/api", + "family": "etherscan", + "name": "MoonScan", + "url": "https://moonscan.io" + } + ], + "blocks": { + "confirmations": 2, + "estimateBlockTime": 12, + "reorgPeriod": 2 + }, "chainId": 1284, + "displayName": "Moonbeam", "domainId": 1284, + "gnosisSafeTransactionServiceUrl": "https://transaction.multisig.moonbeam.network", "name": "moonbeam", - "protocol": "ethereum", - "displayName": "Moonbeam", "nativeToken": { "decimals": 18, "name": "GLMR", "symbol": "GLMR" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://rpc.api.moonbeam.network" } ], - "blockExplorers": [ - { - "name": "MoonScan", - "url": "https://moonscan.io", - "apiUrl": "https://api-moonbeam.moonscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 2, - "reorgPeriod": 2, - "estimateBlockTime": 12 - }, - "gnosisSafeTransactionServiceUrl": "https://transaction.multisig.moonbeam.network", "storageGasOracle": "0x448b7ADB0dA36d41AA2AfDc9d63b97541A7b3819", "proxyAdmin": "0x6A9cdA3dd1F593983BFd142Eb35e6ce4137bd5ce", "merkleRootMultisigIsmFactory": "0xE2f485bc031Feb5a4C41C1967bf028653d75f0C3", "messageIdMultisigIsmFactory": "0x84Df48F8f241f11d0fA302d09d73030429Bd9C73", "aggregationIsmFactory": "0x40c6Abcb6A2CdC8882d4bEcaC47927005c7Bb8c2", "aggregationHookFactory": "0x59cC3E7A49DdC4893eB8754c7908f96072A7DbE8", - "routingIsmFactory": "0x98Aa6239FfCcEc73A662a5e5e26Bc3fD7c7291B7", "mailbox": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", "merkleTreeHook": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", "interchainGasPaymaster": "0x14760E32C0746094cF14D97124865BC7F0F7368F", "aggregationHook": "0x23cca255aE83F57F39EAf9D14fB9FdaDF22D5863", "protocolFee": "0xCd3e29A9D293DcC7341295996a118913F7c582c0", "validatorAnnounce": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0x8061Af3A459093540d17823D651BC5E2A92669a7", + "pausableHook": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "fallbackRoutingHook": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "interchainSecurityModule": "0x373836DFa82f2D27ec79Ca32A197Aa1665F0E1e9", "index": { - "from": 4720894 + "from": 4719713 } }, - "optimism": { - "chainId": 10, - "domainId": 10, - "name": "optimism", - "protocol": "ethereum", - "displayName": "Optimism", + "inevm": { + "blockExplorers": [ + { + "apiUrl": "https://inevm.calderaexplorer.xyz/api", + "family": "blockscout", + "name": "Caldera inEVM Explorer", + "url": "https://inevm.calderaexplorer.xyz/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 0 + }, + "chainId": 2525, + "domainId": 2525, + "displayName": "Injective EVM", + "displayNameShort": "inEVM", + "name": "inevm", "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "decimals": 18, + "name": "Injective", + "symbol": "INJ" }, + "protocol": "ethereum", "rpcUrls": [ { - "http": "https://mainnet.optimism.io" + "http": "https://inevm.calderachain.xyz/http" } ], + "merkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "messageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "aggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "aggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "routingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "storageGasOracle": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", + "aggregationHook": "0xe0dDb5dE7D52918237cC1Ae131F29dcAbcb0F62B", + "protocolFee": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "validatorAnnounce": "0x15ab173bDB6832f9b64276bA128659b0eD77730B", + "interchainSecurityModule": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "index": { + "from": 18972465 + } + }, + "optimism": { "blockExplorers": [ { - "name": "Etherscan", - "url": "https://optimistic.etherscan.io", "apiUrl": "https://api-optimistic.etherscan.io/api", - "family": "etherscan" + "family": "etherscan", + "name": "Etherscan", + "url": "https://optimistic.etherscan.io" } ], "blocks": { "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 3 + "estimateBlockTime": 3, + "reorgPeriod": 0 }, + "chainId": 10, + "displayName": "Optimism", + "domainId": 10, "gasCurrencyCoinGeckoId": "ethereum", "gnosisSafeTransactionServiceUrl": "https://safe-transaction-optimism.safe.global/", + "name": "optimism", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://mainnet.optimism.io" + } + ], "storageGasOracle": "0x27e88AeB8EA4B159d81df06355Ea3d20bEB1de38", "proxyAdmin": "0xE047cb95FB3b7117989e911c6afb34771183fC35", "merkleRootMultisigIsmFactory": "0xCA6Cb9Bc3cfF9E11003A06617cF934B684Bc78BC", "messageIdMultisigIsmFactory": "0xAa4Be20E9957fE21602c74d7C3cF5CB1112EA9Ef", "aggregationIsmFactory": "0x7491843F3A5Ba24E0f17a22645bDa04A1Ae2c584", "aggregationHookFactory": "0x15DEeAB8dECDe553bb0B1F9C00984cbcae1af3D7", - "routingIsmFactory": "0x89E3530137aD51743536443a3EC838b502E72eb7", "merkleTreeHook": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", "interchainGasPaymaster": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", "aggregationHook": "0x4ccC6d8eB79f2a1EC9bcb0f211fef7907631F91f", "protocolFee": "0xD71Ff941120e8f935b8b1E2C1eD72F5d140FF458", "mailbox": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", "validatorAnnounce": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0xD2e905108c5e44dADA680274740f896Ea96Cf2Fb", + "pausableHook": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "fallbackRoutingHook": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "interchainSecurityModule": "0x04938856bE60c8e734ffDe5f720E2238302BE8D2", "index": { - "from": 111298042 + "from": 111290758 } }, "polygon": { + "blockExplorers": [ + { + "apiUrl": "https://api.polygonscan.com/api", + "family": "etherscan", + "name": "PolygonScan", + "url": "https://polygonscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 256 + }, "chainId": 137, + "displayName": "Polygon", "domainId": 137, + "gasCurrencyCoinGeckoId": "matic-network", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-polygon.safe.global/", "name": "polygon", - "protocol": "ethereum", - "displayName": "Polygon", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://rpc-mainnet.matic.quiknode.pro", @@ -565,26 +729,14 @@ }, { "http": "https://polygon-rpc.com" - } - ], - "blockExplorers": [ + }, { - "name": "PolygonScan", - "url": "https://polygonscan.com", - "apiUrl": "https://api.polygonscan.com/api", - "family": "etherscan" + "http": "https://rpc.ankr.com/polygon" } ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 256, - "estimateBlockTime": 2 - }, - "gasCurrencyCoinGeckoId": "matic-network", - "gnosisSafeTransactionServiceUrl": "https://safe-transaction-polygon.safe.global/", "transactionOverrides": { - "maxFeePerGas": 500000000000, - "maxPriorityFeePerGas": 100000000000 + "maxFeePerGas": 1000000000000, + "maxPriorityFeePerGas": 200000000000 }, "storageGasOracle": "0xA3a24EC5670F1F416AB9fD554FcE2f226AE9D7eB", "proxyAdmin": "0xC4F7590C5d30BE959225dC75640657954A86b980", @@ -592,57 +744,57 @@ "messageIdMultisigIsmFactory": "0xEa5Be2AD66BB1BA321B7aCf0A079fBE304B09Ca0", "aggregationIsmFactory": "0x81AdDD9Ca89105063DaDEBd5B4408551Ce850E22", "aggregationHookFactory": "0xFeeB86e70e4a640cDd29636CCE19BD6fe8628135", - "routingIsmFactory": "0xF0752A65ffB2153EaE53F6a70c858a87022d5c56", "mailbox": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", "merkleTreeHook": "0x73FbD25c3e817DC4B4Cd9d00eff6D83dcde2DfF6", "interchainGasPaymaster": "0x0071740Bf129b05C4684abfbBeD248D80971cce2", "aggregationHook": "0x34dAb05650Cf590088bA18aF9d597f3e081bCc47", "protocolFee": "0xF8F3629e308b4758F8396606405989F8D8C9c578", "validatorAnnounce": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", + "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", + "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", + "routingIsmFactory": "0x0d0E816eE4557689d34fAd5885C53b9393C1D9fA", + "interchainSecurityModule": "0x9a795fB62f86146ec06e2377e3C95Af65c7C20eB", + "fallbackRoutingHook": "0xca4cCe24E7e06241846F5EA0cda9947F0507C40C", + "pausableHook": "0x748040afB89B8FdBb992799808215419d36A0930", "index": { - "from": 49114872 + "from": 49108065 } }, "polygonzkevm": { - "protocol": "ethereum", + "blockExplorers": [ + { + "apiUrl": "https://api-zkevm.polygonscan.com/api", + "family": "etherscan", + "name": "PolygonScan", + "url": "https://zkevm.polygonscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 10, + "reorgPeriod": 1 + }, "chainId": 1101, + "displayName": "Polygon zkEVM", + "displayNameShort": "zkEVM", "domainId": 1101, + "gasCurrencyCoinGeckoId": "ethereum", "name": "polygonzkevm", - "displayName": "Polygon zkEVM", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { - "http": "https://polygonzkevm-mainnet.g.alchemy.com/v2/demo" - }, - { - "http": "https://rpc.ankr.com/polygon_zkevm" - }, - { - "http": "https://zkevm.polygonscan.com/" + "http": "https://polygon-zkevm.drpc.org" } ], - "blockExplorers": [ - { - "name": "PolygonScan", - "url": "https://zkevm.polygonscan.com/", - "apiUrl": "https://api-zkevm.polygonscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 10 - }, "merkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "messageIdMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", "aggregationIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "aggregationHookFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", - "routingIsmFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "proxyAdmin": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "storageGasOracle": "0x19dc38aeae620380430C200a6E990D5Af5480117", @@ -651,47 +803,48 @@ "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "routingIsmFactory": "0xe4057c5B0c43Dc18E36b08C39B419F190D29Ac2d", + "interchainSecurityModule": "0xf2BEE9D2c15Ba9D7e06799B5912dE1F05533c141", + "fallbackRoutingHook": "0x01aE937A7B05d187bBCBE80F44F41879D3D335a4", + "pausableHook": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", "index": { - "from": 6581140 + "from": 6577743 } }, "scroll": { + "blockExplorers": [ + { + "apiUrl": "https://api.scrollscan.com/api", + "family": "etherscan", + "name": "Scroll Explorer", + "url": "https://scrollscan.com/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 1 + }, "chainId": 534352, + "displayName": "Scroll", "domainId": 534352, + "gasCurrencyCoinGeckoId": "ethereum", "name": "scroll", - "protocol": "ethereum", - "displayName": "Scroll", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://scroll.blockpi.network/v1/rpc/public" - }, - { - "http": "https://scroll-mainnet.public.blastapi.io" } ], - "blockExplorers": [ - { - "name": "Scroll Explorer", - "url": "https://scrollscan.com/", - "apiUrl": "https://api.scrollscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 - }, "merkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "messageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "aggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "aggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", - "routingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "merkleTreeHook": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "storageGasOracle": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", @@ -700,11 +853,54 @@ "protocolFee": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "validatorAnnounce": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "routingIsmFactory": "0xe03dad16074BC5EEA9A9311257BF02Eb0B6AAA2b", + "pausableHook": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "fallbackRoutingHook": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "interchainSecurityModule": "0xaDc0cB48E8DB81855A930C0C1165ea3dCe4Ba5C7", + "index": { + "from": 271840 + } + }, + "viction": { + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 0 + }, + "blockExplorers": [], + "chainId": 88, + "domainId": 88, + "displayName": "Viction", + "name": "viction", + "nativeToken": { + "decimals": 18, + "name": "Viction", + "symbol": "VIC" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://viction.blockpi.network/v1/rpc/public" + } + ], + "merkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "messageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "aggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "aggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "routingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "interchainSecurityModule": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "storageGasOracle": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "index": { - "from": 426670, - "chunk": 999 + "from": 73573878, + "chunk": 1000 } } }, "defaultRpcConsensusType": "fallback" -} +} \ No newline at end of file diff --git a/rust/config/testnet4_config.json b/rust/config/testnet4_config.json index d0fbd12598..76fcec119c 100644 --- a/rust/config/testnet4_config.json +++ b/rust/config/testnet4_config.json @@ -1,46 +1,45 @@ { "chains": { "alfajores": { + "blockExplorers": [ + { + "apiUrl": "https://api-alfajores.celoscan.io/api", + "family": "etherscan", + "name": "CeloScan", + "url": "https://alfajores.celoscan.io" + }, + { + "apiUrl": "https://explorer.celo.org/alfajores/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://explorer.celo.org/alfajores" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, "chainId": 44787, + "displayName": "Alfajores", "domainId": 44787, + "isTestnet": true, "name": "alfajores", - "protocol": "ethereum", - "displayName": "Alfajores", "nativeToken": { "decimals": 18, "name": "CELO", "symbol": "CELO" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://alfajores-forno.celo-testnet.org" } ], - "blockExplorers": [ - { - "name": "CeloScan", - "url": "https://alfajores.celoscan.io", - "apiUrl": "https://api-alfajores.celoscan.io/api", - "family": "etherscan" - }, - { - "name": "Blockscout", - "url": "https://explorer.celo.org/alfajores", - "apiUrl": "https://explorer.celo.org/alfajores/api", - "family": "blockscout" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 0, - "estimateBlockTime": 5 - }, - "isTestnet": true, "merkleRootMultisigIsmFactory": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", "messageIdMultisigIsmFactory": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", "aggregationIsmFactory": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", "aggregationHookFactory": "0x71bB34Ee833467443628CEdFAA188B2387827Cee", - "routingIsmFactory": "0x37308d498bc7B0f002cb02Cf8fA01770dC2169c8", "proxyAdmin": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", @@ -49,257 +48,55 @@ "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", "aggregationHook": "0xdBabD76358897E68E4964647C1fb8Bf524f5EFdB", "protocolFee": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", - "defaultIsm": "0xFBb1D475d2275D4643B6ba9Dae16f2F5465F9436", - "basegoerli": { - "MERKLE_ROOT_MULTISIG": "0x79c7799a9E686Ed93EEa208B67a2D7E93852F182", - "LEGACY_MULTISIG": "0x2c940Cfe1Fa2bdf2F2746ba52acDf2f0106c3cf1" - }, - "fuji": { - "MERKLE_ROOT_MULTISIG": "0x3c9eD95aD9a3613a7c036F44fB6745052861f74C", - "LEGACY_MULTISIG": "0x8d3cf154259E3aF61BA8F62410d9D0E644c6A19f" - }, - "mumbai": { - "MERKLE_ROOT_MULTISIG": "0x3e844a5dA9247756F0182D652680065502BCB078", - "LEGACY_MULTISIG": "0x5aABB6B06112FC5Bb9f7e1BC8Ec17c06B01fb6Ed" - }, - "bsctestnet": { - "MERKLE_ROOT_MULTISIG": "0x6b6bEF043905810994a60Cd08F7CdF5BB798f19c", - "LEGACY_MULTISIG": "0xf75B0F727f1dea2FA491caD2Faf8f3B04B705294" - }, - "goerli": { - "MERKLE_ROOT_MULTISIG": "0x2aEEc24F5997D7C19833Bf9f520b1e6c0Ef1Eda5", - "LEGACY_MULTISIG": "0x62f6EfC5a7A978821a5111261902b0A218ABF99F" - }, - "scrollsepolia": { - "MERKLE_ROOT_MULTISIG": "0x0caB24DBBDcA1E1bc27dd33481c09d9b306AE4a1", - "LEGACY_MULTISIG": "0xf8afa14F1cd8600d802C5a82A4406ca83629FC23" - }, - "sepolia": { - "MERKLE_ROOT_MULTISIG": "0x2183183bdc371c67302097DC60952e6E99484720", - "LEGACY_MULTISIG": "0x631749C86E90Cea9cF3Fb7686eBb6E80EfC9064c" - }, - "moonbasealpha": { - "MERKLE_ROOT_MULTISIG": "0x4A0eFE3CCE8DEDAD4faC8A8521F920F5C2f553fC", - "LEGACY_MULTISIG": "0x19eeB6f283aBAb2A9A70a11C2a8972D416aA2af9" - }, - "optimismgoerli": { - "MERKLE_ROOT_MULTISIG": "0x5E81d0bc59A632f319C75E339874209a29A0D9D2", - "LEGACY_MULTISIG": "0x533fb8cAb191B7094C545A191Ac770deBb1B8EEc" - }, - "arbitrumgoerli": { - "MERKLE_ROOT_MULTISIG": "0xf32f54Ec925AFf962ad164f94E7437E288901015", - "LEGACY_MULTISIG": "0x6BbF423f3742cB6594f9B9D844e780381aa4Ad91" - }, - "polygonzkevmtestnet": { - "MERKLE_ROOT_MULTISIG": "0xe48BDf85C269603AcB79444D874bb80DFDd93FC5", - "LEGACY_MULTISIG": "0x17B141F3278624B9882e275B8D1aC6a310afCCbe" - }, - "ROUTING": "0x2b5D84351aA22d860CE206EB79089F9bE8050890", - "AGGREGATION": "0x6f36a2dCC2eE58eB6b4B43330788Baa350F952EC", - "fallbackRoutingHook": "0xE1386148385275A27D29fC39Bd58a969CD5dCAF0", + "fallbackRoutingHook": "0x3528B1aeF3a3d29E0eae90ad777A2b4A6a48aC3F", + "testRecipient": "0x6489d13AcAd3B8dce4c5B31f375DE4f9451E7b38", + "testTokenRecipient": "0x92dC0a76452a9D9358D2d2dEd8CddA209DF67c45", + "routingIsmFactory": "0x30d9A03762431F8A917a0C469E7A62Bf55092Ca6", "index": { - "from": 20566929 + "from": 20231908 } }, - "arbitrumgoerli": { - "chainId": 421613, - "domainId": 421613, - "name": "arbitrumgoerli", - "protocol": "ethereum", - "displayName": "Arbitrum Goerli", - "displayNameShort": "Arb. Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://goerli-rollup.arbitrum.io/rpc" - } - ], - "blockExplorers": [ - { - "name": "Arbiscan", - "url": "https://goerli.arbiscan.io", - "apiUrl": "https://api-goerli.arbiscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 - }, - "isTestnet": true, - "merkleRootMultisigIsmFactory": "0x17D58eBb5Ea0E2d360c877E119FAef4C4052e6B9", - "messageIdMultisigIsmFactory": "0x922CeEe9e8832a047e6aD68Df4F079F271b73Ac3", - "aggregationIsmFactory": "0xC5Bb8CDD44B6c56695df45c7AA8012a97dD6ED13", - "aggregationHookFactory": "0x39a8711BF44165A2292Cb5cB43229659c2Bb11c9", - "routingIsmFactory": "0x735491727b9a1206E16AF4964aF68d5BB9122333", - "proxyAdmin": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "mailbox": "0x13dABc0351407d5aAa0A50003a166A73b4febfDc", - "validatorAnnounce": "0x4a01EEBa1CC20F47A2e60aE4ec932051601FcB9e", - "defaultIsm": "0x8C841784947dEa42f78263D54bec15c0cF65fA22", - "merkleTreeHook": "0xf0A38e1eEA49dAc7968F470c3aA0BDE2565A5d80", - "storageGasOracle": "0xFc8229ADB46D96056A6e451Fb3c55d60FFeD056f", - "interchainGasPaymaster": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "aggregationHook": "0xf852EB6b98d84A4296754043a56759a0Ae0E06df", - "protocolFee": "0x0358ba0D90ED2d90fB8cBb610F27C274D8077a0B", - "fallbackRoutingHook": "0x3Ce607F6FcE5Dfb9821f33504d86E04A4CD0C75f", - "index": { - "from": 50669378 - } - }, - "basegoerli": { - "chainId": 84531, - "domainId": 84531, - "name": "basegoerli", - "protocol": "ethereum", - "displayName": "Base Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://base-goerli.publicnode.com" - }, - { - "http": "https://goerli.base.org" - } - ], + "bsctestnet": { "blockExplorers": [ { - "name": "BaseScan", - "url": "https://goerli.basescan.org", - "apiUrl": "https://api-goerli.basescan.org/api", - "family": "etherscan" + "apiUrl": "https://api-testnet.bscscan.com/api", + "family": "etherscan", + "name": "BscScan", + "url": "https://testnet.bscscan.com" } ], "blocks": { "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 - }, - "isTestnet": true, - "merkleRootMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "messageIdMultisigIsmFactory": "0x54148470292C24345fb828B003461a9444414517", - "aggregationIsmFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", - "aggregationHookFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", - "routingIsmFactory": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", - "proxyAdmin": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "mailbox": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "validatorAnnounce": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "defaultIsm": "0x7147a48429D6FA06ec08Ed95b500e68356819f2b", - "merkleTreeHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "aggregationHook": "0x168e606fE4A9c8d7F83a3aAA132E831f153e4bAa", - "protocolFee": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "fallbackRoutingHook": "0x4Ece7b15ba5dCA2708dCE2812016683193102b9F", - "alfajores": { - "messageIdMultisigIsm": "0xCc44a0dB101E08CB0C13f928aa8d4686042dA576", - "merkleRootMultisigIsm": "0xf113Ea1a825505840451A09113A9bb53908ED8f1", - "staticAggregationIsm": "0x3F5Bd4c5B3c0D91F11Aa7b57099fc3d71e5d26A4" - }, - "fuji": { - "messageIdMultisigIsm": "0x27F351ae5f5C0A58ea18aEAD9c1Dc07a53401721", - "merkleRootMultisigIsm": "0xefde4A00A72ef1eb08FFAd3475bCbCd6D336cA4f", - "staticAggregationIsm": "0x7A86e695a0A051Bcf1CB83128613E7561090c677" - }, - "mumbai": { - "messageIdMultisigIsm": "0x8Ce0Eda893a1727D171A55515D11420f1841549c", - "merkleRootMultisigIsm": "0x8FEc8D3efEe43Fd096F53b851c60E465A04384C7", - "staticAggregationIsm": "0xE9094c6172b905972D77FF4F5E3f28a73A6c5Dc1" - }, - "bsctestnet": { - "messageIdMultisigIsm": "0x4E7EAcA5D2d3B01005cFb0528d3c52cfF09BCA36", - "merkleRootMultisigIsm": "0x3eE9CA0355ae566A8776B41b8D5f96A996d6144e", - "staticAggregationIsm": "0xe111Ac8b252c41D787b4b68F7987B8aAAC3bc1Ce" - }, - "goerli": { - "opStackIsm": "0x14EE2f01907707Ce8d13C4F5DBC40778b5b664e0" - }, - "scrollsepolia": { - "messageIdMultisigIsm": "0xD5161cD144Ca3C88F87A1db8228D33708c5938Cc", - "merkleRootMultisigIsm": "0xEc5b9b4dc7088B450aa74994EBf65d569d027716", - "staticAggregationIsm": "0x343513A06727FE3268Ef78c3E53591fC67599Ec1" - }, - "sepolia": { - "messageIdMultisigIsm": "0x4d7C3c7592A874b8e1d6396646fC45005874beC9", - "merkleRootMultisigIsm": "0x173C1626542165A5F1B5cE20c0026e5E23ef19Ad", - "staticAggregationIsm": "0x0CAC5c5e7Ce997C39B51877E15ce18C5a9a4d00D" - }, - "moonbasealpha": { - "messageIdMultisigIsm": "0xC2A3802E2f6150a4d633b5328c6C01797177D2C0", - "merkleRootMultisigIsm": "0x31C77b1C9AD838ed75AAb535fe4fDca1b5dEfFfD", - "staticAggregationIsm": "0xEAfC371E012CC89D5a335AfF326F38b474Bc5E27" + "estimateBlockTime": 3, + "reorgPeriod": 9 }, - "optimismgoerli": { - "messageIdMultisigIsm": "0x75e1ad301B0E96f82d5a87D0Eb95cF1A250bf071", - "merkleRootMultisigIsm": "0xB98cE9298891Be9371F9b7fE021beaDBaD1189DD", - "staticAggregationIsm": "0x1629F7cf0561b7863C2E31F126Ab726Fe2bc307b" - }, - "arbitrumgoerli": { - "messageIdMultisigIsm": "0x19b34FDb98F4A6F41e803225bf4E258b7eC55876", - "merkleRootMultisigIsm": "0x93db3fA75bA99a040e6D5d3474350A0564E6E722", - "staticAggregationIsm": "0x830e7814c7C02a174208786CA65917405B845f20" - }, - "polygonzkevmtestnet": { - "messageIdMultisigIsm": "0x2c1bF9f9FE80D41Db769050Fb096be9aB41327Bc", - "merkleRootMultisigIsm": "0x8B2069eaa894995141F1879dDB612E05874F0116", - "staticAggregationIsm": "0x74bf2D0B09D371D54BD087df006786384E5525F8" - }, - "domainRoutingIsm": "0x4c8b1B480988f24b0F3602A4de641776586DbF90", - "index": { - "from": 11714799 - } - }, - "bsctestnet": { "chainId": 97, + "displayName": "BSC Testnet", "domainId": 97, + "isTestnet": true, "name": "bsctestnet", - "protocol": "ethereum", - "displayName": "BSC Testnet", "nativeToken": { "decimals": 18, "name": "BNB", "symbol": "BNB" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://bsc-testnet.publicnode.com" }, - { - "http": "https://bsc-testnet.public.blastapi.io" - }, { "http": "https://bsc-testnet.blockpi.network/v1/rpc/public" } ], - "blockExplorers": [ - { - "name": "BscScan", - "url": "https://testnet.bscscan.com", - "apiUrl": "https://api-testnet.bscscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 9, - "estimateBlockTime": 3 + "transactionOverrides": { + "gasPrice": 80000000000 }, - "isTestnet": true, "merkleRootMultisigIsmFactory": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", "messageIdMultisigIsmFactory": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", "aggregationIsmFactory": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", "aggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", - "routingIsmFactory": "0xea12ECFD1f241da323e93F12b4ed936403990190", "proxyAdmin": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", - "defaultIsm": "0xEf7cacD303D1886b3dE396B45a184d16f39248E0", "storageGasOracle": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", @@ -307,68 +104,38 @@ "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465", - "alfajores": { - "MERKLE_ROOT_MULTISIG": "0x352b56D019C85B1e7Db163c08876f0E4060e0EC0", - "LEGACY_MULTISIG": "0xc9764347811A0DDB36180273ADB6226E25e8134f" - }, - "basegoerli": { - "MERKLE_ROOT_MULTISIG": "0x32e4C6C49e9a568Da17f87ee80A9542B5c3cE0bf", - "LEGACY_MULTISIG": "0x55eC2978E6c63DbF4AD2670d36539737d0341c76" - }, - "fuji": { - "MERKLE_ROOT_MULTISIG": "0x4E368F65Ef1F765acf149052A5ad656AB4ecFeaf", - "LEGACY_MULTISIG": "0xB751c8366708A8cAa8E55580A6aE2CCAeD97Ca76" - }, - "mumbai": { - "MERKLE_ROOT_MULTISIG": "0x13999996CbA4E3BB793726e6Ed66B6DD0b8c19A6", - "LEGACY_MULTISIG": "0x6Bc6514ace1edf23cea6477d3fEDA7954571940C" - }, - "goerli": { - "MERKLE_ROOT_MULTISIG": "0xD953CC38fb2FF4CDE97739A024cd7D87e398Dcaf", - "LEGACY_MULTISIG": "0x16D1B0F3B3279c2793337FE8AfCC804c639C6627" - }, - "scrollsepolia": { - "MERKLE_ROOT_MULTISIG": "0xcc7e00D24249D7BCF0aD2c79f6D90951C80CB47B", - "LEGACY_MULTISIG": "0x0521e8571a72445B8a5F758ffF2707d055C0b53F" - }, - "sepolia": { - "MERKLE_ROOT_MULTISIG": "0x16A3e41135b1339B925c6e49E64CEEadA5bAdBb7", - "LEGACY_MULTISIG": "0x5AE613e0D9aEF132f7034d6eF36b418ac9dE1f2E" - }, - "moonbasealpha": { - "MERKLE_ROOT_MULTISIG": "0x6e37E41e7E4682d0dF68E34606f3254D89B7cDD0", - "LEGACY_MULTISIG": "0x8FEA7B15d7BCA0E09cb5B3BEB1dE71738A4a92B5" - }, - "optimismgoerli": { - "MERKLE_ROOT_MULTISIG": "0xE61fF63Ff2000Da24B72f1C29209554E8Ba79171", - "LEGACY_MULTISIG": "0x363Fa03085A7D089245d0b637D7FfCF5A1aacb7F" - }, - "arbitrumgoerli": { - "MERKLE_ROOT_MULTISIG": "0x91aB65A54DaE3B71f8cE31A20B42e4f337126ffF", - "LEGACY_MULTISIG": "0x32241E9d721E0D61c82BFF037C8A6e33D6cB8F18" - }, - "polygonzkevmtestnet": { - "MERKLE_ROOT_MULTISIG": "0xdFf1FE7F04c03A8788a728E3fcAe8A50eEAd6e11", - "LEGACY_MULTISIG": "0x6528cB0B4f9065bB4562096963a6C98BC619da7e" - }, - "ROUTING": "0x5a052832973d0A988cb553C46F5CfAcA4E078c92", - "AGGREGATION": "0x16c328B3976e5624D8AC38E362574e694676Ac6b", - "fallbackRoutingHook": "0xc278DDe83018F0e8c624b208e6D9E6251d263B1d", + "fallbackRoutingHook": "0x2670ED2EC08cAd135307556685a96bD4c16b007b", + "testRecipient": "0xfbcD1c00a3d809f36cC1A15918694B17B32c0b6c", + "testTokenRecipient": "0x260f6024119549a40595d0937471e607411E8ea5", "index": { - "from": 34506952 + "from": 34323977 } }, "fuji": { + "blockExplorers": [ + { + "apiUrl": "https://api-testnet.snowtrace.io/api", + "family": "etherscan", + "name": "SnowTrace", + "url": "https://testnet.snowtrace.io" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 2, + "reorgPeriod": 3 + }, "chainId": 43113, + "displayName": "Fuji", "domainId": 43113, + "isTestnet": true, "name": "fuji", - "protocol": "ethereum", - "displayName": "Fuji", "nativeToken": { "decimals": 18, "name": "Avalanche", "symbol": "AVAX" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://api.avax-test.network/ext/bc/C/rpc", @@ -377,276 +144,51 @@ } } ], - "blockExplorers": [ - { - "name": "SnowTrace", - "url": "https://testnet.snowtrace.io", - "apiUrl": "https://api-testnet.snowtrace.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 3, - "estimateBlockTime": 2 - }, - "isTestnet": true, "merkleRootMultisigIsmFactory": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", "messageIdMultisigIsmFactory": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", "aggregationIsmFactory": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", "aggregationHookFactory": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", - "routingIsmFactory": "0xf9271189Cb30AD1F272f1A9EB2272224135B9350", "proxyAdmin": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", - "defaultIsm": "0x2d6891ac730E14521Ea0C45A00d3Eb62f2e432df", "merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", "storageGasOracle": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", "protocolFee": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", - "alfajores": { - "MERKLE_ROOT_MULTISIG": "0xd8325468566964245FAdf15Ef0BD3587B598b3bc", - "LEGACY_MULTISIG": "0x04cB6d0616b0059751F2CFc0Ea7dde62d959CEEC" - }, - "basegoerli": { - "MERKLE_ROOT_MULTISIG": "0x0f56E6D62ddc37cbe2e4a8FA31dCA994c67c2A3d", - "LEGACY_MULTISIG": "0x8F2e9a004d3F0e8E1CEbE52375b44D79fB6Ff6ea" - }, - "mumbai": { - "MERKLE_ROOT_MULTISIG": "0x5C38d0d8686aAb40F81Ff5D9929bFD01f9C38899", - "LEGACY_MULTISIG": "0x4509Ee247C37738DC067b22fc84be9b8d7c18e83" - }, - "bsctestnet": { - "MERKLE_ROOT_MULTISIG": "0xA62d029c16354F5845AC3c2728C8027645f16517", - "LEGACY_MULTISIG": "0xb7FFc29CF7be4a969282AE3E171c953D43212C30" - }, - "goerli": { - "MERKLE_ROOT_MULTISIG": "0x1b50E9729155D3F7e3eE886003818D1Fa80920Fd", - "LEGACY_MULTISIG": "0x4D57430F0cb764A9835521acad3653199b039327" - }, - "scrollsepolia": { - "MERKLE_ROOT_MULTISIG": "0xA90541C64f3eDc22a47Bc28D7cfdfE17F573A434", - "LEGACY_MULTISIG": "0xc1440Dbb7FeE4179D85d9dAD598A4E78cc5E222B" - }, - "sepolia": { - "MERKLE_ROOT_MULTISIG": "0xE5dd4fDE4c99DCeB5F753B6F536E99523dA6EE06", - "LEGACY_MULTISIG": "0x4Bd1319ABc8Bf7633198A1f0e529158D91cfDa9F" - }, - "moonbasealpha": { - "MERKLE_ROOT_MULTISIG": "0x40D79400513cf7fccaE4d110d3B74c23B08337B6", - "LEGACY_MULTISIG": "0x0a2cD3A4D324A322821A829A1211a8483214ef2a" - }, - "optimismgoerli": { - "MERKLE_ROOT_MULTISIG": "0x33b1A7480EC6dB39bE5919d695d06776E3bD7fA9", - "LEGACY_MULTISIG": "0x807AEb2b851Cc843Ae3d4D6f931cAEA893698823" - }, - "arbitrumgoerli": { - "MERKLE_ROOT_MULTISIG": "0x247Dd2058D239ADC1348674BFA929F952Af331a0", - "LEGACY_MULTISIG": "0xD6e42987D0533cED33fcC9003B708bC56896C7Fe" - }, - "polygonzkevmtestnet": { - "MERKLE_ROOT_MULTISIG": "0xD02753F38198DEB05924e503867f0e7bEC04d500", - "LEGACY_MULTISIG": "0x1A28Bc9F1D65661C4338bc374D4f133cB5763c50" - }, - "ROUTING": "0xd914570ea19385334e8DAcf5F1794f75895010dF", - "AGGREGATION": "0x39202263246c48eb80879870e9c547Bc787cdfC6", - "fallbackRoutingHook": "0x50897eDCb3f1bB2A90f20DA5a8dF0e5c57A146e3", + "fallbackRoutingHook": "0xc684f7F50DB4b2563218512e021fBdd0BeD6b57E", + "testRecipient": "0x44a7e1d76fD8AfA244AdE7278336E3D5C658D398", + "testTokenRecipient": "0x9CC10c844B3Bbae2444E39991aB027C4A05D1F2e", + "routingIsmFactory": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", "index": { - "from": 27090437 + "from": 26503317 } }, - "goerli": { - "chainId": 5, - "domainId": 5, - "name": "goerli", - "protocol": "ethereum", - "displayName": "Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" - }, - { - "http": "https://rpc.ankr.com/eth_goerli" - }, - { - "http": "https://eth-goerli.public.blastapi.io" - } - ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://goerli.etherscan.io", - "apiUrl": "https://api-goerli.etherscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 2, - "estimateBlockTime": 13 - }, - "isTestnet": true, - "merkleRootMultisigIsmFactory": "0x8e43aCfb338B137A3befd9b92BfD84E128adE0B8", - "messageIdMultisigIsmFactory": "0xDdB54502A8e2a31C48148C62A8a9E83a693d6173", - "aggregationIsmFactory": "0x8a176773d54292123d271FA0B9C7C8Def4c3a31b", - "aggregationHookFactory": "0x6bc243963f80AEa80948e8538bB114d4122DD9c5", - "routingIsmFactory": "0xd16c3f34d6A2e62185aC61f76F83D3AA1E969018", - "proxyAdmin": "0x0EdB3604D230963ecE9d83963164CFe2fDef576B", - "defaultIsm": "0x8BbdB0023ef47992b3E83E2B1B290D72A7477EfE", - "storageGasOracle": "0xeC34c715ee6d050b2172E8aF650Db779561266C1", - "interchainGasPaymaster": "0x0cD26594ea6c6526927C0F5225AC09F6288e7140", - "aggregationHook": "0x2dF77b3efe9B8f9aEDf7bFC86f40B048178d8116", - "protocolFee": "0x9293B8dAcA7933765de499C992B0Fa86Bb104b0f", - "merkleTreeHook": "0x28c294C61D3dE053462d2Cfa5d5f8c8D70605A59", - "mailbox": "0x49cfd6Ef774AcAb14814D699e3F7eE36Fdfba932", - "validatorAnnounce": "0x3c182AD9cA8A71bc107Ef440C2667E8360e1158E", - "alfajores": { - "messageIdMultisigIsm": "0x4683D18bD896acf67bC022f2dc0Cf9913E83a8C2", - "merkleRootMultisigIsm": "0xA09db9436C89376FbDCC731c61f3e96194d77549", - "staticAggregationIsm": "0xc80989C697d2dB54827293E9399461F5E17b50AE" - }, - "basegoerli": { - "messageIdMultisigIsm": "0x6EF750Fef341239fa0Fd7c9081508590A9527C69", - "merkleRootMultisigIsm": "0x8BB1F2bc7dd305440Dfe4E91c003510ef14044Ba", - "staticAggregationIsm": "0x110C79D5104f3d6a0291397d2f2896f10157C0f6" - }, - "fuji": { - "messageIdMultisigIsm": "0x44B0757b1C9512cff0C51089c7D5094C47D9316E", - "merkleRootMultisigIsm": "0xf3C288C5a4E17DCE46d06257B36ec7f4A82C1aF9", - "staticAggregationIsm": "0x0DB339E394665A7bDCb621B8f05A6dE746E4d28D" - }, - "mumbai": { - "MERKLE_ROOT_MULTISIG": "0x374A6953e95ADdC76c2d87cC7B89C3227DAf1Fd9", - "LEGACY_MULTISIG": "0x5d05c8152667f73e219CBb37531425C09e894de2", - "messageIdMultisigIsm": "0x374A6953e95ADdC76c2d87cC7B89C3227DAf1Fd9", - "merkleRootMultisigIsm": "0x5d05c8152667f73e219CBb37531425C09e894de2", - "staticAggregationIsm": "0x582643Ca4235195167b2195A4a8F71BDe56b8A1F" - }, - "bsctestnet": { - "MERKLE_ROOT_MULTISIG": "0x72a6Fcd41b68e6FAE71A5d0F21e574F3e6Ec5B9D", - "LEGACY_MULTISIG": "0x0F7C158d6afea27987f655A2d464E16fAe2aD8c7", - "messageIdMultisigIsm": "0x72a6Fcd41b68e6FAE71A5d0F21e574F3e6Ec5B9D", - "merkleRootMultisigIsm": "0x0F7C158d6afea27987f655A2d464E16fAe2aD8c7", - "staticAggregationIsm": "0x444a6FAc09220024f9D65943598829Fd4BD38bD7" - }, - "scrollsepolia": { - "MERKLE_ROOT_MULTISIG": "0x89c32A1Ca1Ae39886b18B0466dA5C97a6e031ca2", - "LEGACY_MULTISIG": "0x050798aFB2EF5A847f6A77764F19D207086bdDD3", - "messageIdMultisigIsm": "0x89c32A1Ca1Ae39886b18B0466dA5C97a6e031ca2", - "merkleRootMultisigIsm": "0x050798aFB2EF5A847f6A77764F19D207086bdDD3", - "staticAggregationIsm": "0x4A58088ff4B77dCcf8678A04C945CC552D6e65D6" - }, - "sepolia": { - "MERKLE_ROOT_MULTISIG": "0xf45819C46A54cdD816DC8c9EE580712e76eaAc0C", - "LEGACY_MULTISIG": "0xD22fddaB4595a7C0f78Fa3d20cc9485b19Ee861d", - "messageIdMultisigIsm": "0xf45819C46A54cdD816DC8c9EE580712e76eaAc0C", - "merkleRootMultisigIsm": "0xD22fddaB4595a7C0f78Fa3d20cc9485b19Ee861d", - "staticAggregationIsm": "0xE0593C964e8BA2481d0f23BAF27e36DC7CCd9F13" - }, - "moonbasealpha": { - "MERKLE_ROOT_MULTISIG": "0x9B04e5971738c427c1b0660e89859BD4164B0d67", - "LEGACY_MULTISIG": "0xD3Ad23079611e2cc306E8a60ddFdAD250512d43F", - "messageIdMultisigIsm": "0x9B04e5971738c427c1b0660e89859BD4164B0d67", - "merkleRootMultisigIsm": "0xD3Ad23079611e2cc306E8a60ddFdAD250512d43F", - "staticAggregationIsm": "0xc3AF7BC41D75909cFA34e424Fd505b7121C0CF90" - }, - "optimismgoerli": { - "MERKLE_ROOT_MULTISIG": "0x432B0ad68cbA0DFf644F30CC8b3275663Cb692E8", - "LEGACY_MULTISIG": "0xA50e98B2d52d4c9B242bD86f1B5C54D7E33190a6", - "messageIdMultisigIsm": "0x432B0ad68cbA0DFf644F30CC8b3275663Cb692E8", - "merkleRootMultisigIsm": "0xA50e98B2d52d4c9B242bD86f1B5C54D7E33190a6", - "staticAggregationIsm": "0x5d77806Fde4BbA8a665720d528CEACBb694B43b2" - }, - "arbitrumgoerli": { - "MERKLE_ROOT_MULTISIG": "0x07733788489efD7F10533ab889144A7C67F6d9EF", - "LEGACY_MULTISIG": "0x44D117A356AfFE5C67D4A53b3eC1Ded1260Bd20d", - "messageIdMultisigIsm": "0x07733788489efD7F10533ab889144A7C67F6d9EF", - "merkleRootMultisigIsm": "0x44D117A356AfFE5C67D4A53b3eC1Ded1260Bd20d", - "staticAggregationIsm": "0x7F28dFed655a4C1a7AeC911B983Bc68EF9195D90" - }, - "polygonzkevmtestnet": { - "MERKLE_ROOT_MULTISIG": "0x070eDaC389b85aEe38507339Cd80aeBd95b1cb22", - "LEGACY_MULTISIG": "0x5c7e4Eb938DB4f1dE746aBb01eC1228EC38Ed3bd", - "messageIdMultisigIsm": "0x070eDaC389b85aEe38507339Cd80aeBd95b1cb22", - "merkleRootMultisigIsm": "0x5c7e4Eb938DB4f1dE746aBb01eC1228EC38Ed3bd", - "staticAggregationIsm": "0xE2fc443F458B6DbdD8987CDb6830d4F9a1AfDFeb" - }, - "ROUTING": "0x656cC76A0D05bC3F40c11DEBe0fF6EEEDabB856e", - "AGGREGATION": "0x595e45990F67A9795cE4FEB0b6ba4684c6258cD0", - "domainRoutingIsm": "0xE2401EB414B41DC2A97100f2Dab75bBcD7377119", - "opStackHook": "0xce59701919507F2d379270657A4e410F570aBe0D", - "fallbackRoutingHook": "0xDd66CB60D4Ffb7f0d8FB91CB1D20aBcaBC82900a", - "index": { - "from": 9954053 - } - }, - "moonbasealpha": { - "chainId": 1287, - "domainId": 1287, - "name": "moonbasealpha", - "protocol": "ethereum", - "displayName": "Moonbase Alpha", - "displayNameShort": "Moonbase", - "nativeToken": { - "decimals": 18, - "name": "DEV", - "symbol": "DEV" - }, - "rpcUrls": [ - { - "http": "https://rpc.api.moonbase.moonbeam.network" - } - ], + "mumbai": { "blockExplorers": [ { - "name": "MoonScan", - "url": "https://moonbase.moonscan.io", - "apiUrl": "https://api-moonbase.moonscan.io/api", - "family": "etherscan" + "apiUrl": "https://api-testnet.polygonscan.com/api", + "family": "etherscan", + "name": "PolygonScan", + "url": "https://mumbai.polygonscan.com" } ], "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 12 + "confirmations": 3, + "estimateBlockTime": 5, + "reorgPeriod": 32 }, - "isTestnet": true, - "merkleRootMultisigIsmFactory": "0xA59Ba0A8D4ea5A5DC9c8B0101ba7E6eE6C3399A4", - "messageIdMultisigIsmFactory": "0x8f919348F9C4619A196Acb5e377f49E5E2C0B569", - "aggregationIsmFactory": "0x0048FaB53526D9a0478f66D660059E3E3611FE3E", - "aggregationHookFactory": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "routingIsmFactory": "0x385C7f179168f5Da92c72E17AE8EF50F3874077f", - "proxyAdmin": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "mailbox": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "defaultIsm": "0x367241b198C29C6a8B2aa0c9D8350B6F81C2Abb3", - "merkleTreeHook": "0x155B1CD2f7Cbc58d403B9BE341FaB6CD77425175", - "storageGasOracle": "0x62fA20dE68Dbe425f0bc474b12235a4F8449E608", - "interchainGasPaymaster": "0x92F05669A354a032A84FcfABfD13beE1aBc5bFd0", - "aggregationHook": "0xaA9d918C49Cea0D2a877252aFb7976B6e3A48623", - "protocolFee": "0xe2A73F106902983452713F24Bd019F6eb8712986", - "validatorAnnounce": "0x07543860AE9E72aBcF2Bae9827b23621A64Fa416", - "fallbackRoutingHook": "0x6c9EB73793F9Cd535DB1bF86dC307f6d899b2fE3", - "index": { - "from": 5355188 - } - }, - "mumbai": { "chainId": 80001, + "displayName": "Mumbai", "domainId": 80001, + "isTestnet": true, "name": "mumbai", - "protocol": "ethereum", - "displayName": "Mumbai", "nativeToken": { + "decimals": 18, "name": "MATIC", - "symbol": "MATIC", - "decimals": 18 + "symbol": "MATIC" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://rpc.ankr.com/polygon_mumbai", @@ -654,25 +196,8 @@ "maxBlockRange": 10000, "minBlockNumber": 22900000 } - }, - { - "http": "https://matic-mumbai.chainstacklabs.com" } ], - "blockExplorers": [ - { - "name": "PolygonScan", - "url": "https://mumbai.polygonscan.com", - "apiUrl": "https://api-testnet.polygonscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 3, - "reorgPeriod": 32, - "estimateBlockTime": 5 - }, - "isTestnet": true, "transactionOverrides": { "maxFeePerGas": 150000000000, "maxPriorityFeePerGas": 40000000000 @@ -681,8 +206,6 @@ "messageIdMultisigIsmFactory": "0x23c2483ab814177bA79DCDCb5dFA1B105387AAB1", "aggregationIsmFactory": "0x54b0d9AB6a99E9C9425D20fa4D9eE9dbf067e886", "aggregationHookFactory": "0x54CA9De95B37365909364672D363D2ecFC4e1Af4", - "routingIsmFactory": "0x276C07098879f44F6C4a6ab91B6AAca6a56AD4B1", - "defaultIsm": "0xCbDc6B43fcC9465E18E0eE925170f6F2893625b8", "merkleTreeHook": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", "proxyAdmin": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", "storageGasOracle": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", @@ -691,311 +214,160 @@ "protocolFee": "0x244d1F7e30Be144A87602905baBF86630e8f39DC", "mailbox": "0x2d1889fe5B092CD988972261434F7E5f26041115", "validatorAnnounce": "0x99303EFF09332cDd93E8BC8b2F07b2416e4501e5", - "alfajores": { - "MERKLE_ROOT_MULTISIG": "0x2ab7f5DeC6f8Ef411315487c23a0FD1955824274", - "LEGACY_MULTISIG": "0x46c3f1a2734568Ab4582E5e990Be37e9A90c8d5C" - }, - "basegoerli": { - "MERKLE_ROOT_MULTISIG": "0xcA58F63034D79d00742153636f40975616996569", - "LEGACY_MULTISIG": "0x256578935Ea39D5B6eD49722C20D6c7734c17442" - }, - "fuji": { - "MERKLE_ROOT_MULTISIG": "0xbd3Cd908B44b1AB4A5c9A2E2Abc567dbd3265BaF", - "LEGACY_MULTISIG": "0xB24FadDB99664D85755a4ee5402Cce2f936e2A85" - }, - "bsctestnet": { - "MERKLE_ROOT_MULTISIG": "0x20fC9FF58AA29FB015195735Cb87999D1169acA7", - "LEGACY_MULTISIG": "0x1A445E9f7f5E6230A11a960E1d77af94ec1Dc70B" - }, - "goerli": { - "MERKLE_ROOT_MULTISIG": "0x94AfA49591B42C681CaFcBA9C1deb6d394dd358c", - "LEGACY_MULTISIG": "0x0025156297d59772cc3836EB15BcEb7b65bF2Ac0" - }, - "scrollsepolia": { - "MERKLE_ROOT_MULTISIG": "0xf90Ad611612d3D7f45C1EB525e2f487373286717", - "LEGACY_MULTISIG": "0x00a20F2637437151cBDB5DD8e80672458bcCAE68" - }, - "sepolia": { - "MERKLE_ROOT_MULTISIG": "0x4234b78713bB6623b123CdBC4E163F497643D2b1", - "LEGACY_MULTISIG": "0x22A56d69B838897dacA4f87d6cC9c602de6bFaDC" - }, - "moonbasealpha": { - "MERKLE_ROOT_MULTISIG": "0xDbb5fc87d9CA83dE79E111acFb96882A70AE490c", - "LEGACY_MULTISIG": "0xe32F2c4129FeA483c55241Ab11413e0E9F38c716" - }, - "optimismgoerli": { - "MERKLE_ROOT_MULTISIG": "0xeD282a84F9cd27A827fF07D6894FE9dc63a64D63", - "LEGACY_MULTISIG": "0x45B1784587aCE88E534588e8d43E1A3282ecBA8b" - }, - "arbitrumgoerli": { - "MERKLE_ROOT_MULTISIG": "0x75204D086D263d475A6390b476126B2BeB6c37A7", - "LEGACY_MULTISIG": "0x1004f5636f3348bbAae626cAf5b6578f2f7e0425" - }, - "polygonzkevmtestnet": { - "MERKLE_ROOT_MULTISIG": "0xBae6ED82c61C1aa9a0df9741193aE5a8eE84fB28", - "LEGACY_MULTISIG": "0x2487E9d66E62B3de58C246b3e1d588f41b39d433" - }, - "ROUTING": "0xBAad51c021e4bb94b9a10b1fC82b773Ca9e35D30", - "AGGREGATION": "0xE794ff773fcd25ad43749CbCAE8e69f7f64810EF", - "fallbackRoutingHook": "0xFA005A892EbDACFcc3f1EF0111A7406c779c3647", + "fallbackRoutingHook": "0x31191BA83143b4745745389fEe64990c65F36829", + "testRecipient": "0xF45A4D54223DA32bf7b5D43a9a460Ef3C94C713B", + "testTokenRecipient": "0x57d098e6952B6C1c85Ce0B68C9Deada3dCf7D05A", + "routingIsmFactory": "0x832Ea28749C93C05E5AaF8207E4e61Bd56aE3877", "index": { - "from": 41618135 + "from": 40879305 } }, - "optimismgoerli": { - "chainId": 420, - "domainId": 420, - "name": "optimismgoerli", - "protocol": "ethereum", - "displayName": "Optimism Goerli", - "displayNameShort": "Opt. Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://goerli.optimism.io" - } - ], + "plumetestnet": { "blockExplorers": [ { - "name": "Etherscan", - "url": "https://goerli-optimism.etherscan.io", - "apiUrl": "https://api-goerli-optimism.etherscan.io/api", - "family": "etherscan" + "apiUrl": "https://plume-testnet.explorer.caldera.xyz/api", + "family": "blockscout", + "name": "Plume Testnet Explorer", + "url": "https://plume-testnet.explorer.caldera.xyz" } ], "blocks": { "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 + "estimateBlockTime": 3, + "reorgPeriod": 0 }, + "chainId": 161221135, + "displayName": "Plume Testnet", + "domainId": 161221135, "isTestnet": true, - "merkleRootMultisigIsmFactory": "0xAbC25d7daDD748948F5cC912A807b0f8FcBb56a9", - "messageIdMultisigIsmFactory": "0x7868B6026E36C4b6E2ca6a0CaBDb1A6D0CcC443B", - "aggregationIsmFactory": "0xf666A33C451E8371907aD22dd545E1678fCa1582", - "aggregationHookFactory": "0x00cE81F7B02e0673815a8b0A54e62AeabDE78685", - "routingIsmFactory": "0x1807e7d67F00393a49c445E367face82D65d86c7", - "proxyAdmin": "0x800b4be4Dc91E56DE934D9f16888d113eFf89Ebb", - "mailbox": "0xB5f021728Ea6223E3948Db2da61d612307945eA2", - "validatorAnnounce": "0x24D31e12E4d3bc2C46C994FcE0c828b218A1aeAb", - "defaultIsm": "0x986e076aA22342282B6c2a287e9AaBC8a36161f3", - "merkleTreeHook": "0xFEe074B31B5B259eB3109737bE13D39B853b47b9", - "storageGasOracle": "0x4927C33299091033D935C15DE6b6073164e99BE0", - "interchainGasPaymaster": "0x02A7661273528EfF3d78CBE7CbD1a717b28B89fC", - "aggregationHook": "0x1C8A2588b8038BF9B7b1b60dD0EdF5b995A45599", - "protocolFee": "0x962e30F6A3ECDA85c7fa1FcF38cD04efA991Ee20", - "fallbackRoutingHook": "0xba962f31B8DE02238fDdf8CE6a21260Af8C5Dd2F", - "index": { - "from": 16395052 - } - }, - "polygonzkevmtestnet": { - "protocol": "ethereum", - "chainId": 1442, - "domainId": 1442, - "name": "polygonzkevmtestnet", - "displayName": "Polygon zkEVM Testnet", + "name": "plumetestnet", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", + "technicalStack": "arbitrumnitro", "rpcUrls": [ { - "http": "https://rpc.public.zkevm-test.net" + "http": "https://plume-testnet.rpc.caldera.xyz/http" } ], + "rpcConsensusType": "single", + "transactionOverrides": { + "gasPrice": 1000000 + }, + "merkleRootMultisigIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "messageIdMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "aggregationIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "aggregationHookFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "routingIsmFactory": "0x54148470292C24345fb828B003461a9444414517", + "mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "proxyAdmin": "0x589C201a07c26b4725A4A829d772f24423da480B", + "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "interchainSecurityModule": "0x7B40deb01A127E3A5eECdbCDF263e41899a90078", + "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "testRecipient": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "index": { + "from": 4206 + } + }, + "scrollsepolia": { "blockExplorers": [ { - "name": "PolygonScan", - "url": "https://testnet-zkevm.polygonscan.com/", - "apiUrl": "https://api-testnet-zkevm.polygonscan.com/api", - "family": "etherscan" + "apiUrl": "https://api-sepolia.scrollscan.com/api", + "family": "etherscan", + "name": "Scroll Explorer", + "url": "https://sepolia.scrollscan.dev/" } ], "blocks": { "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 + "estimateBlockTime": 3, + "reorgPeriod": 1 }, - "isTestnet": true, - "merkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "messageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "aggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "aggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "routingIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "proxyAdmin": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", - "mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "validatorAnnounce": "0x7914A3349107A7295Bbf2374db5A973d73D1b324", - "defaultIsm": "0xfF5512D605018c185ac159B20354994BD3d75Ae2", - "merkleTreeHook": "0x68311418D79fE8d96599384ED767d225635d88a8", - "storageGasOracle": "0x3707bc8C7342aA6f693bCe1Bd7671Fca146F7F0A", - "interchainGasPaymaster": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "aggregationHook": "0x0Fd2C6F0Ad45e766660b9fDebCF36a2AD69536D1", - "protocolFee": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "fallbackRoutingHook": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", - "index": { - "from": 3019086 - } - }, - "scrollsepolia": { "chainId": 534351, + "displayName": "Scroll Sepolia", "domainId": 534351, + "isTestnet": true, "name": "scrollsepolia", - "protocol": "ethereum", - "displayName": "Scroll Sepolia", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { "http": "https://sepolia-rpc.scroll.io" } ], - "blockExplorers": [ - { - "name": "Scroll Explorer", - "url": "https://sepolia.scrollscan.dev/", - "apiUrl": "https://api-sepolia.scrollscan.com/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 - }, - "isTestnet": true, - "transactionOverrides": { - "gasLimit": 5000000 - }, "merkleRootMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", "messageIdMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", "aggregationIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "aggregationHookFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "routingIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", "proxyAdmin": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381", - "defaultIsm": "0x4983DDBd279DB930cd883B44AF3f0da7567a06E6", "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", "storageGasOracle": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", "protocolFee": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "alfajores": { - "MERKLE_ROOT_MULTISIG": "0xd9cbF08CaC905F78d961A72716Ef8EeD3aB7e5Eb", - "LEGACY_MULTISIG": "0xdC87a06493FaDE515c623464BE3F1580E5d8EC9A", - "MESSAGE_ID_MULTISIG": "0xd9cbF08CaC905F78d961A72716Ef8EeD3aB7e5Eb" - }, - "basegoerli": { - "MERKLE_ROOT_MULTISIG": "0xCC7DfEB63bbE762D46C9bb1B27D680b226A94b23", - "LEGACY_MULTISIG": "0xE38a0156e2968d336D43efEC716De54cF243974A", - "MESSAGE_ID_MULTISIG": "0xCC7DfEB63bbE762D46C9bb1B27D680b226A94b23" - }, - "fuji": { - "MERKLE_ROOT_MULTISIG": "0x6479E7a0f62db3E4cfC16dfa4960953572cE4d91", - "LEGACY_MULTISIG": "0x8671d101e6A5430D856234811e493CbB3e08f00b", - "MESSAGE_ID_MULTISIG": "0x6479E7a0f62db3E4cfC16dfa4960953572cE4d91" - }, - "mumbai": { - "MERKLE_ROOT_MULTISIG": "0xf449003Fdb780bEFac5F946CfC12172dc1e732DF", - "LEGACY_MULTISIG": "0x8838412195Eec3B10Ad78aE3e05d7b3f29d93A4B", - "MESSAGE_ID_MULTISIG": "0xf449003Fdb780bEFac5F946CfC12172dc1e732DF" - }, - "bsctestnet": { - "MERKLE_ROOT_MULTISIG": "0x3074E40aA0C52d83FbB9Be642946f5fadA2212bf", - "LEGACY_MULTISIG": "0xB766e93CFf3B16cBfA698955E71f27a0bE0BD42e", - "MESSAGE_ID_MULTISIG": "0x3074E40aA0C52d83FbB9Be642946f5fadA2212bf" - }, - "goerli": { - "MERKLE_ROOT_MULTISIG": "0xf98a3dfCC6384b6f19F3cdfD992552CA876261f3", - "LEGACY_MULTISIG": "0xde5013E6Ace9c9Da898f98Fafd1a85547f5B893b", - "MESSAGE_ID_MULTISIG": "0xf98a3dfCC6384b6f19F3cdfD992552CA876261f3" - }, - "sepolia": { - "MERKLE_ROOT_MULTISIG": "0xD3a84669f2F1896a1E9f77c7Dfe93F32EFf0Ea4E", - "LEGACY_MULTISIG": "0x4a072E0EB5AE0087A080F7b24E56d140E2aDd3f1", - "MESSAGE_ID_MULTISIG": "0xD3a84669f2F1896a1E9f77c7Dfe93F32EFf0Ea4E" - }, - "moonbasealpha": { - "MERKLE_ROOT_MULTISIG": "0x47D285b171B5E2FDDf387EBdDcBeC58b188445De", - "LEGACY_MULTISIG": "0x3C1FA7196AAEAd718e741014B5Af7D46068335e3", - "MESSAGE_ID_MULTISIG": "0x47D285b171B5E2FDDf387EBdDcBeC58b188445De" - }, - "optimismgoerli": { - "MERKLE_ROOT_MULTISIG": "0xef317A77273BE269a2d9861F3e10808126608f4A", - "LEGACY_MULTISIG": "0x21c9A0085c58E08693bC9Bf106066B8b23c51A19", - "MESSAGE_ID_MULTISIG": "0xef317A77273BE269a2d9861F3e10808126608f4A" - }, - "arbitrumgoerli": { - "MERKLE_ROOT_MULTISIG": "0x6a3D436940697D2e1b351b366816121E9d291dDd", - "LEGACY_MULTISIG": "0xc144381ccfEc30F40f32FC18C45dD5CC20510aCd", - "MESSAGE_ID_MULTISIG": "0x6a3D436940697D2e1b351b366816121E9d291dDd" - }, - "polygonzkevmtestnet": { - "MERKLE_ROOT_MULTISIG": "0x6B4374a792DbC69c0EfAdb076190D137df7145F9", - "LEGACY_MULTISIG": "0xD9b48cD1F0B98FaEBafa5BEB1A78bdaCd5731d08", - "MESSAGE_ID_MULTISIG": "0x6B4374a792DbC69c0EfAdb076190D137df7145F9" - }, - "ROUTING": "0x30861DDAEFa5F85DAB234F4f67a0bFF0a5E02C48", - "AGGREGATION": "0x5861Baf9aa7eC91e88585aB3bd8e3183B0768437", - "fallbackRoutingHook": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", + "fallbackRoutingHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", + "testRecipient": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "testTokenRecipient": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "routingIsmFactory": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", "index": { - "from": 1846115 + "from": 1344054 } }, "sepolia": { + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://sepolia.etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 13, + "reorgPeriod": 2 + }, "chainId": 11155111, + "displayName": "Sepolia", "domainId": 11155111, + "isTestnet": true, "name": "sepolia", - "protocol": "ethereum", - "displayName": "Sepolia", "nativeToken": { + "decimals": 18, "name": "Ether", - "symbol": "ETH", - "decimals": 18 + "symbol": "ETH" }, + "protocol": "ethereum", "rpcUrls": [ { - "http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" + "http": "https://ethereum-sepolia.publicnode.com" }, { - "http": "https://eth-sepolia.g.alchemy.com/v2/demo" + "http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" }, { "http": "https://rpc.sepolia.org" } ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://sepolia.etherscan.io", - "apiUrl": "https://api-sepolia.etherscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 2, - "estimateBlockTime": 13 - }, - "isTestnet": true, - "transactionOverrides": { - "gasLimit": 1000000 - }, "merkleRootMultisigIsmFactory": "0x0a71AcC99967829eE305a285750017C4916Ca269", "messageIdMultisigIsmFactory": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", "aggregationIsmFactory": "0xC83e12EF2627ACE445C298e6eC418684918a6002", "aggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", - "routingIsmFactory": "0x3603458990BfEb30f99E61B58427d196814D8ce1", "proxyAdmin": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", - "defaultIsm": "0x97FE534674A0fA312b730C946A8A8AC9DcF90100", "storageGasOracle": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", @@ -1003,11 +375,62 @@ "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", - "fallbackRoutingHook": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", + "fallbackRoutingHook": "0x17Dc724B7a2F09141C13b8AC33B396073785c2BC", + "testRecipient": "0xeDc1A3EDf87187085A3ABb7A9a65E1e7aE370C07", + "testTokenRecipient": "0x031AD9c560D37baC7d6Bd2d27A2443bAfd10101A", + "routingIsmFactory": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", + "interchainSecurityModule": "0x958124472b14B7940Ed5317C44a2508791dB1d48", + "pausableHook": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", + "index": { + "from": 4517401 + } + }, + "solanatestnet": { + "name": "solanatestnet", + "chainId": 1399811150, + "domainId": 1399811150, + "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", + "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "protocol": "sealevel", + "blocks": { + "reorgPeriod": 0, + "confirmations": 0 + }, + "rpcUrls": [ + { + "http": "https://api.testnet.solana.com" + } + ], + "index": { + "from": 1, + "mode": "sequence" + } + }, + "eclipsetestnet": { + "name": "eclipsetestnet", + "chainId": 239092742, + "domainId": 239092742, + "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "merkleTreeHook": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "interchainGasPaymaster": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", + "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "protocol": "sealevel", + "blocks": { + "reorgPeriod": 0, + "confirmations": 0 + }, + "rpcUrls": [ + { + "http": "https://testnet.dev2.eclipsenetwork.xyz" + } + ], "index": { - "from": 4558491 + "from": 1, + "mode": "sequence" } } }, "defaultRpcConsensusType": "fallback" -} \ No newline at end of file +} diff --git a/rust/ethers-prometheus/Cargo.toml b/rust/ethers-prometheus/Cargo.toml index 92e304d6fa..2f4344fa5d 100644 --- a/rust/ethers-prometheus/Cargo.toml +++ b/rust/ethers-prometheus/Cargo.toml @@ -14,6 +14,7 @@ async-trait.workspace = true derive-new.workspace = true derive_builder.workspace = true ethers.workspace = true +ethers-core.workspace = true futures.workspace = true log.workspace = true maplit.workspace = true diff --git a/rust/ethers-prometheus/src/json_rpc_client.rs b/rust/ethers-prometheus/src/json_rpc_client.rs index 4116d7d63a..2cc8defe9b 100644 --- a/rust/ethers-prometheus/src/json_rpc_client.rs +++ b/rust/ethers-prometheus/src/json_rpc_client.rs @@ -8,6 +8,9 @@ use async_trait::async_trait; use derive_builder::Builder; use derive_new::new; use ethers::prelude::JsonRpcClient; +use ethers_core::types::U64; +use hyperlane_core::rpc_clients::BlockNumberGetter; +use hyperlane_core::ChainCommunicationError; use maplit::hashmap; use prometheus::{CounterVec, IntCounterVec}; use serde::{de::DeserializeOwned, Serialize}; @@ -111,6 +114,16 @@ pub struct PrometheusJsonRpcClient { config: PrometheusJsonRpcClientConfig, } +impl Clone for PrometheusJsonRpcClient { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + metrics: self.metrics.clone(), + config: self.config.clone(), + } + } +} + impl Debug for PrometheusJsonRpcClient where C: JsonRpcClient, @@ -172,3 +185,34 @@ where res } } + +impl From> + for JsonRpcBlockGetter> +{ + fn from(val: PrometheusJsonRpcClient) -> Self { + JsonRpcBlockGetter::new(val) + } +} + +/// Utility struct for implementing `BlockNumberGetter` +#[derive(Debug, new)] +pub struct JsonRpcBlockGetter(T); + +/// RPC method for getting the latest block number +pub const BLOCK_NUMBER_RPC: &str = "eth_blockNumber"; + +#[async_trait] +impl BlockNumberGetter for JsonRpcBlockGetter +where + C: JsonRpcClient, +{ + async fn get_block_number(&self) -> Result { + let res = self + .0 + .request(BLOCK_NUMBER_RPC, ()) + .await + .map(|r: U64| r.as_u64()) + .map_err(Into::into)?; + Ok(res) + } +} diff --git a/rust/ethers-prometheus/src/middleware/mod.rs b/rust/ethers-prometheus/src/middleware/mod.rs index c63447484e..592e6a200f 100644 --- a/rust/ethers-prometheus/src/middleware/mod.rs +++ b/rust/ethers-prometheus/src/middleware/mod.rs @@ -4,9 +4,8 @@ use std::clone::Clone; use std::collections::HashMap; use std::fmt::{Debug, Formatter}; -use std::future::Future; use std::sync::Arc; -use std::time::{Duration, Instant}; +use std::time::Instant; use async_trait::async_trait; use derive_builder::Builder; @@ -14,16 +13,12 @@ use ethers::abi::AbiEncode; use ethers::prelude::*; use ethers::types::transaction::eip2718::TypedTransaction; use ethers::utils::hex::ToHex; -use hyperlane_core::metrics::agent::u256_as_scaled_f64; -use hyperlane_core::HyperlaneDomainProtocol; -use log::{debug, trace}; use maplit::hashmap; -use prometheus::{CounterVec, GaugeVec, IntCounterVec, IntGaugeVec}; +use prometheus::{CounterVec, IntCounterVec}; use static_assertions::assert_impl_all; use tokio::sync::RwLock; pub use error::PrometheusMiddlewareError; -use tokio::time::MissedTickBehavior; pub use crate::ChainInfo; @@ -51,16 +46,6 @@ pub struct ContractInfo { pub functions: HashMap, } -/// Expected label names for the `block_height` metric. -pub const BLOCK_HEIGHT_LABELS: &[&str] = &["chain"]; -/// Help string for the metric. -pub const BLOCK_HEIGHT_HELP: &str = "Tracks the current block height of the chain"; - -/// Expected label names for the `gas_price_gwei` metric. -pub const GAS_PRICE_GWEI_LABELS: &[&str] = &["chain"]; -/// Help string for the metric. -pub const GAS_PRICE_GWEI_HELP: &str = "Tracks the current gas price of the chain"; - /// Expected label names for the `contract_call_duration_seconds` metric. pub const CONTRACT_CALL_DURATION_SECONDS_LABELS: &[&str] = &[ "chain", @@ -128,19 +113,6 @@ pub const TRANSACTION_SEND_TOTAL_HELP: &str = "Number of transactions sent"; /// Container for all the relevant middleware metrics. #[derive(Clone, Builder)] pub struct MiddlewareMetrics { - /// Tracks the current block height of the chain. - /// - `chain`: the chain name (or ID if the name is unknown) of the chain - /// the block number refers to. - #[builder(setter(into, strip_option), default)] - block_height: Option, - - /// Tracks the current gas price of the chain. Uses the base_fee_per_gas if - /// available or else the median of the transactions. - /// - `chain`: the chain name (or chain ID if the name is unknown) of the - /// chain the gas price refers to. - #[builder(setter(into, strip_option), default)] - gas_price_gwei: Option, - /// Contract call durations by contract and function /// - `chain`: the chain name (or chain ID if the name is unknown) of the /// chain the tx occurred on. @@ -468,89 +440,6 @@ impl PrometheusMiddleware { } } -impl PrometheusMiddleware { - /// Start the update cycle using tokio. This must be called if you want - /// some metrics to be updated automatically. Alternatively you could call - /// update yourself. - pub fn start_updating_on_interval( - self: &Arc, - period: Duration, - ) -> impl Future + Send { - let zelf = Arc::downgrade(self); - - async move { - let mut interval = tokio::time::interval(period); - interval.set_missed_tick_behavior(MissedTickBehavior::Skip); - loop { - if let Some(zelf) = zelf.upgrade() { - zelf.update().await; - } else { - return; - } - interval.tick().await; - } - } - } -} - -impl PrometheusMiddleware { - /// Update gauges. You should submit this on a schedule to your runtime to - /// be collected once on a regular interval that ideally aligns with the - /// prometheus scrape interval. - pub fn update(&self) -> impl Future { - // all metrics are Arcs internally so just clone the ones we want to report for. - let block_height = self.metrics.block_height.clone(); - let gas_price_gwei = self.metrics.gas_price_gwei.clone(); - - let data_ref = self.conf.clone(); - let client = self.inner.clone(); - - async move { - let data = data_ref.read().await; - let chain = chain_name(&data.chain); - debug!("Updating metrics for chain ({chain})"); - - if block_height.is_some() || gas_price_gwei.is_some() { - Self::update_block_details(&*client, chain, block_height, gas_price_gwei).await; - } - - // more metrics to come... - } - } - - async fn update_block_details( - client: &M, - chain: &str, - block_height: Option, - gas_price_gwei: Option, - ) { - let Ok(Some(current_block)) = client.get_block(BlockNumber::Latest).await else { - return; - }; - - if let Some(block_height) = block_height { - let height = current_block - .number - .expect("Block number should always be Some for included blocks.") - .as_u64() as i64; - trace!("Block height for chain {chain} is {height}"); - block_height - .with(&hashmap! { "chain" => chain }) - .set(height); - } - if let Some(gas_price_gwei) = gas_price_gwei { - if let Some(london_fee) = current_block.base_fee_per_gas { - let gas = - u256_as_scaled_f64(london_fee.into(), HyperlaneDomainProtocol::Ethereum) * 1e9; - trace!("Gas price for chain {chain} is {gas:.1}gwei"); - gas_price_gwei.with(&hashmap! { "chain" => chain }).set(gas); - } else { - trace!("Gas price for chain {chain} unknown, chain is pre-london"); - } - } - } -} - impl Debug for PrometheusMiddleware { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "PrometheusMiddleware({:?})", self.inner) diff --git a/rust/helm/hyperlane-agent/templates/external-secret.yaml b/rust/helm/hyperlane-agent/templates/external-secret.yaml index 5d0eae5ced..9be0700b75 100644 --- a/rust/helm/hyperlane-agent/templates/external-secret.yaml +++ b/rust/helm/hyperlane-agent/templates/external-secret.yaml @@ -26,11 +26,9 @@ spec: * to replace the correct value in the created secret. */}} {{- range .Values.hyperlane.chains }} - {{- if not .disabled }} HYP_CHAINS_{{ .name | upper }}_CUSTOMRPCURLS: {{ printf "'{{ .%s_rpcs | mustFromJson | join \",\" }}'" .name }} {{- if eq .protocol "cosmos" }} - HYP_CHAINS_{{ .name | upper }}_GRPCURL: {{ printf "'{{ .%s_grpc }}'" .name }} - {{- end }} + HYP_CHAINS_{{ .name | upper }}_CUSTOMGRPCURLS: {{ printf "'{{ .%s_grpcs | mustFromJson | join \",\" }}'" .name }} {{- end }} {{- end }} data: @@ -39,14 +37,12 @@ spec: * and associate it with the secret key networkname_rpcs. */}} {{- range .Values.hyperlane.chains }} - {{- if not .disabled }} - secretKey: {{ printf "%s_rpcs" .name }} remoteRef: key: {{ printf "%s-rpc-endpoints-%s" $.Values.hyperlane.runEnv .name }} {{- if eq .protocol "cosmos" }} - - secretKey: {{ printf "%s_grpc" .name }} + - secretKey: {{ printf "%s_grpcs" .name }} remoteRef: - key: {{ printf "%s-grpc-endpoint-%s" $.Values.hyperlane.runEnv .name }} - {{- end }} + key: {{ printf "%s-grpc-endpoints-%s" $.Values.hyperlane.runEnv .name }} {{- end }} {{- end }} diff --git a/rust/helm/hyperlane-agent/values.yaml b/rust/helm/hyperlane-agent/values.yaml index 299cf0e63a..691bb81c61 100644 --- a/rust/helm/hyperlane-agent/values.yaml +++ b/rust/helm/hyperlane-agent/values.yaml @@ -50,7 +50,6 @@ hyperlane: # This should mirror @hyperlane-xyz/sdk AgentChainMetadata chains: - name: examplechain - disabled: false rpcConsensusType: fallback signer: type: # aws @@ -113,8 +112,8 @@ hyperlane: name: '' resources: requests: - cpu: 500m - memory: 256Mi + cpu: 1000m + memory: 1024Mi config: relayChains: '' multisigCheckpointSyncer: diff --git a/rust/hyperlane-base/Cargo.toml b/rust/hyperlane-base/Cargo.toml index 4e78c30243..ca27e4a771 100644 --- a/rust/hyperlane-base/Cargo.toml +++ b/rust/hyperlane-base/Cargo.toml @@ -11,6 +11,7 @@ version.workspace = true [dependencies] async-trait.workspace = true +axum.workspace = true bs58.workspace = true color-eyre = { workspace = true, optional = true } config.workspace = true @@ -21,6 +22,7 @@ ed25519-dalek.workspace = true ethers.workspace = true eyre.workspace = true fuels.workspace = true +futures.worksapce = true futures-util.workspace = true itertools.workspace = true maplit.workspace = true @@ -40,6 +42,8 @@ tracing-subscriber = { workspace = true, features = ["json", "ansi"] } tracing.workspace = true url.workspace = true warp.workspace = true +ya-gcp.workspace = true + backtrace = { workspace = true, optional = true } backtrace-oneline = { path = "../utils/backtrace-oneline", optional = true } @@ -52,6 +56,7 @@ hyperlane-sealevel = { path = "../chains/hyperlane-sealevel" } hyperlane-cosmos = { path = "../chains/hyperlane-cosmos"} hyperlane-test = { path = "../hyperlane-test" } + # dependency version is determined by etheres rusoto_core = "*" rusoto_kms = "*" @@ -60,7 +65,9 @@ rusoto_sts = "*" [dev-dependencies] color-eyre.workspace = true +reqwest.workspace = true tempfile.workspace = true +tracing-test.workspace = true walkdir.workspace = true [features] diff --git a/rust/hyperlane-base/src/agent.rs b/rust/hyperlane-base/src/agent.rs index 5df9250889..5f6b504e9a 100644 --- a/rust/hyperlane-base/src/agent.rs +++ b/rust/hyperlane-base/src/agent.rs @@ -1,15 +1,15 @@ use std::{env, fmt::Debug, sync::Arc}; use async_trait::async_trait; -use eyre::{Report, Result}; -use futures_util::future::select_all; +use eyre::Result; use hyperlane_core::config::*; -use tokio::task::JoinHandle; -use tracing::{debug_span, instrument::Instrumented, Instrument}; +use tracing::info; use crate::{ + create_chain_metrics, metrics::{create_agent_metrics, AgentMetrics, CoreMetrics}, settings::Settings, + ChainMetrics, }; /// Properties shared across all hyperlane agents @@ -43,13 +43,14 @@ pub trait BaseAgent: Send + Sync + Debug { settings: Self::Settings, metrics: Arc, agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, ) -> Result where Self: Sized; /// Start running this agent. #[allow(clippy::async_yields_async)] - async fn run(self) -> Instrumented>>; + async fn run(self); } /// Call this from `main` to fully initialize and run the agent for its entire @@ -76,29 +77,11 @@ pub async fn agent_main() -> Result<()> { let metrics = settings.as_ref().metrics(A::AGENT_NAME)?; core_settings.tracing.start_tracing(&metrics)?; let agent_metrics = create_agent_metrics(&metrics)?; - let agent = A::from_settings(settings, metrics.clone(), agent_metrics).await?; - metrics.run_http_server(); + let chain_metrics = create_chain_metrics(&metrics)?; + let agent = A::from_settings(settings, metrics.clone(), agent_metrics, chain_metrics).await?; - agent.run().await.await? -} - -/// Utility to run multiple tasks and shutdown if any one task ends. -#[allow(clippy::unit_arg, unused_must_use)] -pub fn run_all( - tasks: Vec>>>, -) -> Instrumented>> { - debug_assert!(!tasks.is_empty(), "No tasks submitted"); - let span = debug_span!("run_all"); - tokio::spawn(async move { - let (res, _, remaining) = select_all(tasks).await; - - for task in remaining.into_iter() { - let t = task.into_inner(); - t.abort(); - t.await; - } - - res? - }) - .instrument(span) + // This await will only end if a panic happens. We won't crash, but instead gracefully shut down + agent.run().await; + info!(agent = A::AGENT_NAME, "Shutting down agent..."); + Ok(()) } diff --git a/rust/hyperlane-base/src/contract_sync/cursor.rs b/rust/hyperlane-base/src/contract_sync/cursor.rs deleted file mode 100644 index cbbd393dbd..0000000000 --- a/rust/hyperlane-base/src/contract_sync/cursor.rs +++ /dev/null @@ -1,605 +0,0 @@ -use std::{ - cmp::Ordering, - fmt::Debug, - ops::RangeInclusive, - sync::Arc, - time::{Duration, Instant}, -}; - -use async_trait::async_trait; -use derive_new::new; -use eyre::Result; -use hyperlane_core::{ - ChainCommunicationError, ChainResult, ContractSyncCursor, CursorAction, - HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, IndexMode, Indexer, LogMeta, - SequenceIndexer, Sequenced, -}; -use tokio::time::sleep; -use tracing::{debug, warn}; - -use crate::contract_sync::eta_calculator::SyncerEtaCalculator; - -/// Time window for the moving average used in the eta calculator in seconds. -const ETA_TIME_WINDOW: f64 = 2. * 60.; - -const MAX_SEQUENCE_RANGE: u32 = 100; - -/// A struct that holds the data needed for forwards and backwards -/// sequence sync cursors. -#[derive(Debug, new)] -pub(crate) struct SequenceSyncCursor { - indexer: Arc>, - db: Arc>, - sync_state: SyncState, -} - -#[derive(Debug, new)] -pub(crate) struct SyncState { - chunk_size: u32, - /// The starting block for the cursor - start_block: u32, - /// The next block that should be indexed. - next_block: u32, - mode: IndexMode, - /// The next sequence index that the cursor is looking for. - /// In the EVM, this is used for optimizing indexing, - /// because it's cheaper to make read calls for the sequence index than - /// to call `eth_getLogs` with a block range. - /// In Sealevel, historic queries aren't supported, so the sequence field - /// is used to query storage in sequence. - next_sequence: u32, - direction: SyncDirection, -} - -impl SyncState { - async fn get_next_range( - &mut self, - max_sequence: Option, - tip: u32, - ) -> ChainResult>> { - // We attempt to index a range of blocks that is as large as possible. - let range = match self.mode { - IndexMode::Block => self.block_range(tip), - IndexMode::Sequence => { - let max_sequence = max_sequence.ok_or_else(|| { - ChainCommunicationError::from_other_str( - "Sequence indexing requires a max sequence", - ) - })?; - if let Some(range) = self.sequence_range(max_sequence)? { - range - } else { - return Ok(None); - } - } - }; - if range.is_empty() { - return Ok(None); - } - Ok(Some(range)) - } - - fn block_range(&mut self, tip: u32) -> RangeInclusive { - let (from, to) = match self.direction { - SyncDirection::Forward => { - let from = self.next_block; - let mut to = from + self.chunk_size; - to = u32::min(to, tip); - self.next_block = to + 1; - (from, to) - } - SyncDirection::Backward => { - let to = self.next_block; - let from = to.saturating_sub(self.chunk_size); - self.next_block = from.saturating_sub(1); - (from, to) - } - }; - from..=to - } - - /// Returns the next sequence range to index. - /// - /// # Arguments - /// - /// * `tip` - The current tip of the chain. - /// * `max_sequence` - The maximum sequence that should be indexed. - /// `max_sequence` is the exclusive upper bound of the range to be indexed. - /// (e.g. `0..max_sequence`) - fn sequence_range(&mut self, max_sequence: u32) -> ChainResult>> { - let (from, to) = match self.direction { - SyncDirection::Forward => { - let sequence_start = self.next_sequence; - let mut sequence_end = sequence_start + MAX_SEQUENCE_RANGE; - if self.next_sequence >= max_sequence { - return Ok(None); - } - sequence_end = u32::min(sequence_end, max_sequence.saturating_sub(1)); - self.next_sequence = sequence_end + 1; - (sequence_start, sequence_end) - } - SyncDirection::Backward => { - let sequence_end = self.next_sequence; - let sequence_start = sequence_end.saturating_sub(MAX_SEQUENCE_RANGE); - self.next_sequence = sequence_start.saturating_sub(1); - (sequence_start, sequence_end) - } - }; - Ok(Some(from..=to)) - } -} - -impl SequenceSyncCursor { - async fn retrieve_by_sequence(&self, sequence: u32) -> Option { - self.db.retrieve_by_sequence(sequence).await.ok().flatten() - } - - async fn retrieve_log_block_number(&self, sequence: u32) -> Option { - self.db - .retrieve_log_block_number(sequence) - .await - .ok() - .flatten() - .map(|num| u32::try_from(num).unwrap()) - } - - async fn update(&mut self, logs: Vec<(T, LogMeta)>, prev_sequence: u32) -> Result<()> { - // If we found logs, but did *not* find the log we were looking for, - // we need to rewind to the block at which we found the last log. - if !logs.is_empty() - && !logs - .iter() - .any(|m| m.0.sequence() == self.sync_state.next_sequence) - { - warn!(next_sequence=?self.sync_state.next_sequence, "Target sequence not found, rewinding"); - // If the previous sequence has been synced, rewind to the block number - // at which it was dispatched. Otherwise, rewind all the way back to the start block. - if let Some(block_number) = self.retrieve_log_block_number(prev_sequence).await { - self.sync_state.next_block = block_number; - warn!(block_number, "Rewound to previous known sequenced log"); - } else { - self.sync_state.next_block = self.sync_state.start_block; - } - Ok(()) - } else { - Ok(()) - } - } -} - -/// A SequenceSyncCursor that syncs forwards in perpetuity. -pub(crate) struct ForwardSequenceSyncCursor { - cursor: SequenceSyncCursor, -} - -impl ForwardSequenceSyncCursor { - pub fn new( - indexer: Arc>, - db: Arc>, - chunk_size: u32, - start_block: u32, - next_block: u32, - mode: IndexMode, - next_sequence: u32, - ) -> Self { - Self { - cursor: SequenceSyncCursor::new( - indexer, - db, - SyncState::new( - chunk_size, - start_block, - next_block, - mode, - next_sequence, - SyncDirection::Forward, - ), - ), - } - } - - async fn get_next_range(&mut self) -> ChainResult>> { - // Check if any new logs have been inserted into the DB, - // and update the cursor accordingly. - while self - .cursor - .retrieve_by_sequence(self.cursor.sync_state.next_sequence) - .await - .is_some() - { - if let Some(block_number) = self - .cursor - .retrieve_log_block_number(self.cursor.sync_state.next_sequence) - .await - { - debug!(next_block = block_number, "Fast forwarding next block"); - // It's possible that eth_getLogs dropped logs from this block, therefore we cannot do block_number + 1. - self.cursor.sync_state.next_block = block_number; - } - debug!( - next_sequence = self.cursor.sync_state.next_sequence + 1, - "Fast forwarding next sequence" - ); - self.cursor.sync_state.next_sequence += 1; - } - - let (Some(mailbox_count), tip) = self.cursor.indexer.sequence_and_tip().await? else { - return Ok(None); - }; - let cursor_count = self.cursor.sync_state.next_sequence; - Ok(match cursor_count.cmp(&mailbox_count) { - Ordering::Equal => { - // We are synced up to the latest sequence so we don't need to index anything. - // We update our next block number accordingly. - self.cursor.sync_state.next_block = tip; - None - } - Ordering::Less => { - // The cursor is behind the mailbox, so we need to index some blocks. - self.cursor - .sync_state - .get_next_range(Some(mailbox_count), tip) - .await? - } - Ordering::Greater => { - // Providers may be internally inconsistent, e.g. RPC request A could hit a node - // whose tip is N and subsequent RPC request B could hit a node whose tip is < N. - debug!("Cursor count is greater than Mailbox count"); - None - } - }) - } -} - -#[async_trait] -impl ContractSyncCursor for ForwardSequenceSyncCursor { - async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)> { - // TODO: Fix ETA calculation - let eta = Duration::from_secs(0); - if let Some(range) = self.get_next_range().await? { - Ok((CursorAction::Query(range), eta)) - } else { - // TODO: Define the sleep time from interval flag - Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)) - } - } - - fn latest_block(&self) -> u32 { - self.cursor.sync_state.next_block.saturating_sub(1) - } - - /// If the previous block has been synced, rewind to the block number - /// at which it was dispatched. - /// Otherwise, rewind all the way back to the start block. - async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> Result<()> { - let prev_sequence = self.cursor.sync_state.next_sequence.saturating_sub(1); - // We may wind up having re-indexed logs that are previous to the sequence that we are looking for. - // We should not consider these logs when checking for continuity errors. - let filtered_logs = logs - .into_iter() - .filter(|m| m.0.sequence() >= self.cursor.sync_state.next_sequence) - .collect(); - self.cursor.update(filtered_logs, prev_sequence).await - } -} - -/// A SequenceSyncCursor that syncs backwards to sequence zero. -pub(crate) struct BackwardSequenceSyncCursor { - cursor: SequenceSyncCursor, - synced: bool, -} - -impl BackwardSequenceSyncCursor { - #[allow(clippy::too_many_arguments)] - pub fn new( - indexer: Arc>, - db: Arc>, - chunk_size: u32, - start_block: u32, - next_block: u32, - mode: IndexMode, - next_sequence: u32, - synced: bool, - ) -> Self { - Self { - cursor: SequenceSyncCursor::new( - indexer, - db, - SyncState::new( - chunk_size, - start_block, - next_block, - mode, - next_sequence, - SyncDirection::Backward, - ), - ), - synced, - } - } - - async fn get_next_range(&mut self) -> ChainResult>> { - // Check if any new logs have been inserted into the DB, - // and update the cursor accordingly. - while !self.synced { - if self - .cursor - .retrieve_by_sequence(self.cursor.sync_state.next_sequence) - .await - .is_none() - { - break; - }; - // If we found sequence zero or hit block zero, we are done rewinding. - if self.cursor.sync_state.next_sequence == 0 || self.cursor.sync_state.next_block == 0 { - self.synced = true; - break; - } - - if let Some(block_number) = self - .cursor - .retrieve_log_block_number(self.cursor.sync_state.next_sequence) - .await - { - // It's possible that eth_getLogs dropped logs from this block, therefore we cannot do block_number - 1. - self.cursor.sync_state.next_block = block_number; - } - - self.cursor.sync_state.next_sequence = - self.cursor.sync_state.next_sequence.saturating_sub(1); - } - if self.synced { - return Ok(None); - } - - // Just keep going backwards. - let (count, tip) = self.cursor.indexer.sequence_and_tip().await?; - self.cursor.sync_state.get_next_range(count, tip).await - } - - /// If the previous block has been synced, rewind to the block number - /// at which it was dispatched. - /// Otherwise, rewind all the way back to the start block. - async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> Result<()> { - let prev_sequence = self.cursor.sync_state.next_sequence.saturating_add(1); - // We may wind up having re-indexed logs that are previous to the sequence that we are looking for. - // We should not consider these logs when checking for continuity errors. - let filtered_logs = logs - .into_iter() - .filter(|m| m.0.sequence() <= self.cursor.sync_state.next_sequence) - .collect(); - self.cursor.update(filtered_logs, prev_sequence).await - } -} - -#[derive(Debug)] -pub enum SyncDirection { - Forward, - Backward, -} - -/// A SequenceSyncCursor that syncs forwards in perpetuity. -pub(crate) struct ForwardBackwardSequenceSyncCursor { - forward: ForwardSequenceSyncCursor, - backward: BackwardSequenceSyncCursor, - direction: SyncDirection, -} - -impl ForwardBackwardSequenceSyncCursor { - /// Construct a new contract sync helper. - pub async fn new( - indexer: Arc>, - db: Arc>, - chunk_size: u32, - mode: IndexMode, - ) -> Result { - let (sequence, tip) = indexer.sequence_and_tip().await?; - let sequence = sequence.ok_or(ChainCommunicationError::from_other_str( - "Failed to query sequence", - ))?; - let forward_cursor = ForwardSequenceSyncCursor::new( - indexer.clone(), - db.clone(), - chunk_size, - tip, - tip, - mode, - sequence, - ); - let backward_cursor = BackwardSequenceSyncCursor::new( - indexer.clone(), - db.clone(), - chunk_size, - tip, - tip, - mode, - sequence.saturating_sub(1), - sequence == 0, - ); - Ok(Self { - forward: forward_cursor, - backward: backward_cursor, - direction: SyncDirection::Forward, - }) - } -} - -#[async_trait] -impl ContractSyncCursor for ForwardBackwardSequenceSyncCursor { - async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)> { - // TODO: Proper ETA for backwards sync - let eta = Duration::from_secs(0); - // Prioritize forward syncing over backward syncing. - if let Some(forward_range) = self.forward.get_next_range().await? { - self.direction = SyncDirection::Forward; - return Ok((CursorAction::Query(forward_range), eta)); - } - - if let Some(backward_range) = self.backward.get_next_range().await? { - self.direction = SyncDirection::Backward; - return Ok((CursorAction::Query(backward_range), eta)); - } - // TODO: Define the sleep time from interval flag - return Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)); - } - - fn latest_block(&self) -> u32 { - self.forward.cursor.sync_state.next_block.saturating_sub(1) - } - - async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> Result<()> { - match self.direction { - SyncDirection::Forward => self.forward.update(logs).await, - SyncDirection::Backward => self.backward.update(logs).await, - } - } -} - -/// Tool for handling the logic of what the next block range that should be -/// queried is and also handling rate limiting. Rate limiting is automatically -/// performed by `next_action`. -pub(crate) struct RateLimitedContractSyncCursor { - indexer: Arc>, - db: Arc>, - tip: u32, - max_sequence: Option, - last_tip_update: Instant, - eta_calculator: SyncerEtaCalculator, - sync_state: SyncState, -} - -impl RateLimitedContractSyncCursor { - /// Construct a new contract sync helper. - pub async fn new( - indexer: Arc>, - db: Arc>, - chunk_size: u32, - initial_height: u32, - mode: IndexMode, - ) -> Result { - let (max_sequence, tip) = indexer.sequence_and_tip().await?; - Ok(Self { - indexer, - db, - tip, - max_sequence, - last_tip_update: Instant::now(), - eta_calculator: SyncerEtaCalculator::new(initial_height, tip, ETA_TIME_WINDOW), - sync_state: SyncState::new( - chunk_size, - initial_height, - initial_height, - mode, - Default::default(), - // The rate limited cursor currently only syncs in the forward direction. - SyncDirection::Forward, - ), - }) - } - - /// Wait based on how close we are to the tip and update the tip, - /// i.e. the highest block we may scrape. - async fn get_rate_limit(&mut self) -> ChainResult> { - if self.sync_state.next_block + self.sync_state.chunk_size < self.tip { - // If doing the full chunk wouldn't exceed the already known tip we do not need to rate limit. - Ok(None) - } else { - // We are within one chunk size of the known tip. - // If it's been fewer than 30s since the last tip update, sleep for a bit until we're ready to fetch the next tip. - if let Some(sleep_time) = - Duration::from_secs(30).checked_sub(self.last_tip_update.elapsed()) - { - return Ok(Some(sleep_time)); - } - match self.indexer.get_finalized_block_number().await { - Ok(tip) => { - // we retrieved a new tip value, go ahead and update. - self.last_tip_update = Instant::now(); - self.tip = tip; - Ok(None) - } - Err(e) => { - warn!(error = %e, "Failed to get next block range because we could not get the current tip"); - // we are failing to make a basic query, we should wait before retrying. - sleep(Duration::from_secs(10)).await; - Err(e) - } - } - } - } - - fn sync_end(&self) -> ChainResult { - match self.sync_state.mode { - IndexMode::Block => Ok(self.tip), - IndexMode::Sequence => { - self.max_sequence - .ok_or(ChainCommunicationError::from_other_str( - "Sequence indexing requires a max sequence", - )) - } - } - } - - fn sync_position(&self) -> u32 { - match self.sync_state.mode { - IndexMode::Block => self.sync_state.next_block, - IndexMode::Sequence => self.sync_state.next_sequence, - } - } - - fn sync_step(&self) -> u32 { - match self.sync_state.mode { - IndexMode::Block => self.sync_state.chunk_size, - IndexMode::Sequence => MAX_SEQUENCE_RANGE, - } - } -} - -#[async_trait] -impl ContractSyncCursor for RateLimitedContractSyncCursor -where - T: Send + Debug + 'static, -{ - async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)> { - let sync_end = self.sync_end()?; - let to = u32::min(sync_end, self.sync_position() + self.sync_step()); - let from = self.sync_position(); - let eta = if to < sync_end { - self.eta_calculator.calculate(from, sync_end) - } else { - Duration::from_secs(0) - }; - - let rate_limit = self.get_rate_limit().await?; - if let Some(rate_limit) = rate_limit { - return Ok((CursorAction::Sleep(rate_limit), eta)); - } - let (max_sequence, tip) = self.indexer.sequence_and_tip().await?; - self.tip = tip; - self.max_sequence = max_sequence; - if let Some(range) = self.sync_state.get_next_range(max_sequence, tip).await? { - return Ok((CursorAction::Query(range), eta)); - } - - // TODO: Define the sleep time from interval flag - Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)) - } - - fn latest_block(&self) -> u32 { - self.sync_state.next_block.saturating_sub(1) - } - - async fn update(&mut self, _: Vec<(T, LogMeta)>) -> Result<()> { - // Store a relatively conservative view of the high watermark, which should allow a single watermark to be - // safely shared across multiple cursors, so long as they are running sufficiently in sync - self.db - .store_high_watermark(u32::max( - self.sync_state.start_block, - self.sync_state - .next_block - .saturating_sub(self.sync_state.chunk_size), - )) - .await?; - Ok(()) - } -} diff --git a/rust/hyperlane-base/src/contract_sync/cursors/mod.rs b/rust/hyperlane-base/src/contract_sync/cursors/mod.rs new file mode 100644 index 0000000000..c9b8d7a015 --- /dev/null +++ b/rust/hyperlane-base/src/contract_sync/cursors/mod.rs @@ -0,0 +1,278 @@ +use std::{ + fmt::Debug, + ops::RangeInclusive, + sync::Arc, + time::{Duration, Instant}, +}; + +use async_trait::async_trait; +use derive_new::new; +use eyre::Result; +use hyperlane_core::{ + ChainCommunicationError, ContractSyncCursor, CursorAction, HyperlaneWatermarkedLogStore, + IndexMode, Indexer, LogMeta, SequenceAwareIndexer, +}; +use tokio::time::sleep; +use tracing::warn; + +use crate::contract_sync::eta_calculator::SyncerEtaCalculator; + +pub(crate) mod sequence_aware; + +pub(crate) use sequence_aware::{ + ForwardBackwardSequenceAwareSyncCursor, ForwardSequenceAwareSyncCursor, +}; + +/// Time window for the moving average used in the eta calculator in seconds. +const ETA_TIME_WINDOW: f64 = 2. * 60.; + +const MAX_SEQUENCE_RANGE: u32 = 20; + +#[derive(Debug, new)] +pub(crate) struct SyncState { + chunk_size: u32, + /// The starting block for the cursor + start_block: u32, + /// The next block that should be indexed. + next_block: u32, + mode: IndexMode, + /// The next sequence index that the cursor is looking for. + /// In the EVM, this is used for optimizing indexing, + /// because it's cheaper to make read calls for the sequence index than + /// to call `eth_getLogs` with a block range. + /// In Sealevel, historic queries aren't supported, so the sequence field + /// is used to query storage in sequence. + next_sequence: u32, + direction: SyncDirection, +} + +impl SyncState { + async fn get_next_range( + &mut self, + max_sequence: Option, + tip: u32, + ) -> Result>> { + // We attempt to index a range of blocks that is as large as possible. + let range = match self.mode { + IndexMode::Block => self.block_range(tip), + IndexMode::Sequence => { + let max_sequence = max_sequence.ok_or_else(|| { + ChainCommunicationError::from_other_str( + "Sequence indexing requires a max sequence", + ) + })?; + if let Some(range) = self.sequence_range(max_sequence)? { + range + } else { + return Ok(None); + } + } + }; + if range.is_empty() { + return Ok(None); + } + Ok(Some(range)) + } + + fn block_range(&mut self, tip: u32) -> RangeInclusive { + let (from, to) = match self.direction { + SyncDirection::Forward => { + let from = self.next_block; + let mut to = from + self.chunk_size; + to = u32::min(to, tip); + self.next_block = to + 1; + (from, to) + } + SyncDirection::Backward => { + let to = self.next_block; + let from = to.saturating_sub(self.chunk_size); + self.next_block = from.saturating_sub(1); + (from, to) + } + }; + from..=to + } + + /// Returns the next sequence range to index. + /// + /// # Arguments + /// + /// * `tip` - The current tip of the chain. + /// * `max_sequence` - The maximum sequence that should be indexed. + /// `max_sequence` is the exclusive upper bound of the range to be indexed. + /// (e.g. `0..max_sequence`) + fn sequence_range(&mut self, max_sequence: u32) -> Result>> { + let (from, to) = match self.direction { + SyncDirection::Forward => { + let sequence_start = self.next_sequence; + let mut sequence_end = sequence_start + MAX_SEQUENCE_RANGE; + if self.next_sequence >= max_sequence { + return Ok(None); + } + sequence_end = u32::min(sequence_end, max_sequence.saturating_sub(1)); + self.next_sequence = sequence_end + 1; + (sequence_start, sequence_end) + } + SyncDirection::Backward => { + let sequence_end = self.next_sequence; + let sequence_start = sequence_end.saturating_sub(MAX_SEQUENCE_RANGE); + self.next_sequence = sequence_start.saturating_sub(1); + (sequence_start, sequence_end) + } + }; + Ok(Some(from..=to)) + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub enum SyncDirection { + Forward, + Backward, +} + +/// Tool for handling the logic of what the next block range that should be +/// queried is and also handling rate limiting. Rate limiting is automatically +/// performed by `next_action`. +pub(crate) struct RateLimitedContractSyncCursor { + indexer: Arc>, + db: Arc>, + tip: u32, + max_sequence: Option, + last_tip_update: Instant, + eta_calculator: SyncerEtaCalculator, + sync_state: SyncState, +} + +impl RateLimitedContractSyncCursor { + /// Construct a new contract sync helper. + pub async fn new( + indexer: Arc>, + db: Arc>, + chunk_size: u32, + initial_height: u32, + mode: IndexMode, + ) -> Result { + let (max_sequence, tip) = indexer.latest_sequence_count_and_tip().await?; + Ok(Self { + indexer, + db, + tip, + max_sequence, + last_tip_update: Instant::now(), + eta_calculator: SyncerEtaCalculator::new(initial_height, tip, ETA_TIME_WINDOW), + sync_state: SyncState::new( + chunk_size, + initial_height, + initial_height, + mode, + Default::default(), + // The rate limited cursor currently only syncs in the forward direction. + SyncDirection::Forward, + ), + }) + } + + /// Wait based on how close we are to the tip and update the tip, + /// i.e. the highest block we may scrape. + async fn get_rate_limit(&mut self) -> Result> { + if self.sync_state.next_block + self.sync_state.chunk_size < self.tip { + // If doing the full chunk wouldn't exceed the already known tip we do not need to rate limit. + Ok(None) + } else { + // We are within one chunk size of the known tip. + // If it's been fewer than 30s since the last tip update, sleep for a bit until we're ready to fetch the next tip. + if let Some(sleep_time) = + Duration::from_secs(30).checked_sub(self.last_tip_update.elapsed()) + { + return Ok(Some(sleep_time)); + } + match self.indexer.get_finalized_block_number().await { + Ok(tip) => { + // we retrieved a new tip value, go ahead and update. + self.last_tip_update = Instant::now(); + self.tip = tip; + Ok(None) + } + Err(e) => { + warn!(error = %e, "Failed to get next block range because we could not get the current tip"); + // we are failing to make a basic query, we should wait before retrying. + sleep(Duration::from_secs(10)).await; + Err(e.into()) + } + } + } + } + + fn sync_end(&self) -> Result { + match self.sync_state.mode { + IndexMode::Block => Ok(self.tip), + IndexMode::Sequence => self + .max_sequence + .ok_or(eyre::eyre!("Sequence indexing requires a max sequence",)), + } + } + + fn sync_position(&self) -> u32 { + match self.sync_state.mode { + IndexMode::Block => self.sync_state.next_block, + IndexMode::Sequence => self.sync_state.next_sequence, + } + } + + fn sync_step(&self) -> u32 { + match self.sync_state.mode { + IndexMode::Block => self.sync_state.chunk_size, + IndexMode::Sequence => MAX_SEQUENCE_RANGE, + } + } +} + +#[async_trait] +impl ContractSyncCursor for RateLimitedContractSyncCursor +where + T: Send + Debug + 'static, +{ + async fn next_action(&mut self) -> Result<(CursorAction, Duration)> { + let sync_end = self.sync_end()?; + let to = u32::min(sync_end, self.sync_position() + self.sync_step()); + let from = self.sync_position(); + let eta = if to < sync_end { + self.eta_calculator.calculate(from, sync_end) + } else { + Duration::from_secs(0) + }; + + let rate_limit = self.get_rate_limit().await?; + if let Some(rate_limit) = rate_limit { + return Ok((CursorAction::Sleep(rate_limit), eta)); + } + let (max_sequence, tip) = self.indexer.latest_sequence_count_and_tip().await?; + self.tip = tip; + self.max_sequence = max_sequence; + if let Some(range) = self.sync_state.get_next_range(max_sequence, tip).await? { + return Ok((CursorAction::Query(range), eta)); + } + + // TODO: Define the sleep time from interval flag + Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)) + } + + fn latest_queried_block(&self) -> u32 { + self.sync_state.next_block.saturating_sub(1) + } + + async fn update(&mut self, _: Vec<(T, LogMeta)>, _range: RangeInclusive) -> Result<()> { + // Store a relatively conservative view of the high watermark, which should allow a single watermark to be + // safely shared across multiple cursors, so long as they are running sufficiently in sync + self.db + .store_high_watermark(u32::max( + self.sync_state.start_block, + self.sync_state + .next_block + .saturating_sub(self.sync_state.chunk_size), + )) + .await?; + Ok(()) + } +} diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs new file mode 100644 index 0000000000..3dcbf7aa17 --- /dev/null +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs @@ -0,0 +1,989 @@ +//! A sequence-aware cursor that syncs backwards until there are no earlier logs to index. + +use std::{collections::HashSet, fmt::Debug, ops::RangeInclusive, sync::Arc, time::Duration}; + +use async_trait::async_trait; +use eyre::Result; +use hyperlane_core::{ + ContractSyncCursor, CursorAction, HyperlaneSequenceAwareIndexerStoreReader, IndexMode, LogMeta, + Sequenced, +}; +use itertools::Itertools; +use tracing::{debug, warn}; + +use super::{LastIndexedSnapshot, TargetSnapshot}; + +/// A sequence-aware cursor that syncs backward until there are no earlier logs to index. +#[derive(Debug)] +pub(crate) struct BackwardSequenceAwareSyncCursor { + /// The max chunk size to query for logs. + /// If in sequence mode, this is the max number of sequences to query. + /// If in block mode, this is the max number of blocks to query. + chunk_size: u32, + /// A DB used to check which logs have already been indexed. + db: Arc>, + /// A snapshot of the last log to be indexed, or if no indexing has occurred yet, + /// the initial log to start indexing backward from. + last_indexed_snapshot: LastIndexedSnapshot, + /// The current snapshot we're indexing. As this is a backward cursor, + /// if the last indexed snapshot was sequence 100, this would be sequence 99. + /// A None value indicates we're fully synced. + current_indexing_snapshot: Option, + /// The mode of indexing to use. + index_mode: IndexMode, +} + +impl BackwardSequenceAwareSyncCursor { + pub fn new( + chunk_size: u32, + db: Arc>, + current_sequence_count: u32, + start_block: u32, + index_mode: IndexMode, + ) -> Self { + // If the current sequence count is 0, we haven't indexed anything yet. + // Otherwise, consider the current sequence count as the last indexed snapshot, + // indicating the upper bound of sequences to index. + let last_indexed_snapshot = LastIndexedSnapshot { + sequence: (current_sequence_count > 0).then_some(current_sequence_count), + at_block: start_block, + }; + + Self { + chunk_size, + db, + current_indexing_snapshot: last_indexed_snapshot.previous_target(), + last_indexed_snapshot, + index_mode, + } + } + + /// Gets the next range of logs to query. + /// If the cursor is fully synced, this returns None. + /// Otherwise, it returns the next range to query, either by block or sequence depending on the mode. + pub async fn get_next_range(&mut self) -> Result>> { + // Skip any already indexed logs. + self.skip_indexed().await?; + + // If `self.current_indexing_snapshot` is None, we are synced and there are no more ranges to query. + // Otherwise, we query the next range, searching for logs prior to and including the current indexing snapshot. + Ok(self + .current_indexing_snapshot + .as_ref() + .map(|current_indexing_snapshot| match &self.index_mode { + IndexMode::Block => self.get_next_block_range(current_indexing_snapshot), + IndexMode::Sequence => self.get_next_sequence_range(current_indexing_snapshot), + })) + } + + /// Gets the next block range to index. + /// Only used in block mode. + fn get_next_block_range( + &self, + current_indexing_snapshot: &TargetSnapshot, + ) -> RangeInclusive { + // Query the block range ending at the current_indexing_snapshot's at_block. + current_indexing_snapshot + .at_block + .saturating_sub(self.chunk_size)..=current_indexing_snapshot.at_block + } + + /// Gets the next sequence range to index. + /// Only used in sequence mode. + fn get_next_sequence_range( + &self, + current_indexing_snapshot: &TargetSnapshot, + ) -> RangeInclusive { + // Query the sequence range ending at the current_indexing_snapshot's sequence. + current_indexing_snapshot + .sequence + .saturating_sub(self.chunk_size)..=current_indexing_snapshot.sequence + } + + /// Reads the DB to check if the current indexing sequence has already been indexed, + /// iterating until we find a sequence that hasn't been indexed. + async fn skip_indexed(&mut self) -> Result<()> { + // While we're not fully synced, check if the next log we're looking for has been + // inserted into the db, and update the cursor accordingly. + while let Some(current_indexing_sequence) = + self.current_indexing_snapshot.as_ref().map(|s| s.sequence) + { + // Require the block number as well. + if let Some(block_number) = self + .get_sequence_log_block_number(current_indexing_sequence) + .await? + { + self.last_indexed_snapshot = LastIndexedSnapshot { + sequence: Some(current_indexing_sequence), + at_block: block_number, + }; + + self.current_indexing_snapshot = self.last_indexed_snapshot.previous_target(); + + debug!( + last_indexed_snapshot=?self.last_indexed_snapshot, + current_indexing_snapshot=?self.current_indexing_snapshot, + "Fast forwarded current sequence" + ); + } else { + // If the sequence hasn't been indexed, break out of the loop. + break; + } + } + + Ok(()) + } + + /// Gets the log block number of a previously indexed sequence. Returns None if the + /// log for the sequence number hasn't been indexed. + async fn get_sequence_log_block_number(&self, sequence: u32) -> Result> { + // Ensure there's a full entry for the sequence. + if self.db.retrieve_by_sequence(sequence).await?.is_some() { + // And get the block number. + if let Some(block_number) = self + .db + .retrieve_log_block_number_by_sequence(sequence) + .await? + { + return Ok(Some(block_number.try_into()?)); + } + } + + Ok(None) + } + + /// Updates the cursor with the logs that were found in the range. + /// Only used in sequence mode. + /// Logs are expected to be sorted by sequence in ascending order and deduplicated. + /// + /// Behavior: + /// - Empty logs are allowed, but no gaps are allowed. The logs must build upon the last indexed snapshot. + /// - If there are any gaps, the cursor rewinds to the last indexed snapshot, and ranges will be retried. + fn update_block_range( + &mut self, + logs: Vec<(T, LogMeta)>, + all_log_sequences: &HashSet, + range: RangeInclusive, + current_indexing_snapshot: TargetSnapshot, + ) -> Result<()> { + // We require no sequence gaps and to build upon the last snapshot. + // A non-inclusive range is used to allow updates without any logs. + let expected_sequences = ((current_indexing_snapshot.sequence + 1) + .saturating_sub(logs.len() as u32) + ..(current_indexing_snapshot.sequence + 1)) + .collect::>(); + if all_log_sequences != &expected_sequences { + // If there are any missing sequences, rewind to just before the last indexed snapshot. + // Rewind to the last snapshot. + self.rewind_due_to_sequence_gaps(&logs, all_log_sequences, &expected_sequences, &range); + return Ok(()); + } + + let logs_len: u32 = logs.len().try_into()?; + + // If the number of logs, which start at the current sequence and go backwards, + // exceeds the current indexing snapshot sequence, we've synced everything including + // sequence 0. Otherwise, we're not fully synced yet. + self.current_indexing_snapshot = current_indexing_snapshot + .sequence + .checked_sub(logs_len) + .map(|new_current_sequence| TargetSnapshot { + sequence: new_current_sequence, + at_block: *range.start(), + }); + + // This means we indexed at least one log that builds on the last snapshot. + // Recall logs is sorted in ascending order, so the last log is the "oldest" / "earliest" + // log in the range. + if let Some(lowest_sequence_log) = logs.first() { + // Update the last snapshot. + self.last_indexed_snapshot = LastIndexedSnapshot { + sequence: Some(lowest_sequence_log.0.sequence()), + at_block: lowest_sequence_log.1.block_number.try_into()?, + }; + } + + Ok(()) + } + + /// Updates the cursor with the logs that were found in the range. + /// Only used in sequence mode. + /// Logs are expected to be sorted by sequence in ascending order and deduplicated. + /// + /// Behavior: + /// - The sequences of the logs must exactly match the range. + /// - If there are any gaps, the cursor rewinds and the range will be retried. + fn update_sequence_range( + &mut self, + logs: Vec<(T, LogMeta)>, + all_log_sequences: &HashSet, + range: RangeInclusive, + current_indexing_snapshot: TargetSnapshot, + ) -> Result<()> { + // We require that the range starts at the current sequence. + // This should always be the case, but to be extra safe we handle this case. + if *range.end() != current_indexing_snapshot.sequence { + warn!( + ?logs, + ?range, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + "Expected range to end at the current sequence", + ); + self.rewind(); + return Ok(()); + } + + // We require that we've gotten all sequences in the range. + let expected_sequences = range.clone().collect::>(); + if all_log_sequences != &expected_sequences { + // If there are any missing sequences, rewind to just before the last indexed snapshot. + // Rewind to the last snapshot. + self.rewind_due_to_sequence_gaps(&logs, all_log_sequences, &expected_sequences, &range); + return Ok(()); + } + + // If we've gotten here, it means we indexed the entire range. + // We update the last snapshot accordingly and set ourselves up to index the previous sequence. + // Recall logs is sorted in ascending order, so the first log is the lowest sequence. + let Some(lowest_sequence_log) = logs.first() else { + // Sequence range indexing should never have empty ranges, + // but to be safe we handle this anyways. + warn!( + ?logs, + ?range, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + "Expected non-empty logs and range in sequence mode", + ); + return Ok(()); + }; + + // Update the last indexed snapshot. + self.last_indexed_snapshot = LastIndexedSnapshot { + sequence: Some(lowest_sequence_log.0.sequence()), + at_block: lowest_sequence_log.1.block_number.try_into()?, + }; + // Position the current snapshot to the previous sequence. + self.current_indexing_snapshot = self.last_indexed_snapshot.previous_target(); + + Ok(()) + } + + /// Rewinds the cursor to target immediately preceding the last indexed snapshot, + /// and logs the inconsistencies. + fn rewind_due_to_sequence_gaps( + &mut self, + logs: &Vec<(T, LogMeta)>, + all_log_sequences: &HashSet, + expected_sequences: &HashSet, + expected_sequence_range: &RangeInclusive, + ) { + warn!( + all_log_sequences=?all_log_sequences.iter().sorted().collect::>(), + expected_sequences=?expected_sequences.iter().sorted().collect::>(), + ?expected_sequence_range, + missing_expected_sequences=?expected_sequences.difference(all_log_sequences).sorted().collect::>(), + unexpected_sequences=?all_log_sequences.difference(expected_sequences).sorted().collect::>(), + ?logs, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + "Log sequences don't exactly match the expected sequence range, rewinding to last indexed snapshot", + ); + // Rewind to the last snapshot. + self.rewind(); + } + + fn rewind(&mut self) { + self.current_indexing_snapshot = self.last_indexed_snapshot.previous_target(); + } +} + +#[async_trait] +impl ContractSyncCursor for BackwardSequenceAwareSyncCursor { + async fn next_action(&mut self) -> Result<(CursorAction, Duration)> { + // TODO: Fix ETA calculation + let eta = Duration::from_secs(0); + if let Some(range) = self.get_next_range().await? { + Ok((CursorAction::Query(range), eta)) + } else { + // TODO: Define the sleep time from interval flag + Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)) + } + } + + fn latest_queried_block(&self) -> u32 { + self.current_indexing_snapshot + .as_ref() + .map(|snapshot| snapshot.at_block) + .unwrap_or(self.last_indexed_snapshot.at_block) + } + + /// Updates the cursor with the logs that were found in the range. + /// + /// Inconsistencies in the logs are not considered errors, instead they're handled by rewinding the cursor + /// to retry ranges. + /// + /// ## logs + /// The logs to ingest. If any logs are duplicated or their sequence is higher than the current indexing snapshot, + /// they are filtered out. + async fn update(&mut self, logs: Vec<(T, LogMeta)>, range: RangeInclusive) -> Result<()> { + let Some(current_indexing_snapshot) = self.current_indexing_snapshot.clone() else { + // We're synced, no need to update at all. + return Ok(()); + }; + + // Remove any duplicates, filter out any logs with a higher sequence than our + // current snapshot, and sort in ascending order. + let logs = logs + .into_iter() + .unique_by(|(log, _)| log.sequence()) + .filter(|(log, _)| log.sequence() <= current_indexing_snapshot.sequence) + .sorted_by(|(log_a, _), (log_b, _)| log_a.sequence().cmp(&log_b.sequence())) + .collect::>(); + + let all_log_sequences = logs + .iter() + .map(|(log, _)| log.sequence()) + .collect::>(); + + match &self.index_mode { + IndexMode::Sequence => self.update_sequence_range( + logs, + &all_log_sequences, + range, + current_indexing_snapshot, + )?, + IndexMode::Block => { + self.update_block_range(logs, &all_log_sequences, range, current_indexing_snapshot)? + } + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::super::forward::test::*; + use super::*; + + const INITIAL_CURRENT_INDEXING_SNAPSHOT: TargetSnapshot = TargetSnapshot { + sequence: 99, + at_block: 1000, + }; + const INITIAL_LAST_INDEXED_SNAPSHOT: LastIndexedSnapshot = LastIndexedSnapshot { + sequence: Some(INITIAL_CURRENT_INDEXING_SNAPSHOT.sequence + 1), + at_block: INITIAL_CURRENT_INDEXING_SNAPSHOT.at_block, + }; + + // Start at sequence 101 to illustrate fast forwarding works + const INITIAL_SEQUENCE_COUNT: u32 = 101; + const INITIAL_START_BLOCK: u32 = 1001; + + /// Returns a cursor with the current indexing snapshot as INITIAL_CURRENT_INDEXING_SNAPSHOT. + async fn get_test_backward_sequence_aware_sync_cursor( + mode: IndexMode, + chunk_size: u32, + ) -> BackwardSequenceAwareSyncCursor { + let db = Arc::new(MockHyperlaneSequenceAwareIndexerStore { + logs: vec![ + ( + MockSequencedData::new(INITIAL_LAST_INDEXED_SNAPSHOT.sequence.unwrap()), + log_meta_with_block(INITIAL_LAST_INDEXED_SNAPSHOT.at_block.into()), + ), + ( + MockSequencedData::new(INITIAL_SEQUENCE_COUNT), + log_meta_with_block(INITIAL_START_BLOCK.into()), + ), + (MockSequencedData::new(102), log_meta_with_block(1002)), + ], + }); + + let mut cursor = BackwardSequenceAwareSyncCursor::new( + chunk_size, + db, + INITIAL_SEQUENCE_COUNT, + INITIAL_START_BLOCK, + mode, + ); + + // Skip any already indexed logs and sanity check we start at the correct spot. + cursor.skip_indexed().await.unwrap(); + assert_eq!( + cursor.current_indexing_snapshot, + Some(INITIAL_CURRENT_INDEXING_SNAPSHOT), + ); + assert_eq!(cursor.last_indexed_snapshot, INITIAL_LAST_INDEXED_SNAPSHOT); + + cursor + } + + mod block_range { + use super::*; + + const INDEX_MODE: IndexMode = IndexMode::Block; + const CHUNK_SIZE: u32 = 100; + + async fn get_cursor() -> BackwardSequenceAwareSyncCursor { + get_test_backward_sequence_aware_sync_cursor(INDEX_MODE, CHUNK_SIZE).await + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_normal_indexing() { + let mut cursor = get_cursor().await; + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 900..=1000; + assert_eq!(range, expected_range); + + // Calling get_next_range again should yield the same range. + let range = cursor.get_next_range().await.unwrap().unwrap(); + assert_eq!(range, expected_range); + + // Update the cursor with some found logs. + cursor + .update( + vec![ + (MockSequencedData::new(97), log_meta_with_block(970)), + (MockSequencedData::new(98), log_meta_with_block(980)), + (MockSequencedData::new(99), log_meta_with_block(990)), + ], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the previous sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 96, + at_block: 900, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(97), + at_block: 970, + } + ); + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_multiple_ranges() { + let mut cursor = get_cursor().await; + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 900..=1000; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. + cursor.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have moved the current indexing snapshot's block number (but not sequence), + // and made no changes to the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 99, + at_block: 900, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(100), + at_block: 1000, + } + ); + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 800..=900; + assert_eq!(range, expected_range); + + // Update the cursor with some found logs now. + cursor + .update( + vec![ + (MockSequencedData::new(96), log_meta_with_block(850)), + (MockSequencedData::new(97), log_meta_with_block(860)), + (MockSequencedData::new(98), log_meta_with_block(870)), + (MockSequencedData::new(99), log_meta_with_block(880)), + ], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the previous sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 95, + at_block: 800, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(96), + at_block: 850, + } + ); + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_for_sequence_gap() { + let mut cursor = get_cursor().await; + + async fn update_and_expect_rewind( + cur: &mut BackwardSequenceAwareSyncCursor, + logs: Vec<(MockSequencedData, LogMeta)>, + ) { + // For a more rigorous test case, first do a range where no logs are found, + // then in the next range there are issues, and we should rewind to the last indexed snapshot. + + // Expect the range to be: + // (current - chunk_size, current) + let range = cur.get_next_range().await.unwrap().unwrap(); + let expected_range = 900..=1000; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. + cur.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have moved the current indexing snapshot's block number (but not sequence), + // and made no changes to the last indexed snapshot. + assert_eq!( + cur.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 99, + at_block: 900, + }) + ); + assert_eq!( + cur.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(100), + at_block: 1000, + } + ); + + // Expect the range to be: + // (start, tip) + let range = cur.get_next_range().await.unwrap().unwrap(); + let expected_range = 800..=900; + assert_eq!(range, expected_range); + + // Update the cursor, expecting a rewind now + cur.update(logs, expected_range).await.unwrap(); + + // Expect the cursor rewound to just prior to the last indexed snapshot. + assert_eq!( + cur.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 99, + at_block: 1000, + }) + ); + assert_eq!( + cur.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(100), + at_block: 1000, + } + ); + } + + // Not building upon last snapshot + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(96), log_meta_with_block(850)), + (MockSequencedData::new(97), log_meta_with_block(860)), + (MockSequencedData::new(98), log_meta_with_block(870)), + ], + ) + .await; + + // Now with a gap, missing 98 + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(96), log_meta_with_block(850)), + (MockSequencedData::new(97), log_meta_with_block(860)), + (MockSequencedData::new(99), log_meta_with_block(890)), + ], + ) + .await; + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_handles_unexpected_logs() { + let mut cursor = get_cursor().await; + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 900..=1000; + assert_eq!(range, expected_range); + + // Update the cursor with some paritally bogus logs: + // - Three logs of sequence 99, i.e. duplicated + // - A log at sequence 100, which was already indexed and should be ignored + cursor + .update( + vec![ + (MockSequencedData::new(99), log_meta_with_block(990)), + (MockSequencedData::new(99), log_meta_with_block(990)), + (MockSequencedData::new(100), log_meta_with_block(1000)), + (MockSequencedData::new(99), log_meta_with_block(990)), + ], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the previous sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 98, + at_block: 900, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(99), + at_block: 990, + } + ); + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_stops_after_indexing_sequence_0() { + let mut cursor = get_cursor().await; + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 900..=1000; + assert_eq!(range, expected_range); + + // Update the with all the missing logs. + cursor + .update( + (0..=99) + .map(|i| { + ( + MockSequencedData::new(i), + log_meta_with_block(900 + i as u64), + ) + }) + .collect(), + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to indicate that it's fully synced. + assert_eq!(cursor.current_indexing_snapshot, None,); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(0), + at_block: 900, + } + ); + + // Expect the range to be None + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_skip_indexed_when_fully_synced() { + let db = Arc::new(MockHyperlaneSequenceAwareIndexerStore { + logs: (0..=INITIAL_SEQUENCE_COUNT) + .map(|i| { + ( + MockSequencedData::new(i), + log_meta_with_block(900 + i as u64), + ) + }) + .collect(), + }); + + let mut cursor = BackwardSequenceAwareSyncCursor::new( + CHUNK_SIZE, + db, + INITIAL_SEQUENCE_COUNT, + INITIAL_START_BLOCK, + INDEX_MODE, + ); + + // We're fully synced, so expect no range + assert_eq!(cursor.get_next_range().await.unwrap(), None); + } + } + + mod sequence_range { + use super::*; + + const INDEX_MODE: IndexMode = IndexMode::Sequence; + const CHUNK_SIZE: u32 = 5; + + async fn get_cursor() -> BackwardSequenceAwareSyncCursor { + get_test_backward_sequence_aware_sync_cursor(INDEX_MODE, CHUNK_SIZE).await + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_normal_indexing() { + let mut cursor = get_cursor().await; + + // We should have fast forwarded to sequence 99, block 1000 + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 99, + at_block: 1000, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(100), + at_block: 1000, + } + ); + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 94..=99; + assert_eq!(range, expected_range); + + // Calling get_next_range again should yield the same range. + let range = cursor.get_next_range().await.unwrap().unwrap(); + assert_eq!(range, expected_range); + + // Update the cursor with some found logs. These have some duplicates + // and are not sorted, and we expect the cursor to handle this. + cursor + .update( + vec![ + (MockSequencedData::new(95), log_meta_with_block(950)), + (MockSequencedData::new(96), log_meta_with_block(960)), + (MockSequencedData::new(97), log_meta_with_block(970)), + // Add a duplicate here + (MockSequencedData::new(98), log_meta_with_block(980)), + (MockSequencedData::new(98), log_meta_with_block(980)), + (MockSequencedData::new(99), log_meta_with_block(990)), + // Put this out of order + (MockSequencedData::new(94), log_meta_with_block(940)), + ], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the previous sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 93, + at_block: 940, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(94), + at_block: 940, + } + ); + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_if_updated_with_no_logs() { + let mut cursor = get_cursor().await; + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 94..=99; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. + cursor.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have "rewound", i.e. no changes to the current indexing snapshot or last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 99, + at_block: 1000, + }) + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(100), + at_block: 1000, + } + ); + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_if_gap_or_unexpected_logs() { + // Starts with current snapshot at sequence 99, block 1000 + let mut cursor = get_cursor().await; + + async fn update_and_expect_rewind( + cur: &mut BackwardSequenceAwareSyncCursor, + logs: Vec<(MockSequencedData, LogMeta)>, + ) { + // Expect the range to be: + // (current - chunk_size, current) + let range = cur.get_next_range().await.unwrap().unwrap(); + let expected_range = 94..=99; + assert_eq!(range, expected_range); + + // Update the cursor + cur.update(logs, expected_range).await.unwrap(); + + // Expect the cursor to have "rewound", i.e. no changes to the current indexing snapshot or last indexed snapshot. + assert_eq!( + cur.current_indexing_snapshot, + Some(TargetSnapshot { + sequence: 99, + at_block: 1000, + }) + ); + assert_eq!( + cur.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(100), + at_block: 1000, + } + ); + } + + // First, try without building upon the last snapshot + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(94), log_meta_with_block(940)), + (MockSequencedData::new(95), log_meta_with_block(950)), + (MockSequencedData::new(96), log_meta_with_block(960)), + (MockSequencedData::new(98), log_meta_with_block(980)), + ], + ) + .await; + + // This time with a gap (missing 97) + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(94), log_meta_with_block(940)), + (MockSequencedData::new(95), log_meta_with_block(950)), + (MockSequencedData::new(96), log_meta_with_block(960)), + (MockSequencedData::new(98), log_meta_with_block(980)), + (MockSequencedData::new(99), log_meta_with_block(990)), + ], + ) + .await; + + // This time building upon the last snapshot, but the first sequence in the range isn't present + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(95), log_meta_with_block(950)), + (MockSequencedData::new(96), log_meta_with_block(960)), + (MockSequencedData::new(97), log_meta_with_block(970)), + (MockSequencedData::new(98), log_meta_with_block(980)), + (MockSequencedData::new(99), log_meta_with_block(990)), + ], + ) + .await; + + // An unexpected log, sequence 93 + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(93), log_meta_with_block(940)), + (MockSequencedData::new(94), log_meta_with_block(950)), + (MockSequencedData::new(95), log_meta_with_block(950)), + (MockSequencedData::new(96), log_meta_with_block(960)), + (MockSequencedData::new(97), log_meta_with_block(970)), + (MockSequencedData::new(98), log_meta_with_block(980)), + (MockSequencedData::new(99), log_meta_with_block(990)), + ], + ) + .await; + } + + #[tracing_test::traced_test] + #[tokio::test] + async fn test_stops_after_indexing_sequence_0() { + let mut cursor = get_cursor().await; + + // Set the chunk size to 100 to make it easier to test. + cursor.chunk_size = 100; + + // Expect the range to be: + // (current - chunk_size, current) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 0..=99; + assert_eq!(range, expected_range); + + // Update the with all the missing logs. + cursor + .update( + (0..=99) + .map(|i| { + ( + MockSequencedData::new(i), + log_meta_with_block(900 + i as u64), + ) + }) + .collect(), + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to indicate that it's fully synced. + assert_eq!(cursor.current_indexing_snapshot, None); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(0), + at_block: 900, + } + ); + + // Expect the range to be None + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + } + } +} diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs new file mode 100644 index 0000000000..97b34aa40c --- /dev/null +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs @@ -0,0 +1,1186 @@ +//! A sequence-aware cursor that syncs forwards in perpetuity, reacting to gaps in log sequences +//! and only indexing ranges of logs that are likely to contain new logs. + +use std::{ + cmp::Ordering, collections::HashSet, fmt::Debug, ops::RangeInclusive, sync::Arc, time::Duration, +}; + +use async_trait::async_trait; +use eyre::Result; +use hyperlane_core::{ + ContractSyncCursor, CursorAction, HyperlaneSequenceAwareIndexerStoreReader, IndexMode, LogMeta, + SequenceAwareIndexer, Sequenced, +}; +use itertools::Itertools; +use tracing::{debug, warn}; + +use super::{LastIndexedSnapshot, TargetSnapshot}; + +/// A sequence-aware cursor that syncs forwards in perpetuity. +#[derive(Debug)] +pub(crate) struct ForwardSequenceAwareSyncCursor { + /// The max chunk size to query for logs. + /// If in sequence mode, this is the max number of sequences to query. + /// If in block mode, this is the max number of blocks to query. + chunk_size: u32, + /// The latest sequence count querier. + /// This is used to check if there are new logs to index and to + /// establish targets to index towards. + latest_sequence_querier: Arc>, + /// A DB used to check which logs have already been indexed. + db: Arc>, + /// A snapshot of the last indexed log, or if no indexing has occurred yet, + /// the initial log to start indexing forward from. + last_indexed_snapshot: LastIndexedSnapshot, + /// The current snapshot we're indexing. As this is a forward cursor, + /// if the last indexed snapshot was sequence 100, this would be sequence 101. + current_indexing_snapshot: TargetSnapshot, + /// The target snapshot to index towards. + target_snapshot: Option, + /// The mode of indexing. + index_mode: IndexMode, +} + +impl ForwardSequenceAwareSyncCursor { + pub fn new( + chunk_size: u32, + latest_sequence_querier: Arc>, + db: Arc>, + next_sequence: u32, + start_block: u32, + index_mode: IndexMode, + ) -> Self { + // If the next sequence is 0, we're starting from the beginning and haven't + // indexed anything yet. + let last_indexed_snapshot = LastIndexedSnapshot { + sequence: (next_sequence > 0).then(|| next_sequence.saturating_sub(1)), + at_block: start_block, + }; + + Self { + chunk_size, + latest_sequence_querier, + db, + last_indexed_snapshot, + current_indexing_snapshot: TargetSnapshot { + sequence: next_sequence, + at_block: start_block, + }, + target_snapshot: None, + index_mode, + } + } + + /// Gets the next range of logs to index. + /// If there are no logs to index, returns `None`. + /// If there are logs to index, returns the range of logs, either by sequence or block number + /// depending on the mode. + pub async fn get_next_range(&mut self) -> Result>> { + // Skip any already indexed logs. + self.skip_indexed().await?; + + let (Some(onchain_sequence_count), tip) = self + .latest_sequence_querier + .latest_sequence_count_and_tip() + .await? + else { + return Ok(None); + }; + + let current_sequence = self.current_indexing_snapshot.sequence; + let range = match current_sequence.cmp(&onchain_sequence_count) { + Ordering::Equal => { + // We are synced up to the latest sequence so we don't need to index anything. + + // We can update the current indexing snapshot to the tip. + // This will let us only index blocks that are likely to have new logs once + // there's a new sequence to search for. + self.current_indexing_snapshot.at_block = tip; + + None + } + Ordering::Less => { + // The cursor is behind the onchain sequence count, so we need to index. + + // Minus one because this is the sequence we're targeting, not the count. + let target_sequence = onchain_sequence_count.saturating_sub(1); + + // Set the target to the highest sequence and tip. + // We don't necessarily expect to hit this target in the next query (because we + // have limits to the range size based off the chunk size), but we will use it + // as an eventual target. + self.target_snapshot = Some(TargetSnapshot { + sequence: target_sequence, + at_block: tip, + }); + + match &self.index_mode { + IndexMode::Block => self.get_next_block_range(tip), + IndexMode::Sequence => { + Some(self.get_next_sequence_range(current_sequence, target_sequence)) + } + } + } + Ordering::Greater => { + // Providers may be internally inconsistent, e.g. RPC request A could hit a node + // whose tip is N and subsequent RPC request B could hit a node whose tip is < N. + // Just warn and try to continue as normal. + warn!( + current_sequence, + onchain_sequence_count, + "Current sequence is greater than the onchain sequence count" + ); + None + } + }; + + Ok(range) + } + + /// Gets the next block range to index. + /// Only used in block mode. + fn get_next_block_range(&self, tip: u32) -> Option> { + // This should never happen, but if it does, we log a warning and return None. + if self.current_indexing_snapshot.at_block > tip { + warn!( + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + target_snapshot=?self.target_snapshot, + tip, + "Current indexing snapshot's block number is greater than the tip" + ); + return None; + } + + // Query the block range starting from the current_indexing_snapshot's at_block. + Some( + self.current_indexing_snapshot.at_block + ..=u32::min( + self.current_indexing_snapshot.at_block + self.chunk_size, + tip, + ), + ) + } + + /// Gets the next sequence range to index. + /// Only used in sequence mode. + fn get_next_sequence_range( + &self, + current_sequence: u32, + target_sequence: u32, + ) -> RangeInclusive { + // Query the sequence range starting from the cursor count. + current_sequence..=u32::min(target_sequence, current_sequence + self.chunk_size) + } + + /// Reads the DB to check if the current indexing sequence has already been indexed, + /// iterating until we find a sequence that hasn't been indexed. + async fn skip_indexed(&mut self) -> Result<()> { + // Check if any new logs have been inserted into the DB, + // and update the cursor accordingly. + while let Some(block_number) = self + .get_sequence_log_block_number(self.current_indexing_snapshot.sequence) + .await? + { + self.last_indexed_snapshot = LastIndexedSnapshot { + sequence: Some(self.current_indexing_snapshot.sequence), + at_block: block_number, + }; + + self.current_indexing_snapshot = self.last_indexed_snapshot.next_target(); + + debug!( + last_indexed_snapshot=?self.last_indexed_snapshot, + current_indexing_snapshot=?self.current_indexing_snapshot, + "Fast forwarded current sequence" + ); + } + + Ok(()) + } + + /// Gets the log block number of a previously indexed sequence. Returns None if the + /// log for the sequence number hasn't been indexed. + async fn get_sequence_log_block_number(&self, sequence: u32) -> Result> { + // Ensure there's a full entry for the sequence. + if self.db.retrieve_by_sequence(sequence).await?.is_some() { + // And get the block number. + if let Some(block_number) = self + .db + .retrieve_log_block_number_by_sequence(sequence) + .await? + { + return Ok(Some(block_number.try_into()?)); + } + } + + Ok(None) + } + + /// Updates the cursor with the logs that were found in the range. + /// Only used in sequence mode. + /// Logs are expected to be sorted by sequence in ascending order and deduplicated. + /// + /// Behavior: + /// - Empty logs are allowed, but no gaps are allowed. The logs must build upon the last indexed snapshot. + /// - If there are any gaps, the cursor rewinds to the last indexed snapshot, and ranges will be retried. + /// - If the target block is reached and the target sequence hasn't been reached, the cursor rewinds to the last indexed snapshot. + fn update_block_range( + &mut self, + logs: Vec<(T, LogMeta)>, + all_log_sequences: &HashSet, + range: RangeInclusive, + ) -> Result<()> { + // We require no sequence gaps and to build upon the last snapshot. + // A non-inclusive range is used to allow updates without any logs. + let expected_sequences = (self.current_indexing_snapshot.sequence + ..(self.current_indexing_snapshot.sequence + logs.len() as u32)) + .collect::>(); + if all_log_sequences != &expected_sequences { + // If there are any missing sequences, rewind to just after the last snapshot. + self.rewind_due_to_sequence_gaps(&logs, all_log_sequences, &expected_sequences, &range); + return Ok(()); + } + + // Update the current indexing snapshot forward. + self.current_indexing_snapshot = TargetSnapshot { + sequence: self.current_indexing_snapshot.sequence + logs.len() as u32, + at_block: *range.end(), + }; + + // This means we indexed at least one log that builds on the last snapshot. + if let Some(highest_sequence_log) = logs.last() { + // Update the last indexed snapshot. + self.last_indexed_snapshot = LastIndexedSnapshot { + sequence: Some(highest_sequence_log.0.sequence()), + at_block: highest_sequence_log.1.block_number.try_into()?, + }; + } + + let Some(target_snapshot) = self.target_snapshot.as_ref() else { + warn!( + ?logs, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + target_snapshot=?self.target_snapshot, + "No target snapshot, cursor should not updated unless one is set", + ); + return Ok(()); + }; + + // If the end block is >= the target block and we haven't yet reached the target sequence, + // rewind to just after the last indexed snapshot. + if self + .last_indexed_snapshot + .sequence + .map(|last_indexed_sequence| last_indexed_sequence < target_snapshot.sequence) + .unwrap_or(true) + && *range.end() >= target_snapshot.at_block + { + warn!( + ?logs, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + target_snapshot=?self.target_snapshot, + "Reached the target block number but not the target sequence, rewinding to last snapshot", + ); + self.rewind(); + return Ok(()); + } + + Ok(()) + } + + /// Updates the cursor with the logs that were found in the range. + /// Only used in sequence mode. + /// Logs are expected to be sorted by sequence in ascending order and deduplicated. + /// + /// Behavior: + /// - The sequences of the logs must exactly match the range. + /// - If there are any gaps, the cursor rewinds and the range will be retried. + fn update_sequence_range( + &mut self, + logs: Vec<(T, LogMeta)>, + all_log_sequences: &HashSet, + range: RangeInclusive, + ) -> Result<()> { + // We require that the range starts at the current sequence. + // This should always be the case, but to be extra safe we handle this case. + if *range.start() != self.current_indexing_snapshot.sequence { + warn!( + ?logs, + ?range, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + target_snapshot=?self.target_snapshot, + "Expected range to start at the current sequence", + ); + self.rewind(); + return Ok(()); + } + + // We require that we've gotten all sequences in the range. + let expected_sequences = range.clone().collect::>(); + if all_log_sequences != &expected_sequences { + // If there are any missing sequences, rewind to just after the last snapshot. + self.rewind_due_to_sequence_gaps(&logs, all_log_sequences, &expected_sequences, &range); + return Ok(()); + } + + // If we've gotten here, it means we indexed the entire range. + // We update the last snapshot accordingly and set ourselves up for the next sequence. + let Some(highest_sequence_log) = logs.last() else { + // Sequence range indexing should never have empty ranges, + // but to be safe we handle this anyways. + warn!( + ?logs, + ?range, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + target_snapshot=?self.target_snapshot, + "Expected non-empty logs and range in sequence mode", + ); + return Ok(()); + }; + + // Update the last indexed snapshot. + self.last_indexed_snapshot = LastIndexedSnapshot { + sequence: Some(highest_sequence_log.0.sequence()), + at_block: highest_sequence_log.1.block_number.try_into()?, + }; + // Position the current snapshot to the next sequence. + self.current_indexing_snapshot = self.last_indexed_snapshot.next_target(); + + Ok(()) + } + + /// Rewinds the cursor to target immediately after the last indexed snapshot, + /// and logs the inconsistencies due to sequence gaps. + fn rewind_due_to_sequence_gaps( + &mut self, + logs: &Vec<(T, LogMeta)>, + all_log_sequences: &HashSet, + expected_sequences: &HashSet, + expected_sequence_range: &RangeInclusive, + ) { + warn!( + all_log_sequences=?all_log_sequences.iter().sorted().collect::>(), + expected_sequences=?expected_sequences.iter().sorted().collect::>(), + ?expected_sequence_range, + missing_expected_sequences=?expected_sequences.difference(all_log_sequences).sorted().collect::>(), + unexpected_sequences=?all_log_sequences.difference(expected_sequences).sorted().collect::>(), + ?logs, + current_indexing_snapshot=?self.current_indexing_snapshot, + last_indexed_snapshot=?self.last_indexed_snapshot, + target_snapshot=?self.target_snapshot, + "Log sequences don't exactly match the expected sequence range, rewinding to last indexed snapshot", + ); + // If there are any missing sequences, rewind to index immediately after the last snapshot. + self.rewind(); + } + + // Rewinds the cursor to target immediately after the last indexed snapshot. + fn rewind(&mut self) { + self.current_indexing_snapshot = self.last_indexed_snapshot.next_target(); + } +} + +#[async_trait] +impl ContractSyncCursor for ForwardSequenceAwareSyncCursor { + async fn next_action(&mut self) -> Result<(CursorAction, Duration)> { + // TODO: Fix ETA calculation + let eta = Duration::from_secs(0); + if let Some(range) = self.get_next_range().await? { + Ok((CursorAction::Query(range), eta)) + } else { + // TODO: Define the sleep time from interval flag + Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)) + } + } + + // TODO: revisit to establish a better heuristic for cursor / indexing health + fn latest_queried_block(&self) -> u32 { + self.current_indexing_snapshot.at_block + } + + /// Updates the cursor with the logs that were found in the range. + /// + /// Inconsistencies in the logs are not considered errors, instead they're handled by rewinding the cursor + /// to retry ranges. + /// + /// ## logs + /// The logs to ingest. If any logs are duplicated or their sequence is lower than the current indexing snapshot, + /// they are filtered out. See `update_sequence_range` and `update_block_range` for more details based + /// off the indexing mode. + /// + /// Note: + /// - Even if the logs include a gap, in practice these logs will have already been inserted into the DB. + /// This means that while gaps result in a rewind here, already known logs may be "fast forwarded" through, + /// and the cursor won't actually end up re-indexing already known logs. + async fn update(&mut self, logs: Vec<(T, LogMeta)>, range: RangeInclusive) -> Result<()> { + // Remove any sequence duplicates, filter out any logs preceding our current snapshot, + // and sort in ascending order. + let logs = logs + .into_iter() + .unique_by(|(log, _)| log.sequence()) + .filter(|(log, _)| log.sequence() >= self.current_indexing_snapshot.sequence) + .sorted_by(|(log_a, _), (log_b, _)| log_a.sequence().cmp(&log_b.sequence())) + .collect::>(); + + let all_log_sequences = logs + .iter() + .map(|(log, _)| log.sequence()) + .collect::>(); + + match &self.index_mode { + IndexMode::Block => self.update_block_range(logs, &all_log_sequences, range)?, + IndexMode::Sequence => self.update_sequence_range(logs, &all_log_sequences, range)?, + }; + Ok(()) + } +} + +#[cfg(test)] +pub(crate) mod test { + use derive_new::new; + use hyperlane_core::{ChainResult, HyperlaneLogStore, Indexer}; + + use super::*; + + #[derive(Debug, Clone)] + pub struct MockLatestSequenceQuerier { + pub latest_sequence_count: Option, + pub tip: u32, + } + + #[async_trait] + impl SequenceAwareIndexer for MockLatestSequenceQuerier + where + T: Sequenced + Debug, + { + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)> { + Ok((self.latest_sequence_count, self.tip)) + } + } + + #[async_trait] + impl Indexer for MockLatestSequenceQuerier + where + T: Sequenced + Debug, + { + async fn fetch_logs(&self, _range: RangeInclusive) -> ChainResult> { + Ok(vec![]) + } + + async fn get_finalized_block_number(&self) -> ChainResult { + Ok(self.tip) + } + } + + #[derive(Debug, Clone)] + pub struct MockHyperlaneSequenceAwareIndexerStore { + pub logs: Vec<(T, LogMeta)>, + } + + #[async_trait] + impl HyperlaneLogStore for MockHyperlaneSequenceAwareIndexerStore { + async fn store_logs(&self, logs: &[(T, LogMeta)]) -> eyre::Result { + Ok(logs.len() as u32) + } + } + + #[async_trait] + impl HyperlaneSequenceAwareIndexerStoreReader + for MockHyperlaneSequenceAwareIndexerStore + { + async fn retrieve_by_sequence(&self, sequence: u32) -> eyre::Result> { + Ok(self + .logs + .iter() + .find(|(log, _)| log.sequence() == sequence) + .map(|(log, _)| log.clone())) + } + + async fn retrieve_log_block_number_by_sequence( + &self, + sequence: u32, + ) -> eyre::Result> { + Ok(self + .logs + .iter() + .find(|(log, _)| log.sequence() == sequence) + .map(|(_, meta)| meta.block_number)) + } + } + + #[derive(Debug, Clone, new)] + pub struct MockSequencedData { + pub sequence: u32, + } + + impl Sequenced for MockSequencedData { + fn sequence(&self) -> u32 { + self.sequence + } + } + + pub fn log_meta_with_block(block_number: u64) -> LogMeta { + LogMeta { + address: Default::default(), + block_number, + block_hash: Default::default(), + transaction_id: Default::default(), + transaction_index: 0, + log_index: Default::default(), + } + } + + const INITIAL_CURRENT_INDEXING_SNAPSHOT: TargetSnapshot = TargetSnapshot { + sequence: 5, + at_block: 90, + }; + const INITIAL_LAST_INDEXED_SNAPSHOT: LastIndexedSnapshot = LastIndexedSnapshot { + sequence: Some(INITIAL_CURRENT_INDEXING_SNAPSHOT.sequence - 1), + at_block: INITIAL_CURRENT_INDEXING_SNAPSHOT.at_block, + }; + + /// Gets a cursor starting at INITIAL_CURRENT_INDEXING_SNAPSHOT. + async fn get_test_forward_sequence_aware_sync_cursor( + mode: IndexMode, + chunk_size: u32, + ) -> ForwardSequenceAwareSyncCursor { + let latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(5), + tip: 100, + }); + + let db = Arc::new(MockHyperlaneSequenceAwareIndexerStore { + logs: vec![ + (MockSequencedData::new(0), log_meta_with_block(50)), + (MockSequencedData::new(1), log_meta_with_block(60)), + (MockSequencedData::new(2), log_meta_with_block(70)), + (MockSequencedData::new(3), log_meta_with_block(80)), + ( + MockSequencedData::new(INITIAL_LAST_INDEXED_SNAPSHOT.sequence.unwrap()), + log_meta_with_block(INITIAL_LAST_INDEXED_SNAPSHOT.at_block.into()), + ), + ], + }); + + let mut cursor = ForwardSequenceAwareSyncCursor::new( + chunk_size, + latest_sequence_querier, + db, + // Start at sequence 3 and block 70 to illustrate fast forwarding + 3, + 70, + mode, + ); + + // Skip any already indexed logs and sanity check we start at the correct spot. + cursor.skip_indexed().await.unwrap(); + assert_eq!( + cursor.current_indexing_snapshot, + INITIAL_CURRENT_INDEXING_SNAPSHOT, + ); + assert_eq!(cursor.last_indexed_snapshot, INITIAL_LAST_INDEXED_SNAPSHOT); + + cursor + } + + mod block_range { + use super::*; + + const INDEX_MODE: IndexMode = IndexMode::Block; + const CHUNK_SIZE: u32 = 100; + + async fn get_cursor() -> ForwardSequenceAwareSyncCursor { + get_test_forward_sequence_aware_sync_cursor(INDEX_MODE, CHUNK_SIZE).await + } + + /// Tests successful fast forwarding & indexing where all ranges return logs. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_normal_indexing() { + let mut cursor = get_cursor().await; + + // As the latest sequence count is 5 and the current indexing snapshot is sequence 5, we should + // expect no range to index. + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + + // Update the tip, expect to still not index anything. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(5), + tip: 110, + }); + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + + // Update the latest sequence count to 6, now we expect to index. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(6), + tip: 120, + }); + + // Expect the range to be: + // (last polled block where the sequence had already been indexed, tip) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 110..=120; + assert_eq!(range, expected_range); + + // Expect the target snapshot to be set to the latest sequence and tip. + assert_eq!( + cursor.target_snapshot, + Some(TargetSnapshot { + sequence: 5, + at_block: 120, + }) + ); + + // Getting the range again without updating the cursor should yield the same range. + let range = cursor.get_next_range().await.unwrap().unwrap(); + assert_eq!(range, expected_range); + + // Update the cursor with the found log. + cursor + .update( + vec![(MockSequencedData::new(5), log_meta_with_block(115))], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the next sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 6, + at_block: 120, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(5), + at_block: 115, + } + ); + + // And now we should get no range to index. + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + } + + // Tests when the cursor is so behind the tip that it'll need to index multiple ranges (due to the + // chunk size) to catch up. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_multiple_ranges_till_target() { + let mut cursor = get_cursor().await; + + // Pretend like the tip is 200, and a message occurred at block 195. + + // Increase the latest sequence count, and with a tip that exceeds the chunk size. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(6), + tip: 200, + }); + + // Expect the range to be: + // (start, start + chunk_size) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 90..=190; + assert_eq!(range, expected_range); + + // Update the cursor. Update with no logs, because the log happened in block 195. + cursor.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have moved the current indexing snapshot's block number (but not sequence), + // and made no changes to the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 190, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + + // Expect the range to be: + // (start, tip) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 190..=200; + assert_eq!(range, expected_range); + + // Update the cursor with the found log. + cursor + .update( + vec![(MockSequencedData::new(5), log_meta_with_block(195))], + expected_range, + ) + .await + .unwrap(); + + // Expect the current indexing snapshot to have moved to the next sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 6, + at_block: 200, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(5), + at_block: 195, + } + ); + + // And now we should get no range to index. + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + } + + /// Tests when the cursor is so behind the tip that it'll need to index multiple ranges, but by the time + /// it gets to the target snapshot, it realizes it missed a log and needs to rewind. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_for_missed_target_sequence() { + let mut cursor = get_cursor().await; + + // Pretend like the tip is 200, and a message occurred at block 195, but we somehow miss it. + + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(6), + tip: 200, + }); + + // Expect the range to be: + // (start, start + chunk_size) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 90..=190; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. + cursor.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have moved the current indexing snapshot's block number (but not sequence), + // and made no changes to the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 190, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + + // Expect the range to be: + // (start, tip) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 190..=200; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. + cursor.update(vec![], expected_range).await.unwrap(); + + // Expect a rewind to occur back to the last indexed snapshot's block number. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 90, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + } + + /// Tests when the cursor is so behind the tip that it'll need to index multiple ranges. It successfully + /// finds a log in the second range, but missed log in the first range, showing a gap. It should rewind to the + /// last indexed snapshot. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_for_sequence_gaps() { + let mut cursor = get_cursor().await; + + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + // 3 new messages since we last indexed have come in! + latest_sequence_count: Some(7), + tip: 200, + }); + + async fn update_and_expect_rewind( + cur: &mut ForwardSequenceAwareSyncCursor, + logs: Vec<(MockSequencedData, LogMeta)>, + ) { + // For a more rigorous test case, first do a range where no logs are found, + // then in the next range there are issues, and we should rewind to the last indexed snapshot. + + // Expect the range to be: + // (start, start + chunk_size) + let range = cur.get_next_range().await.unwrap().unwrap(); + let expected_range = 90..=190; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. We should've found one here though! + cur.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have moved the current indexing snapshot's block number (but not sequence), + // and made no changes to the last indexed snapshot. + assert_eq!( + cur.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 190, + } + ); + assert_eq!( + cur.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + + // Expect the range to be: + // (start, tip) + let range = cur.get_next_range().await.unwrap().unwrap(); + let expected_range = 190..=200; + assert_eq!(range, expected_range); + + // Update the cursor, expecting a rewind now + cur.update(logs, expected_range).await.unwrap(); + + // Expect a rewind to occur back to the last indexed snapshot's block number. + assert_eq!( + cur.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 90, + } + ); + assert_eq!( + cur.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + } + + // We don't build upon the last sequence (5 missing) + update_and_expect_rewind( + &mut cursor, + vec![(MockSequencedData::new(6), log_meta_with_block(100))], + ) + .await; + + // There's a gap (sequence 6 missing) + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(5), log_meta_with_block(95)), + (MockSequencedData::new(7), log_meta_with_block(105)), + ], + ) + .await; + } + + /// Tests when the cursor is so behind the tip that it'll need to index multiple ranges, but by the time + /// it gets to the target snapshot, it realizes it missed a log and needs to rewind. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_handles_unexpected_logs() { + let mut cursor = get_cursor().await; + + // Pretend like the tip is 100, and a message occurred at block 95. + + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(6), + tip: 100, + }); + + // Expect the range to be: + // (start, start + chunk_size) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 90..=100; + assert_eq!(range, expected_range); + + // Update the cursor with some paritally bogus logs: + // - A log at sequence 4, which was already indexed and should be ignored + // - Three logs of sequence 5, i.e. duplicated + // - A log at sequence 6, which is unexpected, but tolerated nonetheless + cursor + .update( + vec![ + (MockSequencedData::new(4), log_meta_with_block(90)), + (MockSequencedData::new(5), log_meta_with_block(95)), + (MockSequencedData::new(5), log_meta_with_block(95)), + (MockSequencedData::new(6), log_meta_with_block(100)), + (MockSequencedData::new(5), log_meta_with_block(95)), + ], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the next sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 7, + at_block: 100, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(6), + at_block: 100, + } + ); + } + } + + mod sequence_range { + use super::*; + + const INDEX_MODE: IndexMode = IndexMode::Sequence; + const CHUNK_SIZE: u32 = 10; + + async fn get_cursor() -> ForwardSequenceAwareSyncCursor { + get_test_forward_sequence_aware_sync_cursor(INDEX_MODE, CHUNK_SIZE).await + } + + /// Tests successful fast forwarding & successful indexing with a correct sequence range. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_normal_indexing() { + let mut cursor = get_cursor().await; + + // As the latest sequence count is 5 and the current indexing snapshot is sequence 5, we should + // expect no range to index. + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + + // Update the tip, expect to still not index anything. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(5), + tip: 110, + }); + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + + // Update the latest sequence count to 6, now we expect to index. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(6), + tip: 120, + }); + + // Expect the range to be: + // (new sequence, new sequence) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 5..=5; + assert_eq!(range, expected_range); + + // Expect the target snapshot to be set to the latest sequence and tip. + assert_eq!( + cursor.target_snapshot, + Some(TargetSnapshot { + sequence: 5, + at_block: 120, + }) + ); + + // Getting the range again without updating the cursor should yield the same range. + let range = cursor.get_next_range().await.unwrap().unwrap(); + assert_eq!(range, expected_range); + + // Update the cursor with the found log. + cursor + .update( + vec![(MockSequencedData::new(5), log_meta_with_block(115))], + expected_range, + ) + .await + .unwrap(); + + // Expect the cursor to have moved to the next sequence and updated the last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 6, + at_block: 115, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(5), + at_block: 115, + } + ); + + // And now we should get no range to index. + let range = cursor.get_next_range().await.unwrap(); + assert_eq!(range, None); + + // Update the latest sequence count to 30 to test we use the chunk size. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(30), + tip: 150, + }); + + // Expect the range to be: + // (next sequence, next sequence + chunk size) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 6..=16; + assert_eq!(range, expected_range); + } + + /// Tests getting no logs when a sequence range is expected. + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_if_updated_with_no_logs() { + let mut cursor = get_cursor().await; + + // Update the latest sequence count to 6, expecting to index. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(6), + tip: 120, + }); + + // Expect the range to be: + // (new sequence, new sequence) + let range = cursor.get_next_range().await.unwrap().unwrap(); + let expected_range = 5..=5; + assert_eq!(range, expected_range); + + // Update the cursor with no found logs. + cursor.update(vec![], expected_range).await.unwrap(); + + // Expect the cursor to have rewound to the last indexed snapshot - really this is + // the same as not updating current indexing snapshot / last indexed snapshot. + assert_eq!( + cursor.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 90, + } + ); + assert_eq!( + cursor.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + } + + /// Tests getting a gap in the expected logs + #[tracing_test::traced_test] + #[tokio::test] + async fn test_rewinds_for_sequence_gaps() { + let mut cursor = get_cursor().await; + + // Update the latest sequence count to 8, expecting to index 3 messages. + cursor.latest_sequence_querier = Arc::new(MockLatestSequenceQuerier { + latest_sequence_count: Some(8), + tip: 120, + }); + + async fn update_and_expect_rewind( + cur: &mut ForwardSequenceAwareSyncCursor, + logs: Vec<(MockSequencedData, LogMeta)>, + ) { + // Expect the range to be: + // (new sequence, new sequence) + let range = cur.get_next_range().await.unwrap().unwrap(); + let expected_range = 5..=7; + assert_eq!(range, expected_range); + + // Update the cursor with sequence 5 and 7, but not 6. + cur.update(logs, expected_range.clone()).await.unwrap(); + + // Expect the cursor to have rewound to the last indexed snapshot - really this is + // the same as not updating current indexing snapshot / last indexed snapshot. + assert_eq!( + cur.current_indexing_snapshot, + TargetSnapshot { + sequence: 5, + at_block: 90, + } + ); + assert_eq!( + cur.last_indexed_snapshot, + LastIndexedSnapshot { + sequence: Some(4), + at_block: 90, + } + ); + } + + // Don't build upon the last sequence (5 missing) + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(6), log_meta_with_block(100)), + (MockSequencedData::new(7), log_meta_with_block(105)), + ], + ) + .await; + + // There's a gap (missing sequence 6) + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(5), log_meta_with_block(115)), + (MockSequencedData::new(7), log_meta_with_block(120)), + ], + ) + .await; + + // Final sequence is missing + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(5), log_meta_with_block(115)), + (MockSequencedData::new(6), log_meta_with_block(120)), + ], + ) + .await; + + // Correct sequences but also an unexpected sequence (8) + update_and_expect_rewind( + &mut cursor, + vec![ + (MockSequencedData::new(5), log_meta_with_block(115)), + (MockSequencedData::new(6), log_meta_with_block(115)), + (MockSequencedData::new(7), log_meta_with_block(120)), + (MockSequencedData::new(8), log_meta_with_block(125)), + ], + ) + .await; + } + } +} diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs new file mode 100644 index 0000000000..b99e00fff8 --- /dev/null +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs @@ -0,0 +1,132 @@ +use std::{fmt::Debug, sync::Arc, time::Duration}; + +use async_trait::async_trait; +use eyre::Result; +use hyperlane_core::{ + ChainCommunicationError, ContractSyncCursor, CursorAction, + HyperlaneSequenceAwareIndexerStoreReader, IndexMode, LogMeta, SequenceAwareIndexer, Sequenced, +}; +use std::ops::RangeInclusive; + +mod backward; +mod forward; + +pub(crate) use backward::BackwardSequenceAwareSyncCursor; +pub(crate) use forward::ForwardSequenceAwareSyncCursor; + +#[derive(Debug, Clone, PartialEq, Eq)] +struct LastIndexedSnapshot { + /// The last sequence that was indexed. + /// It's possible for this to be None if nothing has been indexed yet + /// e.g. upon first starting up or if no sequenced data exists yet. + pub sequence: Option, + /// The block number at which the last sequence was indexed. + /// If the sequence is None, this can be thought of as the starting block + /// number to index from. + pub at_block: u32, +} + +impl LastIndexedSnapshot { + fn next_target(&self) -> TargetSnapshot { + TargetSnapshot { + // If we haven't indexed anything yet, we start at 0, otherwise we increment. + sequence: self.sequence.map(|s| s + 1).unwrap_or(0), + at_block: self.at_block, + } + } + + fn previous_target(&self) -> Option { + match &self.sequence { + // A previous target doesn't exist if we're trying to go backward + // from sequence 0 or if nothing has been indexed yet. + Some(0) | None => None, + Some(s) => Some(TargetSnapshot { + sequence: s.saturating_sub(1), + at_block: self.at_block, + }), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct TargetSnapshot { + pub sequence: u32, + pub at_block: u32, +} + +#[derive(Debug)] +pub enum SyncDirection { + Forward, + Backward, +} + +/// A cursor that prefers to sync forward, but will sync backward if there is nothing to +/// sync forward. +pub(crate) struct ForwardBackwardSequenceAwareSyncCursor { + forward: ForwardSequenceAwareSyncCursor, + backward: BackwardSequenceAwareSyncCursor, + last_direction: SyncDirection, +} + +impl ForwardBackwardSequenceAwareSyncCursor { + /// Construct a new contract sync helper. + pub async fn new( + latest_sequence_querier: Arc>, + db: Arc>, + chunk_size: u32, + mode: IndexMode, + ) -> Result { + let (sequence_count, tip) = latest_sequence_querier + .latest_sequence_count_and_tip() + .await?; + let sequence_count = sequence_count.ok_or(ChainCommunicationError::from_other_str( + "Failed to query sequence", + ))?; + let forward_cursor = ForwardSequenceAwareSyncCursor::new( + chunk_size, + latest_sequence_querier.clone(), + db.clone(), + sequence_count, + tip, + mode, + ); + let backward_cursor = + BackwardSequenceAwareSyncCursor::new(chunk_size, db, sequence_count, tip, mode); + Ok(Self { + forward: forward_cursor, + backward: backward_cursor, + last_direction: SyncDirection::Forward, + }) + } +} + +#[async_trait] +impl ContractSyncCursor for ForwardBackwardSequenceAwareSyncCursor { + async fn next_action(&mut self) -> Result<(CursorAction, Duration)> { + // TODO: Proper ETA for backwards sync + let eta = Duration::from_secs(0); + // Prioritize forward syncing over backward syncing. + if let Some(forward_range) = self.forward.get_next_range().await? { + self.last_direction = SyncDirection::Forward; + return Ok((CursorAction::Query(forward_range), eta)); + } + + if let Some(backward_range) = self.backward.get_next_range().await? { + self.last_direction = SyncDirection::Backward; + return Ok((CursorAction::Query(backward_range), eta)); + } + // TODO: Define the sleep time from interval flag + return Ok((CursorAction::Sleep(Duration::from_secs(5)), eta)); + } + + fn latest_queried_block(&self) -> u32 { + self.forward.latest_queried_block() + } + + async fn update(&mut self, logs: Vec<(T, LogMeta)>, range: RangeInclusive) -> Result<()> { + match self.last_direction { + SyncDirection::Forward => self.forward.update(logs, range).await, + SyncDirection::Backward => self.backward.update(logs, range).await, + } + } +} diff --git a/rust/hyperlane-base/src/contract_sync/mod.rs b/rust/hyperlane-base/src/contract_sync/mod.rs index 219f06c905..d07aedf36d 100644 --- a/rust/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/mod.rs @@ -1,22 +1,28 @@ -use std::{collections::HashSet, fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc}; +use std::{ + collections::HashSet, fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc, time::Duration, +}; -use cursor::*; +use cursors::*; use derive_new::new; use hyperlane_core::{ utils::fmt_sync_time, ContractSyncCursor, CursorAction, HyperlaneDomain, HyperlaneLogStore, - HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, Indexer, SequenceIndexer, - Sequenced, + HyperlaneSequenceAwareIndexerStore, HyperlaneWatermarkedLogStore, Indexer, + SequenceAwareIndexer, Sequenced, }; pub use metrics::ContractSyncMetrics; use tokio::time::sleep; -use tracing::{debug, info}; +use tracing::{debug, info, warn}; use crate::settings::IndexSettings; -mod cursor; +mod cursors; mod eta_calculator; mod metrics; +use cursors::{ForwardBackwardSequenceAwareSyncCursor, ForwardSequenceAwareSyncCursor}; + +const SLEEP_DURATION: Duration = Duration::from_secs(5); + /// Entity that drives the syncing of an agent's db with on-chain data. /// Extracts chain-specific data (emitted checkpoints, messages, etc) from an /// `indexer` and fills the agent's db with this data. @@ -42,11 +48,7 @@ where /// Sync logs and write them to the LogStore #[tracing::instrument(name = "ContractSync", fields(domain=self.domain().name()), skip(self, cursor))] - pub async fn sync( - &self, - label: &'static str, - mut cursor: Box>, - ) -> eyre::Result<()> { + pub async fn sync(&self, label: &'static str, mut cursor: Box>) { let chain_name = self.domain.as_ref(); let indexed_height = self .metrics @@ -58,15 +60,30 @@ where .with_label_values(&[label, chain_name]); loop { - indexed_height.set(cursor.latest_block() as i64); - let Ok((action, eta)) = cursor.next_action().await else { - continue; + indexed_height.set(cursor.latest_queried_block() as i64); + + let (action, eta) = match cursor.next_action().await { + Ok((action, eta)) => (action, eta), + Err(err) => { + warn!(?err, "Error getting next action"); + sleep(SLEEP_DURATION).await; + continue; + } }; - match action { - CursorAction::Query(range) => { + let sleep_duration = match action { + // Use `loop` but always break - this allows for returning a value + // from the loop (the sleep duration) + #[allow(clippy::never_loop)] + CursorAction::Query(range) => loop { debug!(?range, "Looking for for events in index range"); - let logs = self.indexer.fetch_logs(range.clone()).await?; + let logs = match self.indexer.fetch_logs(range.clone()).await { + Ok(logs) => logs, + Err(err) => { + warn!(?err, "Error fetching logs"); + break SLEEP_DURATION; + } + }; let deduped_logs = HashSet::<_>::from_iter(logs); let logs = Vec::from_iter(deduped_logs); @@ -77,23 +94,32 @@ where "Found log(s) in index range" ); // Store deliveries - let stored = self.db.store_logs(&logs).await?; + let stored = match self.db.store_logs(&logs).await { + Ok(stored) => stored, + Err(err) => { + warn!(?err, "Error storing logs in db"); + break SLEEP_DURATION; + } + }; // Report amount of deliveries stored into db stored_logs.inc_by(stored as u64); // Update cursor - cursor.update(logs).await?; - } - CursorAction::Sleep(duration) => { - sleep(duration).await; - } - } + if let Err(err) = cursor.update(logs, range).await { + warn!(?err, "Error updating cursor"); + break SLEEP_DURATION; + }; + break Default::default(); + }, + CursorAction::Sleep(duration) => duration, + }; + sleep(sleep_duration).await; } } } /// A ContractSync for syncing events using a RateLimitedContractSyncCursor pub type WatermarkContractSync = - ContractSync>, Arc>>; + ContractSync>, Arc>>; impl WatermarkContractSync where T: Debug + Send + Sync + Clone + 'static, @@ -124,23 +150,25 @@ where } /// A ContractSync for syncing messages using a SequenceSyncCursor -pub type SequencedDataContractSync = - ContractSync>, Arc>>; -impl SequencedDataContractSync { +pub type SequencedDataContractSync = ContractSync< + T, + Arc>, + Arc>, +>; +impl SequencedDataContractSync { /// Returns a new cursor to be used for syncing dispatched messages from the indexer pub async fn forward_message_sync_cursor( &self, index_settings: IndexSettings, next_nonce: u32, ) -> Box> { - Box::new(ForwardSequenceSyncCursor::new( - self.indexer.clone(), - self.db.clone(), + Box::new(ForwardSequenceAwareSyncCursor::new( index_settings.chunk_size, - index_settings.from, + self.indexer.clone(), + Arc::new(self.db.clone()), + next_nonce, index_settings.from, index_settings.mode, - next_nonce, )) } @@ -150,9 +178,9 @@ impl SequencedDataContractSync { index_settings: IndexSettings, ) -> Box> { Box::new( - ForwardBackwardSequenceSyncCursor::new( + ForwardBackwardSequenceAwareSyncCursor::new( self.indexer.clone(), - self.db.clone(), + Arc::new(self.db.clone()), index_settings.chunk_size, index_settings.mode, ) diff --git a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs index 807645beb0..acb73ff1fa 100644 --- a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -5,8 +5,9 @@ use tracing::{debug, instrument, trace}; use hyperlane_core::{ GasPaymentKey, HyperlaneDomain, HyperlaneLogStore, HyperlaneMessage, - HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, InterchainGasExpenditure, - InterchainGasPayment, InterchainGasPaymentMeta, LogMeta, MerkleTreeInsertion, H256, + HyperlaneSequenceAwareIndexerStoreReader, HyperlaneWatermarkedLogStore, + InterchainGasExpenditure, InterchainGasPayment, InterchainGasPaymentMeta, LogMeta, + MerkleTreeInsertion, H256, }; use super::{ @@ -281,7 +282,7 @@ impl HyperlaneLogStore for HyperlaneRocksDB { } #[async_trait] -impl HyperlaneSequenceIndexerStore for HyperlaneRocksDB { +impl HyperlaneSequenceAwareIndexerStoreReader for HyperlaneRocksDB { /// Gets data by its sequence. async fn retrieve_by_sequence(&self, sequence: u32) -> Result> { let message = self.retrieve_message_by_nonce(sequence)?; @@ -289,14 +290,14 @@ impl HyperlaneSequenceIndexerStore for HyperlaneRocksDB { } /// Gets the block number at which the log occurred. - async fn retrieve_log_block_number(&self, sequence: u32) -> Result> { + async fn retrieve_log_block_number_by_sequence(&self, sequence: u32) -> Result> { let number = self.retrieve_dispatched_block_number_by_nonce(&sequence)?; Ok(number) } } #[async_trait] -impl HyperlaneSequenceIndexerStore for HyperlaneRocksDB { +impl HyperlaneSequenceAwareIndexerStoreReader for HyperlaneRocksDB { /// Gets data by its sequence. async fn retrieve_by_sequence(&self, sequence: u32) -> Result> { let insertion = self.retrieve_merkle_tree_insertion_by_leaf_index(&sequence)?; @@ -304,7 +305,7 @@ impl HyperlaneSequenceIndexerStore for HyperlaneRocksDB { } /// Gets the block number at which the log occurred. - async fn retrieve_log_block_number(&self, sequence: u32) -> Result> { + async fn retrieve_log_block_number_by_sequence(&self, sequence: u32) -> Result> { let number = self.retrieve_merkle_tree_insertion_block_number_by_leaf_index(&sequence)?; Ok(number) } diff --git a/rust/hyperlane-base/src/db/rocks/mod.rs b/rust/hyperlane-base/src/db/rocks/mod.rs index 1cec492dec..e9a626a157 100644 --- a/rust/hyperlane-base/src/db/rocks/mod.rs +++ b/rust/hyperlane-base/src/db/rocks/mod.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::{io, path::Path, sync::Arc}; -use hyperlane_core::HyperlaneProtocolError; +use hyperlane_core::{ChainCommunicationError, HyperlaneProtocolError}; use rocksdb::{Options, DB as Rocks}; use tracing::info; @@ -58,6 +58,12 @@ pub enum DbError { HyperlaneError(#[from] HyperlaneProtocolError), } +impl From for ChainCommunicationError { + fn from(value: DbError) -> Self { + ChainCommunicationError::from_other(value) + } +} + type Result = std::result::Result; impl DB { diff --git a/rust/hyperlane-base/src/lib.rs b/rust/hyperlane-base/src/lib.rs index b4a0b1cf9c..ce6843e583 100644 --- a/rust/hyperlane-base/src/lib.rs +++ b/rust/hyperlane-base/src/lib.rs @@ -15,6 +15,10 @@ pub use agent::*; pub mod metrics; pub use metrics::*; +/// Hyperlane server utils +pub mod server; +pub use server::*; + mod contract_sync; pub use contract_sync::*; diff --git a/rust/hyperlane-base/src/metrics/agent_metrics.rs b/rust/hyperlane-base/src/metrics/agent_metrics.rs index ab4e01f513..bc046dde1e 100644 --- a/rust/hyperlane-base/src/metrics/agent_metrics.rs +++ b/rust/hyperlane-base/src/metrics/agent_metrics.rs @@ -1,16 +1,23 @@ +//! Metrics either related to the agents, or observed by them + +use std::sync::Arc; use std::time::Duration; use derive_builder::Builder; -use derive_new::new; use eyre::Result; +use hyperlane_core::metrics::agent::decimals_by_protocol; use hyperlane_core::metrics::agent::u256_as_scaled_f64; +use hyperlane_core::metrics::agent::METRICS_SCRAPE_INTERVAL; use hyperlane_core::HyperlaneDomain; use hyperlane_core::HyperlaneProvider; use maplit::hashmap; use prometheus::GaugeVec; -use tokio::time::MissedTickBehavior; -use tracing::{trace, warn}; +use prometheus::IntGaugeVec; +use tokio::{task::JoinHandle, time::MissedTickBehavior}; +use tracing::info_span; +use tracing::{debug, instrument::Instrumented, trace, warn, Instrument}; +use crate::settings::ChainConf; use crate::CoreMetrics; /// Expected label names for the `wallet_balance` metric. @@ -26,8 +33,19 @@ pub const WALLET_BALANCE_LABELS: &[&str] = &[ pub const WALLET_BALANCE_HELP: &str = "Current native token balance for the wallet addresses in the `wallets` set"; +/// Expected label names for the `block_height` metric. +pub const BLOCK_HEIGHT_LABELS: &[&str] = &["chain"]; +/// Help string for the metric. +pub const BLOCK_HEIGHT_HELP: &str = "Tracks the current block height of the chain"; + +/// Expected label names for the `gas_price` metric. +pub const GAS_PRICE_LABELS: &[&str] = &["chain"]; +/// Help string for the metric. +pub const GAS_PRICE_HELP: &str = + "Tracks the current gas price of the chain, in the lowest denomination (e.g. wei)"; + /// Agent-specific metrics -#[derive(Clone, Builder)] +#[derive(Clone, Builder, Debug)] pub struct AgentMetrics { /// Current balance of native tokens for the /// wallet address. @@ -52,6 +70,35 @@ pub(crate) fn create_agent_metrics(metrics: &CoreMetrics) -> Result, +} + +pub(crate) fn create_chain_metrics(metrics: &CoreMetrics) -> Result { + Ok(ChainMetricsBuilder::default() + .block_height(metrics.new_int_gauge( + "block_height", + BLOCK_HEIGHT_HELP, + BLOCK_HEIGHT_LABELS, + )?) + .gas_price(metrics.new_gauge("gas_price", GAS_PRICE_HELP, GAS_PRICE_LABELS)?) + .build()?) +} + /// Configuration for the prometheus middleware. This can be loaded via serde. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] @@ -68,38 +115,55 @@ pub struct AgentMetricsConf { pub name: String, } -/// Utility struct to update agent metrics for a given chain -#[derive(new)] -pub struct AgentMetricsUpdater { - metrics: AgentMetrics, +/// Utility struct to update various metrics using a standalone tokio task +pub struct MetricsUpdater { + agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, conf: AgentMetricsConf, provider: Box, } -impl AgentMetricsUpdater { - async fn update_wallet_balances(&self) { +impl MetricsUpdater { + /// Creates a new instance of the `MetricsUpdater` + pub async fn new( + chain_conf: &ChainConf, + core_metrics: Arc, + agent_metrics: AgentMetrics, + chain_metrics: ChainMetrics, + agent_name: String, + ) -> Result { + let agent_metrics_conf = chain_conf.agent_metrics_conf(agent_name).await?; + let provider = chain_conf.build_provider(&core_metrics).await?; + + Ok(Self { + agent_metrics, + chain_metrics, + conf: agent_metrics_conf, + provider, + }) + } + + async fn update_agent_metrics(&self) { let Some(wallet_addr) = self.conf.address.clone() else { return; }; let wallet_name = self.conf.name.clone(); - let Some(wallet_balance_metric) = self.metrics.wallet_balance.clone() else { + let Some(wallet_balance_metric) = self.agent_metrics.wallet_balance.clone() else { return; }; let chain = self.conf.domain.name(); match self.provider.get_balance(wallet_addr.clone()).await { Ok(balance) => { - // Okay, so the native type is not a token, but whatever, close enough. - // Note: This is ETH for many chains, but not all so that is why we use `N` and `Native` - // TODO: can we get away with scaling as 18 in all cases here? I am guessing not. let balance = u256_as_scaled_f64(balance, self.conf.domain.domain_protocol()); trace!("Wallet {wallet_name} ({wallet_addr}) on chain {chain} balance is {balance} of the native currency"); wallet_balance_metric - .with(&hashmap! { + .with(&hashmap! { "chain" => chain, "wallet_address" => wallet_addr.as_str(), "wallet_name" => wallet_name.as_str(), "token_address" => "none", + // Note: Whatever this `chain`'s native currency is "token_symbol" => "Native", "token_name" => "Native" }).set(balance) @@ -108,13 +172,60 @@ impl AgentMetricsUpdater { } } + async fn update_block_details(&self) { + let block_height = self.chain_metrics.block_height.clone(); + let gas_price = self.chain_metrics.gas_price.clone(); + if let HyperlaneDomain::Unknown { .. } = self.conf.domain { + return; + }; + let chain = self.conf.domain.name(); + debug!(?chain, "Updating metrics"); + let chain_metrics = match self.provider.get_chain_metrics().await { + Ok(Some(chain_metrics)) => chain_metrics, + Err(err) => { + trace!(?chain, ?err, "Failed to get chain metrics"); + return; + } + // This is the case hit by chains with an empty impl, no need to log an error + _ => return, + }; + + let height = chain_metrics.latest_block.number as i64; + trace!("Block height for chain {chain} is {height}"); + block_height + .with(&hashmap! { "chain" => chain }) + .set(height); + if let Some(gas_price) = gas_price { + let protocol = self.conf.domain.domain_protocol(); + let decimals_scale = 10f64.powf(decimals_by_protocol(protocol).into()); + let gas = u256_as_scaled_f64(chain_metrics.min_gas_price.unwrap_or_default(), protocol) + * decimals_scale; + trace!( + ?chain, + gas = format!("{gas:.2}"), + "Gas price updated for chain (using lowest denomination)" + ); + gas_price.with(&hashmap! { "chain" => chain }).set(gas); + } + } + /// Periodically updates the metrics pub async fn start_updating_on_interval(self, period: Duration) { let mut interval = tokio::time::interval(period); interval.set_missed_tick_behavior(MissedTickBehavior::Skip); loop { - self.update_wallet_balances().await; + self.update_agent_metrics().await; + self.update_block_details().await; interval.tick().await; } } + + /// Spawns a tokio task to update the metrics + pub fn spawn(self) -> Instrumented> { + tokio::spawn(async move { + self.start_updating_on_interval(METRICS_SCRAPE_INTERVAL) + .await; + }) + .instrument(info_span!("MetricsUpdater")) + } } diff --git a/rust/hyperlane-base/src/metrics/core.rs b/rust/hyperlane-base/src/metrics/core.rs index 738538dae0..447c6f2f45 100644 --- a/rust/hyperlane-base/src/metrics/core.rs +++ b/rust/hyperlane-base/src/metrics/core.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; use std::fmt::{Debug, Formatter}; -use std::sync::{Arc, OnceLock}; +use std::sync::OnceLock; use eyre::Result; use hyperlane_core::{HyperlaneDomain, H160}; @@ -11,8 +11,6 @@ use prometheus::{ Encoder, GaugeVec, HistogramVec, IntCounterVec, IntGaugeVec, Registry, }; use tokio::sync::RwLock; -use tokio::task::JoinHandle; -use tracing::warn; use ethers_prometheus::{json_rpc_client::JsonRpcClientMetrics, middleware::MiddlewareMetrics}; @@ -415,41 +413,6 @@ impl CoreMetrics { Ok(out_buf) } - /// Run an HTTP server serving OpenMetrics format reports on `/metrics` - /// - /// This is compatible with Prometheus, which ought to be configured to - /// scrape me! - pub fn run_http_server(self: Arc) -> JoinHandle<()> { - use warp::Filter; - let port = self.listen_port; - tracing::info!(port, "starting prometheus server on 0.0.0.0"); - tokio::spawn(async move { - warp::serve( - warp::path!("metrics") - .map(move || { - warp::reply::with_header( - self.gather().expect("failed to encode metrics"), - "Content-Type", - // OpenMetrics specs demands "application/openmetrics-text; - // version=1.0.0; charset=utf-8" - // but the prometheus scraper itself doesn't seem to care? - // try text/plain to make web browsers happy. - "text/plain; charset=utf-8", - ) - }) - .or(warp::any().map(|| { - warp::reply::with_status( - "go look at /metrics", - warp::http::StatusCode::NOT_FOUND, - ) - })), - ) - .try_bind(([0, 0, 0, 0], port)) - .await; - warn!("Prometheus server could not be started or exited early"); - }) - } - /// Get the name of this agent, e.g. "relayer" pub fn agent_name(&self) -> &str { &self.agent_name @@ -461,6 +424,21 @@ impl CoreMetrics { .map(|(k, v)| (k.as_str(), v.as_str())) .collect() } + + /// Get the difference between the latest observed checkpoint and the latest signed checkpoint. + /// + /// This is useful for reporting the health of the validator and reporting it via EigenNodeAPI + pub fn get_latest_checkpoint_validator_delta(&self, origin_chain: HyperlaneDomain) -> i64 { + let observed_checkpoint = self + .latest_checkpoint() + .with_label_values(&["validator_observed", origin_chain.name()]) + .get(); + let signed_checkpoint = self + .latest_checkpoint() + .with_label_values(&["validator_processed", origin_chain.name()]) + .get(); + observed_checkpoint - signed_checkpoint + } } impl Debug for CoreMetrics { diff --git a/rust/hyperlane-base/src/metrics/provider.rs b/rust/hyperlane-base/src/metrics/provider.rs index 54def51ae0..1694a0e2c9 100644 --- a/rust/hyperlane-base/src/metrics/provider.rs +++ b/rust/hyperlane-base/src/metrics/provider.rs @@ -6,16 +6,6 @@ use crate::CoreMetrics; pub(crate) fn create_provider_metrics(metrics: &CoreMetrics) -> Result { Ok(MiddlewareMetricsBuilder::default() - .block_height(metrics.new_int_gauge( - "block_height", - BLOCK_HEIGHT_HELP, - BLOCK_HEIGHT_LABELS, - )?) - .gas_price_gwei(metrics.new_gauge( - "gas_price_gwei", - GAS_PRICE_GWEI_HELP, - GAS_PRICE_GWEI_LABELS, - )?) .contract_call_duration_seconds(metrics.new_counter( "contract_call_duration_seconds", CONTRACT_CALL_DURATION_SECONDS_HELP, diff --git a/rust/hyperlane-base/src/server/base_server.rs b/rust/hyperlane-base/src/server/base_server.rs new file mode 100644 index 0000000000..bf412571e6 --- /dev/null +++ b/rust/hyperlane-base/src/server/base_server.rs @@ -0,0 +1,107 @@ +use crate::CoreMetrics; +use axum::{http::StatusCode, response::IntoResponse, routing::get, Router}; +use derive_new::new; +use std::{net::SocketAddr, sync::Arc}; +use tokio::task::JoinHandle; + +/// A server that serves agent-specific routes +#[derive(new, Debug)] +pub struct Server { + listen_port: u16, + core_metrics: Arc, +} + +impl Server { + /// Run an HTTP server serving agent-specific different routes + /// + /// routes: + /// - metrics - serving OpenMetrics format reports on `/metrics` + /// (this is compatible with Prometheus, which ought to be configured to scrape this endpoint) + /// - additional_routes - additional routes to be served by the server as per the specific agent + pub fn run(self: Arc, additional_routes: Vec<(&str, Router)>) -> JoinHandle<()> { + let port = self.listen_port; + tracing::info!(port, "starting server on 0.0.0.0"); + + let core_metrics_clone = self.core_metrics.clone(); + + let mut app = Router::new().route( + "/metrics", + get(move || Self::gather_metrics(core_metrics_clone)), + ); + + for (route, router) in additional_routes { + app = app.nest(route, router); + } + + tokio::spawn(async move { + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .expect("Failed to start server"); + }) + } + + /// Gather available metrics into an encoded (plaintext, OpenMetrics format) + /// report. + async fn gather_metrics(core_metrics: Arc) -> impl IntoResponse { + tracing::debug!("Traversing route for /metrics endpoint for serving Prometheus metrics"); + match core_metrics.gather() { + Ok(metrics) => { + let metrics = match String::from_utf8(metrics) { + Ok(metrics_string) => metrics_string, + Err(_) => { + return ( + StatusCode::INTERNAL_SERVER_ERROR, + "Internal Server Error".into(), + ) + } + }; + (StatusCode::OK, metrics) + } + Err(_) => ( + StatusCode::INTERNAL_SERVER_ERROR, + "Failed to gather metrics".into(), + ), + } + } +} + +#[cfg(test)] +mod tests { + use prometheus::{Counter, Registry}; + use reqwest; + + use super::*; + + #[tokio::test] + async fn test_metrics_endpoint() { + let mock_registry = Registry::new(); + let counter = Counter::new("expected_metric_content", "test123").unwrap(); + mock_registry.register(Box::new(counter.clone())).unwrap(); + counter.inc(); + + let server = Server::new( + 8080, + Arc::new(CoreMetrics::new("test", 8080, mock_registry).unwrap()), + ); + let server = Arc::new(server); + // Run the server in the background + let _server_task = tokio::spawn(async move { + server.run(vec![]).await.unwrap(); + }); + + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + + let client = reqwest::Client::new(); + let response = client + .get("http://127.0.0.1:8080/metrics") + .send() + .await + .expect("Failed to send request"); + assert!(response.status().is_success()); + + let body = response.text().await.expect("Failed to read response body"); + assert!(body.contains("expected_metric_content")); + } +} diff --git a/rust/hyperlane-base/src/server/mod.rs b/rust/hyperlane-base/src/server/mod.rs new file mode 100644 index 0000000000..032093c794 --- /dev/null +++ b/rust/hyperlane-base/src/server/mod.rs @@ -0,0 +1,2 @@ +mod base_server; +pub use base_server::Server; diff --git a/rust/hyperlane-base/src/settings/base.rs b/rust/hyperlane-base/src/settings/base.rs index 135d80ea0b..2899b99a39 100644 --- a/rust/hyperlane-base/src/settings/base.rs +++ b/rust/hyperlane-base/src/settings/base.rs @@ -4,7 +4,7 @@ use eyre::{eyre, Context, Result}; use futures_util::future::try_join_all; use hyperlane_core::{ Delivery, HyperlaneChain, HyperlaneDomain, HyperlaneMessage, HyperlaneProvider, - HyperlaneSequenceIndexerStore, HyperlaneWatermarkedLogStore, InterchainGasPaymaster, + HyperlaneSequenceAwareIndexerStore, HyperlaneWatermarkedLogStore, InterchainGasPaymaster, InterchainGasPayment, Mailbox, MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, ValidatorAnnounce, H256, }; @@ -12,7 +12,7 @@ use hyperlane_core::{ use crate::{ settings::{chains::ChainConf, trace::TracingConfig}, ContractSync, ContractSyncMetrics, CoreMetrics, HyperlaneAgentCore, SequencedDataContractSync, - WatermarkContractSync, + Server, WatermarkContractSync, }; /// Settings. Usually this should be treated as a base config and used as @@ -95,6 +95,11 @@ impl Settings { )?)) } + /// Create the server from the settings given the name of the agent. + pub fn server(&self, core_metrics: Arc) -> Result> { + Ok(Arc::new(Server::new(self.metrics_port, core_metrics))) + } + /// Private to preserve linearity of AgentCore::from_settings -- creating an /// agent consumes the settings. fn clone(&self) -> Self { @@ -184,7 +189,7 @@ impl Settings { build_contract_fns!(build_validator_announce, build_validator_announces -> dyn ValidatorAnnounce); build_contract_fns!(build_provider, build_providers -> dyn HyperlaneProvider); build_indexer_fns!(build_delivery_indexer, build_delivery_indexers -> dyn HyperlaneWatermarkedLogStore, WatermarkContractSync); - build_indexer_fns!(build_message_indexer, build_message_indexers -> dyn HyperlaneSequenceIndexerStore, SequencedDataContractSync); + build_indexer_fns!(build_message_indexer, build_message_indexers -> dyn HyperlaneSequenceAwareIndexerStore, SequencedDataContractSync); build_indexer_fns!(build_interchain_gas_payment_indexer, build_interchain_gas_payment_indexers -> dyn HyperlaneWatermarkedLogStore, WatermarkContractSync); - build_indexer_fns!(build_merkle_tree_hook_indexer, build_merkle_tree_hook_indexers -> dyn HyperlaneSequenceIndexerStore, SequencedDataContractSync); + build_indexer_fns!(build_merkle_tree_hook_indexer, build_merkle_tree_hook_indexers -> dyn HyperlaneSequenceAwareIndexerStore, SequencedDataContractSync); } diff --git a/rust/hyperlane-base/src/settings/chains.rs b/rust/hyperlane-base/src/settings/chains.rs index a5bb21de37..c8e66f66a6 100644 --- a/rust/hyperlane-base/src/settings/chains.rs +++ b/rust/hyperlane-base/src/settings/chains.rs @@ -9,7 +9,7 @@ use hyperlane_core::{ AggregationIsm, CcipReadIsm, ContractLocator, HyperlaneAbi, HyperlaneDomain, HyperlaneDomainProtocol, HyperlaneMessage, HyperlaneProvider, IndexMode, InterchainGasPaymaster, InterchainGasPayment, InterchainSecurityModule, Mailbox, - MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, RoutingIsm, SequenceIndexer, + MerkleTreeHook, MerkleTreeInsertion, MultisigIsm, RoutingIsm, SequenceAwareIndexer, ValidatorAnnounce, H256, }; use hyperlane_cosmos as h_cosmos; @@ -203,7 +203,7 @@ impl ChainConf { pub async fn build_message_indexer( &self, metrics: &CoreMetrics, - ) -> Result>> { + ) -> Result>> { let ctx = "Building delivery indexer"; let locator = self.locator(self.addresses.mailbox); @@ -222,7 +222,7 @@ impl ChainConf { ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let indexer = Box::new(h_sealevel::SealevelMailboxIndexer::new(conf, locator)?); - Ok(indexer as Box>) + Ok(indexer as Box>) } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; @@ -232,7 +232,7 @@ impl ChainConf { signer, self.reorg_period, )?); - Ok(indexer as Box>) + Ok(indexer as Box>) } } .context(ctx) @@ -242,7 +242,7 @@ impl ChainConf { pub async fn build_delivery_indexer( &self, metrics: &CoreMetrics, - ) -> Result>> { + ) -> Result>> { let ctx = "Building delivery indexer"; let locator = self.locator(self.addresses.mailbox); @@ -261,7 +261,7 @@ impl ChainConf { ChainConnectionConf::Fuel(_) => todo!(), ChainConnectionConf::Sealevel(conf) => { let indexer = Box::new(h_sealevel::SealevelMailboxIndexer::new(conf, locator)?); - Ok(indexer as Box>) + Ok(indexer as Box>) } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; @@ -271,7 +271,7 @@ impl ChainConf { signer, self.reorg_period, )?); - Ok(indexer as Box>) + Ok(indexer as Box>) } } .context(ctx) @@ -320,7 +320,7 @@ impl ChainConf { pub async fn build_interchain_gas_payment_indexer( &self, metrics: &CoreMetrics, - ) -> Result>> { + ) -> Result>> { let ctx = "Building IGP indexer"; let locator = self.locator(self.addresses.interchain_gas_paymaster); @@ -342,7 +342,7 @@ impl ChainConf { let indexer = Box::new( h_sealevel::SealevelInterchainGasPaymasterIndexer::new(conf, locator).await?, ); - Ok(indexer as Box>) + Ok(indexer as Box>) } ChainConnectionConf::Cosmos(conf) => { let indexer = Box::new(h_cosmos::CosmosInterchainGasPaymasterIndexer::new( @@ -350,7 +350,7 @@ impl ChainConf { locator, self.reorg_period, )?); - Ok(indexer as Box>) + Ok(indexer as Box>) } } .context(ctx) @@ -360,7 +360,7 @@ impl ChainConf { pub async fn build_merkle_tree_hook_indexer( &self, metrics: &CoreMetrics, - ) -> Result>> { + ) -> Result>> { let ctx = "Building merkle tree hook indexer"; let locator = self.locator(self.addresses.merkle_tree_hook); @@ -383,7 +383,7 @@ impl ChainConf { let indexer = Box::new(h_sealevel::SealevelMerkleTreeHookIndexer::new( *mailbox_indexer, )); - Ok(indexer as Box>) + Ok(indexer as Box>) } ChainConnectionConf::Cosmos(conf) => { let signer = self.cosmos_signer().await.context(ctx)?; @@ -394,7 +394,7 @@ impl ChainConf { signer, self.reorg_period, )?); - Ok(indexer as Box>) + Ok(indexer as Box>) } } .context(ctx) diff --git a/rust/hyperlane-base/src/settings/checkpoint_syncer.rs b/rust/hyperlane-base/src/settings/checkpoint_syncer.rs index 01640747eb..cb4fa1c8d6 100644 --- a/rust/hyperlane-base/src/settings/checkpoint_syncer.rs +++ b/rust/hyperlane-base/src/settings/checkpoint_syncer.rs @@ -1,11 +1,13 @@ +use crate::{ + CheckpointSyncer, GcsStorageClientBuilder, LocalStorage, S3Storage, GCS_SERVICE_ACCOUNT_KEY, + GCS_USER_SECRET, +}; use core::str::FromStr; -use std::path::PathBuf; - use eyre::{eyre, Context, Report, Result}; use prometheus::IntGauge; use rusoto_core::Region; - -use crate::{CheckpointSyncer, LocalStorage, S3Storage}; +use std::{env, path::PathBuf}; +use ya_gcp::{AuthFlow, ServiceAccountAuth}; /// Checkpoint Syncer types #[derive(Debug, Clone)] @@ -24,6 +26,18 @@ pub enum CheckpointSyncerConf { /// S3 Region region: Region, }, + /// A checkpoint syncer on Google Cloud Storage + Gcs { + /// Bucket name + bucket: String, + /// Folder name inside bucket - defaults to the root of the bucket + folder: Option, + /// A path to the oauth service account key json file. + service_account_key: Option, + /// Path to oauth user secrets, like those created by + /// `gcloud auth application-default login` + user_secrets: Option, + }, } impl FromStr for CheckpointSyncerConf { @@ -54,6 +68,28 @@ impl FromStr for CheckpointSyncerConf { "file" => Ok(CheckpointSyncerConf::LocalStorage { path: suffix.into(), }), + // for google cloud both options (with or without folder) from str are for anonymous access only + // or env variables parsing + "gs" => { + let service_account_key = env::var(GCS_SERVICE_ACCOUNT_KEY).ok(); + let user_secrets = env::var(GCS_USER_SECRET).ok(); + if let Some(ind) = suffix.find('/') { + let (bucket, folder) = suffix.split_at(ind); + Ok(Self::Gcs { + bucket: bucket.into(), + folder: Some(folder.into()), + service_account_key, + user_secrets, + }) + } else { + Ok(Self::Gcs { + bucket: suffix.into(), + folder: None, + service_account_key, + user_secrets, + }) + } + } _ => Err(eyre!("Unknown storage location prefix `{prefix}`")), } } @@ -61,7 +97,7 @@ impl FromStr for CheckpointSyncerConf { impl CheckpointSyncerConf { /// Turn conf info a Checkpoint Syncer - pub fn build( + pub async fn build( &self, latest_index_gauge: Option, ) -> Result, Report> { @@ -79,6 +115,27 @@ impl CheckpointSyncerConf { region.clone(), latest_index_gauge, )), + CheckpointSyncerConf::Gcs { + bucket, + folder, + service_account_key, + user_secrets, + } => { + let auth = if let Some(path) = service_account_key { + AuthFlow::ServiceAccount(ServiceAccountAuth::Path(path.into())) + } else if let Some(path) = user_secrets { + AuthFlow::UserAccount(path.into()) + } else { + // Public data access only - no `insert` + AuthFlow::NoAuth + }; + + Box::new( + GcsStorageClientBuilder::new(auth) + .build(bucket, folder.to_owned()) + .await?, + ) + } }) } } diff --git a/rust/hyperlane-base/src/settings/parser/connection_parser.rs b/rust/hyperlane-base/src/settings/parser/connection_parser.rs index 55cce2be38..0d47d6eca9 100644 --- a/rust/hyperlane-base/src/settings/parser/connection_parser.rs +++ b/rust/hyperlane-base/src/settings/parser/connection_parser.rs @@ -6,7 +6,7 @@ use url::Url; use crate::settings::envs::*; use crate::settings::ChainConnectionConf; -use super::{parse_cosmos_gas_price, ValueParser}; +use super::{parse_base_and_override_urls, parse_cosmos_gas_price, ValueParser}; pub fn build_ethereum_connection_conf( rpcs: &[Url], @@ -43,19 +43,8 @@ pub fn build_cosmos_connection_conf( err: &mut ConfigParsingError, ) -> Option { let mut local_err = ConfigParsingError::default(); - - let grpc_url = chain - .chain(&mut local_err) - .get_key("grpcUrl") - .parse_string() - .end() - .or_else(|| { - local_err.push( - &chain.cwp + "grpc_url", - eyre!("Missing grpc definitions for chain"), - ); - None - }); + let grpcs = + parse_base_and_override_urls(chain, "grpcUrls", "customGrpcUrls", "http", &mut local_err); let chain_id = chain .chain(&mut local_err) @@ -69,11 +58,14 @@ pub fn build_cosmos_connection_conf( let prefix = chain .chain(err) - .get_key("prefix") + .get_key("bech32Prefix") .parse_string() .end() .or_else(|| { - local_err.push(&chain.cwp + "prefix", eyre!("Missing prefix for chain")); + local_err.push( + &chain.cwp + "bech32Prefix", + eyre!("Missing bech32 prefix for chain"), + ); None }); @@ -100,17 +92,24 @@ pub fn build_cosmos_connection_conf( .and_then(parse_cosmos_gas_price) .end(); + let contract_address_bytes = chain + .chain(err) + .get_opt_key("contractAddressBytes") + .parse_u64() + .end(); + if !local_err.is_ok() { err.merge(local_err); None } else { Some(ChainConnectionConf::Cosmos(h_cosmos::ConnectionConf::new( - grpc_url.unwrap().to_string(), + grpcs, rpcs.first().unwrap().to_string(), chain_id.unwrap().to_string(), prefix.unwrap().to_string(), canonical_asset.unwrap(), gas_price.unwrap(), + contract_address_bytes.unwrap().try_into().unwrap(), ))) } } diff --git a/rust/hyperlane-base/src/settings/parser/mod.rs b/rust/hyperlane-base/src/settings/parser/mod.rs index 76c88fe1b3..81e0692800 100644 --- a/rust/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/hyperlane-base/src/settings/parser/mod.rs @@ -13,11 +13,13 @@ use convert_case::{Case, Casing}; use eyre::{eyre, Context}; use h_cosmos::RawCosmosAmount; use hyperlane_core::{ - cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, IndexMode, + cfg_unwrap_all, config::*, HyperlaneDomain, HyperlaneDomainProtocol, + HyperlaneDomainTechnicalStack, IndexMode, }; use itertools::Itertools; use serde::Deserialize; use serde_json::Value; +use url::Url; pub use self::json_value_parser::ValueParser; pub use super::envs::*; @@ -134,47 +136,7 @@ fn parse_chain( .parse_u32() .unwrap_or(1); - let rpcs_base = chain - .chain(&mut err) - .get_key("rpcUrls") - .into_array_iter() - .map(|urls| { - urls.filter_map(|v| { - v.chain(&mut err) - .get_key("http") - .parse_from_str("Invalid http url") - .end() - }) - .collect_vec() - }) - .unwrap_or_default(); - - let rpc_overrides = chain - .chain(&mut err) - .get_opt_key("customRpcUrls") - .parse_string() - .end() - .map(|urls| { - urls.split(',') - .filter_map(|url| { - url.parse() - .take_err(&mut err, || &chain.cwp + "customRpcUrls") - }) - .collect_vec() - }); - - let rpcs = rpc_overrides.unwrap_or(rpcs_base); - - if rpcs.is_empty() { - err.push( - &chain.cwp + "rpc_urls", - eyre!("Missing base rpc definitions for chain"), - ); - err.push( - &chain.cwp + "custom_rpc_urls", - eyre!("Also missing rpc overrides for chain"), - ); - } + let rpcs = parse_base_and_override_urls(&chain, "rpcUrls", "customRpcUrls", "http", &mut err); let from = chain .chain(&mut err) @@ -286,9 +248,16 @@ fn parse_domain(chain: ValueParser, name: &str) -> ConfigResult .parse_from_str::("Invalid Hyperlane domain protocol") .end(); - cfg_unwrap_all!(&chain.cwp, err: [domain_id, protocol]); + let technical_stack = chain + .chain(&mut err) + .get_opt_key("technicalStack") + .parse_from_str::("Invalid chain technical stack") + .end() + .or_else(|| Some(HyperlaneDomainTechnicalStack::default())); + + cfg_unwrap_all!(&chain.cwp, err: [domain_id, protocol, technical_stack]); - let domain = HyperlaneDomain::from_config(domain_id, name, protocol) + let domain = HyperlaneDomain::from_config(domain_id, name, protocol, technical_stack) .context("Invalid domain data") .take_err(&mut err, || chain.cwp.clone()); @@ -418,3 +387,66 @@ fn parse_cosmos_gas_price(gas_price: ValueParser) -> ConfigResult Vec { + chain + .chain(err) + .get_key(key) + .into_array_iter() + .map(|urls| { + urls.filter_map(|v| { + v.chain(err) + .get_key(protocol) + .parse_from_str("Invalid url") + .end() + }) + .collect_vec() + }) + .unwrap_or_default() +} + +fn parse_custom_urls( + chain: &ValueParser, + key: &str, + err: &mut ConfigParsingError, +) -> Option> { + chain + .chain(err) + .get_opt_key(key) + .parse_string() + .end() + .map(|urls| { + urls.split(',') + .filter_map(|url| url.parse().take_err(err, || &chain.cwp + "customGrpcUrls")) + .collect_vec() + }) +} + +fn parse_base_and_override_urls( + chain: &ValueParser, + base_key: &str, + override_key: &str, + protocol: &str, + err: &mut ConfigParsingError, +) -> Vec { + let base = parse_urls(chain, base_key, protocol, err); + let overrides = parse_custom_urls(chain, override_key, err); + let combined = overrides.unwrap_or(base); + + if combined.is_empty() { + err.push( + &chain.cwp + "rpc_urls", + eyre!("Missing base rpc definitions for chain"), + ); + err.push( + &chain.cwp + "custom_rpc_urls", + eyre!("Also missing rpc overrides for chain"), + ); + } + combined +} diff --git a/rust/hyperlane-base/src/settings/signers.rs b/rust/hyperlane-base/src/settings/signers.rs index c48de65f73..d0642434f5 100644 --- a/rust/hyperlane-base/src/settings/signers.rs +++ b/rust/hyperlane-base/src/settings/signers.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use async_trait::async_trait; use ed25519_dalek::SecretKey; use ethers::prelude::{AwsSigner, LocalWallet}; @@ -7,11 +5,12 @@ use ethers::utils::hex::ToHex; use eyre::{bail, Context, Report}; use hyperlane_core::H256; use hyperlane_sealevel::Keypair; -use rusoto_core::{HttpClient, HttpConfig, Region}; +use rusoto_core::Region; use rusoto_kms::KmsClient; use tracing::instrument; use super::aws_credentials::AwsChainCredentialsProvider; +use crate::types::utils; /// Signer types #[derive(Default, Debug, Clone)] @@ -73,13 +72,10 @@ impl BuildableWithSignerConf for hyperlane_ethereum::Signers { ), )), SignerConf::Aws { id, region } => { - let mut config = HttpConfig::new(); - // see https://github.com/hyperium/hyper/issues/2136#issuecomment-589345238 - config.pool_idle_timeout(Duration::from_secs(20)); let client = KmsClient::new_with_client( rusoto_core::Client::new_with( AwsChainCredentialsProvider::new(), - HttpClient::new_with_config(config).unwrap(), + utils::http_client_with_timeout().unwrap(), ), region.clone(), ); diff --git a/rust/hyperlane-base/src/settings/trace/mod.rs b/rust/hyperlane-base/src/settings/trace/mod.rs index a8d835494e..b0640f36ee 100644 --- a/rust/hyperlane-base/src/settings/trace/mod.rs +++ b/rust/hyperlane-base/src/settings/trace/mod.rs @@ -24,7 +24,7 @@ pub enum Level { /// Warn Warn = 2, /// Debug - Debug = 3, + Debug = 4, /// Trace Trace = 5, /// Trace + Additional logs from dependencies @@ -32,7 +32,7 @@ pub enum Level { /// Info #[serde(other)] #[default] - Info = 4, + Info = 3, } impl From for LevelFilter { @@ -68,6 +68,7 @@ impl TracingConfig { target_layer = target_layer .with_target("hyper", Level::Info) .with_target("rusoto_core", Level::Info) + .with_target("rustls", Level::Info) .with_target("reqwest", Level::Info) .with_target("h2", Level::Info) .with_target("tower", Level::Info) diff --git a/rust/hyperlane-base/src/types/gcs_storage.rs b/rust/hyperlane-base/src/types/gcs_storage.rs new file mode 100644 index 0000000000..ebd1589a4f --- /dev/null +++ b/rust/hyperlane-base/src/types/gcs_storage.rs @@ -0,0 +1,204 @@ +use crate::CheckpointSyncer; +use async_trait::async_trait; +use derive_new::new; +use eyre::{bail, Result}; +use hyperlane_core::{SignedAnnouncement, SignedCheckpointWithMessageId}; +use std::fmt; +use ya_gcp::{storage::StorageClient, AuthFlow, ClientBuilder, ClientBuilderConfig}; + +const LATEST_INDEX_KEY: &str = "gcsLatestIndexKey"; +const ANNOUNCEMENT_KEY: &str = "gcsAnnouncementKey"; +/// Path to GCS users_secret file +pub const GCS_USER_SECRET: &str = "GCS_USER_SECRET"; +/// Path to GCS Service account key +pub const GCS_SERVICE_ACCOUNT_KEY: &str = "GCS_SERVICE_ACCOUNT_KEY"; + +/// Google Cloud Storage client builder +/// Provide `AuthFlow::NoAuth` for no-auth access to public bucket +/// # Example 1 - anonymous client with access to public bucket +/// ``` +/// use hyperlane_base::GcsStorageClientBuilder; +/// use ya_gcp::AuthFlow; +/// # #[tokio::main] +/// # async fn main() { +/// let client = GcsStorageClientBuilder::new(AuthFlow::NoAuth) +/// .build("HyperlaneBucket", None) +/// .await.expect("failed to instantiate anonymous client"); +/// # } +///``` +/// +/// For authenticated write access to bucket proper file path must be provided. +/// # WARN: panic-s if file path is incorrect or data in it as faulty +/// +/// # Example 2 - service account key +/// ```should_panic +/// use hyperlane_base::GcsStorageClientBuilder; +/// use ya_gcp::{AuthFlow, ServiceAccountAuth}; +/// # #[tokio::main] +/// # async fn main() { +/// let auth = +/// AuthFlow::ServiceAccount(ServiceAccountAuth::Path("path/to/sac.json".into())); +/// +/// let client = GcsStorageClientBuilder::new(auth) +/// .build("HyperlaneBucket", None) +/// .await.expect("failed to instantiate anonymous client"); +/// # } +///``` +/// # Example 3 - user secret access +/// ```should_panic +/// use hyperlane_base::GcsStorageClientBuilder; +/// use ya_gcp::AuthFlow; +/// # #[tokio::main] +/// # async fn main() { +/// let auth = +/// AuthFlow::UserAccount("path/to/user_secret.json".into()); +/// +/// let client = GcsStorageClientBuilder::new(auth) +/// .build("HyperlaneBucket", None) +/// .await.expect("failed to instantiate anonymous client"); +/// # } +///``` +#[derive(Debug, new)] +pub struct GcsStorageClientBuilder { + auth: AuthFlow, +} + +/// Google Cloud Storage client +/// Enables use of any of service account key OR user secrets to authenticate +/// For anonymous access to public data provide `(None, None)` to Builder +pub struct GcsStorageClient { + // GCS storage client + // # Details: + inner: StorageClient, + // bucket name of this client's storage + bucket: String, +} + +impl GcsStorageClientBuilder { + /// Instantiates `ya_gcp:StorageClient` based on provided auth method + /// # Param + /// * `baucket_name` - String name of target bucket to work with, will be used by all store and get ops + pub async fn build( + self, + bucket_name: impl Into, + folder: Option, + ) -> Result { + let inner = ClientBuilder::new(ClientBuilderConfig::new().auth_flow(self.auth)) + .await? + .build_storage_client(); + let bucket = if let Some(folder) = folder { + format! {"{}/{}", bucket_name.into(), folder} + } else { + bucket_name.into() + }; + + Ok(GcsStorageClient { inner, bucket }) + } +} + +impl GcsStorageClient { + // convinience formatter + fn get_checkpoint_key(index: u32) -> String { + format!("checkpoint_{index}_with_id.json") + } + // #test only method[s] + #[cfg(test)] + pub(crate) async fn get_by_path(&self, path: impl AsRef) -> Result<()> { + self.inner.get_object(&self.bucket, path).await?; + Ok(()) + } +} + +// required by `CheckpointSyncer` +impl fmt::Debug for GcsStorageClient { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("S3Storage") + .field("bucket", &self.bucket) + .finish() + } +} + +#[async_trait] +impl CheckpointSyncer for GcsStorageClient { + /// Read the highest index of this Syncer + async fn latest_index(&self) -> Result> { + match self.inner.get_object(&self.bucket, LATEST_INDEX_KEY).await { + Ok(data) => Ok(Some(serde_json::from_slice(data.as_ref())?)), + Err(e) => match e { + // never written before to this bucket + ya_gcp::storage::ObjectError::InvalidName(_) => Ok(None), + _ => bail!(e), + }, + } + } + + /// Writes the highest index of this Syncer + async fn write_latest_index(&self, index: u32) -> Result<()> { + let d = serde_json::to_vec(&index)?; + self.inner + .insert_object(&self.bucket, LATEST_INDEX_KEY, d) + .await?; + Ok(()) + } + + /// Update the latest index of this syncer if necessary + async fn update_latest_index(&self, index: u32) -> Result<()> { + let curr = self.latest_index().await?.unwrap_or(0); + if index > curr { + self.write_latest_index(index).await?; + } + Ok(()) + } + + /// Attempt to fetch the signed (checkpoint, messageId) tuple at this index + async fn fetch_checkpoint(&self, index: u32) -> Result> { + let res = self + .inner + .get_object(&self.bucket, GcsStorageClient::get_checkpoint_key(index)) + .await?; + Ok(Some(serde_json::from_slice(res.as_ref())?)) + } + + /// Write the signed (checkpoint, messageId) tuple to this syncer + async fn write_checkpoint( + &self, + signed_checkpoint: &SignedCheckpointWithMessageId, + ) -> Result<()> { + self.inner + .insert_object( + &self.bucket, + GcsStorageClient::get_checkpoint_key(signed_checkpoint.value.index), + serde_json::to_vec(signed_checkpoint)?, + ) + .await?; + Ok(()) + } + + /// Write the signed announcement to this syncer + async fn write_announcement(&self, signed_announcement: &SignedAnnouncement) -> Result<()> { + self.inner + .insert_object( + &self.bucket, + ANNOUNCEMENT_KEY, + serde_json::to_string(signed_announcement)?, + ) + .await?; + Ok(()) + } + + /// Return the announcement storage location for this syncer + fn announcement_location(&self) -> String { + format!("gs://{}/{}", &self.bucket, ANNOUNCEMENT_KEY) + } +} + +#[tokio::test] +async fn public_landset_no_auth_works_test() { + const LANDSAT_BUCKET: &str = "gcp-public-data-landsat"; + const LANDSAT_KEY: &str = "LC08/01/001/003/LC08_L1GT_001003_20140812_20170420_01_T2/LC08_L1GT_001003_20140812_20170420_01_T2_B3.TIF"; + let client = GcsStorageClientBuilder::new(AuthFlow::NoAuth) + .build(LANDSAT_BUCKET, None) + .await + .unwrap(); + assert!(client.get_by_path(LANDSAT_KEY).await.is_ok()); +} diff --git a/rust/hyperlane-base/src/types/mod.rs b/rust/hyperlane-base/src/types/mod.rs index f1e23c773c..1f6b3b792f 100644 --- a/rust/hyperlane-base/src/types/mod.rs +++ b/rust/hyperlane-base/src/types/mod.rs @@ -1,7 +1,12 @@ +mod gcs_storage; mod local_storage; mod multisig; mod s3_storage; +/// Reusable logic for working with storage backends. +pub mod utils; + +pub use gcs_storage::*; pub use local_storage::*; pub use multisig::*; pub use s3_storage::*; diff --git a/rust/hyperlane-base/src/types/s3_storage.rs b/rust/hyperlane-base/src/types/s3_storage.rs index 2d09a18a86..4db179ad4d 100644 --- a/rust/hyperlane-base/src/types/s3_storage.rs +++ b/rust/hyperlane-base/src/types/s3_storage.rs @@ -8,11 +8,12 @@ use hyperlane_core::{SignedAnnouncement, SignedCheckpointWithMessageId}; use prometheus::IntGauge; use rusoto_core::{ credential::{Anonymous, AwsCredentials, StaticProvider}, - HttpClient, Region, RusotoError, + Region, RusotoError, }; use rusoto_s3::{GetObjectError, GetObjectRequest, PutObjectRequest, S3Client, S3}; use tokio::time::timeout; +use crate::types::utils; use crate::{settings::aws_credentials::AwsChainCredentialsProvider, CheckpointSyncer}; /// The timeout for S3 requests. Rusoto doesn't offer timeout configuration @@ -93,7 +94,7 @@ impl S3Storage { fn authenticated_client(&self) -> &S3Client { self.authenticated_client.get_or_init(|| { S3Client::new_with( - HttpClient::new().unwrap(), + utils::http_client_with_timeout().unwrap(), AwsChainCredentialsProvider::new(), self.region.clone(), ) @@ -113,7 +114,7 @@ impl S3Storage { assert!(credentials.is_anonymous(), "AWS credentials not anonymous"); S3Client::new_with( - HttpClient::new().unwrap(), + utils::http_client_with_timeout().unwrap(), StaticProvider::from(credentials), self.region.clone(), ) diff --git a/rust/hyperlane-base/src/types/utils.rs b/rust/hyperlane-base/src/types/utils.rs new file mode 100644 index 0000000000..a84c55d5ad --- /dev/null +++ b/rust/hyperlane-base/src/types/utils.rs @@ -0,0 +1,15 @@ +use std::time::Duration; + +use eyre::Result; +use rusoto_core::{HttpClient, HttpConfig}; + +/// See https://github.com/hyperium/hyper/issues/2136#issuecomment-589488526 +pub const HYPER_POOL_IDLE_TIMEOUT: Duration = Duration::from_secs(15); + +/// Create a new HTTP client with a timeout for the connection pool. +/// This is a workaround for https://github.com/hyperium/hyper/issues/2136#issuecomment-589345238 +pub fn http_client_with_timeout() -> Result { + let mut config = HttpConfig::new(); + config.pool_idle_timeout(HYPER_POOL_IDLE_TIMEOUT); + Ok(HttpClient::new_with_config(config)?) +} diff --git a/rust/hyperlane-core/Cargo.toml b/rust/hyperlane-core/Cargo.toml index d5c93b937a..5f34bc2091 100644 --- a/rust/hyperlane-core/Cargo.toml +++ b/rust/hyperlane-core/Cargo.toml @@ -11,6 +11,7 @@ version = { workspace = true } [dependencies] async-trait.workspace = true +async-rwlock.workspace = true auto_impl.workspace = true bigdecimal.workspace = true borsh.workspace = true @@ -25,6 +26,7 @@ ethers-core = { workspace = true, optional = true } ethers-providers = { workspace = true, optional = true } eyre.workspace = true fixed-hash.workspace = true +futures = { workspace = true, optional = true } getrandom.workspace = true hex.workspace = true itertools.workspace = true @@ -36,6 +38,8 @@ serde_json = { workspace = true } sha3 = { workspace = true } strum = { workspace = true, optional = true, features = ["derive"] } thiserror = { workspace = true } +tokio = { workspace = true, optional = true, features = ["rt", "time"] } +tracing.workspace = true primitive-types = { workspace = true, optional = true } solana-sdk = { workspace = true, optional = true } tiny-keccak = { workspace = true, features = ["keccak"]} @@ -52,3 +56,4 @@ agent = ["ethers", "strum"] strum = ["dep:strum"] ethers = ["dep:ethers-core", "dep:ethers-contract", "dep:ethers-providers", "dep:primitive-types"] solana = ["dep:solana-sdk"] +async = ["tokio", "futures"] diff --git a/rust/hyperlane-core/src/chain.rs b/rust/hyperlane-core/src/chain.rs index 8f2f37db98..6181806e5b 100644 --- a/rust/hyperlane-core/src/chain.rs +++ b/rust/hyperlane-core/src/chain.rs @@ -50,21 +50,17 @@ impl<'a> std::fmt::Display for ContractLocator<'a> { )] pub enum KnownHyperlaneDomain { Ethereum = 1, - Goerli = 5, Sepolia = 11155111, Polygon = 137, Mumbai = 80001, - PolygonZkEvmTestnet = 1442, Avalanche = 43114, Fuji = 43113, Arbitrum = 42161, - ArbitrumGoerli = 421613, Optimism = 10, - OptimismGoerli = 420, #[cfg_attr(feature = "strum", strum(serialize = "bsc"))] BinanceSmartChain = 56, @@ -80,6 +76,15 @@ pub enum KnownHyperlaneDomain { Gnosis = 100, Chiado = 10200, + MantaPacific = 169, + + Neutron = 1853125230, + + Injective = 6909546, + InEvm = 2525, + + PlumeTestnet = 161221135, + // -- Local test chains -- /// Test1 local chain Test1 = 13371, @@ -97,8 +102,6 @@ pub enum KnownHyperlaneDomain { SealevelTest2 = 13376, // -- v3 testnets -- - LineaGoerli = 59140, - BaseGoerli = 84531, ScrollSepolia = 534351, /// Cosmos local chains @@ -114,6 +117,7 @@ pub enum HyperlaneDomain { domain_name: String, domain_type: HyperlaneDomainType, domain_protocol: HyperlaneDomainProtocol, + domain_technical_stack: HyperlaneDomainTechnicalStack, }, } @@ -125,6 +129,7 @@ impl HyperlaneDomain { domain_name: name.to_owned(), domain_type: HyperlaneDomainType::LocalTestChain, domain_protocol: HyperlaneDomainProtocol::Ethereum, + domain_technical_stack: HyperlaneDomainTechnicalStack::Other, } } } @@ -150,7 +155,7 @@ pub enum HyperlaneDomainType { Unknown, } -/// A selector for which base library should handle this domain. +/// Hyperlane domain protocol types. #[derive(FromPrimitive, Copy, Clone, Eq, PartialEq, Debug)] #[cfg_attr( feature = "strum", @@ -183,6 +188,22 @@ impl HyperlaneDomainProtocol { } } +/// Hyperlane domain technical stack types. +#[derive(Default, FromPrimitive, Copy, Clone, Eq, PartialEq, Debug)] +#[cfg_attr( + feature = "strum", + derive(strum::Display, EnumString, IntoStaticStr, EnumIter) +)] +#[cfg_attr( + feature = "strum", + strum(serialize_all = "lowercase", ascii_case_insensitive) +)] +pub enum HyperlaneDomainTechnicalStack { + ArbitrumNitro, + #[default] + Other, +} + impl KnownHyperlaneDomain { #[cfg(feature = "strum")] pub fn as_str(self) -> &'static str { @@ -195,11 +216,11 @@ impl KnownHyperlaneDomain { many_to_one!(match self { Mainnet: [ Ethereum, Avalanche, Arbitrum, Polygon, Optimism, BinanceSmartChain, Celo, - Moonbeam, Gnosis + Moonbeam, Gnosis, MantaPacific, Neutron, Injective, InEvm ], Testnet: [ - Goerli, Mumbai, Fuji, ArbitrumGoerli, OptimismGoerli, BinanceSmartChainTestnet, - Alfajores, MoonbaseAlpha, Sepolia, PolygonZkEvmTestnet, LineaGoerli, BaseGoerli, ScrollSepolia, Chiado + Mumbai, Fuji, BinanceSmartChainTestnet, + Alfajores, MoonbaseAlpha, Sepolia, ScrollSepolia, Chiado, PlumeTestnet ], LocalTestChain: [Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, CosmosTest99991], }) @@ -210,13 +231,28 @@ impl KnownHyperlaneDomain { many_to_one!(match self { HyperlaneDomainProtocol::Ethereum: [ - Ethereum, Goerli, Sepolia, Polygon, Mumbai, Avalanche, Fuji, Arbitrum, ArbitrumGoerli, - Optimism, OptimismGoerli, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, - Alfajores, Moonbeam, MoonbaseAlpha, PolygonZkEvmTestnet, LineaGoerli, BaseGoerli, ScrollSepolia, Chiado, Test1, Test2, Test3 + Ethereum, Sepolia, Polygon, Mumbai, Avalanche, Fuji, Arbitrum, + Optimism, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, + Alfajores, Moonbeam, InEvm, MoonbaseAlpha, ScrollSepolia, + Chiado, MantaPacific, PlumeTestnet, Test1, Test2, Test3 ], HyperlaneDomainProtocol::Fuel: [FuelTest1], HyperlaneDomainProtocol::Sealevel: [SealevelTest1, SealevelTest2], - HyperlaneDomainProtocol::Cosmos: [CosmosTest99990, CosmosTest99991], + HyperlaneDomainProtocol::Cosmos: [CosmosTest99990, CosmosTest99991, Neutron, Injective], + }) + } + + pub const fn domain_technical_stack(self) -> HyperlaneDomainTechnicalStack { + use KnownHyperlaneDomain::*; + + many_to_one!(match self { + HyperlaneDomainTechnicalStack::ArbitrumNitro: [Arbitrum, PlumeTestnet], + HyperlaneDomainTechnicalStack::Other: [ + Ethereum, Sepolia, Polygon, Mumbai, Avalanche, Fuji, Optimism, + BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, Alfajores, Moonbeam, MoonbaseAlpha, + ScrollSepolia, Chiado, MantaPacific, Neutron, Injective, InEvm, + Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, CosmosTest99991 + ], }) } } @@ -288,6 +324,12 @@ impl Debug for HyperlaneDomain { } } +impl From for HyperlaneDomain { + fn from(domain: KnownHyperlaneDomain) -> Self { + HyperlaneDomain::Known(domain) + } +} + #[derive(thiserror::Error, Debug)] pub enum HyperlaneDomainConfigError { #[error("Domain name (`{0}`) does not match the name of a known domain id; the name is probably misspelled.")] @@ -302,6 +344,7 @@ impl HyperlaneDomain { domain_id: u32, name: &str, protocol: HyperlaneDomainProtocol, + domain_technical_stack: HyperlaneDomainTechnicalStack, ) -> Result { let name = name.to_ascii_lowercase(); if let Ok(domain) = KnownHyperlaneDomain::try_from(domain_id) { @@ -321,6 +364,7 @@ impl HyperlaneDomain { domain_protocol: protocol, // we might want to support accepting this from the config later domain_type: HyperlaneDomainType::Unknown, + domain_technical_stack, }) } } @@ -363,20 +407,32 @@ impl HyperlaneDomain { } } - pub fn is_arbitrum_nitro(&self) -> bool { + pub const fn domain_technical_stack(&self) -> HyperlaneDomainTechnicalStack { + match self { + HyperlaneDomain::Known(domain) => domain.domain_technical_stack(), + HyperlaneDomain::Unknown { + domain_technical_stack, + .. + } => *domain_technical_stack, + } + } + + pub const fn is_arbitrum_nitro(&self) -> bool { matches!( - self, - HyperlaneDomain::Known( - KnownHyperlaneDomain::Arbitrum | KnownHyperlaneDomain::ArbitrumGoerli, - ) + self.domain_technical_stack(), + HyperlaneDomainTechnicalStack::ArbitrumNitro ) } + pub const fn is_injective(&self) -> bool { + matches!(self, Self::Known(KnownHyperlaneDomain::Injective)) + } + pub const fn index_mode(&self) -> IndexMode { use HyperlaneDomainProtocol::*; let protocol = self.domain_protocol(); many_to_one!(match protocol { - IndexMode::Block: [Ethereum, Cosmos], // TODO: Is cosmos index-mode is correct? + IndexMode::Block: [Ethereum, Cosmos], IndexMode::Sequence : [Sealevel, Fuel], }) } diff --git a/rust/hyperlane-core/src/error.rs b/rust/hyperlane-core/src/error.rs index b3fb6ce356..3eb1ea5b58 100644 --- a/rust/hyperlane-core/src/error.rs +++ b/rust/hyperlane-core/src/error.rs @@ -4,13 +4,14 @@ use std::fmt::{Debug, Display, Formatter}; use std::ops::Deref; use bigdecimal::ParseBigDecimalError; +use derive_new::new; use crate::config::StrOrIntParseError; +use crate::rpc_clients::RpcClientError; use std::string::FromUtf8Error; -use crate::Error as PrimitiveTypeError; use crate::HyperlaneProviderError; -use crate::H256; +use crate::{Error as PrimitiveTypeError, HyperlaneSignerError, H256, U256}; /// The result of interacting with a chain. pub type ChainResult = Result; @@ -23,6 +24,7 @@ impl HyperlaneCustomError for E {} /// Thin wrapper around a boxed HyperlaneCustomError; required to satisfy /// AsDynError implementations. Basically a trait-object adaptor. #[repr(transparent)] +#[derive(new)] pub struct HyperlaneCustomErrorWrapper(Box); impl Debug for HyperlaneCustomErrorWrapper { @@ -119,15 +121,36 @@ pub enum ChainCommunicationError { /// Error message msg: String, }, - /// Failed to estimate transaction gas cost. - #[error("Failed to estimate transaction gas cost {0}")] - TxCostEstimateError(String), + /// Insufficient funds. + #[error("Insufficient funds. Required: {required:?}, available: {available:?}")] + InsufficientFunds { + /// The required amount of funds. + required: U256, + /// The available amount of funds. + available: U256, + }, /// Primitive type error #[error(transparent)] PrimitiveTypeError(#[from] PrimitiveTypeError), /// Big decimal parsing error #[error(transparent)] ParseBigDecimalError(#[from] ParseBigDecimalError), + /// Rpc client error + #[error(transparent)] + RpcClientError(#[from] RpcClientError), + /// Tokio join error + #[cfg(feature = "async")] + #[error(transparent)] + TokioJoinError(#[from] tokio::task::JoinError), + /// Custom error + #[error("{0}")] + CustomError(String), + /// Eyre error + #[error("{0}")] + EyreError(#[from] eyre::Report), + /// Hyperlane signer error + #[error("{0}")] + HyperlaneSignerError(#[from] HyperlaneSignerError), } impl ChainCommunicationError { diff --git a/rust/hyperlane-core/src/lib.rs b/rust/hyperlane-core/src/lib.rs index 6834df3951..610fa3d81b 100644 --- a/rust/hyperlane-core/src/lib.rs +++ b/rust/hyperlane-core/src/lib.rs @@ -8,6 +8,7 @@ extern crate core; pub use chain::*; +pub use error::*; pub use error::{ChainCommunicationError, ChainResult, HyperlaneProtocolError}; pub use identifiers::HyperlaneIdentifier; pub use traits::*; @@ -35,6 +36,9 @@ mod types; mod chain; mod error; +/// Implementations of custom rpc client logic (e.g. fallback) +pub mod rpc_clients; + /// Enum for validity of a list of messages #[derive(Debug)] pub enum ListValidity { diff --git a/rust/hyperlane-core/src/rpc_clients/error.rs b/rust/hyperlane-core/src/rpc_clients/error.rs new file mode 100644 index 0000000000..f896fc4983 --- /dev/null +++ b/rust/hyperlane-core/src/rpc_clients/error.rs @@ -0,0 +1,11 @@ +use thiserror::Error; + +use crate::ChainCommunicationError; + +/// Errors specific to fallback provider. +#[derive(Error, Debug)] +pub enum RpcClientError { + /// Fallback providers failed + #[error("All fallback providers failed. (Errors: {0:?})")] + FallbackProvidersFailed(Vec), +} diff --git a/rust/hyperlane-core/src/rpc_clients/fallback.rs b/rust/hyperlane-core/src/rpc_clients/fallback.rs new file mode 100644 index 0000000000..6f75cbc4c8 --- /dev/null +++ b/rust/hyperlane-core/src/rpc_clients/fallback.rs @@ -0,0 +1,334 @@ +use async_rwlock::RwLock; +use async_trait::async_trait; +use derive_new::new; +use itertools::Itertools; +use std::{ + fmt::{Debug, Formatter}, + future::Future, + marker::PhantomData, + pin::Pin, + sync::Arc, + time::{Duration, Instant}, +}; +use tokio; +use tracing::{info, trace, warn_span}; + +use crate::ChainCommunicationError; + +use super::RpcClientError; + +/// Read the current block number from a chain. +#[async_trait] +pub trait BlockNumberGetter: Send + Sync + Debug { + /// Latest block number getter + async fn get_block_number(&self) -> Result; +} + +const MAX_BLOCK_TIME: Duration = Duration::from_secs(2 * 60); + +/// Information about a provider in `PrioritizedProviders` + +#[derive(Clone, Copy, new)] +pub struct PrioritizedProviderInner { + /// Index into the `providers` field of `PrioritizedProviders` + pub index: usize, + /// Tuple of the block number and the time when it was queried + #[new(value = "(0, Instant::now())")] + last_block_height: (u64, Instant), +} + +impl PrioritizedProviderInner { + fn from_block_height(index: usize, block_height: u64) -> Self { + Self { + index, + last_block_height: (block_height, Instant::now()), + } + } +} +/// Sub-providers and priority information +pub struct PrioritizedProviders { + /// Unsorted list of providers this provider calls + pub providers: Vec, + /// Sorted list of providers this provider calls, in descending order or reliability + pub priorities: RwLock>, +} + +/// A provider that bundles multiple providers and attempts to call the first, +/// then the second, and so on until a response is received. +/// +/// Although no trait bounds are used in the struct definition, the intended purpose of `B` +/// is to be bound by `BlockNumberGetter` and have `T` be convertible to `B`. That is, +/// inner providers should be able to get the current block number, or be convertible into +/// something that is. +pub struct FallbackProvider { + /// The sub-providers called by this provider + pub inner: Arc>, + max_block_time: Duration, + _phantom: PhantomData, +} + +impl Clone for FallbackProvider { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + max_block_time: self.max_block_time, + _phantom: PhantomData, + } + } +} + +impl Debug for FallbackProvider +where + T: Debug, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + // iterate the inner providers and write them to the formatter + f.debug_struct("FallbackProvider") + .field( + "providers", + &self + .inner + .providers + .iter() + .map(|v| format!("{:?}", v)) + .join(", "), + ) + .finish() + } +} + +impl FallbackProvider +where + T: Into + Debug + Clone, + B: BlockNumberGetter, +{ + /// Convenience method for creating a `FallbackProviderBuilder` with same + /// `JsonRpcClient` types + pub fn builder() -> FallbackProviderBuilder { + FallbackProviderBuilder::default() + } + + /// Create a new fallback provider + pub fn new(providers: impl IntoIterator) -> Self { + Self::builder().add_providers(providers).build() + } + + async fn deprioritize_provider(&self, priority: PrioritizedProviderInner) { + // De-prioritize the current provider by moving it to the end of the queue + let mut priorities = self.inner.priorities.write().await; + priorities.retain(|&p| p.index != priority.index); + priorities.push(priority); + } + + async fn update_last_seen_block(&self, provider_index: usize, current_block_height: u64) { + let mut priorities = self.inner.priorities.write().await; + // Get provider position in the up-to-date priorities vec + if let Some(position) = priorities.iter().position(|p| p.index == provider_index) { + priorities[position] = + PrioritizedProviderInner::from_block_height(provider_index, current_block_height); + } + } + + /// Used to iterate the providers in a non-blocking way + pub async fn take_priorities_snapshot(&self) -> Vec { + let read_lock = self.inner.priorities.read().await; + (*read_lock).clone() + } + + /// De-prioritize a provider that has either timed out or returned a bad response + pub async fn handle_stalled_provider(&self, priority: &PrioritizedProviderInner, provider: &T) { + let now = Instant::now(); + if now + .duration_since(priority.last_block_height.1) + .le(&self.max_block_time) + { + // Do nothing, it's too early to tell if the provider has stalled + return; + } + + let block_getter: B = provider.clone().into(); + let current_block_height = block_getter + .get_block_number() + .await + .unwrap_or(priority.last_block_height.0); + if current_block_height <= priority.last_block_height.0 { + // The `max_block_time` elapsed but the block number returned by the provider has not increased + self.deprioritize_provider(*priority).await; + info!( + provider_index=%priority.index, + provider=?self.inner.providers[priority.index], + "Deprioritizing an inner provider in FallbackProvider", + ); + } else { + self.update_last_seen_block(priority.index, current_block_height) + .await; + } + } + + /// Call the first provider, then the second, and so on (in order of priority) until a response is received. + /// If all providers fail, return an error. + pub async fn call( + &self, + mut f: impl FnMut(T) -> Pin> + Send>>, + ) -> Result { + let mut errors = vec![]; + // make sure we do at least 4 total retries. + while errors.len() <= 3 { + if !errors.is_empty() { + tokio::time::sleep(Duration::from_millis(100)).await; + } + let priorities_snapshot = self.take_priorities_snapshot().await; + for (idx, priority) in priorities_snapshot.iter().enumerate() { + let provider = &self.inner.providers[priority.index]; + let resp = f(provider.clone()).await; + self.handle_stalled_provider(priority, provider).await; + let _span = + warn_span!("FallbackProvider::call", fallback_count=%idx, provider_index=%priority.index, ?provider).entered(); + match resp { + Ok(v) => return Ok(v), + Err(e) => { + trace!( + error=?e, + "Got error from inner fallback provider", + ); + errors.push(e) + } + } + } + } + + Err(RpcClientError::FallbackProvidersFailed(errors).into()) + } +} + +/// Builder to create a new fallback provider. +#[derive(Debug, Clone)] +pub struct FallbackProviderBuilder { + providers: Vec, + max_block_time: Duration, + _phantom: PhantomData, +} + +impl Default for FallbackProviderBuilder { + fn default() -> Self { + Self { + providers: Vec::new(), + max_block_time: MAX_BLOCK_TIME, + _phantom: PhantomData, + } + } +} + +impl FallbackProviderBuilder { + /// Add a new provider to the set. Each new provider will be a lower + /// priority than the previous. + pub fn add_provider(mut self, provider: T) -> Self { + self.providers.push(provider); + self + } + + /// Add many providers sorted by highest priority to lowest. + pub fn add_providers(mut self, providers: impl IntoIterator) -> Self { + self.providers.extend(providers); + self + } + + /// Only used for testing purposes. + /// TODO: Move tests into this crate to control the visiblity with conditional compilation. + pub fn with_max_block_time(mut self, max_block_time: Duration) -> Self { + self.max_block_time = max_block_time; + self + } + + /// Create a fallback provider. + pub fn build(self) -> FallbackProvider { + let provider_count = self.providers.len(); + let prioritized_providers = PrioritizedProviders { + providers: self.providers, + // The order of `self.providers` gives the initial priority. + priorities: RwLock::new( + (0..provider_count) + .map(PrioritizedProviderInner::new) + .collect(), + ), + }; + FallbackProvider { + inner: Arc::new(prioritized_providers), + max_block_time: self.max_block_time, + _phantom: PhantomData, + } + } +} + +/// Utilities to import when testing chain-specific fallback providers +pub mod test { + use super::*; + use std::{ + ops::Deref, + sync::{Arc, Mutex}, + }; + + /// Provider that stores requests and optionally sleeps before returning a dummy value + #[derive(Debug, Clone)] + pub struct ProviderMock { + // Store requests as tuples of (method, params) + // Even if the tests were single-threaded, need the arc-mutex + // for interior mutability in `JsonRpcClient::request` + requests: Arc>>, + request_sleep: Option, + } + + impl Default for ProviderMock { + fn default() -> Self { + Self { + requests: Arc::new(Mutex::new(vec![])), + request_sleep: None, + } + } + } + + impl ProviderMock { + /// Create a new provider + pub fn new(request_sleep: Option) -> Self { + Self { + request_sleep, + ..Default::default() + } + } + + /// Push a request to the internal store for later inspection + pub fn push(&self, method: &str, params: T) { + self.requests + .lock() + .unwrap() + .push((method.to_owned(), format!("{:?}", params))); + } + + /// Get the stored requests + pub fn requests(&self) -> Vec<(String, String)> { + self.requests.lock().unwrap().clone() + } + + /// Set the sleep duration + pub fn request_sleep(&self) -> Option { + self.request_sleep + } + + /// Get how many times each provider was called + pub async fn get_call_counts, B>( + fallback_provider: &FallbackProvider, + ) -> Vec { + fallback_provider + .inner + .priorities + .read() + .await + .iter() + .map(|p| { + let provider = &fallback_provider.inner.providers[p.index]; + provider.requests().len() + }) + .collect() + } + } +} diff --git a/rust/hyperlane-core/src/rpc_clients/mod.rs b/rust/hyperlane-core/src/rpc_clients/mod.rs new file mode 100644 index 0000000000..892718918f --- /dev/null +++ b/rust/hyperlane-core/src/rpc_clients/mod.rs @@ -0,0 +1,14 @@ +pub use self::error::*; + +#[cfg(feature = "async")] +pub use self::fallback::*; + +#[cfg(feature = "async")] +pub use self::retry::*; + +mod error; +#[cfg(feature = "async")] +mod fallback; + +#[cfg(feature = "async")] +mod retry; diff --git a/rust/hyperlane-core/src/rpc_clients/retry.rs b/rust/hyperlane-core/src/rpc_clients/retry.rs new file mode 100644 index 0000000000..b5c4e0bd72 --- /dev/null +++ b/rust/hyperlane-core/src/rpc_clients/retry.rs @@ -0,0 +1,51 @@ +use futures::Future; +use std::{pin::Pin, time::Duration}; +use tokio::time::sleep; +use tracing::{instrument, warn}; + +use crate::{ChainCommunicationError, ChainResult}; + +/// Max number of times to retry a call for +pub const DEFAULT_MAX_RPC_RETRIES: usize = 10; + +/// Duration to sleep between retries +pub const RPC_RETRY_SLEEP_DURATION: Duration = Duration::from_secs(2); + +// TODO: Refactor this function into a retrying provider +/// Retry calling a fallible async function a certain number of times, with a delay between each retry +#[instrument(err, skip(f))] +pub async fn call_and_retry_n_times( + mut f: impl FnMut() -> Pin> + Send>>, + n: usize, +) -> ChainResult { + for retry_number in 1..n { + match f().await { + Ok(res) => return Ok(res), + Err(err) => { + warn!(retries=retry_number, error=?err, "Retrying call"); + sleep(RPC_RETRY_SLEEP_DURATION).await; + } + } + } + + // TODO: Return the last error, or a vec of all the error instead of this string error + Err(ChainCommunicationError::CustomError( + "Retrying call failed".to_string(), + )) +} + +/// Retry calling a fallible async function a predefined number of times +#[instrument(err, skip(f))] +pub async fn call_with_retry( + f: impl FnMut() -> Pin> + Send>>, +) -> ChainResult { + 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( + f: impl FnMut() -> Pin> + Send>>, +) -> T { + // It's ok to unwrap, because `usize::MAX * RPC_RETRY_SLEEP_DURATION` means billions of years worth of retrying + call_and_retry_n_times(f, usize::MAX).await.unwrap() +} diff --git a/rust/hyperlane-core/src/traits/cursor.rs b/rust/hyperlane-core/src/traits/cursor.rs index 4deb063fde..1d052c4fc7 100644 --- a/rust/hyperlane-core/src/traits/cursor.rs +++ b/rust/hyperlane-core/src/traits/cursor.rs @@ -2,22 +2,29 @@ use std::{fmt, ops::RangeInclusive, time::Duration}; use async_trait::async_trait; use auto_impl::auto_impl; +use eyre::Result; -use crate::{ChainResult, LogMeta}; +use crate::LogMeta; /// A cursor governs event indexing for a contract. #[async_trait] #[auto_impl(Box)] pub trait ContractSyncCursor: Send + Sync + 'static { /// The next block range that should be queried. - async fn next_action(&mut self) -> ChainResult<(CursorAction, Duration)>; + /// This method should be tolerant to being called multiple times in a row + /// without any updates in between. + async fn next_action(&mut self) -> Result<(CursorAction, Duration)>; - /// The latest block that has been queried - fn latest_block(&self) -> u32; + /// The latest block that has been queried, used as a proxy for health. + /// TODO: consider a better way to assess health + fn latest_queried_block(&self) -> u32; - /// Ingests the logs that were fetched from the chain, and adjusts the cursor - /// accordingly. - async fn update(&mut self, logs: Vec<(T, LogMeta)>) -> eyre::Result<()>; + /// Ingests the logs that were fetched from the chain and the range that was queried, + /// and adjusts the cursor accordingly. + /// This is called after the logs have been written to the store, + /// however may require logs to meet certain criteria (e.g. no gaps), that if + /// not met, should result in internal state changes (e.g. rewinding) and not an Err. + async fn update(&mut self, logs: Vec<(T, LogMeta)>, range: RangeInclusive) -> Result<()>; } /// The action that should be taken by the contract sync loop diff --git a/rust/hyperlane-core/src/traits/db.rs b/rust/hyperlane-core/src/traits/db.rs index 3fbebf52db..080a182b45 100644 --- a/rust/hyperlane-core/src/traits/db.rs +++ b/rust/hyperlane-core/src/traits/db.rs @@ -23,18 +23,28 @@ pub trait Sequenced: 'static + Send + Sync { fn sequence(&self) -> u32; } -/// Extension of HyperlaneLogStore trait that supports indexed sequenced data. +/// A read-only interface for a sequence-aware indexer store. #[async_trait] #[auto_impl(&, Box, Arc)] -pub trait HyperlaneSequenceIndexerStore: HyperlaneLogStore -where - T: Send + Sync, -{ +pub trait HyperlaneSequenceAwareIndexerStoreReader: Send + Sync + Debug { /// Gets data by its sequence. async fn retrieve_by_sequence(&self, sequence: u32) -> Result>; /// Gets the block number at which the log occurred. - async fn retrieve_log_block_number(&self, nonce: u32) -> Result>; + async fn retrieve_log_block_number_by_sequence(&self, sequence: u32) -> Result>; +} + +/// Extension of HyperlaneLogStore trait for sequence-aware indexer stores. +#[async_trait] +pub trait HyperlaneSequenceAwareIndexerStore: + HyperlaneLogStore + HyperlaneSequenceAwareIndexerStoreReader +{ +} + +/// Auto-impl for HyperlaneSequenceAwareIndexerStore +impl HyperlaneSequenceAwareIndexerStore for U where + U: HyperlaneLogStore + HyperlaneSequenceAwareIndexerStoreReader + Send + Sync + Debug +{ } /// Extension of HyperlaneLogStore trait that supports a high watermark for the highest indexed block number. diff --git a/rust/hyperlane-core/src/traits/indexer.rs b/rust/hyperlane-core/src/traits/indexer.rs index a9a3752bfa..db93dedf88 100644 --- a/rust/hyperlane-core/src/traits/indexer.rs +++ b/rust/hyperlane-core/src/traits/indexer.rs @@ -38,7 +38,7 @@ pub trait Indexer: Send + Sync + Debug { /// Interface for indexing data in sequence. #[async_trait] #[auto_impl(&, Box, Arc)] -pub trait SequenceIndexer: Indexer { +pub trait SequenceAwareIndexer: Indexer { /// Return the latest finalized sequence (if any) and block number - async fn sequence_and_tip(&self) -> ChainResult<(Option, u32)>; + async fn latest_sequence_count_and_tip(&self) -> ChainResult<(Option, u32)>; } diff --git a/rust/hyperlane-core/src/traits/mod.rs b/rust/hyperlane-core/src/traits/mod.rs index 1b82599895..e85b04f4a6 100644 --- a/rust/hyperlane-core/src/traits/mod.rs +++ b/rust/hyperlane-core/src/traits/mod.rs @@ -15,7 +15,7 @@ pub use routing_ism::*; pub use signing::*; pub use validator_announce::*; -use crate::{FixedPointNumber, U256}; +use crate::{FixedPointNumber, H512, U256}; mod aggregation_ism; mod ccip_read_ism; @@ -38,11 +38,11 @@ mod validator_announce; #[derive(Debug, Clone)] pub struct TxOutcome { /// The transaction identifier/hash - pub transaction_id: crate::H512, + pub transaction_id: H512, /// True if executed, false otherwise (reverted, etc.) pub executed: bool, /// Amount of gas used on this transaction. - pub gas_used: crate::U256, + pub gas_used: U256, /// Price paid for the gas pub gas_price: FixedPointNumber, // TODO: more? What can be abstracted across all chains? @@ -54,7 +54,7 @@ impl From for TxOutcome { Self { transaction_id: t.transaction_hash.into(), executed: t.status.unwrap().low_u32() == 1, - gas_used: t.gas_used.map(Into::into).unwrap_or(crate::U256::zero()), + gas_used: t.gas_used.map(Into::into).unwrap_or(U256::zero()), gas_price: t .effective_gas_price .and_then(|price| U256::from(price).try_into().ok()) diff --git a/rust/hyperlane-core/src/traits/provider.rs b/rust/hyperlane-core/src/traits/provider.rs index 7b2c930926..654e80218f 100644 --- a/rust/hyperlane-core/src/traits/provider.rs +++ b/rust/hyperlane-core/src/traits/provider.rs @@ -4,7 +4,7 @@ use async_trait::async_trait; use auto_impl::auto_impl; use thiserror::Error; -use crate::{BlockInfo, ChainResult, HyperlaneChain, TxnInfo, H256, U256}; +use crate::{BlockInfo, ChainInfo, ChainResult, HyperlaneChain, TxnInfo, H256, U256}; /// Interface for a provider. Allows abstraction over different provider types /// for different chains. @@ -27,6 +27,9 @@ pub trait HyperlaneProvider: HyperlaneChain + Send + Sync + Debug { /// Fetch the balance of the wallet address associated with the chain provider. async fn get_balance(&self, address: String) -> ChainResult; + + /// Fetch metrics related to this chain + async fn get_chain_metrics(&self) -> ChainResult>; } /// Errors when querying for provider information. diff --git a/rust/hyperlane-core/src/traits/signing.rs b/rust/hyperlane-core/src/traits/signing.rs index 9fe8c89151..34e5676c42 100644 --- a/rust/hyperlane-core/src/traits/signing.rs +++ b/rust/hyperlane-core/src/traits/signing.rs @@ -6,7 +6,7 @@ use serde::{ }; use std::fmt::{Debug, Formatter}; -use crate::utils::fmt_bytes; +use crate::utils::bytes_to_hex; use crate::{Signature, H160, H256}; /// An error incurred by a signer @@ -99,7 +99,7 @@ impl Serialize for SignedType { state.serialize_field("value", &self.value)?; state.serialize_field("signature", &self.signature)?; let sig: [u8; 65] = self.signature.into(); - state.serialize_field("serialized_signature", &fmt_bytes(&sig))?; + state.serialize_field("serialized_signature", &bytes_to_hex(&sig))?; state.end() } } diff --git a/rust/hyperlane-core/src/types/chain_data.rs b/rust/hyperlane-core/src/types/chain_data.rs index b4ecec9208..5f5ecb2e3b 100644 --- a/rust/hyperlane-core/src/types/chain_data.rs +++ b/rust/hyperlane-core/src/types/chain_data.rs @@ -1,7 +1,9 @@ +use derive_new::new; + use crate::{H256, U256}; /// Info about a given block in the chain. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct BlockInfo { /// Hash of this block pub hash: H256, @@ -11,6 +13,16 @@ pub struct BlockInfo { pub number: u64, } +/// Metrics about the chain. +#[derive(Debug, Clone, Default, new)] +pub struct ChainInfo { + /// Information about the latest block + pub latest_block: BlockInfo, + /// The current gas price, in the lowest denomination (e.g. wei) + /// Unless the chain implements an EIP-1559 style tx fee mechanism, this field will be `None` + pub min_gas_price: Option, +} + /// Information about a given transaction in the chain. #[derive(Debug, Clone)] pub struct TxnInfo { diff --git a/rust/hyperlane-core/src/utils.rs b/rust/hyperlane-core/src/utils.rs index 56a3e4c9b3..59b9afd26e 100644 --- a/rust/hyperlane-core/src/utils.rs +++ b/rust/hyperlane-core/src/utils.rs @@ -57,8 +57,8 @@ pub fn fmt_address_for_domain(domain: u32, addr: H256) -> String { .unwrap_or_else(|_| format!("{addr:?}")) } -/// Pretty print a byte slice for logging -pub fn fmt_bytes(bytes: &[u8]) -> String { +/// Pretty print a byte slice, including a hex prefix +pub fn bytes_to_hex(bytes: &[u8]) -> String { format!("0x{}", hex::encode(bytes)) } diff --git a/rust/sealevel/.gitignore b/rust/sealevel/.gitignore index b21c3b0132..b8a7e200a5 100644 --- a/rust/sealevel/.gitignore +++ b/rust/sealevel/.gitignore @@ -1,2 +1,3 @@ /target -environments/**/deploy-logs.txt \ No newline at end of file +environments/**/deploy-logs.txt +**/**/keys \ No newline at end of file diff --git a/rust/sealevel/client/Cargo.toml b/rust/sealevel/client/Cargo.toml index 976bf11632..8c14541fac 100644 --- a/rust/sealevel/client/Cargo.toml +++ b/rust/sealevel/client/Cargo.toml @@ -10,6 +10,7 @@ borsh.workspace = true bs58.workspace = true bincode.workspace = true clap = { workspace = true, features = ["derive"] } +ethers.workspace = true hex.workspace = true pretty_env_logger.workspace = true serde.workspace = true diff --git a/rust/sealevel/client/src/artifacts.rs b/rust/sealevel/client/src/artifacts.rs index 7caf4806f0..d6e39beeec 100644 --- a/rust/sealevel/client/src/artifacts.rs +++ b/rust/sealevel/client/src/artifacts.rs @@ -54,6 +54,13 @@ pub(crate) fn read_json(path: &Path) -> T where T: DeserializeOwned, { - let file = File::open(path).expect("Failed to open JSON file"); - serde_json::from_reader(file).expect("Failed to read JSON file") + try_read_json(path).expect("Failed to read JSON from file") +} + +pub(crate) fn try_read_json(path: &Path) -> std::io::Result +where + T: DeserializeOwned, +{ + let file = File::open(path)?; + Ok(serde_json::from_reader(file)?) } diff --git a/rust/sealevel/client/src/context.rs b/rust/sealevel/client/src/context.rs index 80b0ea9e2b..5cb9ba383b 100644 --- a/rust/sealevel/client/src/context.rs +++ b/rust/sealevel/client/src/context.rs @@ -239,14 +239,17 @@ impl<'ctx, 'rpc> TxnBuilder<'ctx, 'rpc> { fn wait_for_user_confirmation() { println!("Continue? [y/n] then press Enter"); let mut input = [0u8; 1]; - std::io::stdin().read_exact(&mut input).unwrap(); - match input[0] { - b'y' => { - println!("Continuing..."); + loop { + std::io::stdin().read_exact(&mut input).unwrap(); + match input[0] { + b'y' => { + println!("Continuing..."); + break; + } + b'n' => { + panic!("User requested exit"); + } + _ => {} } - b'n' => { - panic!("User requested exit"); - } - _ => {} } } diff --git a/rust/sealevel/client/src/core.rs b/rust/sealevel/client/src/core.rs index 8aaf51adb1..783fd3a5f4 100644 --- a/rust/sealevel/client/src/core.rs +++ b/rust/sealevel/client/src/core.rs @@ -18,7 +18,8 @@ use hyperlane_sealevel_igp::accounts::{SOL_DECIMALS, TOKEN_EXCHANGE_RATE_SCALE}; pub(crate) fn process_core_cmd(mut ctx: Context, cmd: CoreCmd) { match cmd.cmd { CoreSubCmd::Deploy(core) => { - let environments_dir = create_new_directory(&core.environments_dir, &core.environment); + let environments_dir = + create_new_directory(&core.env_args.environments_dir, &core.env_args.environment); let chain_dir = create_new_directory(&environments_dir, &core.chain); let core_dir = create_new_directory(&chain_dir, "core"); let key_dir = create_new_directory(&core_dir, "keys"); diff --git a/rust/sealevel/client/src/helloworld.rs b/rust/sealevel/client/src/helloworld.rs index 6226b48282..999e623f7d 100644 --- a/rust/sealevel/client/src/helloworld.rs +++ b/rust/sealevel/client/src/helloworld.rs @@ -10,6 +10,7 @@ use hyperlane_sealevel_hello_world::{ }, program_storage_pda_seeds, }; +use hyperlane_sealevel_igp::accounts::InterchainGasPaymasterType; use serde::{Deserialize, Serialize}; use solana_sdk::{instruction::Instruction, pubkey::Pubkey}; @@ -180,6 +181,26 @@ impl ConnectionClient for HelloWorldDeployer { set_interchain_security_module_instruction(*program_id, storage.owner.unwrap(), ism) .unwrap() } + + fn get_interchain_gas_paymaster( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option<(Pubkey, InterchainGasPaymasterType)> { + let storage = self.get_storage(client, program_id); + + storage.igp + } + + fn set_interchain_gas_paymaster_instruction( + &self, + _client: &RpcClient, + _program_id: &Pubkey, + _igp_config: Option<(Pubkey, InterchainGasPaymasterType)>, + ) -> Option { + // There is no way to set the IGP on HelloWorld + None + } } fn deploy_helloworld(ctx: &mut Context, deploy: HelloWorldDeploy) { @@ -190,8 +211,8 @@ fn deploy_helloworld(ctx: &mut Context, deploy: HelloWorldDeploy) { &deploy.context, deploy.config_file, deploy.chain_config_file, - deploy.environments_dir, - &deploy.environment, + deploy.env_args.environments_dir, + &deploy.env_args.environment, deploy.built_so_dir, ) } diff --git a/rust/sealevel/client/src/igp.rs b/rust/sealevel/client/src/igp.rs new file mode 100644 index 0000000000..f41ddad2fb --- /dev/null +++ b/rust/sealevel/client/src/igp.rs @@ -0,0 +1,610 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::{ + artifacts::{read_json, try_read_json, write_json, SingularProgramIdArtifact}, + cmd_utils::{create_and_write_keypair, create_new_directory, deploy_program}, + read_core_program_ids, + router::ChainMetadata, + Context, GasOverheadSubCmd, GetSetCmd, IgpCmd, IgpSubCmd, +}; + +use std::{ + fs::File, + path::{Path, PathBuf}, + str::FromStr, +}; + +use solana_sdk::{ + pubkey::Pubkey, + signature::{Keypair, Signer as _}, +}; + +use hyperlane_core::H256; + +use hyperlane_sealevel_igp::{ + accounts::{ + GasOracle, GasPaymentAccount, IgpAccount, InterchainGasPaymasterType, OverheadIgpAccount, + ProgramDataAccount as IgpProgramDataAccount, RemoteGasData, + }, + igp_program_data_pda_seeds, + instruction::{GasOracleConfig, GasOverheadConfig}, +}; + +#[derive(Debug, Serialize, Deserialize, Default)] +struct IgpAccountsArtifacts { + salt: H256, + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + igp_account: Option, + #[serde(default)] + #[serde(with = "crate::serde::serde_option_pubkey")] + overhead_igp_account: Option, +} + +fn get_context_salt(context: Option<&String>) -> H256 { + context + .map(|c| { + if c == "default" { + H256::zero() + } else { + ethers::utils::keccak256(c.as_bytes()).into() + } + }) + .unwrap_or_else(H256::zero) +} + +fn get_context_dir_name(context: Option<&String>) -> &str { + context.map(|c| c.as_str()).unwrap_or("default") +} + +pub(crate) fn process_igp_cmd(mut ctx: Context, cmd: IgpCmd) { + match cmd.cmd { + IgpSubCmd::DeployProgram(deploy) => { + let environments_dir = create_new_directory( + &deploy.env_args.environments_dir, + &deploy.env_args.environment, + ); + let ism_dir = create_new_directory(&environments_dir, "igp"); + let chain_dir = create_new_directory(&ism_dir, &deploy.chain); + let key_dir = create_new_directory(&chain_dir, "keys"); + + let program_id = deploy_igp_program(&mut ctx, &deploy.built_so_dir, true, &key_dir); + + write_json::( + &chain_dir.join("program-ids.json"), + program_id.into(), + ); + } + IgpSubCmd::InitIgpAccount(init) => { + let environments_dir = + create_new_directory(&init.env_args.environments_dir, &init.env_args.environment); + let ism_dir = create_new_directory(&environments_dir, "igp"); + let chain_dir = create_new_directory(&ism_dir, &init.chain); + let context_dir = + create_new_directory(&chain_dir, get_context_dir_name(init.context.as_ref())); + + let artifacts_path = context_dir.join("igp-accounts.json"); + + let existing_artifacts = try_read_json::(&artifacts_path).ok(); + + let salt = get_context_salt(init.context.as_ref()); + + let chain_configs = + read_json::>(&init.chain_config_file); + + let igp_account = init_and_configure_igp_account( + &mut ctx, + init.program_id, + chain_configs.get(&init.chain).unwrap().domain_id(), + salt, + init.gas_oracle_config_file, + ); + + let artifacts = IgpAccountsArtifacts { + salt, + igp_account: Some(igp_account), + overhead_igp_account: existing_artifacts.and_then(|a| a.overhead_igp_account), + }; + + write_json(&artifacts_path, artifacts); + } + IgpSubCmd::InitOverheadIgpAccount(init) => { + let environments_dir = + create_new_directory(&init.env_args.environments_dir, &init.env_args.environment); + let ism_dir = create_new_directory(&environments_dir, "igp"); + let chain_dir = create_new_directory(&ism_dir, &init.chain); + let context_dir = + create_new_directory(&chain_dir, get_context_dir_name(init.context.as_ref())); + + let artifacts_path = context_dir.join("igp-accounts.json"); + + let existing_artifacts = try_read_json::(&artifacts_path).ok(); + + let salt = get_context_salt(init.context.as_ref()); + + let chain_configs = + read_json::>(&init.chain_config_file); + + let overhead_igp_account = init_and_configure_overhead_igp_account( + &mut ctx, + init.program_id, + init.inner_igp_account, + chain_configs.get(&init.chain).unwrap().domain_id(), + salt, + init.overhead_config_file, + ); + + let artifacts = IgpAccountsArtifacts { + salt, + igp_account: existing_artifacts.and_then(|a| a.igp_account), + overhead_igp_account: Some(overhead_igp_account), + }; + + write_json(&artifacts_path, artifacts); + } + IgpSubCmd::Query(query) => { + let (program_data_account_pda, _program_data_account_bump) = + Pubkey::find_program_address(igp_program_data_pda_seeds!(), &query.program_id); + + let accounts = ctx + .client + .get_multiple_accounts_with_commitment( + &[program_data_account_pda, query.igp_account], + ctx.commitment, + ) + .unwrap() + .value; + + let igp_program_data = + IgpProgramDataAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) + .unwrap() + .into_inner(); + + println!("IGP program data: {:?}", igp_program_data); + + let igp = IgpAccount::fetch(&mut &accounts[1].as_ref().unwrap().data[..]) + .unwrap() + .into_inner(); + + println!("IGP account: {:?}", igp); + + if let Some(gas_payment_account_pubkey) = query.gas_payment_account { + let account = ctx + .client + .get_account_with_commitment(&gas_payment_account_pubkey, ctx.commitment) + .unwrap() + .value + .unwrap(); + let gas_payment_account = GasPaymentAccount::fetch(&mut &account.data[..]) + .unwrap() + .into_inner(); + println!("Gas payment account: {:?}", gas_payment_account); + } + } + IgpSubCmd::PayForGas(payment_details) => { + let unique_gas_payment_keypair = Keypair::new(); + let salt = H256::zero(); + let (igp_account, _igp_account_bump) = Pubkey::find_program_address( + hyperlane_sealevel_igp::igp_pda_seeds!(salt), + &payment_details.program_id, + ); + + let (overhead_igp_account, _) = Pubkey::find_program_address( + hyperlane_sealevel_igp::overhead_igp_pda_seeds!(salt), + &payment_details.program_id, + ); + let (ixn, gas_payment_data_account) = + hyperlane_sealevel_igp::instruction::pay_for_gas_instruction( + payment_details.program_id, + ctx.payer_pubkey, + igp_account, + Some(overhead_igp_account), + unique_gas_payment_keypair.pubkey(), + H256::from_str(&payment_details.message_id).unwrap(), + payment_details.destination_domain, + payment_details.gas, + ) + .unwrap(); + + ctx.new_txn() + .add(ixn) + .send(&[&*ctx.payer_signer(), &unique_gas_payment_keypair]); + + println!( + "Made a payment for message {} with gas payment data account {}", + payment_details.message_id, gas_payment_data_account + ); + } + IgpSubCmd::Claim(claim) => { + let igp_account = ctx + .client + .get_account_with_commitment(&claim.igp_account, ctx.commitment) + .unwrap() + .value + .unwrap(); + let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) + .unwrap() + .into_inner(); + + let ixn = hyperlane_sealevel_igp::instruction::claim_instruction( + claim.program_id, + claim.igp_account, + igp_account.beneficiary, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + ixn, + format!( + "Claiming from IGP account {} to beneficiary {}", + claim.igp_account, igp_account.beneficiary + ), + ) + .send_with_payer(); + } + IgpSubCmd::SetIgpBeneficiary(set_beneficiary) => { + let igp_account = ctx + .client + .get_account_with_commitment(&set_beneficiary.igp_account, ctx.commitment) + .unwrap() + .value + .unwrap(); + let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) + .unwrap() + .into_inner(); + + let ixn = hyperlane_sealevel_igp::instruction::set_beneficiary_instruction( + set_beneficiary.program_id, + set_beneficiary.igp_account, + igp_account.owner.unwrap(), + set_beneficiary.new_beneficiary, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + ixn, + format!( + "Change beneficiary of IGP account {} to beneficiary {}", + set_beneficiary.igp_account, set_beneficiary.new_beneficiary + ), + ) + .send_with_payer(); + } + IgpSubCmd::GasOracleConfig(args) => { + let core_program_ids = read_core_program_ids( + &args.env_args.environments_dir, + &args.env_args.environment, + &args.chain_name, + ); + match args.cmd { + GetSetCmd::Set(set_args) => { + let remote_gas_data = RemoteGasData { + token_exchange_rate: set_args.token_exchange_rate, + gas_price: set_args.gas_price, + token_decimals: set_args.token_decimals, + }; + let gas_oracle_config = GasOracleConfig { + domain: args.remote_domain, + gas_oracle: Some(GasOracle::RemoteGasData(remote_gas_data)), + }; + let instruction = + hyperlane_sealevel_igp::instruction::set_gas_oracle_configs_instruction( + core_program_ids.igp_program_id, + core_program_ids.igp_account, + ctx.payer_pubkey, + vec![gas_oracle_config], + ) + .unwrap(); + ctx.new_txn().add(instruction).send_with_payer(); + println!("Set gas oracle for remote domain {:?}", args.remote_domain); + } + GetSetCmd::Get(_) => { + let igp_account = ctx + .client + .get_account_with_commitment(&core_program_ids.igp_account, ctx.commitment) + .unwrap() + .value + .expect( + "IGP account not found. Make sure you are connected to the right RPC.", + ); + + let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) + .unwrap() + .into_inner(); + + println!( + "IGP account gas oracle: {:#?}", + igp_account.gas_oracles.get(&args.remote_domain) + ); + } + } + } + IgpSubCmd::DestinationGasOverhead(args) => { + let core_program_ids = read_core_program_ids( + &args.env_args.environments_dir, + &args.env_args.environment, + &args.chain_name, + ); + match args.cmd { + GasOverheadSubCmd::Get => { + // Read the gas overhead config + let overhead_igp_account = ctx + .client + .get_account_with_commitment( + &core_program_ids.overhead_igp_account, + ctx.commitment, + ) + .unwrap() + .value + .expect("Overhead IGP account not found. Make sure you are connected to the right RPC."); + let overhead_igp_account = + OverheadIgpAccount::fetch(&mut &overhead_igp_account.data[..]) + .unwrap() + .into_inner(); + println!( + "Overhead IGP account gas oracle: {:#?}", + overhead_igp_account.gas_overheads.get(&args.remote_domain) + ); + } + GasOverheadSubCmd::Set(set_args) => { + let overhead_config = GasOverheadConfig { + destination_domain: args.remote_domain, + gas_overhead: Some(set_args.gas_overhead), + }; + // Set the gas overhead config + let instruction = + hyperlane_sealevel_igp::instruction::set_destination_gas_overheads( + core_program_ids.igp_program_id, + core_program_ids.overhead_igp_account, + ctx.payer_pubkey, + vec![overhead_config], + ) + .unwrap(); + ctx.new_txn().add(instruction).send_with_payer(); + println!( + "Set gas overheads for remote domain {:?}", + args.remote_domain + ) + } + } + } + IgpSubCmd::TransferIgpOwnership(ref transfer_ownership) + | IgpSubCmd::TransferOverheadIgpOwnership(ref transfer_ownership) => { + let igp_account_type = match cmd.cmd { + IgpSubCmd::TransferIgpOwnership(_) => { + InterchainGasPaymasterType::Igp(transfer_ownership.igp_account) + } + IgpSubCmd::TransferOverheadIgpOwnership(_) => { + InterchainGasPaymasterType::OverheadIgp(transfer_ownership.igp_account) + } + _ => unreachable!(), + }; + let instruction = + hyperlane_sealevel_igp::instruction::transfer_igp_account_ownership_instruction( + transfer_ownership.program_id, + igp_account_type.clone(), + ctx.payer_pubkey, + Some(transfer_ownership.new_owner), + ) + .unwrap(); + ctx.new_txn() + .add_with_description( + instruction, + format!( + "Transfer ownership of {:?} to {}", + igp_account_type, transfer_ownership.new_owner + ), + ) + .send_with_payer(); + } + } +} + +#[allow(clippy::too_many_arguments)] +fn deploy_igp_program( + ctx: &mut Context, + built_so_dir: &Path, + use_existing_keys: bool, + key_dir: &Path, +) -> Pubkey { + let (keypair, keypair_path) = create_and_write_keypair( + key_dir, + "hyperlane_sealevel_igp-keypair.json", + use_existing_keys, + ); + let program_id = keypair.pubkey(); + + deploy_program( + ctx.payer_keypair_path(), + keypair_path.to_str().unwrap(), + built_so_dir + .join("hyperlane_sealevel_igp.so") + .to_str() + .unwrap(), + &ctx.client.url(), + ); + + println!("Deployed IGP at program ID {}", program_id); + + let (program_data_account, _program_data_bump) = Pubkey::find_program_address( + hyperlane_sealevel_igp::igp_program_data_pda_seeds!(), + &program_id, + ); + + // Initialize the program data + let instruction = + hyperlane_sealevel_igp::instruction::init_instruction(program_id, ctx.payer_pubkey) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Initializing IGP program data {}", program_data_account), + ) + .send_with_payer(); + + program_id +} + +fn init_and_configure_igp_account( + ctx: &mut Context, + program_id: Pubkey, + local_domain: u32, + salt: H256, + gas_oracle_config_file: Option, +) -> Pubkey { + let gas_oracle_configs = gas_oracle_config_file + .as_deref() + .map(|p| { + let file = File::open(p).expect("Failed to open oracle config file"); + serde_json::from_reader::<_, Vec>(file) + .expect("Failed to parse oracle config file") + }) + .unwrap_or_default() + .into_iter() + .filter(|c| c.domain != local_domain) + .collect::>(); + + // Initialize IGP with the given salt + let (igp_account_pda, _igp_account_bump) = + Pubkey::find_program_address(hyperlane_sealevel_igp::igp_pda_seeds!(salt), &program_id); + + if ctx + .client + .get_account_with_commitment(&igp_account_pda, ctx.commitment) + .unwrap() + .value + .is_none() + { + let instruction = hyperlane_sealevel_igp::instruction::init_igp_instruction( + program_id, + ctx.payer_pubkey, + salt, + Some(ctx.payer_pubkey), + ctx.payer_pubkey, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Initializing IGP account {}", igp_account_pda), + ) + .send_with_payer(); + } else { + println!( + "IGP account {} already exists, not creating", + igp_account_pda + ); + } + + if !gas_oracle_configs.is_empty() { + // TODO: idempotency + + let domains = gas_oracle_configs + .iter() + .map(|c| c.domain) + .collect::>(); + let instruction = hyperlane_sealevel_igp::instruction::set_gas_oracle_configs_instruction( + program_id, + igp_account_pda, + ctx.payer_pubkey, + gas_oracle_configs, + ) + .unwrap(); + + ctx.new_txn().add(instruction).send_with_payer(); + + println!("Set gas oracle for remote domains {domains:?}",); + } else { + println!("Skipping settings gas oracle config"); + } + + igp_account_pda +} + +fn init_and_configure_overhead_igp_account( + ctx: &mut Context, + program_id: Pubkey, + inner_igp_account: Pubkey, + local_domain: u32, + salt: H256, + overhead_config_file: Option, +) -> Pubkey { + let overhead_configs = overhead_config_file + .as_deref() + .map(|p| { + let file = File::open(p).expect("Failed to open overhead config file"); + serde_json::from_reader::<_, Vec>(file) + .expect("Failed to parse overhead config file") + }) + .unwrap_or_default() + .into_iter() + .filter(|c| c.destination_domain != local_domain) + .map(|c| (c.destination_domain, c)) + .collect::>() // dedup + .into_values() + .collect::>(); + + let (overhead_igp_account, _) = Pubkey::find_program_address( + hyperlane_sealevel_igp::overhead_igp_pda_seeds!(salt), + &program_id, + ); + + if ctx + .client + .get_account_with_commitment(&overhead_igp_account, ctx.commitment) + .unwrap() + .value + .is_none() + { + let instruction = hyperlane_sealevel_igp::instruction::init_overhead_igp_instruction( + program_id, + ctx.payer_pubkey, + salt, + Some(ctx.payer_pubkey), + inner_igp_account, + ) + .unwrap(); + + ctx.new_txn() + .add_with_description( + instruction, + format!("Initializing overhead IGP account {}", overhead_igp_account), + ) + .send_with_payer(); + } else { + println!( + "Overhead IGP account {} already exists, not creating", + overhead_igp_account + ); + } + + if !overhead_configs.is_empty() { + // TODO: idempotency + + let domains = overhead_configs + .iter() + .map(|c| c.destination_domain) + .collect::>(); + + let instruction = hyperlane_sealevel_igp::instruction::set_destination_gas_overheads( + program_id, + overhead_igp_account, + ctx.payer_pubkey, + overhead_configs, + ) + .unwrap(); + + ctx.new_txn().add(instruction).send_with_payer(); + + println!("Set gas overheads for remote domains {domains:?}",) + } else { + println!("Skipping setting gas overheads"); + } + + overhead_igp_account +} diff --git a/rust/sealevel/client/src/main.rs b/rust/sealevel/client/src/main.rs index 88e75c7f4f..74e3de7203 100644 --- a/rust/sealevel/client/src/main.rs +++ b/rust/sealevel/client/src/main.rs @@ -23,12 +23,8 @@ use account_utils::DiscriminatorEncode; use hyperlane_core::{H160, H256}; use hyperlane_sealevel_connection_client::router::RemoteRouterConfig; use hyperlane_sealevel_igp::{ - accounts::{ - GasOracle, GasPaymentAccount, IgpAccount, InterchainGasPaymasterType, OverheadIgpAccount, - ProgramDataAccount as IgpProgramDataAccount, RemoteGasData, - }, + accounts::{InterchainGasPaymasterType, OverheadIgpAccount}, igp_gas_payment_pda_seeds, igp_program_data_pda_seeds, - instruction::{GasOracleConfig, GasOverheadConfig}, }; use hyperlane_sealevel_mailbox::{ accounts::{InboxAccount, OutboxAccount}, @@ -67,12 +63,14 @@ mod cmd_utils; mod context; mod r#core; mod helloworld; +mod igp; mod multisig_ism; mod router; mod serde; mod warp_route; use crate::helloworld::process_helloworld_cmd; +use crate::igp::process_igp_cmd; use crate::multisig_ism::process_multisig_ism_message_id_cmd; use crate::warp_route::process_warp_route_cmd; pub(crate) use crate::{context::*, core::*}; @@ -115,6 +113,14 @@ enum HyperlaneSealevelCmd { HelloWorld(HelloWorldCmd), } +#[derive(Args)] +struct EnvironmentArgs { + #[arg(long)] + environment: String, + #[arg(long)] + environments_dir: PathBuf, +} + #[derive(Args)] pub(crate) struct WarpRouteCmd { #[command(subcommand)] @@ -129,10 +135,8 @@ pub(crate) enum WarpRouteSubCmd { #[derive(Args)] pub(crate) struct WarpRouteDeploy { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] built_so_dir: PathBuf, #[arg(long)] @@ -168,8 +172,8 @@ enum CoreSubCmd { struct CoreDeploy { #[arg(long)] local_domain: u32, - #[arg(long)] - environment: String, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] gas_oracle_config_file: Option, #[arg(long)] @@ -178,8 +182,6 @@ struct CoreDeploy { chain: String, #[arg(long)] use_existing_keys: bool, - #[arg(long)] - environments_dir: PathBuf, #[arg(long, num_args = 1.., value_delimiter = ',')] remote_domains: Vec, #[arg(long)] @@ -381,14 +383,63 @@ struct IgpCmd { #[derive(Subcommand)] enum IgpSubCmd { + DeployProgram(IgpDeployProgramArgs), + InitIgpAccount(InitIgpAccountArgs), + InitOverheadIgpAccount(InitOverheadIgpAccountArgs), Query(IgpQueryArgs), PayForGas(PayForGasArgs), + Claim(ClaimArgs), + SetIgpBeneficiary(SetIgpBeneficiaryArgs), GasOracleConfig(GasOracleConfigArgs), DestinationGasOverhead(DestinationGasOverheadArgs), TransferIgpOwnership(TransferIgpOwnership), TransferOverheadIgpOwnership(TransferIgpOwnership), } +#[derive(Args)] +struct IgpDeployProgramArgs { + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain: String, + #[arg(long)] + built_so_dir: PathBuf, +} + +#[derive(Args)] +struct InitIgpAccountArgs { + #[arg(long)] + program_id: Pubkey, + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain: String, + #[arg(long)] + chain_config_file: PathBuf, + #[arg(long)] + context: Option, + #[arg(long)] + gas_oracle_config_file: Option, +} + +#[derive(Args)] +struct InitOverheadIgpAccountArgs { + #[arg(long)] + program_id: Pubkey, + #[command(flatten)] + env_args: EnvironmentArgs, + #[arg(long)] + chain: String, + #[arg(long)] + chain_config_file: PathBuf, + #[arg(long)] + inner_igp_account: Pubkey, + #[arg(long)] + context: Option, + #[arg(long)] + overhead_config_file: Option, +} + #[derive(Args)] struct IgpQueryArgs { #[arg(long)] @@ -423,11 +474,26 @@ struct PayForGasArgs { } #[derive(Args)] -struct GasOracleConfigArgs { +struct ClaimArgs { #[arg(long)] - environment: String, + program_id: Pubkey, #[arg(long)] - environments_dir: PathBuf, + igp_account: Pubkey, +} + +#[derive(Args)] +struct SetIgpBeneficiaryArgs { + #[arg(long)] + program_id: Pubkey, + #[arg(long)] + igp_account: Pubkey, + new_beneficiary: Pubkey, +} + +#[derive(Args)] +struct GasOracleConfigArgs { + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] chain_name: String, #[arg(long)] @@ -451,10 +517,8 @@ struct GetGasOracleArgs; #[derive(Args)] struct DestinationGasOverheadArgs { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] chain_name: String, #[arg(long)] @@ -535,10 +599,8 @@ enum MultisigIsmMessageIdSubCmd { #[derive(Args)] struct MultisigIsmMessageIdDeploy { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] built_so_dir: PathBuf, #[arg(long)] @@ -597,10 +659,8 @@ pub(crate) enum HelloWorldSubCmd { #[derive(Args)] pub(crate) struct HelloWorldDeploy { - #[arg(long)] - environment: String, - #[arg(long)] - environments_dir: PathBuf, + #[command(flatten)] + env_args: EnvironmentArgs, #[arg(long)] built_so_dir: PathBuf, #[arg(long)] @@ -991,23 +1051,23 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { // Burns the tokens from the sender's associated token account and // then dispatches a message to the remote recipient. // - // 0. `[executable]` The system program. - // 1. `[executable]` The spl_noop program. - // 2. `[]` The token PDA account. - // 3. `[executable]` The mailbox program. - // 4. `[writeable]` The mailbox outbox account. - // 5. `[]` Message dispatch authority. - // 6. `[signer]` The token sender and mailbox payer. - // 7. `[signer]` Unique message / gas payment account. - // 8. `[writeable]` Message storage PDA. + // 0. [executable] The system program. + // 1. [executable] The spl_noop program. + // 2. [] The token PDA account. + // 3. [executable] The mailbox program. + // 4. [writeable] The mailbox outbox account. + // 5. [] Message dispatch authority. + // 6. [signer] The token sender and mailbox payer. + // 7. [signer] Unique message / gas payment account. + // 8. [writeable] Message storage PDA. // ---- If using an IGP ---- - // 9. `[executable]` The IGP program. - // 10. `[writeable]` The IGP program data. - // 11. `[writeable]` Gas payment PDA. - // 12. `[]` OPTIONAL - The Overhead IGP program, if the configured IGP is an Overhead IGP. - // 13. `[writeable]` The IGP account. + // 9. [executable] The IGP program. + // 10. [writeable] The IGP program data. + // 11. [writeable] Gas payment PDA. + // 12. [] OPTIONAL - The Overhead IGP program, if the configured IGP is an Overhead IGP. + // 13. [writeable] The IGP account. // ---- End if ---- - // 14..N `[??..??]` Plugin-specific accounts. + // 14..N [??..??] Plugin-specific accounts. let mut accounts = vec![ AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(spl_noop::id(), false), @@ -1059,8 +1119,8 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { match xfer.token_type { TokenType::Native => { - // 5. `[executable]` The system program. - // 6. `[writeable]` The native token collateral PDA account. + // 5. [executable] The system program. + // 6. [writeable] The native token collateral PDA account. let (native_collateral_account, _native_collateral_bump) = Pubkey::find_program_address( hyperlane_token_native_collateral_pda_seeds!(), @@ -1072,9 +1132,9 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { ]); } TokenType::Synthetic => { - // 5. `[executable]` The spl_token_2022 program. - // 6. `[writeable]` The mint / mint authority PDA account. - // 7. `[writeable]` The token sender's associated token account, from which tokens will be burned. + // 5. [executable] The spl_token_2022 program. + // 6. [writeable] The mint / mint authority PDA account. + // 7. [writeable] The token sender's associated token account, from which tokens will be burned. let (mint_account, _mint_bump) = Pubkey::find_program_address( hyperlane_token_mint_pda_seeds!(), &xfer.program_id, @@ -1092,10 +1152,10 @@ fn process_token_cmd(ctx: Context, cmd: TokenCmd) { ]); } TokenType::Collateral => { - // 5. `[executable]` The SPL token program for the mint. - // 6. `[writeable]` The mint. - // 7. `[writeable]` The token sender's associated token account, from which tokens will be sent. - // 8. `[writeable]` The escrow PDA account. + // 5. [executable] The SPL token program for the mint. + // 6. [writeable] The mint. + // 7. [writeable] The token sender's associated token account, from which tokens will be sent. + // 8. [writeable] The escrow PDA account. let token = HyperlaneTokenAccount::::fetch( &mut &fetched_token_account.data[..], ) @@ -1266,11 +1326,11 @@ fn process_validator_announce_cmd(ctx: Context, cmd: ValidatorAnnounceCmd) { let ixn = ValidatorAnnounceInstruction::Announce(announce_instruction); // Accounts: - // 0. `[signer]` The payer. - // 1. `[executable]` The system program. - // 2. `[]` The ValidatorAnnounce PDA account. - // 3. `[writeable]` The validator-specific ValidatorStorageLocationsAccount PDA account. - // 4. `[writeable]` The ReplayProtection PDA account specific to the announcement being made. + // 0. [signer] The payer. + // 1. [executable] The system program. + // 2. [] The ValidatorAnnounce PDA account. + // 3. [writeable] The validator-specific ValidatorStorageLocationsAccount PDA account. + // 4. [writeable] The ReplayProtection PDA account specific to the announcement being made. let accounts = vec![ AccountMeta::new_readonly(ctx.payer_pubkey, true), AccountMeta::new_readonly(system_program::id(), false), @@ -1313,202 +1373,3 @@ fn process_validator_announce_cmd(ctx: Context, cmd: ValidatorAnnounceCmd) { } } } - -fn process_igp_cmd(ctx: Context, cmd: IgpCmd) { - match cmd.cmd { - IgpSubCmd::Query(query) => { - let (program_data_account_pda, _program_data_account_bump) = - Pubkey::find_program_address(igp_program_data_pda_seeds!(), &query.program_id); - - let accounts = ctx - .client - .get_multiple_accounts_with_commitment( - &[program_data_account_pda, query.igp_account], - ctx.commitment, - ) - .unwrap() - .value; - - let igp_program_data = - IgpProgramDataAccount::fetch(&mut &accounts[0].as_ref().unwrap().data[..]) - .unwrap() - .into_inner(); - - println!("IGP program data: {:?}", igp_program_data); - - let igp = IgpAccount::fetch(&mut &accounts[1].as_ref().unwrap().data[..]) - .unwrap() - .into_inner(); - - println!("IGP account: {:?}", igp); - - if let Some(gas_payment_account_pubkey) = query.gas_payment_account { - let account = ctx - .client - .get_account_with_commitment(&gas_payment_account_pubkey, ctx.commitment) - .unwrap() - .value - .unwrap(); - let gas_payment_account = GasPaymentAccount::fetch(&mut &account.data[..]) - .unwrap() - .into_inner(); - println!("Gas payment account: {:?}", gas_payment_account); - } - } - IgpSubCmd::PayForGas(payment_details) => { - let unique_gas_payment_keypair = Keypair::new(); - let salt = H256::zero(); - let (igp_account, _igp_account_bump) = Pubkey::find_program_address( - hyperlane_sealevel_igp::igp_pda_seeds!(salt), - &payment_details.program_id, - ); - - let (overhead_igp_account, _) = Pubkey::find_program_address( - hyperlane_sealevel_igp::overhead_igp_pda_seeds!(salt), - &payment_details.program_id, - ); - let (ixn, gas_payment_data_account) = - hyperlane_sealevel_igp::instruction::pay_for_gas_instruction( - payment_details.program_id, - ctx.payer_pubkey, - igp_account, - Some(overhead_igp_account), - unique_gas_payment_keypair.pubkey(), - H256::from_str(&payment_details.message_id).unwrap(), - payment_details.destination_domain, - payment_details.gas, - ) - .unwrap(); - - ctx.new_txn() - .add(ixn) - .send(&[&*ctx.payer_signer(), &unique_gas_payment_keypair]); - - println!( - "Made a payment for message {} with gas payment data account {}", - payment_details.message_id, gas_payment_data_account - ); - } - IgpSubCmd::GasOracleConfig(args) => { - let core_program_ids = - read_core_program_ids(&args.environments_dir, &args.environment, &args.chain_name); - match args.cmd { - GetSetCmd::Set(set_args) => { - let remote_gas_data = RemoteGasData { - token_exchange_rate: set_args.token_exchange_rate, - gas_price: set_args.gas_price, - token_decimals: set_args.token_decimals, - }; - let gas_oracle_config = GasOracleConfig { - domain: args.remote_domain, - gas_oracle: Some(GasOracle::RemoteGasData(remote_gas_data)), - }; - let instruction = - hyperlane_sealevel_igp::instruction::set_gas_oracle_configs_instruction( - core_program_ids.igp_program_id, - core_program_ids.igp_account, - ctx.payer_pubkey, - vec![gas_oracle_config], - ) - .unwrap(); - ctx.new_txn().add(instruction).send_with_payer(); - println!("Set gas oracle for remote domain {:?}", args.remote_domain); - } - GetSetCmd::Get(_) => { - let igp_account = ctx - .client - .get_account_with_commitment(&core_program_ids.igp_account, ctx.commitment) - .unwrap() - .value - .expect( - "IGP account not found. Make sure you are connected to the right RPC.", - ); - - let igp_account = IgpAccount::fetch(&mut &igp_account.data[..]) - .unwrap() - .into_inner(); - - println!( - "IGP account gas oracle: {:#?}", - igp_account.gas_oracles.get(&args.remote_domain) - ); - } - } - } - IgpSubCmd::DestinationGasOverhead(args) => { - let core_program_ids = - read_core_program_ids(&args.environments_dir, &args.environment, &args.chain_name); - match args.cmd { - GasOverheadSubCmd::Get => { - // Read the gas overhead config - let overhead_igp_account = ctx - .client - .get_account_with_commitment( - &core_program_ids.overhead_igp_account, - ctx.commitment, - ) - .unwrap() - .value - .expect("Overhead IGP account not found. Make sure you are connected to the right RPC."); - let overhead_igp_account = - OverheadIgpAccount::fetch(&mut &overhead_igp_account.data[..]) - .unwrap() - .into_inner(); - println!( - "Overhead IGP account gas oracle: {:#?}", - overhead_igp_account.gas_overheads.get(&args.remote_domain) - ); - } - GasOverheadSubCmd::Set(set_args) => { - let overhead_config = GasOverheadConfig { - destination_domain: args.remote_domain, - gas_overhead: Some(set_args.gas_overhead), - }; - // Set the gas overhead config - let instruction = - hyperlane_sealevel_igp::instruction::set_destination_gas_overheads( - core_program_ids.igp_program_id, - core_program_ids.overhead_igp_account, - ctx.payer_pubkey, - vec![overhead_config], - ) - .unwrap(); - ctx.new_txn().add(instruction).send_with_payer(); - println!( - "Set gas overheads for remote domain {:?}", - args.remote_domain - ) - } - } - } - IgpSubCmd::TransferIgpOwnership(ref transfer_ownership) - | IgpSubCmd::TransferOverheadIgpOwnership(ref transfer_ownership) => { - let igp_account_type = match cmd.cmd { - IgpSubCmd::TransferIgpOwnership(_) => { - InterchainGasPaymasterType::Igp(transfer_ownership.igp_account) - } - IgpSubCmd::TransferOverheadIgpOwnership(_) => { - InterchainGasPaymasterType::OverheadIgp(transfer_ownership.igp_account) - } - _ => unreachable!(), - }; - let instruction = - hyperlane_sealevel_igp::instruction::transfer_igp_account_ownership_instruction( - transfer_ownership.program_id, - igp_account_type.clone(), - ctx.payer_pubkey, - Some(transfer_ownership.new_owner), - ) - .unwrap(); - ctx.new_txn() - .add_with_description( - instruction, - format!( - "Transfer ownership of {:?} to {}", - igp_account_type, transfer_ownership.new_owner - ), - ) - .send_with_payer(); - } - } -} diff --git a/rust/sealevel/client/src/multisig_ism.rs b/rust/sealevel/client/src/multisig_ism.rs index d5676b2815..e354f39bb8 100644 --- a/rust/sealevel/client/src/multisig_ism.rs +++ b/rust/sealevel/client/src/multisig_ism.rs @@ -45,8 +45,10 @@ impl From for ValidatorsAndThreshold { pub(crate) fn process_multisig_ism_message_id_cmd(mut ctx: Context, cmd: MultisigIsmMessageIdCmd) { match cmd.cmd { MultisigIsmMessageIdSubCmd::Deploy(deploy) => { - let environments_dir = - create_new_directory(&deploy.environments_dir, &deploy.environment); + let environments_dir = create_new_directory( + &deploy.env_args.environments_dir, + &deploy.env_args.environment, + ); let ism_dir = create_new_directory(&environments_dir, "multisig-ism-message-id"); let chain_dir = create_new_directory(&ism_dir, &deploy.chain); let context_dir = create_new_directory(&chain_dir, &deploy.context); diff --git a/rust/sealevel/client/src/router.rs b/rust/sealevel/client/src/router.rs index 4eaf4cae78..ae20abc5bd 100644 --- a/rust/sealevel/client/src/router.rs +++ b/rust/sealevel/client/src/router.rs @@ -268,6 +268,21 @@ pub(crate) trait ConnectionClient: Ownable { program_id: &Pubkey, ism: Option, ) -> Instruction; + + /// Gets the IGP configured on-chain. + fn get_interchain_gas_paymaster( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option<(Pubkey, InterchainGasPaymasterType)>; + + /// Gets an instruction to set the IGP. + fn set_interchain_gas_paymaster_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + igp_config: Option<(Pubkey, InterchainGasPaymasterType)>, + ) -> Option; } /// Idempotently deploys routers on multiple Sealevel chains and enrolls all routers (including @@ -426,8 +441,6 @@ fn configure_connection_client( router_config: &RouterConfig, chain_config: &ChainMetadata, ) { - // Just ISM for now - let client = chain_config.client(); let actual_ism = deployer.get_interchain_security_module(&client, program_id); @@ -451,6 +464,35 @@ fn configure_connection_client( .with_client(&client) .send_with_payer(); } + + let actual_igp = deployer.get_interchain_gas_paymaster(&client, program_id); + let expected_igp = router_config + .connection_client + .interchain_gas_paymaster_config(&client); + + if actual_igp != expected_igp { + let instruction = deployer.set_interchain_gas_paymaster_instruction( + &client, + program_id, + expected_igp.clone(), + ); + if let Some(instruction) = instruction { + ctx.new_txn() + .add_with_description( + instruction, + format!( + "Setting IGP for chain: {} ({}) to {:?}", + chain_config.name, + chain_config.domain_id(), + expected_igp + ), + ) + .with_client(&client) + .send_with_payer(); + } else { + println!("WARNING: Invalid configured IGP {:?}, expected {:?} for chain {} ({}), but cannot craft instruction to change it", actual_igp, expected_igp, chain_config.name, chain_config.domain_id()); + } + } } // Idempotent. @@ -541,17 +583,18 @@ fn enroll_all_remote_routers< .collect::>(); if !router_configs.is_empty() { - println!( - "Enrolling routers for chain: {}, program_id {}, routers: {:?}", - chain_name, program_id, router_configs, - ); - ctx.new_txn() - .add(deployer.enroll_remote_routers_instruction( - program_id, - ctx.payer_pubkey, - router_configs, - )) + .add_with_description( + deployer.enroll_remote_routers_instruction( + program_id, + ctx.payer_pubkey, + router_configs.clone(), + ), + format!( + "Enrolling routers for chain: {}, program_id {}, routers: {:?}", + chain_name, program_id, router_configs, + ), + ) .with_client(&chain_config.client()) .send_with_payer(); } else { diff --git a/rust/sealevel/client/src/serde.rs b/rust/sealevel/client/src/serde.rs index b4c412f098..b73a25bd59 100644 --- a/rust/sealevel/client/src/serde.rs +++ b/rust/sealevel/client/src/serde.rs @@ -24,7 +24,7 @@ pub(crate) mod serde_pubkey { } } -/// For serializing and deserializing `Option` +/// For serializing and deserializing Option pub(crate) mod serde_option_pubkey { use borsh::BorshDeserialize; use serde::{Deserialize, Deserializer, Serializer}; diff --git a/rust/sealevel/client/src/warp_route.rs b/rust/sealevel/client/src/warp_route.rs index 5e67fcf379..28ef3766ca 100644 --- a/rust/sealevel/client/src/warp_route.rs +++ b/rust/sealevel/client/src/warp_route.rs @@ -20,7 +20,7 @@ use hyperlane_sealevel_token_lib::{ accounts::{HyperlaneToken, HyperlaneTokenAccount}, hyperlane_token_pda_seeds, instruction::{ - enroll_remote_routers_instruction, set_destination_gas_configs, + enroll_remote_routers_instruction, set_destination_gas_configs, set_igp_instruction, set_interchain_security_module_instruction, transfer_ownership_instruction, Init, }, }; @@ -125,8 +125,8 @@ pub(crate) fn process_warp_route_cmd(mut ctx: Context, cmd: WarpRouteCmd) { &deploy.warp_route_name, deploy.token_config_file, deploy.chain_config_file, - deploy.environments_dir, - &deploy.environment, + deploy.env_args.environments_dir, + &deploy.env_args.environment, deploy.built_so_dir, ); } @@ -206,7 +206,7 @@ impl RouterDeployer for WarpRouteDeployer { let domain_id = chain_config.domain_id(); // TODO: consider pulling the setting of defaults into router.rs, - // and possibly have a more distinct connection client abstration. + // and possibly have a more distinct connection client abstraction. let mailbox = app_config .router_config() @@ -286,7 +286,7 @@ impl RouterDeployer for WarpRouteDeployer { collateral_info .spl_token_program .as_ref() - .expect("Cannot initalize collateral warp route without SPL token program") + .expect("Cannot initialize collateral warp route without SPL token program") .program_id(), collateral_info.mint.parse().expect("Invalid mint address"), ) @@ -434,6 +434,27 @@ impl ConnectionClient for WarpRouteDeployer { set_interchain_security_module_instruction(*program_id, token_data.owner.unwrap(), ism) .unwrap() } + + fn get_interchain_gas_paymaster( + &self, + client: &RpcClient, + program_id: &Pubkey, + ) -> Option<(Pubkey, InterchainGasPaymasterType)> { + let token_data = get_token_data::<()>(client, program_id); + + token_data.interchain_gas_paymaster + } + + fn set_interchain_gas_paymaster_instruction( + &self, + client: &RpcClient, + program_id: &Pubkey, + igp_config: Option<(Pubkey, InterchainGasPaymasterType)>, + ) -> Option { + let token_data = get_token_data::<()>(client, program_id); + + Some(set_igp_instruction(*program_id, token_data.owner.unwrap(), igp_config).unwrap()) + } } fn get_token_data(client: &RpcClient, program_id: &Pubkey) -> HyperlaneToken diff --git a/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json b/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json index ba62748efe..c5e945eae3 100644 --- a/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json +++ b/rust/sealevel/environments/local-e2e/warp-routes/testwarproute/program-ids.json @@ -1,10 +1,10 @@ { - "sealeveltest1": { - "hex": "0xa77b4e2ed231894cc8cb8eee21adcc705d8489bccc6b2fcf40a358de23e60b7b", - "base58": "CGn8yNtSD3aTTqJfYhUb6s1aVTN75NzwtsFKo1e83aga" - }, "sealeveltest2": { "hex": "0x2317f9615d4ebc2419ad4b88580e2a80a03b2c7a60bc960de7d6934dbc37a87e", "base58": "3MzUPjP5LEkiHH82nEAe28Xtz9ztuMqWc8UmuKxrpVQH" + }, + "sealeveltest1": { + "hex": "0xa77b4e2ed231894cc8cb8eee21adcc705d8489bccc6b2fcf40a358de23e60b7b", + "base58": "CGn8yNtSD3aTTqJfYhUb6s1aVTN75NzwtsFKo1e83aga" } } \ No newline at end of file diff --git a/rust/sealevel/environments/testnet3/chain-config.json b/rust/sealevel/environments/testnet3/chain-config.json index ae5158a8b6..478287bf7a 100644 --- a/rust/sealevel/environments/testnet3/chain-config.json +++ b/rust/sealevel/environments/testnet3/chain-config.json @@ -139,43 +139,6 @@ }, "isTestnet": true }, - "goerli": { - "chainId": 5, - "domainId": 5, - "name": "goerli", - "protocol": "ethereum", - "displayName": "Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" - }, - { - "http": "https://rpc.ankr.com/eth_goerli" - }, - { - "http": "https://eth-goerli.public.blastapi.io" - } - ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://goerli.etherscan.io", - "apiUrl": "https://api-goerli.etherscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 2, - "estimateBlockTime": 13 - }, - "isTestnet": true - }, "moonbasealpha": { "chainId": 1287, "domainId": 1287, @@ -208,70 +171,6 @@ }, "isTestnet": true }, - "optimismgoerli": { - "chainId": 420, - "domainId": 420, - "name": "optimismgoerli", - "protocol": "ethereum", - "displayName": "Optimism Goerli", - "displayNameShort": "Opt. Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://goerli.optimism.io" - } - ], - "blockExplorers": [ - { - "name": "Etherscan", - "url": "https://goerli-optimism.etherscan.io", - "apiUrl": "https://api-goerli-optimism.etherscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 - }, - "isTestnet": true - }, - "arbitrumgoerli": { - "chainId": 421613, - "domainId": 421613, - "name": "arbitrumgoerli", - "protocol": "ethereum", - "displayName": "Arbitrum Goerli", - "displayNameShort": "Arb. Goerli", - "nativeToken": { - "name": "Ether", - "symbol": "ETH", - "decimals": 18 - }, - "rpcUrls": [ - { - "http": "https://goerli-rollup.arbitrum.io/rpc" - } - ], - "blockExplorers": [ - { - "name": "Arbiscan", - "url": "https://goerli.arbiscan.io", - "apiUrl": "https://api-goerli.arbiscan.io/api", - "family": "etherscan" - } - ], - "blocks": { - "confirmations": 1, - "reorgPeriod": 1, - "estimateBlockTime": 3 - }, - "isTestnet": true - }, "sepolia": { "chainId": 11155111, "domainId": 11155111, @@ -332,4 +231,4 @@ } ] } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json index a03eae987f..4432348c78 100644 --- a/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json +++ b/rust/sealevel/environments/testnet3/helloworld/hyperlane/helloworld-config.json @@ -14,19 +14,10 @@ "bsctestnet": { "foreignDeployment": "0xE09BF59dCA6e622efC33f6fbd8EF85dE45233388" }, - "goerli": { - "foreignDeployment": "0x405BFdEcB33230b4Ad93C29ba4499b776CfBa189" - }, "moonbasealpha": { "foreignDeployment": "0x89e02C3C7b97bCBa63279E10E2a44e6cEF69E6B2" }, - "optimismgoerli": { - "foreignDeployment": "0x3582d1238cBC812165981E4fFaB0E8D9a4518910" - }, - "arbitrumgoerli": { - "foreignDeployment": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb" - }, "sepolia": { "foreignDeployment": "0x5d56B8a669F50193b54319442c6EEE5edD662381" } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json b/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json index 1eca03712d..066fd4b0b5 100644 --- a/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json +++ b/rust/sealevel/environments/testnet3/helloworld/hyperlane/program-ids.json @@ -3,10 +3,6 @@ "hex": "0x000000000000000000000000e09bf59dca6e622efc33f6fbd8ef85de45233388", "base58": "11111111111148VaL9DFuVc9DbDjRR7c3qyCEjyy" }, - "optimismgoerli": { - "hex": "0x0000000000000000000000003582d1238cbc812165981e4ffab0e8d9a4518910", - "base58": "111111111111kEreeMSXc3Nh2JoYtisyZj8pb6X" - }, "fuji": { "hex": "0x0000000000000000000000005da3b8d6f73df6003a490072106730218c475aad", "base58": "1111111111112JfXZf7EYaEMM1st6wFZbcLN2uwA" @@ -23,20 +19,12 @@ "hex": "0x000000000000000000000000477d860f8f41bc69ddd32821f2bf2c2af0243f16", "base58": "111111111111zmUjMVNXAe5bcqPR8cvaPz5SrQu" }, - "goerli": { - "hex": "0x000000000000000000000000405bfdecb33230b4ad93c29ba4499b776cfba189", - "base58": "111111111111u1H27LrKRuu1G7bDpPWUXKphQSt" - }, "sepolia": { "hex": "0x0000000000000000000000005d56b8a669f50193b54319442c6eee5edd662381", "base58": "1111111111112JRRxgtLh6eyMDsTHUehn6bJcPJ8" }, - "arbitrumgoerli": { - "hex": "0x000000000000000000000000339b46496d60b1b6b42e9715ded8b3d2154da0bb", - "base58": "111111111111ihbsGG5PRTKTSYSGewGtDFs2vfc" - }, "moonbasealpha": { "hex": "0x00000000000000000000000089e02c3c7b97bcba63279e10e2a44e6cef69e6b2", "base58": "1111111111112vQhuwgKwhQ7SM1HZEm6yXQkzCau" } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json b/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json index e2a072b56c..1655b4d149 100644 --- a/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json +++ b/rust/sealevel/environments/testnet3/helloworld/rc/helloworld-config.json @@ -14,19 +14,10 @@ "bsctestnet": { "foreignDeployment": "0xd259b0e793535325786675542aB296c451535c27" }, - "goerli": { - "foreignDeployment": "0x03e9531ae74e8F0f96DE26788a22d35bdaD24185" - }, "moonbasealpha": { "foreignDeployment": "0xE9D6317a10860340f035f3d09052D9d376855bE8" }, - "optimismgoerli": { - "foreignDeployment": "0x057d38d184d74192B96840D8FbB37e584dDb569A" - }, - "arbitrumgoerli": { - "foreignDeployment": "0xaAF1BF6f2BfaE290ea8615066fd167e396a2f578" - }, "sepolia": { "foreignDeployment": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217" } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json b/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json index 99f4db5461..aaad44915b 100644 --- a/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json +++ b/rust/sealevel/environments/testnet3/helloworld/rc/program-ids.json @@ -3,18 +3,10 @@ "hex": "0x0000000000000000000000006ad4deba8a147d000c09de6465267a9047d1c217", "base58": "1111111111112VKnX2KMsqSTDw9YoXRsZJTwTcUW" }, - "goerli": { - "hex": "0x00000000000000000000000003e9531ae74e8f0f96de26788a22d35bdad24185", - "base58": "1111111111114AKBbRbDjAP93LQgmXJPvfVU7SC" - }, "solanadevnet": { "hex": "0xbba2f483e642449d0a39efe5f9603f7c559423acebd3c854d07560ccd0439228", "base58": "DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7" }, - "optimismgoerli": { - "hex": "0x000000000000000000000000057d38d184d74192b96840d8fbb37e584ddb569a", - "base58": "1111111111115SFp65pWdvPTRK5fmHa3sc4Eq6Z" - }, "fuji": { "hex": "0x000000000000000000000000ac003fcdd0ee223664f2a000b5a59d082745700b", "base58": "1111111111113Pz2bmxxVNgkKkZPpxgouHiZAjTx" @@ -23,10 +15,6 @@ "hex": "0x000000000000000000000000e9d6317a10860340f035f3d09052d9d376855be8", "base58": "1111111111114Fx2onL6wvVgGmyjgzGhy48HzCZM" }, - "arbitrumgoerli": { - "hex": "0x000000000000000000000000aaf1bf6f2bfae290ea8615066fd167e396a2f578", - "base58": "1111111111113P8WPEsejkHP1Zysy1xXafVFFnaT" - }, "bsctestnet": { "hex": "0x000000000000000000000000d259b0e793535325786675542ab296c451535c27", "base58": "1111111111113vyKMMTb6aSQDhDLqEvqcPBcTtRC" @@ -39,4 +27,4 @@ "hex": "0x000000000000000000000000ab0892029c3e7dd4c0235590dc296e618a7b4d03", "base58": "1111111111113PCgiXuWFu2FmvhykJp51x5y5jyC" } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json index c758f40bc3..f28912dad0 100644 --- a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json +++ b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/hyperlane/multisig-config.json @@ -35,15 +35,6 @@ "0x95b76562e4ba1791a27ba4236801271c9115b141" ] }, - "goerli": { - "type": 3, - "threshold": 2, - "validators": [ - "0xf43fbd072fd38e1121d4b3b0b8a35116bbb01ea9", - "0xa33020552a21f35e75bd385c6ab95c3dfa82d930", - "0x0bba4043ff242f8bf3f39bafa8930a84d644d947" - ] - }, "sepolia": { "type": 3, "threshold": 2, @@ -62,24 +53,6 @@ "0xe70b85206a968a99a597581f0fa09c99e7681093" ] }, - "optimismgoerli": { - "type": 3, - "threshold": 2, - "validators": [ - "0xbb8d77eefbecc55db6e5a19b0fc3dc290776f189", - "0x69792508b4ddaa3ca52241ccfcd1e0b119a1ee65", - "0x11ddb46c6b653e0cdd7ad5bee32ae316e18f8453" - ] - }, - "arbitrumgoerli": { - "type": 3, - "threshold": 2, - "validators": [ - "0xce798fa21e323f6b24d9838a10ffecdefdfc4f30", - "0xa792d39dca4426927e0f00c1618d61c9cb41779d", - "0xdf181fcc11dfac5d01467e4547101a856dd5aa04" - ] - }, "proteustestnet": { "type": 3, "threshold": 2, @@ -89,4 +62,4 @@ "0xd4b2a50c53fc6614bb3cd3198e0fdc03f5da973f" ] } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json index 870829e4ab..04d6400dd9 100644 --- a/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json +++ b/rust/sealevel/environments/testnet3/multisig-ism-message-id/solanadevnet/rc/multisig-config.json @@ -2,64 +2,31 @@ "alfajores": { "type": 3, "threshold": 1, - "validators": [ - "0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de" - ] + "validators": ["0x45e5c228b38e1cf09e9a3423ed0cf4862c4bf3de"] }, "fuji": { "type": 3, "threshold": 1, - "validators": [ - "0xd81ba169170a9b582812cf0e152d2c168572e21f" - ] + "validators": ["0xd81ba169170a9b582812cf0e152d2c168572e21f"] }, "mumbai": { "type": 3, "threshold": 1, - "validators": [ - "0xb537c4ce34e1cad718be52aa30b095e416eae46a" - ] + "validators": ["0xb537c4ce34e1cad718be52aa30b095e416eae46a"] }, "bsctestnet": { "type": 3, "threshold": 1, - "validators": [ - "0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb" - ] - }, - "goerli": { - "type": 3, - "threshold": 1, - "validators": [ - "0x9597ddb4ad2af237665559574b820596bb77ae7a" - ] + "validators": ["0x77f80ef5b18977e15d81aea8dd3a88e7df4bc0eb"] }, "sepolia": { "type": 3, "threshold": 1, - "validators": [ - "0x183f15924f3a464c54c9393e8d268eb44d2b208c" - ] + "validators": ["0x183f15924f3a464c54c9393e8d268eb44d2b208c"] }, "moonbasealpha": { "type": 3, "threshold": 1, - "validators": [ - "0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d" - ] - }, - "optimismgoerli": { - "type": 3, - "threshold": 1, - "validators": [ - "0x1d6798671ac532f2bf30c3a5230697a4695705e4" - ] - }, - "arbitrumgoerli": { - "type": 3, - "threshold": 1, - "validators": [ - "0x6d13367c7cd713a4ea79a2552adf824bf1ecdd5e" - ] + "validators": ["0xbeaf158f85d7b64ced36b8aea0bbc4cd0f2d1a5d"] } -} \ No newline at end of file +} diff --git a/rust/sealevel/environments/testnet4/chain-config.json b/rust/sealevel/environments/testnet4/chain-config.json new file mode 100644 index 0000000000..7deb8b8920 --- /dev/null +++ b/rust/sealevel/environments/testnet4/chain-config.json @@ -0,0 +1,309 @@ +{ + "alfajores": { + "blockExplorers": [ + { + "apiUrl": "https://api-alfajores.celoscan.io/api", + "family": "etherscan", + "name": "CeloScan", + "url": "https://alfajores.celoscan.io" + }, + { + "apiUrl": "https://explorer.celo.org/alfajores/api", + "family": "blockscout", + "name": "Blockscout", + "url": "https://explorer.celo.org/alfajores" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 5, + "reorgPeriod": 0 + }, + "chainId": 44787, + "displayName": "Alfajores", + "domainId": 44787, + "isTestnet": true, + "name": "alfajores", + "nativeToken": { + "decimals": 18, + "name": "CELO", + "symbol": "CELO" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://alfajores-forno.celo-testnet.org" + } + ] + }, + "fuji": { + "blockExplorers": [ + { + "apiUrl": "https://api-testnet.snowtrace.io/api", + "family": "etherscan", + "name": "SnowTrace", + "url": "https://testnet.snowtrace.io" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 2, + "reorgPeriod": 3 + }, + "chainId": 43113, + "displayName": "Fuji", + "domainId": 43113, + "isTestnet": true, + "name": "fuji", + "nativeToken": { + "decimals": 18, + "name": "Avalanche", + "symbol": "AVAX" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://api.avax-test.network/ext/bc/C/rpc", + "pagination": { + "maxBlockRange": 2048 + } + } + ] + }, + "mumbai": { + "blockExplorers": [ + { + "apiUrl": "https://api-testnet.polygonscan.com/api", + "family": "etherscan", + "name": "PolygonScan", + "url": "https://mumbai.polygonscan.com" + } + ], + "blocks": { + "confirmations": 3, + "estimateBlockTime": 5, + "reorgPeriod": 32 + }, + "chainId": 80001, + "displayName": "Mumbai", + "domainId": 80001, + "isTestnet": true, + "name": "mumbai", + "nativeToken": { + "decimals": 18, + "name": "MATIC", + "symbol": "MATIC" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.ankr.com/polygon_mumbai", + "pagination": { + "maxBlockRange": 10000, + "minBlockNumber": 22900000 + } + } + ], + "transactionOverrides": { + "maxFeePerGas": 150000000000, + "maxPriorityFeePerGas": 40000000000 + } + }, + "bsctestnet": { + "blockExplorers": [ + { + "apiUrl": "https://api-testnet.bscscan.com/api", + "family": "etherscan", + "name": "BscScan", + "url": "https://testnet.bscscan.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 9 + }, + "chainId": 97, + "displayName": "BSC Testnet", + "domainId": 97, + "isTestnet": true, + "name": "bsctestnet", + "nativeToken": { + "decimals": 18, + "name": "BNB", + "symbol": "BNB" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://bsc-testnet.publicnode.com" + }, + { + "http": "https://bsc-testnet.blockpi.network/v1/rpc/public" + } + ], + "transactionOverrides": { + "gasPrice": 80000000000 + } + }, + "scrollsepolia": { + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.scrollscan.com/api", + "family": "etherscan", + "name": "Scroll Explorer", + "url": "https://sepolia.scrollscan.dev/" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, + "reorgPeriod": 1 + }, + "chainId": 534351, + "displayName": "Scroll Sepolia", + "domainId": 534351, + "isTestnet": true, + "name": "scrollsepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://sepolia-rpc.scroll.io" + } + ] + }, + "sepolia": { + "blockExplorers": [ + { + "apiUrl": "https://api-sepolia.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://sepolia.etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 13, + "reorgPeriod": 2 + }, + "chainId": 11155111, + "displayName": "Sepolia", + "domainId": 11155111, + "isTestnet": true, + "name": "sepolia", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://ethereum-sepolia.blockpi.network/v1/rpc/public" + }, + { + "http": "https://rpc.sepolia.org" + } + ] + }, + "moonbasealpha": { + "blockExplorers": [ + { + "apiUrl": "https://api-moonbase.moonscan.io/api", + "family": "etherscan", + "name": "MoonScan", + "url": "https://moonbase.moonscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 12, + "reorgPeriod": 1 + }, + "chainId": 1287, + "displayName": "Moonbase Alpha", + "displayNameShort": "Moonbase", + "domainId": 1287, + "isTestnet": true, + "name": "moonbasealpha", + "nativeToken": { + "decimals": 18, + "name": "DEV", + "symbol": "DEV" + }, + "protocol": "ethereum", + "rpcUrls": [ + { + "http": "https://rpc.api.moonbase.moonbeam.network" + } + ] + }, + "solanatestnet": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.solana.com", + "family": "other", + "name": "Solana Explorer", + "url": "https://explorer.solana.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.4, + "reorgPeriod": 0 + }, + "chainId": 1399811150, + "displayName": "Solana Testnet", + "displayNameShort": "Sol Testnet", + "domainId": 1399811150, + "isTestnet": true, + "name": "solanatestnet", + "nativeToken": { + "decimals": 9, + "name": "Sol", + "symbol": "SOL" + }, + "protocol": "sealevel", + "rpcUrls": [ + { + "http": "https://api.testnet.solana.com" + } + ] + }, + "eclipsetestnet": { + "blockExplorers": [ + { + "apiUrl": "https://testnet.dev2.eclipsenetwork.xyz", + "family": "other", + "name": "Eclipse Testnet Explorer", + "url": "https://explorer.dev.eclipsenetwork.xyz/?cluster=testnet" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 0.4, + "reorgPeriod": 0 + }, + "chainId": 239092742, + "displayName": "Eclipse Testnet", + "domainId": 239092742, + "isTestnet": true, + "name": "eclipsetestnet", + "nativeToken": { + "decimals": 9, + "name": "Ether", + "symbol": "ETH" + }, + "protocol": "sealevel", + "rpcUrls": [ + { + "http": "https://testnet.dev2.eclipsenetwork.xyz" + } + ] + } +} diff --git a/rust/sealevel/environments/testnet4/eclipsetestnet/core/program-ids.json b/rust/sealevel/environments/testnet4/eclipsetestnet/core/program-ids.json new file mode 100644 index 0000000000..095ae366cf --- /dev/null +++ b/rust/sealevel/environments/testnet4/eclipsetestnet/core/program-ids.json @@ -0,0 +1,8 @@ +{ + "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "validator_announce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "multisig_ism_message_id": "4GHxwWyKB9exhKG4fdyU2hfLgfFzhHp2WcsSKc2uNR1k", + "igp_program_id": "5p7Hii6CJL4xGBYYTGEQmH9LnUSZteFJUu9AVLDExZX2", + "overhead_igp_account": "hBHAApi5ZoeCYHqDdCKkCzVKmBdwywdT3hMqe327eZB", + "igp_account": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet4/gas-oracle-configs.json b/rust/sealevel/environments/testnet4/gas-oracle-configs.json new file mode 100644 index 0000000000..9e1cb73138 --- /dev/null +++ b/rust/sealevel/environments/testnet4/gas-oracle-configs.json @@ -0,0 +1,29 @@ +[ + { + "domain": 11155111, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "10000000000000000000", + "gasPrice": "15000000000", + "tokenDecimals": 18 + } + }, + { + "domain": 1399811150, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "10000000000000000000", + "gasPrice": "28", + "tokenDecimals": 9 + } + }, + { + "domain": 239092742, + "gasOracle": { + "type": "remoteGasData", + "tokenExchangeRate": "10000000000000000000", + "gasPrice": "28", + "tokenDecimals": 9 + } + } +] \ No newline at end of file diff --git a/rust/sealevel/environments/testnet4/igp/eclipsetestnet/default/igp-accounts.json b/rust/sealevel/environments/testnet4/igp/eclipsetestnet/default/igp-accounts.json new file mode 100644 index 0000000000..9d06cd936c --- /dev/null +++ b/rust/sealevel/environments/testnet4/igp/eclipsetestnet/default/igp-accounts.json @@ -0,0 +1,5 @@ +{ + "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", + "igp_account": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", + "overhead_igp_account": "hBHAApi5ZoeCYHqDdCKkCzVKmBdwywdT3hMqe327eZB" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet4/igp/solanatestnet/default/igp-accounts.json b/rust/sealevel/environments/testnet4/igp/solanatestnet/default/igp-accounts.json new file mode 100644 index 0000000000..9d06cd936c --- /dev/null +++ b/rust/sealevel/environments/testnet4/igp/solanatestnet/default/igp-accounts.json @@ -0,0 +1,5 @@ +{ + "salt": "0x0000000000000000000000000000000000000000000000000000000000000000", + "igp_account": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy", + "overhead_igp_account": "hBHAApi5ZoeCYHqDdCKkCzVKmBdwywdT3hMqe327eZB" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet4/multisig-ism-message-id/eclipsetestnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet4/multisig-ism-message-id/eclipsetestnet/hyperlane/multisig-config.json new file mode 100644 index 0000000000..45f98f8524 --- /dev/null +++ b/rust/sealevel/environments/testnet4/multisig-ism-message-id/eclipsetestnet/hyperlane/multisig-config.json @@ -0,0 +1,70 @@ +{ + "alfajores": { + "threshold": 2, + "validators": [ + "0x2233a5ce12f814bd64c9cdd73410bb8693124d40", + "0xba279f965489d90f90490e3c49e860e0b43c2ae6", + "0x86485dcec5f7bb8478dd251676372d054dea6653" + ], + "type": 3 + }, + "bsctestnet": { + "threshold": 2, + "validators": [ + "0x242d8a855a8c932dec51f7999ae7d1e48b10c95e", + "0xf620f5e3d25a3ae848fec74bccae5de3edcd8796", + "0x1f030345963c54ff8229720dd3a711c15c554aeb" + ], + "type": 3 + }, + "fuji": { + "threshold": 2, + "validators": [ + "0xd8154f73d04cc7f7f0c332793692e6e6f6b2402e", + "0x895ae30bc83ff1493b9cf7781b0b813d23659857", + "0x43e915573d9f1383cbf482049e4a012290759e7f" + ], + "type": 3 + }, + "moonbasealpha": { + "threshold": 2, + "validators": [ + "0x521877064bd7ac7500d300f162c8c47c256a2f9c", + "0xbc1c70f58ae0459d4b8a013245420a893837d568", + "0x01e42c2c44af81dda1ac16fec76fea2a7a54a44c" + ], + "type": 3 + }, + "mumbai": { + "threshold": 2, + "validators": [ + "0xebc301013b6cd2548e347c28d2dc43ec20c068f2", + "0x315db9868fc8813b221b1694f8760ece39f45447", + "0x17517c98358c5937c5d9ee47ce1f5b4c2b7fc9f5" + ], + "type": 3 + }, + "scrollsepolia": { + "threshold": 2, + "validators": [ + "0xbe18dbd758afb367180260b524e6d4bcd1cb6d05", + "0x9a11ed23ae962974018ab45bc133caabff7b3271", + "0x7867bea3c9761fe64e6d124b171f91fd5dd79644" + ], + "type": 3 + }, + "sepolia": { + "threshold": 2, + "validators": [ + "0xb22b65f202558adf86a8bb2847b76ae1036686a5", + "0x469f0940684d147defc44f3647146cb90dd0bc8e", + "0xd3c75dcf15056012a4d74c483a0c6ea11d8c2b83" + ], + "type": 3 + }, + "solanatestnet": { + "threshold": 1, + "validators": ["0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5"], + "type": 3 + } +} diff --git a/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json b/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json new file mode 100644 index 0000000000..727a68d059 --- /dev/null +++ b/rust/sealevel/environments/testnet4/multisig-ism-message-id/solanatestnet/hyperlane/multisig-config.json @@ -0,0 +1,70 @@ +{ + "alfajores": { + "threshold": 2, + "validators": [ + "0x2233a5ce12f814bd64c9cdd73410bb8693124d40", + "0xba279f965489d90f90490e3c49e860e0b43c2ae6", + "0x86485dcec5f7bb8478dd251676372d054dea6653" + ], + "type": 3 + }, + "bsctestnet": { + "threshold": 2, + "validators": [ + "0x242d8a855a8c932dec51f7999ae7d1e48b10c95e", + "0xf620f5e3d25a3ae848fec74bccae5de3edcd8796", + "0x1f030345963c54ff8229720dd3a711c15c554aeb" + ], + "type": 3 + }, + "fuji": { + "threshold": 2, + "validators": [ + "0xd8154f73d04cc7f7f0c332793692e6e6f6b2402e", + "0x895ae30bc83ff1493b9cf7781b0b813d23659857", + "0x43e915573d9f1383cbf482049e4a012290759e7f" + ], + "type": 3 + }, + "moonbasealpha": { + "threshold": 2, + "validators": [ + "0x521877064bd7ac7500d300f162c8c47c256a2f9c", + "0xbc1c70f58ae0459d4b8a013245420a893837d568", + "0x01e42c2c44af81dda1ac16fec76fea2a7a54a44c" + ], + "type": 3 + }, + "mumbai": { + "threshold": 2, + "validators": [ + "0xebc301013b6cd2548e347c28d2dc43ec20c068f2", + "0x315db9868fc8813b221b1694f8760ece39f45447", + "0x17517c98358c5937c5d9ee47ce1f5b4c2b7fc9f5" + ], + "type": 3 + }, + "scrollsepolia": { + "threshold": 2, + "validators": [ + "0xbe18dbd758afb367180260b524e6d4bcd1cb6d05", + "0x9a11ed23ae962974018ab45bc133caabff7b3271", + "0x7867bea3c9761fe64e6d124b171f91fd5dd79644" + ], + "type": 3 + }, + "sepolia": { + "threshold": 2, + "validators": [ + "0xb22b65f202558adf86a8bb2847b76ae1036686a5", + "0x469f0940684d147defc44f3647146cb90dd0bc8e", + "0xd3c75dcf15056012a4d74c483a0c6ea11d8c2b83" + ], + "type": 3 + }, + "eclipsetestnet": { + "threshold": 1, + "validators": ["0xf344f34abca9a444545b5295066348a0ae22dda3"], + "type": 3 + } +} diff --git a/rust/sealevel/environments/testnet4/solanatestnet/core/program-ids.json b/rust/sealevel/environments/testnet4/solanatestnet/core/program-ids.json new file mode 100644 index 0000000000..095ae366cf --- /dev/null +++ b/rust/sealevel/environments/testnet4/solanatestnet/core/program-ids.json @@ -0,0 +1,8 @@ +{ + "mailbox": "75HBBLae3ddeneJVrZeyrDfv6vb7SMC3aCpBucSXS5aR", + "validator_announce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "multisig_ism_message_id": "4GHxwWyKB9exhKG4fdyU2hfLgfFzhHp2WcsSKc2uNR1k", + "igp_program_id": "5p7Hii6CJL4xGBYYTGEQmH9LnUSZteFJUu9AVLDExZX2", + "overhead_igp_account": "hBHAApi5ZoeCYHqDdCKkCzVKmBdwywdT3hMqe327eZB", + "igp_account": "9SQVtTNsbipdMzumhzi6X8GwojiSMwBfqAhS7FgyTcqy" +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet4/warp-routes/eclipsetestnetsol/program-ids.json b/rust/sealevel/environments/testnet4/warp-routes/eclipsetestnetsol/program-ids.json new file mode 100644 index 0000000000..2b614fb946 --- /dev/null +++ b/rust/sealevel/environments/testnet4/warp-routes/eclipsetestnetsol/program-ids.json @@ -0,0 +1,10 @@ +{ + "solanatestnet": { + "hex": "0xb5eb96475b2d58f7b9a9ff2f60f46cf5ac1be244e557574971885337522b59c9", + "base58": "DF99ZiHj8aN4ETbKmpMoMsRTSbm7gtL5j6sKKLC5mkTS" + }, + "eclipsetestnet": { + "hex": "0x6dcd836e4ed228f42f3b2d6e2c2723b431b8e8b0bc9dded26bdff16ffaf818bd", + "base58": "8PdCNrJqkDfSMjg9BvXrDr3Hooo7WZ6cN7Qn9mHL1GZr" + } +} \ No newline at end of file diff --git a/rust/sealevel/environments/testnet4/warp-routes/eclipsetestnetsol/token-config.json b/rust/sealevel/environments/testnet4/warp-routes/eclipsetestnetsol/token-config.json new file mode 100644 index 0000000000..f51cda6452 --- /dev/null +++ b/rust/sealevel/environments/testnet4/warp-routes/eclipsetestnetsol/token-config.json @@ -0,0 +1,14 @@ +{ + "solanatestnet": { + "type": "native", + "decimals": 9, + "interchainGasPaymaster": "hBHAApi5ZoeCYHqDdCKkCzVKmBdwywdT3hMqe327eZB" + }, + "eclipsetestnet": { + "type": "synthetic", + "decimals": 9, + "name": "Solana (solanatestnet)", + "symbol": "SOL", + "interchainGasPaymaster": "hBHAApi5ZoeCYHqDdCKkCzVKmBdwywdT3hMqe327eZB" + } +} diff --git a/rust/sealevel/libraries/test-utils/src/lib.rs b/rust/sealevel/libraries/test-utils/src/lib.rs index e518163b9c..48a15d2dc8 100644 --- a/rust/sealevel/libraries/test-utils/src/lib.rs +++ b/rust/sealevel/libraries/test-utils/src/lib.rs @@ -105,7 +105,7 @@ async fn initialize_test_ism( /// Simulates an instruction, and attempts to deserialize it into a T. /// If no return data at all was returned, returns Ok(None). -/// If some return data was returned but deserialization was unsuccesful, +/// If some return data was returned but deserialization was unsuccessful, /// an Err is returned. pub async fn simulate_instruction( banks_client: &mut BanksClient, diff --git a/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs b/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs index 9381d0af2e..a4fb519846 100644 --- a/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs +++ b/rust/sealevel/programs/hyperlane-sealevel-igp/src/instruction.rs @@ -347,3 +347,56 @@ pub fn transfer_igp_account_ownership_instruction( }; Ok(instruction) } + +/// Gets an instruction to claim funds from an IGP to the beneficiary. +pub fn claim_instruction( + program_id: Pubkey, + igp: Pubkey, + beneficiary: Pubkey, +) -> Result { + let ixn = Instruction::Claim; + + // Accounts: + // 0. `[executable]` The system program. + // 1. `[writeable]` The IGP. + // 2. `[writeable]` The IGP beneficiary. + let accounts = vec![ + AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new(igp, false), + AccountMeta::new(beneficiary, false), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.try_to_vec()?, + accounts, + }; + + Ok(instruction) +} + +/// Gets an instruction to claim funds from an IGP to the beneficiary. +pub fn set_beneficiary_instruction( + program_id: Pubkey, + igp: Pubkey, + igp_owner: Pubkey, + new_beneficiary: Pubkey, +) -> Result { + let ixn = Instruction::SetIgpBeneficiary(new_beneficiary); + + // Accounts: + // 0. `[]` The IGP. + // 1. `[signer]` The owner of the IGP account. + let accounts = vec![ + AccountMeta::new(igp, false), + AccountMeta::new(igp_owner, true), + ]; + + let instruction = SolanaInstruction { + program_id, + data: ixn.try_to_vec()?, + accounts, + }; + + Ok(instruction) +} diff --git a/rust/terraform/.gitignore b/rust/terraform/.gitignore new file mode 100644 index 0000000000..282617e472 --- /dev/null +++ b/rust/terraform/.gitignore @@ -0,0 +1,2 @@ +.terraform +.terraform.lock.* diff --git a/rust/terraform/README.md b/rust/terraform/README.md new file mode 100644 index 0000000000..67e1f7330c --- /dev/null +++ b/rust/terraform/README.md @@ -0,0 +1,7 @@ +# Terraform Module for Hyperlane Validator + +This Terraform module is designed to set up the necessary infrastructure for a Hyperlane validator on AWS. It automates the creation of resources such as ECS clusters, VPCs, subnets, and security groups required for running a validator node. + +> **Note:** This module is intended to be an example of running a validator for a core supported network. You may have to modify the validator module to support more advanced configurations. It is recommended to test thoroughly before using in a production environment. + +For more information, read the [Deploy with Terraform](https://hyp-v3-docs-git-feat-aws-agent-guide-abacus-works.vercel.app/docs/operate/deploy-with-terraform) documentation. diff --git a/rust/terraform/globals.tf b/rust/terraform/globals.tf new file mode 100644 index 0000000000..92e6549404 --- /dev/null +++ b/rust/terraform/globals.tf @@ -0,0 +1,100 @@ +provider "aws" { + region = var.aws_region # Set the AWS region for the provider +} + +resource "aws_ecs_cluster" "validator_cluster" { + name = "hyperlane-validator-cluster" # Name of the ECS cluster for the validator +} + +resource "aws_vpc" "validator_vpc" { + cidr_block = "10.0.0.0/16" # Define the IP range for the VPC + enable_dns_support = true # Enable DNS support in the VPC + enable_dns_hostnames = true # Enable DNS hostnames in the VPC +} + +data "aws_availability_zones" "available" {} # Fetch the list of available AZs + +resource "aws_subnet" "public_subnet" { + vpc_id = aws_vpc.validator_vpc.id # Associate with the VPC + cidr_block = "10.0.2.0/24" # Define the IP range for the public subnet + availability_zone = data.aws_availability_zones.available.names[0] # Use the first available AZ + map_public_ip_on_launch = true # Automatically assign public IP on instance launch +} + +resource "aws_subnet" "validator_subnet" { + vpc_id = aws_vpc.validator_vpc.id # Associate with the VPC + cidr_block = "10.0.1.0/24" # Define the IP range for the validator subnet + availability_zone = data.aws_availability_zones.available.names[0] # Use the first available AZ + map_public_ip_on_launch = false # Do not assign public IP on instance launch +} + +resource "aws_internet_gateway" "vpc_igw" { + vpc_id = aws_vpc.validator_vpc.id # Attach the internet gateway to the VPC +} + +resource "aws_eip" "nat_gateway_eip" { + domain = "vpc" # Allocate an Elastic IP in the VPC domain +} + +resource "aws_nat_gateway" "validator_nat_gateway" { + allocation_id = aws_eip.nat_gateway_eip.id # Associate the EIP with the NAT gateway + subnet_id = aws_subnet.public_subnet.id # Place the NAT gateway in the public subnet + depends_on = [aws_internet_gateway.vpc_igw] # Ensure IGW is created before the NAT gateway +} + +resource "aws_route_table" "public_route_table" { + vpc_id = aws_vpc.validator_vpc.id # Associate the route table with the VPC + + route { + cidr_block = "0.0.0.0/0" # Route all traffic + gateway_id = aws_internet_gateway.vpc_igw.id # Through the internet gateway + } +} + +resource "aws_route_table" "private_route_table" { + vpc_id = aws_vpc.validator_vpc.id # Associate the route table with the VPC + + route { + cidr_block = "0.0.0.0/0" # Route all traffic + nat_gateway_id = aws_nat_gateway.validator_nat_gateway.id # Through the NAT gateway + } +} + +resource "aws_route_table_association" "public_subnet_association" { + subnet_id = aws_subnet.public_subnet.id # Associate the public subnet + route_table_id = aws_route_table.public_route_table.id # With the public route table +} + +resource "aws_route_table_association" "private_subnet_association" { + subnet_id = aws_subnet.validator_subnet.id # Associate the validator subnet + route_table_id = aws_route_table.private_route_table.id # With the private route table +} + +resource "aws_security_group" "validator_sg" { + name = "validator-sg" # Name of the security group for the validator + vpc_id = aws_vpc.validator_vpc.id # Associate with the VPC + + # prometheus + ingress { + from_port = 9090 # Prometheus metrics port + to_port = 9090 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] # Allow traffic from any IP + } + + # efs mounting + ingress { + from_port = 2049 # NFS port for EFS + to_port = 2049 + protocol = "tcp" + cidr_blocks = [aws_subnet.validator_subnet.cidr_block] # Allow traffic from the validator subnet + } + + # all egress + egress { + from_port = 0 # Allow all outbound traffic + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] # To any IP + } +} diff --git a/rust/terraform/main.tf b/rust/terraform/main.tf new file mode 100644 index 0000000000..e67fe80b78 --- /dev/null +++ b/rust/terraform/main.tf @@ -0,0 +1,20 @@ +# Configure a Hyperlane Validator +# Replaces https://docs.hyperlane.xyz/docs/operate/validators/run-validators +module "your_validator_name" { + source = "./modules/validator" + + validator_name = "your-validator-name" + origin_chain_name = "originChainName" + + aws_region = var.aws_region + validator_cluster_id = aws_ecs_cluster.validator_cluster.id + validator_subnet_id = aws_subnet.validator_subnet.id + validator_sg_id = aws_security_group.validator_sg.id + validator_nat_gateway_id = aws_nat_gateway.validator_nat_gateway.id + + # Disabling the validator task allows you to set up all the required infrastructure + # without running the actual validator yet. This is useful when setting up a validator for + # the first time, so that you can find out the validator address and fund it before it + # performs the announcement transaction. + # validator_task_disabled = true +} diff --git a/rust/terraform/modules/efs/main.tf b/rust/terraform/modules/efs/main.tf new file mode 100644 index 0000000000..ba3c499e66 --- /dev/null +++ b/rust/terraform/modules/efs/main.tf @@ -0,0 +1,42 @@ +# This resource defines an EFS file system that acts as persistent storage for the validator. +# The `creation_token` is used to ensure idempotent creation of the file system. +resource "aws_efs_file_system" "validator_fs" { + creation_token = var.creation_token # Unique token to guarantee the idempotence of the resource + + # Tags are key-value pairs that help with the organization and identification of AWS resources. + tags = { + Name = var.creation_token # Name tag using the creation token for easy identification + } +} + +# The EFS access point serves as a custom entry point into the file system. +# It enforces the specified POSIX user and group, and the root directory settings. +resource "aws_efs_access_point" "validator_ap" { + file_system_id = aws_efs_file_system.validator_fs.id # Associates the access point with the file system + + # The POSIX user configuration sets the owner's user and group IDs for all file system requests. + posix_user { + gid = var.posix_user_gid # POSIX group ID + uid = var.posix_user_uid # POSIX user ID + } + + # The root directory configuration specifies the path and creation settings within the EFS. + root_directory { + path = var.root_directory_path # The path where the root directory is mounted + + # The creation info sets the ownership and permissions for the root directory upon creation. + creation_info { + owner_gid = var.posix_user_gid # Group ID of the directory owner + owner_uid = var.posix_user_uid # User ID of the directory owner + permissions = var.root_directory_permissions # Permissions for the root directory + } + } +} + +# This resource creates a mount target within a specific subnet, allowing EC2 instances to access the EFS file system. +# The mount target is secured by associating it with one or more security groups. +resource "aws_efs_mount_target" "validator_mt" { + file_system_id = aws_efs_file_system.validator_fs.id # Associates the mount target with the file system + subnet_id = var.subnet_id # The subnet ID where the mount target is placed + security_groups = var.security_group_ids # Security groups that define the access rules for the mount target +} diff --git a/rust/terraform/modules/efs/outputs.tf b/rust/terraform/modules/efs/outputs.tf new file mode 100644 index 0000000000..8fb002fd29 --- /dev/null +++ b/rust/terraform/modules/efs/outputs.tf @@ -0,0 +1,19 @@ +output "file_system_id" { + description = "The ID of the EFS file system" + value = aws_efs_file_system.validator_fs.id +} + +output "access_point_id" { + description = "The ID of the EFS access point" + value = aws_efs_access_point.validator_ap.id +} + +output "mount_target_id" { + description = "The ID of the EFS mount target" + value = aws_efs_mount_target.validator_mt.id +} + +output "access_point_arn" { + description = "The ARN of the EFS access point" + value = aws_efs_access_point.validator_ap.arn +} diff --git a/rust/terraform/modules/efs/variables.tf b/rust/terraform/modules/efs/variables.tf new file mode 100644 index 0000000000..43803193fb --- /dev/null +++ b/rust/terraform/modules/efs/variables.tf @@ -0,0 +1,38 @@ +variable "creation_token" { + description = "Unique string to ensure the idempotent creation of the file system" + type = string +} + +variable "subnet_id" { + description = "The ID of the subnet to create the mount target in" + type = string +} + +variable "security_group_ids" { + description = "A list of security group IDs to associate with the mount target" + type = list(string) +} + +variable "posix_user_gid" { + description = "The POSIX group ID for the EFS access point" + type = number + default = 1000 +} + +variable "posix_user_uid" { + description = "The POSIX user ID for the EFS access point" + type = number + default = 1000 +} + +variable "root_directory_path" { + description = "Path to the root directory on the EFS volume" + type = string + default = "/hyperlane_db" +} + +variable "root_directory_permissions" { + description = "Permissions to apply to the root directory on the EFS volume" + type = string + default = "700" +} diff --git a/rust/terraform/modules/iam_kms/main.tf b/rust/terraform/modules/iam_kms/main.tf new file mode 100644 index 0000000000..c6e49c2c12 --- /dev/null +++ b/rust/terraform/modules/iam_kms/main.tf @@ -0,0 +1,179 @@ +# Creates an IAM user for the validator to interact with AWS services +resource "aws_iam_user" "ecs_user" { + name = "${var.validator_name}-exec-user" # The name of the IAM user is derived from the validator's name +} + +# Creates a KMS key for the validator to sign transactions securely +resource "aws_kms_key" "validator_signer_key" { + description = "KMS Key for Hyperlane Validator Signing" + key_usage = "SIGN_VERIFY" # Specifies that the key is used for signing and verification + customer_master_key_spec = "ECC_SECG_P256K1" # Specifies the type of key to be used +} + +# Creates an alias for the KMS key to make it easier to reference +resource "aws_kms_alias" "validator_signer_key_alias" { + name = "alias/${var.validator_name}" # The alias name includes the validator's name for easy identification + target_key_id = aws_kms_key.validator_signer_key.key_id # Associates the alias with the created KMS key +} + +# Defines an IAM policy that grants permissions to use the KMS key for signing operations +resource "aws_iam_policy" "validator_user_kms_policy" { + name = "${var.validator_name}-user-kms-policy" + description = "Allow ECS tasks to use the KMS key for signing" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "kms:GetPublicKey", # Allows retrieval of the public key + "kms:Sign", # Allows signing operations + "kms:Verify" # Allows verification of signatures + ], + Resource = aws_kms_key.validator_signer_key.arn # Specifies the KMS key resource + } + ] + }) +} + +# Attaches the KMS policy to the IAM user, granting it the defined permissions +resource "aws_iam_user_policy_attachment" "validator_user_kms_policy_attachment" { + user = aws_iam_user.ecs_user.name # The IAM user to attach the policy to + policy_arn = aws_iam_policy.validator_user_kms_policy.arn # The ARN of the policy to attach +} + +# Generates an access key for the IAM user to authenticate with AWS services +resource "aws_iam_access_key" "ecs_user_key" { + user = aws_iam_user.ecs_user.name # The IAM user for which to create the access key +} + +# Stores the access key ID in SSM Parameter Store for secure retrieval +resource "aws_ssm_parameter" "key_id" { + name = "/ecs/${var.validator_name}/access-key-id" # The parameter name includes the validator's name + type = "String" # The type of the parameter is a simple string + value = aws_iam_access_key.ecs_user_key.id # The value is the access key ID +} + +# Stores the access key secret in SSM Parameter Store for secure retrieval +resource "aws_ssm_parameter" "key_secret" { + name = "/ecs/${var.validator_name}/secret-access-key" # The parameter name includes the validator's name + type = "String" # The type of the parameter is a simple string + value = aws_iam_access_key.ecs_user_key.secret # The value is the access key secret +} + +# Creates an IAM role for ECS tasks to assume during execution +resource "aws_iam_role" "ecs_execution_role" { + name = "${var.validator_name}-exec-role" # The name of the role includes the validator's name + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" # Specifies that ECS tasks can assume this role + } + } + ] + }) +} + +# Attaches the AmazonECSTaskExecutionRolePolicy to the ECS execution role +resource "aws_iam_role_policy_attachment" "ecs_execution_policy" { + role = aws_iam_role.ecs_execution_role.name # The ECS execution role to attach the policy to + policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" # The ARN of the Amazon-managed policy +} + +# Defines an IAM policy to allow ECS tasks to write logs to CloudWatch +resource "aws_iam_policy" "cloudwatch_logs_policy" { + name = "${var.validator_name}-cloudwatch-logs-policy" + description = "IAM policy for ECS tasks to interact with CloudWatch Logs" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = [ + "logs:CreateLogStream", # Allows creation of log streams + "logs:PutLogEvents" # Allows putting log events into log streams + ], + Resource = "arn:aws:logs:${var.aws_region}:*:log-group:/aws/ecs/${var.aws_log_group}:log-stream:*" # Specifies the log group resource + } + ] + }) +} + +# Attaches the CloudWatch logs policy to the ECS execution role +resource "aws_iam_role_policy_attachment" "cloudwatch_logs_policy_attachment" { + role = aws_iam_role.ecs_execution_role.name # The ECS execution role to attach the policy to + policy_arn = aws_iam_policy.cloudwatch_logs_policy.arn # The ARN of the CloudWatch logs policy +} + +# Defines an IAM policy to allow ECS tasks to read SSM parameters for access keys +resource "aws_iam_policy" "ssm_read_policy" { + name = "${var.validator_name}-ssm-read-policy" + description = "Allow ECS tasks to read parameters" + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = ["ssm:GetParameters"], # Allows retrieval of SSM parameters + Resource = [ + aws_ssm_parameter.key_id.arn, # The ARN of the access key ID parameter + aws_ssm_parameter.key_secret.arn # The ARN of the access key secret parameter + ] + } + ] + }) +} + +# Attaches the SSM read policy to the ECS execution role +resource "aws_iam_role_policy_attachment" "ssm_read_policy_execution_attachment" { + role = aws_iam_role.ecs_execution_role.name # The ECS execution role to attach the policy to + policy_arn = aws_iam_policy.ssm_read_policy.arn # The ARN of the SSM read policy +} + +# Creates an IAM role for ECS tasks to perform specific actions +resource "aws_iam_role" "ecs_task_role" { + name = "${var.validator_name}-task-role" # The name of the task role includes the validator's name + + assume_role_policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Action = "sts:AssumeRole", + Effect = "Allow", + Principal = { + Service = "ecs-tasks.amazonaws.com" # Specifies that ECS tasks can assume this role + } + } + ] + }) +} + +# Defines an IAM policy to allow ECS tasks to perform actions on the EFS file system +resource "aws_iam_policy" "ecs_task_policy" { + name = "${var.validator_name}-task-policy" # The name of the policy includes the validator's name + + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Action = "elasticfilesystem:*", # Allows all actions on the EFS file system + Resource = var.efs_access_point_arn # Specifies the EFS access point resource + } + ] + }) +} + +# Attaches the EFS policy to the ECS task role +resource "aws_iam_role_policy_attachment" "ecs_task_policy_attachment" { + role = aws_iam_role.ecs_task_role.name # The ECS task role to attach the policy to + policy_arn = aws_iam_policy.ecs_task_policy.arn # The ARN of the EFS policy +} diff --git a/rust/terraform/modules/iam_kms/outputs.tf b/rust/terraform/modules/iam_kms/outputs.tf new file mode 100644 index 0000000000..81b18a625c --- /dev/null +++ b/rust/terraform/modules/iam_kms/outputs.tf @@ -0,0 +1,35 @@ +output "ecs_user_arn" { + value = aws_iam_user.ecs_user.arn +} + +output "ecs_user_access_key_id_arn" { + value = aws_ssm_parameter.key_id.arn +} + +output "ecs_user_secret_access_key_arn" { + value = aws_ssm_parameter.key_secret.arn +} + +output "validator_signer_key_arn" { + value = aws_kms_key.validator_signer_key.arn +} + +output "validator_signer_key_alias" { + value = aws_kms_alias.validator_signer_key_alias.name +} + +output "validator_execution_role_arn" { + value = aws_iam_role.ecs_execution_role.arn +} + +output "validator_task_role_arn" { + value = aws_iam_role.ecs_task_role.arn +} + +output "aws_access_key_id" { + value = aws_iam_access_key.ecs_user_key.id +} + +output "aws_secret_access_key" { + value = aws_iam_access_key.ecs_user_key.secret +} diff --git a/rust/terraform/modules/iam_kms/variables.tf b/rust/terraform/modules/iam_kms/variables.tf new file mode 100644 index 0000000000..7f6568d42f --- /dev/null +++ b/rust/terraform/modules/iam_kms/variables.tf @@ -0,0 +1,19 @@ +variable "aws_region" { + description = "AWS region" + type = string +} + +variable "validator_name" { + description = "The name of the validator" + type = string +} + +variable "aws_log_group" { + description = "The name of the log group to write to" + type = string +} + +variable "efs_access_point_arn" { + description = "The ARN of the EFS access point" + type = string +} diff --git a/rust/terraform/modules/s3/main.tf b/rust/terraform/modules/s3/main.tf new file mode 100644 index 0000000000..784bea146a --- /dev/null +++ b/rust/terraform/modules/s3/main.tf @@ -0,0 +1,65 @@ +# This resource creates an S3 bucket used to store validator signatures. +# The `force_destroy` attribute is set to true to allow the bucket to be destroyed even if it contains objects. +resource "aws_s3_bucket" "validator_bucket" { + bucket = "${var.validator_name}-signatures" + force_destroy = true # Enables deletion of non-empty bucket during destroy operation +} + +# This resource applies a public access block configuration to the validator signatures bucket. +# It prevents public ACLs from being applied to the bucket and ignores any public ACLs already on the bucket. +resource "aws_s3_bucket_public_access_block" "validator_bucket_public_access_block" { + bucket = aws_s3_bucket.validator_bucket.id + + block_public_acls = true # Blocks public ACLs from being added to the bucket + ignore_public_acls = true # Ignores any public ACLs currently associated with the bucket + block_public_policy = false # Allows public bucket policies (not recommended for sensitive data) + restrict_public_buckets = false # Allows unrestricted public access to the bucket (not recommended for sensitive data) +} + +# This resource defines a bucket policy that allows public read access to the bucket and its objects. +# It also grants additional permissions to a specific IAM role to delete and put objects in the bucket. +resource "aws_s3_bucket_policy" "validator_bucket_policy" { + bucket = aws_s3_bucket.validator_bucket.id + policy = jsonencode({ + Version = "2012-10-17", + Statement = [ + { + Effect = "Allow", + Principal = "*", + Action = [ + "s3:GetObject", # Allows retrieval of objects from the bucket + "s3:ListBucket" # Allows listing of the objects within the bucket + ], + Resource = [ + "${aws_s3_bucket.validator_bucket.arn}", # Bucket ARN + "${aws_s3_bucket.validator_bucket.arn}/*" # All objects within the bucket + ] + }, + { + Effect = "Allow", + Principal = { + AWS = var.validator_iam_user_arn # IAM user ARN of validator + }, + Action = [ + "s3:PutObject", # Allows uploading of new objects to the bucket + "s3:GetObject", # Allows retrieval of objects from the bucket + "s3:ListBucket", # Allows listing of the objects within the bucket + "s3:DeleteObject", # Allows deletion of objects within the bucket + ], + Resource = [ + "${aws_s3_bucket.validator_bucket.arn}", # Bucket ARN + "${aws_s3_bucket.validator_bucket.arn}/*" # All objects within the bucket + ] + } + ] + }) +} + +# This resource enables versioning for the S3 bucket to keep multiple versions of an object in the same bucket. +# Versioning is useful for data retention and recovery, as it allows you to recover from unintended user actions and application failures. +resource "aws_s3_bucket_versioning" "validator_bucket_versioning" { + bucket = aws_s3_bucket.validator_bucket.id + versioning_configuration { + status = "Enabled" # Enables versioning for the specified bucket + } +} diff --git a/rust/terraform/modules/s3/outputs.tf b/rust/terraform/modules/s3/outputs.tf new file mode 100644 index 0000000000..b51bb31375 --- /dev/null +++ b/rust/terraform/modules/s3/outputs.tf @@ -0,0 +1,7 @@ +output "validator_bucket_id" { + value = aws_s3_bucket.validator_bucket.id +} + +output "validator_bucket_arn" { + value = aws_s3_bucket.validator_bucket.arn +} diff --git a/rust/terraform/modules/s3/variables.tf b/rust/terraform/modules/s3/variables.tf new file mode 100644 index 0000000000..8740623102 --- /dev/null +++ b/rust/terraform/modules/s3/variables.tf @@ -0,0 +1,9 @@ +variable "validator_name" { + description = "The name of the validator" + type = string +} + +variable "validator_iam_user_arn" { + description = "The ARN of the IAM user that will write to the S3 bucket" + type = string +} diff --git a/rust/terraform/modules/validator/main.tf b/rust/terraform/modules/validator/main.tf new file mode 100644 index 0000000000..4667de95c3 --- /dev/null +++ b/rust/terraform/modules/validator/main.tf @@ -0,0 +1,138 @@ +# Sets up roles, permissions and KMS key +# Replaces https://docs.hyperlane.xyz/docs/operate/set-up-agent-keys +module "iam_kms" { + source = "../iam_kms" + + aws_region = var.aws_region + aws_log_group = var.aws_log_group + validator_name = var.validator_name + efs_access_point_arn = module.efs.access_point_arn +} + +# Creates bucket for posting validator signatures +# Replaces https://docs.hyperlane.xyz/docs/operate/validators/validator-aws +module "s3" { + source = "../s3" + + validator_name = var.validator_name + validator_iam_user_arn = module.iam_kms.ecs_user_arn +} + +# Creates file system and mounting point for the validator task +module "efs" { + source = "../efs" + + creation_token = "${var.validator_name}-db-fs" + subnet_id = var.validator_subnet_id + security_group_ids = [var.validator_sg_id] +} + +# A template for running the validator task +resource "aws_ecs_task_definition" "validator" { + family = var.validator_name + network_mode = "awsvpc" + requires_compatibilities = ["FARGATE"] + cpu = var.validator_cpu + memory = var.validator_memory + execution_role_arn = module.iam_kms.validator_execution_role_arn + task_role_arn = module.iam_kms.validator_task_role_arn + + container_definitions = jsonencode([ + { + name = "validator", + image = "gcr.io/abacus-labs-dev/hyperlane-agent:${var.validator_image_version}", + user = "1000:1000", + secrets = [ + { + name = "AWS_ACCESS_KEY_ID", + valueFrom = module.iam_kms.ecs_user_access_key_id_arn + }, + { + name = "AWS_SECRET_ACCESS_KEY", + valueFrom = module.iam_kms.ecs_user_secret_access_key_arn + } + ], + mountPoints = [ + { + sourceVolume = "hyperlane_db", + containerPath = "/hyperlane_db" + }, + ], + portMappings = [ + { + containerPort = 9090, # Prometheus metrics port + hostPort = 9090 + } + ], + command = [ + "./validator", + "--db", + "/hyperlane_db", + "--originChainName", + var.origin_chain_name, + "--validator.type", + "aws", + "--validator.id", + module.iam_kms.validator_signer_key_alias, + "--chains.${var.origin_chain_name}.type", + "aws", + "--chains.${var.origin_chain_name}.id", + module.iam_kms.validator_signer_key_alias, + "--checkpointSyncer.type", + "s3", + "--checkpointSyncer.bucket", + module.s3.validator_bucket_id, + "--checkpointSyncer.region", + var.aws_region, + "--validator.region", + var.aws_region + ], + logConfiguration = { + logDriver = "awslogs", + options = { + "awslogs-group" = var.aws_log_group, + "awslogs-region" = var.aws_region, + "awslogs-stream-prefix" = "ecs" + } + } + } + ]) + + volume { + name = "hyperlane_db" + + efs_volume_configuration { + file_system_id = module.efs.file_system_id + transit_encryption = "ENABLED" + + authorization_config { + access_point_id = module.efs.access_point_id + iam = "ENABLED" + } + } + } +} + +# An ECS service for running the validator ECS task +resource "aws_ecs_service" "validator_service" { + name = var.validator_name + cluster = var.validator_cluster_id + task_definition = aws_ecs_task_definition.validator.arn + launch_type = "FARGATE" + + # avoid rolling deployments to not lock agent db + deployment_maximum_percent = 100 + deployment_minimum_healthy_percent = 0 + + network_configuration { + subnets = [var.validator_subnet_id] + security_groups = [var.validator_sg_id] + } + + desired_count = var.validator_task_disabled ? 0 : 1 + + # implicit dependency on nat gateway existing + tags = { + NatGatewayID = var.validator_nat_gateway_id + } +} diff --git a/rust/terraform/modules/validator/outputs.tf b/rust/terraform/modules/validator/outputs.tf new file mode 100644 index 0000000000..f13b9463c4 --- /dev/null +++ b/rust/terraform/modules/validator/outputs.tf @@ -0,0 +1,9 @@ +output "validator_info" { + value = { + aws_access_key_id = module.iam_kms.aws_access_key_id, + aws_secret_access_key = module.iam_kms.aws_secret_access_key, + aws_kms_alias = module.iam_kms.validator_signer_key_alias, + aws_s3_bucket_id = module.s3.validator_bucket_id, + aws_region = var.aws_region, + } +} diff --git a/rust/terraform/modules/validator/variables.tf b/rust/terraform/modules/validator/variables.tf new file mode 100644 index 0000000000..46b8fc51f8 --- /dev/null +++ b/rust/terraform/modules/validator/variables.tf @@ -0,0 +1,64 @@ +variable "aws_region" { + description = "AWS region" + type = string +} + +variable "validator_cluster_id" { + description = "ID of the validator cluster" + type = string +} + +variable "validator_subnet_id" { + description = "ID of the validator subnet" + type = string +} + +variable "validator_sg_id" { + description = "ID of the validator security group" + type = string +} + +variable "validator_nat_gateway_id" { + description = "ID of the validator NAT gateway" + type = string +} + +variable "validator_name" { + description = "The name of the validator" + type = string +} + +variable "origin_chain_name" { + description = "The origin chain of the validator" + type = string +} + +variable "validator_cpu" { + description = "CPU units used by the validator. Default 1 vCPU." + type = string + default = "1024" +} + +variable "validator_memory" { + description = "Memory units used by the validator. Default 6GB." + type = string + default = "6144" +} + +variable "aws_log_group" { + description = "The name of the log group to write to" + type = string + default = "DefaultLogGroup" +} + +variable "validator_image_version" { + description = "The name of the log group to write to" + type = string + default = "f44589e-20231130-114734" +} + +variable "validator_task_disabled" { + description = "Whether to run the validator in addition to auxiliary setup" + type = bool + default = false +} diff --git a/rust/terraform/outputs.tf b/rust/terraform/outputs.tf new file mode 100644 index 0000000000..a3f84a13cb --- /dev/null +++ b/rust/terraform/outputs.tf @@ -0,0 +1,4 @@ +output "your_validator_name" { + value = module.your_validator_name.validator_info + sensitive = true +} diff --git a/rust/terraform/variables.tf b/rust/terraform/variables.tf new file mode 100644 index 0000000000..3af94fb041 --- /dev/null +++ b/rust/terraform/variables.tf @@ -0,0 +1,5 @@ +variable "aws_region" { + description = "AWS region" + type = string + default = "us-east-1" +} diff --git a/rust/utils/abigen/src/lib.rs b/rust/utils/abigen/src/lib.rs index 2e8d5ca081..b4b7970fdc 100644 --- a/rust/utils/abigen/src/lib.rs +++ b/rust/utils/abigen/src/lib.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "fuels")] use fuels_code_gen::ProgramType; use std::collections::BTreeSet; use std::ffi::OsStr; diff --git a/rust/utils/run-locally/Cargo.toml b/rust/utils/run-locally/Cargo.toml index 03d771734e..bf57138f4e 100644 --- a/rust/utils/run-locally/Cargo.toml +++ b/rust/utils/run-locally/Cargo.toml @@ -29,7 +29,7 @@ ureq = { workspace = true, default-features = false } which.workspace = true macro_rules_attribute.workspace = true regex.workspace = true -hpl-interface.workspace = true +hyperlane-cosmwasm-interface.workspace = true cosmwasm-schema.workspace = true [features] diff --git a/rust/utils/run-locally/src/cosmos/crypto.rs b/rust/utils/run-locally/src/cosmos/crypto.rs index 9b336f4df7..75924df69a 100644 --- a/rust/utils/run-locally/src/cosmos/crypto.rs +++ b/rust/utils/run-locally/src/cosmos/crypto.rs @@ -24,7 +24,7 @@ pub fn pub_to_addr(pub_key: &[u8], prefix: &str) -> String { let sha_hash = sha256_digest(pub_key); let rip_hash = ripemd160_digest(sha_hash); - let addr = hpl_interface::types::bech32_encode(prefix, &rip_hash).unwrap(); + let addr = hyperlane_cosmwasm_interface::types::bech32_encode(prefix, &rip_hash).unwrap(); addr.to_string() } diff --git a/rust/utils/run-locally/src/cosmos/deploy.rs b/rust/utils/run-locally/src/cosmos/deploy.rs index 25a8d1dae8..4da016d865 100644 --- a/rust/utils/run-locally/src/cosmos/deploy.rs +++ b/rust/utils/run-locally/src/cosmos/deploy.rs @@ -1,5 +1,5 @@ use cosmwasm_schema::cw_serde; -use hpl_interface::{core, hook, igp, ism}; +use hyperlane_cosmwasm_interface::{core, hook, igp, ism}; use macro_rules_attribute::apply; use crate::utils::as_task; @@ -9,25 +9,24 @@ use super::{ types::{Codes, Deployments}, }; -#[cw_serde] -pub struct IsmMultisigInstantiateMsg { - pub owner: String, -} - #[cw_serde] pub struct TestMockMsgReceiverInstantiateMsg { pub hrp: String, } #[cw_serde] -pub struct IGPOracleInstantiateMsg { +struct IgpInstantiateMsg { + pub hrp: String, pub owner: String, + pub gas_token: String, + pub beneficiary: String, + pub default_gas_usage: String, // u128 doesnt work with cw_serde } #[cw_serde] pub struct EmptyMsg {} -const PREFIX: &str = "osmo"; +const BECH32_PREFIX: &str = "osmo"; #[apply(as_task)] pub fn deploy_cw_hyperlane( @@ -46,29 +45,19 @@ pub fn deploy_cw_hyperlane( codes.hpl_mailbox, core::mailbox::InstantiateMsg { owner: deployer_addr.to_string(), - hrp: PREFIX.to_string(), + hrp: BECH32_PREFIX.to_string(), domain, }, "hpl_mailbox", ); - // deploy igp set - #[cw_serde] - pub struct GasOracleInitMsg { - pub hrp: String, - pub owner: String, - pub gas_token: String, - pub beneficiary: String, - pub default_gas_usage: String, - } - let igp = cli.wasm_init( &endpoint, &deployer, Some(deployer_addr), codes.hpl_igp, - GasOracleInitMsg { - hrp: PREFIX.to_string(), + IgpInstantiateMsg { + hrp: BECH32_PREFIX.to_string(), owner: deployer_addr.clone(), gas_token: "uosmo".to_string(), beneficiary: deployer_addr.clone(), @@ -107,12 +96,25 @@ pub fn deploy_cw_hyperlane( &deployer, Some(deployer_addr), codes.hpl_ism_multisig, - IsmMultisigInstantiateMsg { + ism::multisig::InstantiateMsg { owner: deployer_addr.clone(), }, "hpl_ism_multisig", ); + // deploy pausable ism + let ism_pausable = cli.wasm_init( + &endpoint, + &deployer, + Some(deployer_addr), + codes.hpl_ism_pausable, + ism::pausable::InstantiateMsg { + owner: deployer_addr.clone(), + paused: false, + }, + "hpl_ism_pausable", + ); + // deploy ism - aggregation let ism_aggregate = cli.wasm_init( &endpoint, @@ -121,8 +123,8 @@ pub fn deploy_cw_hyperlane( codes.hpl_ism_aggregate, ism::aggregate::InstantiateMsg { owner: deployer_addr.clone(), - threshold: 1, - isms: vec![ism_multisig.clone()], + threshold: 2, + isms: vec![ism_multisig.clone(), ism_pausable.clone()], }, "hpl_ism_aggregate", ); @@ -134,7 +136,6 @@ pub fn deploy_cw_hyperlane( Some(deployer_addr), codes.hpl_hook_merkle, hook::merkle::InstantiateMsg { - owner: deployer_addr.clone(), mailbox: mailbox.to_string(), }, "hpl_hook_merkle", @@ -159,7 +160,7 @@ pub fn deploy_cw_hyperlane( Some(deployer_addr), codes.hpl_validator_announce, core::va::InstantiateMsg { - hrp: PREFIX.to_string(), + hrp: BECH32_PREFIX.to_string(), mailbox: mailbox.to_string(), }, "hpl_validator_announce", @@ -173,7 +174,7 @@ pub fn deploy_cw_hyperlane( Some(deployer_addr), codes.hpl_test_mock_msg_receiver, TestMockMsgReceiverInstantiateMsg { - hrp: PREFIX.to_string(), + hrp: BECH32_PREFIX.to_string(), }, "hpl_test_mock_msg_receiver", ); diff --git a/rust/utils/run-locally/src/cosmos/link.rs b/rust/utils/run-locally/src/cosmos/link.rs index ff3de059fa..ebf53bd36c 100644 --- a/rust/utils/run-locally/src/cosmos/link.rs +++ b/rust/utils/run-locally/src/cosmos/link.rs @@ -1,7 +1,7 @@ use std::path::Path; use cosmwasm_schema::cw_serde; -use hpl_interface::{ +use hyperlane_cosmwasm_interface::{ core, ism::{self}, }; @@ -69,18 +69,14 @@ pub struct MockQuoteDispatch { #[cw_serde] pub struct GeneralIsmValidatorMessage { - pub enroll_validator: EnrollValidatorMsg, + pub set_validators: SetValidatorsMsg, } #[cw_serde] -pub struct EnrollValidatorMsg { - pub set: EnrollValidatorSet, -} - -#[cw_serde] -pub struct EnrollValidatorSet { +pub struct SetValidatorsMsg { pub domain: u32, - pub validator: String, + pub threshold: u8, + pub validators: Vec, } fn link_network( @@ -105,7 +101,7 @@ fn link_network( let public_key = validator.priv_key.verifying_key().to_encoded_point(false); let public_key = public_key.as_bytes(); - let hash = hpl_interface::types::keccak256_hash(&public_key[1..]); + let hash = hyperlane_cosmwasm_interface::types::keccak256_hash(&public_key[1..]); let mut bytes = [0u8; 20]; bytes.copy_from_slice(&hash.as_slice()[12..]); @@ -115,24 +111,10 @@ fn link_network( linker, &network.deployments.ism_multisig, GeneralIsmValidatorMessage { - enroll_validator: EnrollValidatorMsg { - set: EnrollValidatorSet { - domain: target_domain, - validator: hex::encode(bytes).to_string(), - }, - }, - }, - vec![], - ); - - cli.wasm_execute( - &network.launch_resp.endpoint, - linker, - &network.deployments.ism_multisig, - ism::multisig::ExecuteMsg::SetThreshold { - set: ism::multisig::ThresholdSet { - domain: target_domain, + set_validators: SetValidatorsMsg { threshold: 1, + domain: target_domain, + validators: vec![hex::encode(bytes).to_string()], }, }, vec![], diff --git a/rust/utils/run-locally/src/cosmos/mod.rs b/rust/utils/run-locally/src/cosmos/mod.rs index a61df94166..ab14818295 100644 --- a/rust/utils/run-locally/src/cosmos/mod.rs +++ b/rust/utils/run-locally/src/cosmos/mod.rs @@ -5,8 +5,8 @@ use std::time::{Duration, Instant}; use std::{env, fs}; use cosmwasm_schema::cw_serde; -use hpl_interface::types::bech32_decode; use hyperlane_cosmos::RawCosmosAmount; +use hyperlane_cosmwasm_interface::types::bech32_decode; use macro_rules_attribute::apply; use maplit::hashmap; use tempfile::tempdir; @@ -57,8 +57,8 @@ fn default_keys<'a>() -> [(&'a str, &'a str); 6] { ] } -const CW_HYPERLANE_GIT: &str = "https://github.com/many-things/cw-hyperlane"; -const CW_HYPERLANE_VERSION: &str = "0.0.6-rc6"; +const CW_HYPERLANE_GIT: &str = "https://github.com/hyperlane-xyz/cosmwasm"; +const CW_HYPERLANE_VERSION: &str = "v0.0.6"; fn make_target() -> String { let os = if cfg!(target_os = "linux") { @@ -101,19 +101,22 @@ pub fn install_codes(dir: Option, local: bool) -> BTreeMap path map fs::read_dir(dir_path) @@ -567,7 +570,7 @@ fn termination_invariants_met( let expected_gas_payments = messages_expected; if gas_payments_scraped != expected_gas_payments { log!( - "Scraper has scraped {} gas payments, expected {}", + "Relayer has indexed {} gas payments, expected {}", gas_payments_scraped, expected_gas_payments ); diff --git a/rust/utils/run-locally/src/cosmos/types.rs b/rust/utils/run-locally/src/cosmos/types.rs index 7a15755655..120ed05afd 100644 --- a/rust/utils/run-locally/src/cosmos/types.rs +++ b/rust/utils/run-locally/src/cosmos/types.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeMap, path::PathBuf}; -use hpl_interface::types::bech32_decode; use hyperlane_cosmos::RawCosmosAmount; +use hyperlane_cosmwasm_interface::types::bech32_decode; use super::{cli::OsmosisCLI, CosmosNetwork}; @@ -44,6 +44,7 @@ pub struct Codes { pub hpl_igp_oracle: u64, pub hpl_ism_aggregate: u64, pub hpl_ism_multisig: u64, + pub hpl_ism_pausable: u64, pub hpl_ism_routing: u64, pub hpl_test_mock_ism: u64, pub hpl_test_mock_hook: u64, @@ -118,11 +119,12 @@ pub struct AgentConfig { pub protocol: String, pub chain_id: String, pub rpc_urls: Vec, - pub grpc_url: String, - pub prefix: String, + pub grpc_urls: Vec, + pub bech32_prefix: String, pub signer: AgentConfigSigner, pub index: AgentConfigIndex, pub gas_price: RawCosmosAmount, + pub contract_address_bytes: usize, } #[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] @@ -155,8 +157,16 @@ impl AgentConfig { network.launch_resp.endpoint.rpc_addr.replace("tcp://", "") ), }], - grpc_url: format!("http://{}", network.launch_resp.endpoint.grpc_addr), - prefix: "osmo".to_string(), + grpc_urls: vec![ + // The first url points to a nonexistent node, but is used for checking fallback provider logic + AgentUrl { + http: "localhost:1337".to_string(), + }, + AgentUrl { + http: format!("http://{}", network.launch_resp.endpoint.grpc_addr), + }, + ], + bech32_prefix: "osmo".to_string(), signer: AgentConfigSigner { typ: "cosmosKey".to_string(), key: format!("0x{}", hex::encode(validator.priv_key.to_bytes())), @@ -166,6 +176,7 @@ impl AgentConfig { denom: "uosmo".to_string(), amount: "0.05".to_string(), }, + contract_address_bytes: 32, index: AgentConfigIndex { from: 1, chunk: 100, diff --git a/solidity/.gitignore b/solidity/.gitignore index fe81fe2bfe..c3e1139887 100644 --- a/solidity/.gitignore +++ b/solidity/.gitignore @@ -13,3 +13,4 @@ out forge-cache docs flattened/ +buildArtifact.json diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index 283642d524..cc9557b3d7 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,44 @@ # @hyperlane-xyz/core +## 3.8.0 + +### Minor Changes + +- 9681df08d: Remove support for goerli networks (including optimismgoerli, arbitrumgoerli, lineagoerli and polygonzkevmtestnet) +- 9681df08d: Enabled verification of contracts as part of the deployment flow. + + - Solidity build artifact is now included as part of the `@hyperlane-xyz/core` package. + - Updated the `HyperlaneDeployer` to perform contract verification immediately after deploying a contract. A default verifier is instantiated using the core build artifact. + - Updated the `HyperlaneIsmFactory` to re-use the `HyperlaneDeployer` for deployment where possible. + - Minor logging improvements throughout deployers. + +### Patch Changes + +- Updated dependencies [9681df08d] + - @hyperlane-xyz/utils@3.8.0 + +## 3.7.0 + +### Patch Changes + +- @hyperlane-xyz/utils@3.7.0 + +## 3.6.2 + +### Patch Changes + +- @hyperlane-xyz/utils@3.6.2 + +## 3.6.1 + +### Patch Changes + +- e4e4f93fc: Support pausable ISM in deployer and checker +- Updated dependencies [3c298d064] +- Updated dependencies [df24eec8b] +- Updated dependencies [78e50e7da] + - @hyperlane-xyz/utils@3.6.1 + ## 3.6.0 ### Patch Changes diff --git a/solidity/contracts/middleware/InterchainAccountRouter.sol b/solidity/contracts/middleware/InterchainAccountRouter.sol index b6ae7bdd4e..dc949c1713 100644 --- a/solidity/contracts/middleware/InterchainAccountRouter.sol +++ b/solidity/contracts/middleware/InterchainAccountRouter.sol @@ -1,12 +1,25 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; +/*@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@ HYPERLANE @@@@@@@ + @@@@@@@@@@@@@@@@@@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ + @@@@@@@@@ @@@@@@@@@ +@@@@@@@@@ @@@@@@@@*/ + // ============ Internal Imports ============ import {OwnableMulticall} from "./libs/OwnableMulticall.sol"; import {InterchainAccountMessage} from "./libs/InterchainAccountMessage.sol"; import {CallLib} from "./libs/Call.sol"; import {MinimalProxy} from "../libs/MinimalProxy.sol"; import {TypeCasts} from "../libs/TypeCasts.sol"; +import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol"; import {EnumerableMapExtended} from "../libs/EnumerableMapExtended.sol"; import {Router} from "../client/Router.sol"; @@ -82,17 +95,17 @@ contract InterchainAccountRouter is Router { /** * @notice Initializes the contract with HyperlaneConnectionClient contracts - * @param _interchainGasPaymaster Unused but required by HyperlaneConnectionClient + * @param _customHook used by the Router to set the hook to override with * @param _interchainSecurityModule The address of the local ISM contract * @param _owner The address with owner privileges */ function initialize( - address _interchainGasPaymaster, + address _customHook, address _interchainSecurityModule, address _owner ) external initializer { _MailboxClient_initialize( - _interchainGasPaymaster, + _customHook, _interchainSecurityModule, _owner ); @@ -157,7 +170,7 @@ contract InterchainAccountRouter is Router { address _to, uint256 _value, bytes memory _data - ) external returns (bytes32) { + ) external payable returns (bytes32) { bytes32 _router = routers(_destination); bytes32 _ism = isms[_destination]; bytes memory _body = InterchainAccountMessage.encode( @@ -170,6 +183,44 @@ contract InterchainAccountRouter is Router { return _dispatchMessage(_destination, _router, _ism, _body); } + /** + * @notice Dispatches a single remote call to be made by an owner's + * interchain account on the destination domain + * @dev Uses the default router and ISM addresses for the destination + * domain, reverting if none have been configured + * @param _destination The remote domain of the chain to make calls on + * @param _to The address of the contract to call + * @param _value The value to include in the call + * @param _data The calldata + * @param _hookMetadata The hook metadata to override with for the hook set by the owner + * @return The Hyperlane message ID + */ + function callRemote( + uint32 _destination, + address _to, + uint256 _value, + bytes memory _data, + bytes memory _hookMetadata + ) external payable returns (bytes32) { + bytes32 _router = routers(_destination); + bytes32 _ism = isms[_destination]; + bytes memory _body = InterchainAccountMessage.encode( + msg.sender, + _ism, + _to, + _value, + _data + ); + return + _dispatchMessageWithMetadata( + _destination, + _router, + _ism, + _body, + _hookMetadata + ); + } + /** * @notice Dispatches a sequence of remote calls to be made by an owner's * interchain account on the destination domain @@ -183,10 +234,44 @@ contract InterchainAccountRouter is Router { function callRemote( uint32 _destination, CallLib.Call[] calldata _calls - ) external returns (bytes32) { + ) external payable returns (bytes32) { bytes32 _router = routers(_destination); bytes32 _ism = isms[_destination]; - return callRemoteWithOverrides(_destination, _router, _ism, _calls); + bytes memory _body = InterchainAccountMessage.encode( + msg.sender, + _ism, + _calls + ); + + return _dispatchMessage(_destination, _router, _ism, _body); + } + + /** + * @notice Dispatches a sequence of remote calls to be made by an owner's + * interchain account on the destination domain + * @dev Uses the default router and ISM addresses for the destination + * domain, reverting if none have been configured + * @dev Recommend using CallLib.build to format the interchain calls. + * @param _destination The remote domain of the chain to make calls on + * @param _calls The sequence of calls to make + * @param _hookMetadata The hook metadata to override with for the hook set by the owner + * @return The Hyperlane message ID + */ + function callRemote( + uint32 _destination, + CallLib.Call[] calldata _calls, + bytes calldata _hookMetadata + ) external payable returns (bytes32) { + bytes32 _router = routers(_destination); + bytes32 _ism = isms[_destination]; + return + callRemoteWithOverrides( + _destination, + _router, + _ism, + _calls, + _hookMetadata + ); } /** @@ -395,7 +480,7 @@ contract InterchainAccountRouter is Router { bytes32 _router, bytes32 _ism, CallLib.Call[] calldata _calls - ) public returns (bytes32) { + ) public payable returns (bytes32) { bytes memory _body = InterchainAccountMessage.encode( msg.sender, _ism, @@ -404,6 +489,39 @@ contract InterchainAccountRouter is Router { return _dispatchMessage(_destination, _router, _ism, _body); } + /** + * @notice Dispatches a sequence of remote calls to be made by an owner's + * interchain account on the destination domain + * @dev Recommend using CallLib.build to format the interchain calls + * @param _destination The remote domain of the chain to make calls on + * @param _router The remote router address + * @param _ism The remote ISM address + * @param _calls The sequence of calls to make + * @param _hookMetadata The hook metadata to override with for the hook set by the owner + * @return The Hyperlane message ID + */ + function callRemoteWithOverrides( + uint32 _destination, + bytes32 _router, + bytes32 _ism, + CallLib.Call[] calldata _calls, + bytes memory _hookMetadata + ) public payable returns (bytes32) { + bytes memory _body = InterchainAccountMessage.encode( + msg.sender, + _ism, + _calls + ); + return + _dispatchMessageWithMetadata( + _destination, + _router, + _ism, + _body, + _hookMetadata + ); + } + // ============ Internal Functions ============ /** @@ -474,7 +592,33 @@ contract InterchainAccountRouter is Router { ) private returns (bytes32) { require(_router != bytes32(0), "no router specified for destination"); emit RemoteCallDispatched(_destination, msg.sender, _router, _ism); - return mailbox.dispatch(_destination, _router, _body); + return mailbox.dispatch{value: msg.value}(_destination, _router, _body); + } + + /** + * @notice Dispatches an InterchainAccountMessage to the remote router with hook metadata + * @param _destination The remote domain + * @param _router The address of the remote InterchainAccountRouter + * @param _ism The address of the remote ISM + * @param _body The InterchainAccountMessage body + * @param _hookMetadata The hook metadata to override with for the hook set by the owner + */ + function _dispatchMessageWithMetadata( + uint32 _destination, + bytes32 _router, + bytes32 _ism, + bytes memory _body, + bytes memory _hookMetadata + ) private returns (bytes32) { + require(_router != bytes32(0), "no router specified for destination"); + emit RemoteCallDispatched(_destination, msg.sender, _router, _ism); + return + mailbox.dispatch{value: msg.value}( + _destination, + _router, + _body, + _hookMetadata + ); } /** diff --git a/solidity/contracts/mock/MockHyperlaneEnvironment.sol b/solidity/contracts/mock/MockHyperlaneEnvironment.sol index 9f51b61ad3..1664951da8 100644 --- a/solidity/contracts/mock/MockHyperlaneEnvironment.sol +++ b/solidity/contracts/mock/MockHyperlaneEnvironment.sol @@ -31,9 +31,6 @@ contract MockHyperlaneEnvironment { originMailbox.setDefaultIsm(address(isms[originDomain])); destinationMailbox.setDefaultIsm(address(isms[destinationDomain])); - igps[originDomain] = new TestInterchainGasPaymaster(); - igps[destinationDomain] = new TestInterchainGasPaymaster(); - originMailbox.transferOwnership(msg.sender); destinationMailbox.transferOwnership(msg.sender); diff --git a/solidity/contracts/test/TestInterchainGasPaymaster.sol b/solidity/contracts/test/TestInterchainGasPaymaster.sol index 08a5a0aa3f..33f1480296 100644 --- a/solidity/contracts/test/TestInterchainGasPaymaster.sol +++ b/solidity/contracts/test/TestInterchainGasPaymaster.sol @@ -17,4 +17,8 @@ contract TestInterchainGasPaymaster is InterchainGasPaymaster { ) public pure override returns (uint256) { return gasPrice * gasAmount; } + + function getDefaultGasUsage() public pure returns (uint256) { + return DEFAULT_GAS_USAGE; + } } diff --git a/solidity/contracts/token/FastHypERC20.sol b/solidity/contracts/token/FastHypERC20.sol deleted file mode 100644 index 9ec040809a..0000000000 --- a/solidity/contracts/token/FastHypERC20.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; - -import {HypERC20} from "./HypERC20.sol"; - -import {TokenRouter} from "./libs/TokenRouter.sol"; -import {FastTokenRouter} from "./libs/FastTokenRouter.sol"; -import {TokenMessage} from "./libs/TokenMessage.sol"; - -import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; - -/** - * @title Hyperlane ERC20 Token Router that extends ERC20 with remote transfer functionality. - * @author Abacus Works - * @dev Supply on each chain is not constant but the aggregate supply across all chains is. - */ -contract FastHypERC20 is FastTokenRouter, HypERC20 { - constructor( - uint8 __decimals, - address _mailbox - ) HypERC20(__decimals, _mailbox) {} - - /** - * @dev delegates transfer logic to `_transferTo`. - * @inheritdoc TokenRouter - */ - function _handle( - uint32 _origin, - bytes32 _sender, - bytes calldata _message - ) internal virtual override(FastTokenRouter, TokenRouter) { - FastTokenRouter._handle(_origin, _sender, _message); - } - - /** - * @dev Mints `_amount` of tokens to `_recipient`. - * @inheritdoc FastTokenRouter - */ - function _fastTransferTo( - address _recipient, - uint256 _amount - ) internal override { - _mint(_recipient, _amount); - } - - /** - * @dev Burns `_amount` of tokens from `_recipient`. - * @inheritdoc FastTokenRouter - */ - function _fastRecieveFrom( - address _sender, - uint256 _amount - ) internal override { - _burn(_sender, _amount); - } - - function balanceOf( - address _account - ) public view virtual override(TokenRouter, HypERC20) returns (uint256) { - return HypERC20.balanceOf(_account); - } -} diff --git a/solidity/contracts/token/FastHypERC20Collateral.sol b/solidity/contracts/token/FastHypERC20Collateral.sol deleted file mode 100644 index 434555c17a..0000000000 --- a/solidity/contracts/token/FastHypERC20Collateral.sol +++ /dev/null @@ -1,61 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity >=0.8.0; - -import {HypERC20Collateral} from "./HypERC20Collateral.sol"; -import {TokenRouter} from "./libs/TokenRouter.sol"; -import {FastTokenRouter} from "./libs/FastTokenRouter.sol"; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; - -/** - * @title Hyperlane ERC20 Token Collateral that wraps an existing ERC20 with remote transfer functionality. - * @author Abacus Works - */ -contract FastHypERC20Collateral is FastTokenRouter, HypERC20Collateral { - using SafeERC20 for IERC20; - - /** - * @notice Constructor - * @param erc20 Address of the token to keep as collateral - * @param _mailbox Address of the mailbox address - */ - constructor( - address erc20, - address _mailbox - ) HypERC20Collateral(erc20, _mailbox) {} - - /** - * @dev delegates transfer logic to `_transferTo`. - * @inheritdoc FastTokenRouter - */ - function _handle( - uint32 _origin, - bytes32 _sender, - bytes calldata _message - ) internal virtual override(FastTokenRouter, TokenRouter) { - FastTokenRouter._handle(_origin, _sender, _message); - } - - /** - * @dev Transfers `_amount` of `wrappedToken` to `_recipient`. - * @inheritdoc FastTokenRouter - */ - function _fastTransferTo( - address _recipient, - uint256 _amount - ) internal override { - wrappedToken.safeTransfer(_recipient, _amount); - } - - /** - * @dev Transfers in `_amount` of `wrappedToken` from `_recipient`. - * @inheritdoc FastTokenRouter - */ - function _fastRecieveFrom( - address _sender, - uint256 _amount - ) internal override { - wrappedToken.safeTransferFrom(_sender, address(this), _amount); - } -} diff --git a/solidity/exportBuildArtifact.sh b/solidity/exportBuildArtifact.sh new file mode 100755 index 0000000000..43a7848c35 --- /dev/null +++ b/solidity/exportBuildArtifact.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# set script location as working directory +cd "$(dirname "$0")" + +# Define the artifacts directory +artifactsDir="./artifacts/build-info" +# Define the output file +outputFile="./buildArtifact.json" + +# log that we're in the script +echo 'Finding and processing hardhat build artifact...' + +# Find most recently modified JSON build artifact +if [ "$(uname)" = "Darwin" ]; then + # for local flow + jsonFiles=$(find "$artifactsDir" -type f -name "*.json" -exec stat -f "%m %N" {} \; | sort -rn | head -n 1 | cut -d' ' -f2-) +else + # for CI flow + jsonFiles=$(find "$artifactsDir" -type f -name "*.json" -exec stat -c "%Y %n" {} \; | sort -rn | head -n 1 | cut -d' ' -f2-) +fi + +if [ ! -f "$jsonFiles" ]; then + echo 'Failed to find build artifact' + exit 1 +fi + +# Extract required keys and write to outputFile +if jq -c '{input, solcLongVersion}' "$jsonFiles" > "$outputFile"; then + echo 'Finished processing build artifact.' +else + echo 'Failed to process build artifact with jq' + exit 1 +fi diff --git a/solidity/flatten.sh b/solidity/flatten.sh deleted file mode 100755 index 21c6a09788..0000000000 --- a/solidity/flatten.sh +++ /dev/null @@ -1,23 +0,0 @@ -LICENSE="// SPDX-License-Identifier: MIT OR Apache-2.0" - -rm -rf flattened -mkdir -p flattened - -# flatten contracts -yarn hardhat flatten > flattened/flattened.sol - -# remove duplicate licenses -grep -vE "// SPDX.*" flattened/flattened.sol > flattened/delicensed.sol - -# add license -echo "$LICENSE" | cat - flattened/delicensed.sol > flattened/licensed.sol - -# compile -solc flattened/licensed.sol - -# TODO: automate this? -if [ $? -ne 0 ]; then - echo "Remove @openzeppelin/../ICrossDomainMessenger and replace Optimism_Bridge with ICrossDomainMessenger" - echo "Then try compiling again with solc flattened/licensed.sol" - exit 1 -fi diff --git a/solidity/foundry.toml b/solidity/foundry.toml index 4146b69468..75766fc7d7 100644 --- a/solidity/foundry.toml +++ b/solidity/foundry.toml @@ -14,4 +14,4 @@ verbosity = 4 [rpc_endpoints] mainnet = "https://eth.merkle.io" -optimism = "https://optimism.llamarpc.com" +optimism = "https://mainnet.optimism.io " diff --git a/solidity/package.json b/solidity/package.json index cdb6a29fc0..08d6f3d1bc 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,10 +1,10 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "3.6.0", + "version": "3.8.0", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "3.6.0", + "@hyperlane-xyz/utils": "3.8.0", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3" }, @@ -31,6 +31,7 @@ "test": "test" }, "files": [ + "/buildArtifact.json", "/dist", "/contracts", "/interfaces", @@ -45,12 +46,11 @@ "main": "dist/index.js", "repository": "https://github.com/hyperlane-xyz/hyperlane-monorepo", "scripts": { - "build": "hardhat compile && tsc", + "build": "hardhat compile && ./exportBuildArtifact.sh && tsc", "lint": "solhint contracts/**/*.sol", "clean": "hardhat clean && rm -rf ./dist ./cache ./types ./coverage", "coverage": "./coverage.sh", "docs": "forge doc", - "flatten": "./flatten.sh", "storage": "./storage.sh", "prettier": "prettier --write ./contracts ./test", "test": "hardhat test && forge test -vvv", diff --git a/solidity/test/InterchainAccountRouter.t.sol b/solidity/test/InterchainAccountRouter.t.sol index 1f52848963..a7008060aa 100644 --- a/solidity/test/InterchainAccountRouter.t.sol +++ b/solidity/test/InterchainAccountRouter.t.sol @@ -1,13 +1,16 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; +import {Test} from "forge-std/Test.sol"; import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "forge-std/Test.sol"; -import "../contracts/mock/MockMailbox.sol"; -import "../contracts/mock/MockHyperlaneEnvironment.sol"; + +import {StandardHookMetadata} from "../contracts/hooks/libs/StandardHookMetadata.sol"; +import {MockMailbox} from "../contracts/mock/MockMailbox.sol"; +import {MockHyperlaneEnvironment} from "../contracts/mock/MockHyperlaneEnvironment.sol"; import {TypeCasts} from "../contracts/libs/TypeCasts.sol"; import {IInterchainSecurityModule} from "../contracts/interfaces/IInterchainSecurityModule.sol"; -import {IInterchainGasPaymaster} from "../contracts/interfaces/IInterchainGasPaymaster.sol"; +import {TestInterchainGasPaymaster} from "../contracts/test/TestInterchainGasPaymaster.sol"; +import {IPostDispatchHook} from "../contracts/interfaces/hooks/IPostDispatchHook.sol"; import {CallLib, OwnableMulticall, InterchainAccountRouter} from "../contracts/middleware/InterchainAccountRouter.sol"; import {InterchainAccountIsm} from "../contracts/isms/routing/InterchainAccountIsm.sol"; @@ -45,29 +48,26 @@ contract InterchainAccountRouterTest is Test { address account ); - struct Bytes32Pair { - bytes32 a; - bytes32 b; - } - - MockHyperlaneEnvironment environment; + MockHyperlaneEnvironment internal environment; - uint32 origin = 1; - uint32 destination = 2; + uint32 internal origin = 1; + uint32 internal destination = 2; - InterchainAccountIsm icaIsm; - InterchainAccountRouter originRouter; - InterchainAccountRouter destinationRouter; - bytes32 ismOverride; - bytes32 routerOverride; + TestInterchainGasPaymaster internal igp; + InterchainAccountIsm internal icaIsm; + InterchainAccountRouter internal originRouter; + InterchainAccountRouter internal destinationRouter; + bytes32 internal ismOverride; + bytes32 internal routerOverride; + uint256 gasPaymentQuote; - OwnableMulticall ica; + OwnableMulticall internal ica; - Callable target; + Callable internal target; function deployProxiedIcaRouter( MockMailbox _mailbox, - IInterchainGasPaymaster _igp, + IPostDispatchHook _customHook, IInterchainSecurityModule _ism, address _owner ) public returns (InterchainAccountRouter) { @@ -80,7 +80,7 @@ contract InterchainAccountRouterTest is Test { address(1), // no proxy owner necessary for testing abi.encodeWithSelector( InterchainAccountRouter.initialize.selector, - address(_igp), + address(_customHook), address(_ism), _owner ) @@ -92,6 +92,12 @@ contract InterchainAccountRouterTest is Test { function setUp() public { environment = new MockHyperlaneEnvironment(origin, destination); + igp = new TestInterchainGasPaymaster(); + gasPaymentQuote = igp.quoteGasPayment( + destination, + igp.getDefaultGasUsage() + ); + icaIsm = new InterchainAccountIsm( address(environment.mailboxes(destination)) ); @@ -110,6 +116,8 @@ contract InterchainAccountRouterTest is Test { owner ); + environment.mailboxes(origin).setDefaultHook(address(igp)); + routerOverride = TypeCasts.addressToBytes32(address(destinationRouter)); ismOverride = TypeCasts.addressToBytes32( address(environment.isms(destination)) @@ -124,80 +132,102 @@ contract InterchainAccountRouterTest is Test { target = new Callable(); } - function testConstructor() public { - // The deployed ICA should be owned by the router - destinationRouter.getDeployedInterchainAccount( - origin, - address(this), - address(originRouter), - address(environment.isms(destination)) - ); - assertEq(ica.owner(), address(destinationRouter)); + function testFuzz_constructor(address _localOwner) public { + OwnableMulticall _account = destinationRouter + .getDeployedInterchainAccount( + origin, + _localOwner, + address(originRouter), + address(environment.isms(destination)) + ); + assertEq(_account.owner(), address(destinationRouter)); } - function testGetRemoteInterchainAccount() public { - assertEq( - originRouter.getRemoteInterchainAccount( - address(this), - address(destinationRouter), - address(environment.isms(destination)) - ), - address(ica) + function testFuzz_getRemoteInterchainAccount( + address _localOwner, + address _ism + ) public { + address _account = originRouter.getRemoteInterchainAccount( + address(_localOwner), + address(destinationRouter), + _ism ); originRouter.enrollRemoteRouterAndIsm( destination, routerOverride, - ismOverride + TypeCasts.addressToBytes32(_ism) ); assertEq( - originRouter.getRemoteInterchainAccount(destination, address(this)), - address(ica) + originRouter.getRemoteInterchainAccount( + destination, + address(_localOwner) + ), + _account ); } - function testEnrollRemoteRouters( + function testFuzz_enrollRemoteRouters( uint8 count, uint32 domain, bytes32 router ) public { vm.assume(count > 0 && count < uint256(router) && count < domain); + + // arrange + // count - # of domains and routers uint32[] memory domains = new uint32[](count); bytes32[] memory routers = new bytes32[](count); for (uint256 i = 0; i < count; i++) { domains[i] = domain - uint32(i); routers[i] = bytes32(uint256(router) - i); } + + // act originRouter.enrollRemoteRouters(domains, routers); + + // assert uint32[] memory actualDomains = originRouter.domains(); assertEq(actualDomains.length, domains.length); + assertEq(abi.encode(originRouter.domains()), abi.encode(domains)); + for (uint256 i = 0; i < count; i++) { bytes32 actualRouter = originRouter.routers(domains[i]); bytes32 actualIsm = originRouter.isms(domains[i]); + assertEq(actualRouter, routers[i]); assertEq(actualIsm, bytes32(0)); assertEq(actualDomains[i], domains[i]); } - assertEq(abi.encode(originRouter.domains()), abi.encode(domains)); } - function testEnrollRemoteRouterAndIsm(bytes32 router, bytes32 ism) public { + function testFuzz_enrollRemoteRouterAndIsm( + bytes32 router, + bytes32 ism + ) public { vm.assume(router != bytes32(0)); + + // arrange pre-condition bytes32 actualRouter = originRouter.routers(destination); bytes32 actualIsm = originRouter.isms(destination); assertEq(actualRouter, bytes32(0)); assertEq(actualIsm, bytes32(0)); + + // act originRouter.enrollRemoteRouterAndIsm(destination, router, ism); + + // assert actualRouter = originRouter.routers(destination); actualIsm = originRouter.isms(destination); assertEq(actualRouter, router); assertEq(actualIsm, ism); } - function testEnrollRemoteRouterAndIsms( + function testFuzz_enrollRemoteRouterAndIsms( uint32[] calldata destinations, bytes32[] calldata routers, bytes32[] calldata isms ) public { + // check reverts if ( destinations.length != routers.length || destinations.length != isms.length @@ -207,7 +237,10 @@ contract InterchainAccountRouterTest is Test { return; } + // act originRouter.enrollRemoteRouterAndIsms(destinations, routers, isms); + + // assert for (uint256 i = 0; i < destinations.length; i++) { bytes32 actualRouter = originRouter.routers(destinations[i]); bytes32 actualIsm = originRouter.isms(destinations[i]); @@ -216,27 +249,35 @@ contract InterchainAccountRouterTest is Test { } } - function testEnrollRemoteRouterAndIsmImmutable( + function testFuzz_enrollRemoteRouterAndIsmImmutable( bytes32 routerA, bytes32 ismA, bytes32 routerB, bytes32 ismB ) public { vm.assume(routerA != bytes32(0) && routerB != bytes32(0)); + + // act originRouter.enrollRemoteRouterAndIsm(destination, routerA, ismA); + + // assert vm.expectRevert( bytes("router and ISM defaults are immutable once set") ); originRouter.enrollRemoteRouterAndIsm(destination, routerB, ismB); } - function testEnrollRemoteRouterAndIsmNonOwner( + function testFuzz_enrollRemoteRouterAndIsmNonOwner( address newOwner, bytes32 router, bytes32 ism ) public { vm.assume(newOwner != address(0) && newOwner != originRouter.owner()); + + // act originRouter.transferOwnership(newOwner); + + // assert vm.expectRevert(bytes("Ownable: caller is not the owner")); originRouter.enrollRemoteRouterAndIsm(destination, router, ism); } @@ -245,6 +286,7 @@ contract InterchainAccountRouterTest is Test { bytes32 data ) private view returns (CallLib.Call[] memory) { vm.assume(data != bytes32(0)); + CallLib.Call memory call = CallLib.Call( TypeCasts.addressToBytes32(address(target)), 0, @@ -268,89 +310,240 @@ contract InterchainAccountRouterTest is Test { assertEq(target.data(address(ica)), data); } - function testSingleCallRemoteWithDefault(bytes32 data) public { + function assertIgpPayment( + uint256 balanceBefore, + uint256 balanceAfter, + uint256 gasLimit + ) private { + uint256 expectedGasPayment = gasLimit * igp.gasPrice(); + assertEq(balanceBefore - balanceAfter, expectedGasPayment); + assertEq(address(igp).balance, expectedGasPayment); + } + + function testFuzz_singleCallRemoteWithDefault(bytes32 data) public { + // arrange originRouter.enrollRemoteRouterAndIsm( destination, routerOverride, ismOverride ); + uint256 balanceBefore = address(this).balance; + + // act CallLib.Call[] memory calls = getCalls(data); - originRouter.callRemote( + originRouter.callRemote{value: gasPaymentQuote}( destination, TypeCasts.bytes32ToAddress(calls[0].to), calls[0].value, calls[0].data ); + + // assert + uint256 balanceAfter = address(this).balance; assertRemoteCallReceived(data); + assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); } - function testCallRemoteWithDefault(bytes32 data) public { + function testFuzz_callRemoteWithDefault(bytes32 data) public { + // arrange originRouter.enrollRemoteRouterAndIsm( destination, routerOverride, ismOverride ); - originRouter.callRemote(destination, getCalls(data)); + uint256 balanceBefore = address(this).balance; + + // act + originRouter.callRemote{value: gasPaymentQuote}( + destination, + getCalls(data) + ); + + // assert + uint256 balanceAfter = address(this).balance; assertRemoteCallReceived(data); + assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); } - function testOverrideAndCallRemote(bytes32 data) public { + function testFuzz_overrideAndCallRemote(bytes32 data) public { + // arrange originRouter.enrollRemoteRouterAndIsm( destination, routerOverride, ismOverride ); - originRouter.callRemote(destination, getCalls(data)); + uint256 balanceBefore = address(this).balance; + + // act + originRouter.callRemote{value: gasPaymentQuote}( + destination, + getCalls(data) + ); + + // assert + uint256 balanceAfter = address(this).balance; assertRemoteCallReceived(data); + assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); } - function testCallRemoteWithoutDefaults(bytes32 data) public { + function testFuzz_callRemoteWithoutDefaults_revert_noRouter( + bytes32 data + ) public { + // assert error CallLib.Call[] memory calls = getCalls(data); vm.expectRevert(bytes("no router specified for destination")); originRouter.callRemote(destination, calls); } - function testCallRemoteWithOverrides(bytes32 data) public { - originRouter.callRemoteWithOverrides( + function testFuzz_customMetadata_forIgp( + uint64 gasLimit, + uint64 overpayment, + bytes32 data + ) public { + // arrange + bytes memory metadata = StandardHookMetadata.formatMetadata( + 0, + gasLimit, + address(this), + "" + ); + originRouter.enrollRemoteRouterAndIsm( + destination, + routerOverride, + ismOverride + ); + uint256 balanceBefore = address(this).balance; + + // act + originRouter.callRemote{value: gasLimit * igp.gasPrice() + overpayment}( + destination, + getCalls(data), + metadata + ); + + // assert + uint256 balanceAfter = address(this).balance; + assertRemoteCallReceived(data); + assertIgpPayment(balanceBefore, balanceAfter, gasLimit); + } + + function testFuzz_customMetadata_reverts_underpayment( + uint64 gasLimit, + uint64 payment, + bytes32 data + ) public { + vm.assume(payment < gasLimit * igp.gasPrice()); + // arrange + bytes memory metadata = StandardHookMetadata.formatMetadata( + 0, + gasLimit, + address(this), + "" + ); + originRouter.enrollRemoteRouterAndIsm( + destination, + routerOverride, + ismOverride + ); + + // act + vm.expectRevert("IGP: insufficient interchain gas payment"); + originRouter.callRemote{value: payment}( + destination, + getCalls(data), + metadata + ); + } + + function testFuzz_callRemoteWithOverrides_default(bytes32 data) public { + // arrange + uint256 balanceBefore = address(this).balance; + + // act + originRouter.callRemoteWithOverrides{value: gasPaymentQuote}( destination, routerOverride, ismOverride, getCalls(data) ); + + // assert + uint256 balanceAfter = address(this).balance; + assertRemoteCallReceived(data); + assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage()); + } + + function testFuzz_callRemoteWithOverrides_metadata( + uint64 gasLimit, + bytes32 data + ) public { + // arrange + bytes memory metadata = StandardHookMetadata.formatMetadata( + 0, + gasLimit, + address(this), + "" + ); + uint256 balanceBefore = address(this).balance; + + // act + originRouter.callRemoteWithOverrides{value: gasLimit * igp.gasPrice()}( + destination, + routerOverride, + ismOverride, + getCalls(data), + metadata + ); + + // assert + uint256 balanceAfter = address(this).balance; assertRemoteCallReceived(data); + assertIgpPayment(balanceBefore, balanceAfter, gasLimit); } - function testCallRemoteWithFailingIsmOverride(bytes32 data) public { + function testFuzz_callRemoteWithFailingIsmOverride(bytes32 data) public { + // arrange string memory failureMessage = "failing ism"; bytes32 failingIsm = TypeCasts.addressToBytes32( address(new FailingIsm(failureMessage)) ); - originRouter.callRemoteWithOverrides( + + // act + originRouter.callRemoteWithOverrides{value: gasPaymentQuote}( destination, routerOverride, failingIsm, - getCalls(data) + getCalls(data), + "" ); + + // assert vm.expectRevert(bytes(failureMessage)); environment.processNextPendingMessage(); } - function testCallRemoteWithFailingDefaultIsm(bytes32 data) public { + function testFuzz_callRemoteWithFailingDefaultIsm(bytes32 data) public { + // arrange string memory failureMessage = "failing ism"; FailingIsm failingIsm = new FailingIsm(failureMessage); + // act environment.mailboxes(destination).setDefaultIsm(address(failingIsm)); - originRouter.callRemoteWithOverrides( + originRouter.callRemoteWithOverrides{value: gasPaymentQuote}( destination, routerOverride, bytes32(0), - getCalls(data) + getCalls(data), + "" ); + + // assert vm.expectRevert(bytes(failureMessage)); environment.processNextPendingMessage(); } - function testGetLocalInterchainAccount(bytes32 data) public { + function testFuzz_getLocalInterchainAccount(bytes32 data) public { + // check OwnableMulticall destinationIca = destinationRouter .getLocalInterchainAccount( origin, @@ -369,21 +562,23 @@ contract InterchainAccountRouterTest is Test { ) ) ); - assertEq(address(destinationIca).code.length, 0); - originRouter.callRemoteWithOverrides( + // act + originRouter.callRemoteWithOverrides{value: gasPaymentQuote}( destination, routerOverride, ismOverride, - getCalls(data) + getCalls(data), + "" ); - assertRemoteCallReceived(data); + // recheck + assertRemoteCallReceived(data); assert(address(destinationIca).code.length != 0); } - function testReceiveValue(uint256 value) public { + function testFuzz_receiveValue(uint256 value) public { vm.assume(value > 1 && value <= address(this).balance); // receive value before deployed assert(address(ica).code.length == 0); @@ -408,7 +603,7 @@ contract InterchainAccountRouterTest is Test { assertEq(value, msg.value); } - function testSendValue(uint256 value) public { + function testFuzz_sendValue(uint256 value) public { vm.assume(value > 0 && value <= address(this).balance); payable(address(ica)).transfer(value); @@ -417,13 +612,16 @@ contract InterchainAccountRouterTest is Test { CallLib.Call[] memory calls = new CallLib.Call[](1); calls[0] = call; - originRouter.callRemoteWithOverrides( + originRouter.callRemoteWithOverrides{value: gasPaymentQuote}( destination, routerOverride, ismOverride, - calls + calls, + "" ); vm.expectCall(address(this), value, data); environment.processNextPendingMessage(); } + + receive() external payable {} } diff --git a/solidity/test/message.test.ts b/solidity/test/message.test.ts index f39cb6affc..bdee259fb0 100644 --- a/solidity/test/message.test.ts +++ b/solidity/test/message.test.ts @@ -8,21 +8,26 @@ import { } from '@hyperlane-xyz/utils'; import testCases from '../../vectors/message.json'; -import { TestMessage, TestMessage__factory } from '../types'; +import { Mailbox__factory, TestMessage, TestMessage__factory } from '../types'; const remoteDomain = 1000; const localDomain = 2000; -const version = 0; const nonce = 11; describe('Message', async () => { let messageLib: TestMessage; + let version: number; before(async () => { const [signer] = await ethers.getSigners(); const Message = new TestMessage__factory(signer); messageLib = await Message.deploy(); + + // For consistency with the Mailbox version + const Mailbox = new Mailbox__factory(signer); + const mailbox = await Mailbox.deploy(localDomain); + version = await mailbox.VERSION(); }); it('Returns fields from a message', async () => { diff --git a/solidity/test/test-data/getProof-data.json b/solidity/test/test-data/getProof-data.json new file mode 100644 index 0000000000..73744c40e3 --- /dev/null +++ b/solidity/test/test-data/getProof-data.json @@ -0,0 +1,25 @@ +{ + "address": "0x3ef546f04a1b24eaf9dce2ed4338a1b5c32e2a56", + "balance": "0x0", + "codeHash": "0x2a5a7c6a053518aea9c1affe22c95e2e692204d39c9bdb0826de5012e7c93c4d", + "nonce": "0x1", + "storageHash": "0x3f85413bd5bccd8348f123724fb024c5fbab1ab934b0a7485202f75d84803743", + "accountProof": [ + "0xf90211a0857923ed0df1a7e89c6c157d9f228ca778b5028cce0fe977bbacdc29ebdcbe1da04cc511d34d843e5ed02112368d4885e8bc25e8e394abbc526459f4e38b842737a07f59950cdd53d4bb97096bbd99e7c04a5d21a97dac8a049f9b308610ea66c98fa02b8ce5f6b67ac79c8947b1919460f7bed77488762c059e5e88c64dfc722aaa56a03521e9571b798e0f86681b9ce0f0e9bf698e9059542d778cc4cde5659a31f9d2a0b90c6a5217bfd233f103dba76e18067f90001297c801cb6feb0c1f7912e0a522a0831099c940ebe93be0a1044566150044d22a8d414518eeb5d919d05468c8f510a054a09da1ca52551a35be75a623e419840060f2e56f86eb3678a689f66b38051da05945b528b186c0162d7cbfa281d30a361ece2a9f1788b33e7fda6bd7c3c5eb07a0401c3ac89520c5a59a19086e82314bb30ad2ecc5ddce09cdf613dd19adb41feca031e6288dc6db773d11cd579b974658473547abfcd82531cf1833484079baff58a099184994c461f1d6ce42e5e91ac66d6c6a5b4cc7ed845f058b795005b46328c1a054bde23ceae3fe9564654f16f867f3f321e0b7e16e3b08e925beb5fb5ffcb1fda040f8d48347ec397a261b090c7e3506f939b6033993c2836c24a487318310da67a0a6e90ed0e95843baeec2c8a6a017e61702556b67ca90c9576342fe268d0a9edba0ca8b4b3ea82cc66299ee45708b555de9b6272d3df24cb472c7baffaa72b39a0d80", + "0xf90211a0d124c71cf01214346e6facb7cd9e7743fca3a36f4252045573e37dd5b1fc208aa09318652584a16019f80279509f3133ee0f6fed3d391c1c0522b590b923b9283fa0fc90168fe3e76ff17c23781513065c71d13b5f963e492106f2b9a05ed1c9e3dca0c919acc2a76e4fef630534efea6f77a197646db3823ff861c430944385391672a0708cd1310ea8f59c163d0d7b905b456ed377000e989a343dbc61523ec4da6de3a0ade6811b1e1dc547362bfb0cdc943d6ae5d8283252f1e73d3000176a24ed8320a0effada816260fe1f7053c9c6f0cb49e0ee5ebb72be8a05f0cbcad3e7c95dd518a087d09c9bd032f470eed0637da12daa8e6d7c52102d51e87dfdecfdd0f01fc665a0d830bd0ebb1b7d0ddc716138e4341e64fa0c96d4330ac643c3306a842fdfb72ea05fb1c8a24c36a2ccae1ab7c39c0efdf56325dbc5de011f3c548827d592bd7f3ba0e127892391fcf8d6640c45c518ea6f1014b6983586e97e3ec4ae0e546ac22696a0573a0337bba203efa7a2ba3fab1a145e3cfd3b7a70d096489c2c549f9abad184a0daeb93e98ba67210f9e62088838adf7b2e83caa8ec9902e669095066f5abd1f4a0907a2f066f7bad1ebb9ba8998e217c82b2169336daa0f4c0db3b8ac05ad59fc5a03ac759b9a285ceb7293f13071d838261585bb87ae6a8c5489d13b7a818a60e8ea042abb78fd5f125d0dfd6ea10da83f064ed39920c3616e97b7810ee1f306559ec80", + "0xf90211a0316755c60ab5ac6d1ca0ce7fc7131544cb41bb5907b6c44b0659731c5196f505a0f4d87833b3b7fdb40011cd61f5e4fddf82a99e195e15a6650310d59f276111aba04c923074395b960cd08d04910a3c14ec3cb7d1ae858f9aef5cc888e6e03fbfb8a07c9d54b33c8a9db1e3efe12d0adb042d741fa2f2bccab2bf7d0afcb03626fba8a066400964da5b44e27fadbac20d48cfafc511c3c6bfaa68c1b92ae215e914f61ca038e0e323e92c7a20aaf13c302a493a5241c52a2c169aaaf8d48d23e17f44f643a0646a80b8ac762550cf167377f0837a5993536789eb065369829fd3b13bb52cc5a0de5c001063a09ee8b228199fcd393226a04ac68bc38f600729f7af2305c10368a035cb84ac8c8bb1ddb01e3377a7a17e22e23de20c89f4fa1bd7997bdc0421516aa01a598f35bf00309ed051c4096b8aac157d3411013211b2df2a02a8246eb5d0b6a0fab8ac4cedece1fe1ba89c617413a8df9a28e287e32f92bc52db1485ebb2396ea036f69b2bdc01e9b90102d5df53f9c02f3474db4f613f91d13c46f341b9fb9746a0d0bda7e73681df8b78903454ae252ebd8d34e48af88aa0ad18d6c2fb86a99d89a00d98621a3d3071cc04c492fd2c666637f27138eea11b83f516467f33a00b5eefa0198f566c5350232b08192dfe9cb7dd0a07b21c15638a80f912114a2b5c96be59a035092d3d51e3cd65ad2dcdb2d6c19aadb63a944ac9cedb7093ea68be58b7d83e80", + "0xf90211a0a7aea3e55e49172a7da1f511fceb247072cb0b4d8a87d9cd5cde0bc87046665ca08a057cd8f49b6ad4841f8e23b81706b8a424eb092663e4e1e60f98dba1e07e44a064098aa80e0d7e101f4e87fa5983f9a43ff6c26d8adaafea0c8f745e0fb9099aa09a5ec75b929e80a80ae115a1aec2827dcdb3eec116fce7a805eebe02d51ff3d7a027c8ef5bfc576ad5e7577005716a18d16ae203c440d65e12a9581a252b9f0e4ba01d352d5770faf1ab8a8096739876d9af10846effbd5f0a4bbb3a53bcb687fc9ca0a9da86853b0a80f1be569b6563aa35e8ab5d635132836b9dc95952b83192bdd3a0aa42135324b35a446ec3620ab361bb98d4636ebd2774715f142550b66cb5c723a0cc9734326b403f694d73ee6dd774e56c5675428ebf24b2ec28b319146f0d1ebda0a0d2c0664e654776405127a978a6edf6c249a71e94747158b2cae156b795b9eaa07a5811d019afead667944b567e595b7ef6084036be9bfde82097a479f3b9d01ca009e0186fe2bbb6f5f8399fabcbda45902405241f25b34352ff7ec66c78fa124aa0cacc8baa6b996e057fe8abaa433f50ba5f9b7afd501a1dbb8b952371e4b604efa081e453659b14017d3f3492fecfa4e9b3a03b8b8d3236fd2c3082c9abf528b6aca030e4ddd56b0b072e820f1afd7bd32cf5cf06f92d69231cbdc236a968c1f43283a0961d902b6b6ee68fb1d75a633a76786a7783f7603f8812ada01f865639e9daf880", + "0xf90211a045376ea7070c6b6bba71480a083c5ee916a4c72aa89ddc0539f69ca2f951ce96a0786afe5e76d004a35a17fb7931e70f40b0ce3a57e5246c94ebc4629e0a61e5bca0ce44e7e569e415dc667a71d212d260023fee99415bd0bcfd1e5aeee1cdbd8ec9a051cabbabc99fbd009590821598f342f3c2606a4241c81c46dd7e65c18d9ee098a0dbae505b1ce907ff5d451c5d113736ed911b239897d1f6887d3350da314f174ba047f6b90756987d0268044b85bc44da5a34e988044014a6c27cf62d185c7b46e9a0f7566eb4d5c9bc2361ecfffe8fc6a5ac5b5b9380c8b8152284cd714d44e79b12a0cc4a7ee9c4e83d67d64a9bfb0aec93cc3b8966c235b5b65f17333832b023a464a00b9024cfba692f2385d58ea3dca746f9e062982fb21f6f16ead2cefb70d25657a0d187348c2d25747788855d8094ce6e3df6940e26dca20cec4cc26e4f2292ee67a0355474e9a646331b74d171dd023e68f7c50f4bc1257a6433ee01a9ea8ce1577aa096f7bfa110a35e95127808a58ec44f3478b8b67f98ff3b8b140a3e9d249a6deba0dac2dea453ebb23f8a6a8a82db61ca160792aa967db905d98ee0dbcb2b81ef88a0bf7bac1cc4c2e30b47c5bc62ef9aedba995e46ad0432cf78cc8ec064ebf62ec7a0a0b931ee3c50545a4f91864c9501fb02eaa879ab02e4f677b11a1667d42cd55ca0654f679d8743df36bb61fe7eecca444d1a1773435b876fcafadd36c1c9bfe24980", + "0xf901d1a0c6d1570250983b95bd625c871d0627cc5bf6557738904ec34f6ce54c4d4782dba098975098243c73d0de47c1e3edf85d54a2010b50e85ffdac2192cc71484993e0a028e68d0c580151049649c5c882ddc4fa3f946f832b6fe34f210420f82ad18e0c80a0a0c6abc7c7b8c738ddb43a72c19c6a0dc4eee7808e4653d2ac67d5d777c5cfe8a0a3843b012a7d6170298a6e0024a79e466614296d4d09a72876be8049294d7dcfa02370c2a2892d2eacc15cdc46cef02125499df7afe41a16af6ce78d5fb8d5a7b9a0e9c8fa9b7269050b18b76ac5f07847e93e2403a438783e27bb69bb89484a7a52a08a7fef1f6d80c56ae256362f9de34169d3d66b17a8f0be6dbd0fe82c31a0c4da80a081b05ed42fc65a32afabe8f3ab58eedc8658472f383150e4c631ce1b3a36bacda04fe5deb98851372cb69ab014dba185f1c488ccd9810b4998d7737d0bee884718a0c8d739dcf5707542a9f751ec6a0acb228f28e55659c35973ee8a46aefa9a7a4fa0f0128b39c576d4e23e8180b154d7e8f7031a7d6775191188c87cc11573730008a0d391bf99084848ebff2c014653ec6f9634c80b1115850174cdf0edf83669b073a031d14af58dae3d2fdf510a3c48e988b860d1bfe406e6365be92252d63f4c316080", + "0xf8679e20bc9ed3e4003248a3f81d89ecc98e14f0359535a5195a1d33a6fe19556bb846f8440180a03f85413bd5bccd8348f123724fb024c5fbab1ab934b0a7485202f75d84803743a02a5a7c6a053518aea9c1affe22c95e2e692204d39c9bdb0826de5012e7c93c4d" + ], + "storageProof": [ + { + "key": "0x02c1eed75677f1bd39cc3abdd3042974bf12ab4a12ecc40df73fe3aa103e5e0e", + "proof": [ + "0xf844a120443dd0be11dd8e645a2e5675fd62011681443445ea8b04c77d2cdeb1326739eca1a031ede38d2e93c5aee49c836f329a626d8c6322abfbff3783e82e5759f870d7e9" + ], + "value": "0x31ede38d2e93c5aee49c836f329a626d8c6322abfbff3783e82e5759f870d7e9" + } + ] +} diff --git a/solidity/test/test/TestSendReceiver.t.sol b/solidity/test/test/TestSendReceiver.t.sol index 4ca8cf666b..09b584a8bb 100644 --- a/solidity/test/test/TestSendReceiver.t.sol +++ b/solidity/test/test/TestSendReceiver.t.sol @@ -98,9 +98,8 @@ contract TestSendReceiverTest is Test { vm.assume(blockNumber > 0); vm.roll(blockNumber); - // blockhash(n) = n for forge tests - // previousBlockHash() = blockhash(n-1) = n-1 - if (blockNumber % 16 == 1) { + // previousBlockHash() = blockhash(n-1) + if (uint256(blockhash(blockNumber - 1)) % 16 == 0) { vm.expectRevert("block hash ends in 0"); // blockhash(n-1) ends in 0 } else { vm.expectEmit(true, true, true, false, address(testSendReceiver)); // Process diff --git a/solidity/tsconfig.json b/solidity/tsconfig.json index 3a0356c92d..d5353f6c3b 100644 --- a/solidity/tsconfig.json +++ b/solidity/tsconfig.json @@ -5,4 +5,4 @@ }, "exclude": ["./test", "hardhat.config.ts", "./dist"], "extends": "../tsconfig.json" -} \ No newline at end of file +} diff --git a/typescript/ccip-server/.eslintrc b/typescript/ccip-server/.eslintrc new file mode 100644 index 0000000000..05936cd4e4 --- /dev/null +++ b/typescript/ccip-server/.eslintrc @@ -0,0 +1,6 @@ +{ + "rules": { + "no-console": ["off"] + } + } + \ No newline at end of file diff --git a/typescript/ccip-server/.gitignore b/typescript/ccip-server/.gitignore new file mode 100644 index 0000000000..66abda2f13 --- /dev/null +++ b/typescript/ccip-server/.gitignore @@ -0,0 +1,4 @@ +.env* +/dist +/cache +/configs \ No newline at end of file diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md new file mode 100644 index 0000000000..af4cc393a8 --- /dev/null +++ b/typescript/ccip-server/CHANGELOG.md @@ -0,0 +1,3 @@ +# @hyperlane-xyz/ccip-server + +## 3.8.0 diff --git a/typescript/ccip-server/README.md b/typescript/ccip-server/README.md new file mode 100644 index 0000000000..915acfd999 --- /dev/null +++ b/typescript/ccip-server/README.md @@ -0,0 +1,45 @@ +# CCIP-read service framework + +This package contains the service framework for the CCIP-read project, built off of the [CCIP-server framework](https://github.com/smartcontractkit/ccip-read). It allows building of any execution logic, given a Hyperlane Relayer call. + +# Definitions + +- Server: The main entry point, and refers to `server.ts`. +- Service: A class that handles all logic for a particular service, e.g. ProofService, RPCService, etc. +- Service ABI: The interface for a service that tells the Server what input and output to expect. It serves similar functionalities as the Solidity ABIs, i.e., used for encoding and decoding data. + +# Usage + +The Relayer will make a POST request to the Server with a request body similar to the following: + +```json +{ + "data": "0x0ee9bb2f000000000000000000000000873afca0319f5c04421e90e882566c496877aff8000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001a2d9059b6d822aa460229510c754e9ecec100bb9f649186f5c7d4da8edf59858", + "sender": "0x4a679253410272dd5232b3ff7cf5dbb88f295319" +} +``` + +The `data` property will be ABI-encoded, and server will parse it according to the Service ABI. It then will call the handler function with the parsed input. + +# Building a Service + +1. Create a Service ABI for your Service. This ABI tells the Server how to parse the incoming `data`, and how to encode the output. See `/abi/ProofsServiceAbi.ts` for an example. +2. Create a new Service class to handle your logic. This should inherit from `HandlerDescriptionEnumerated` if a function will be used to handle a Server request. The handler function should return a Promise that resolves to the output of the Service. See `/service/ProofsService.ts` for examples. +3. Instantiate the new Service in `server.ts`. For example: + +```typescript +const proofsService = new ProofsService( + config.LIGHT_CLIENT_ADDR, + config.RPC_ADDRESS, + config.STEP_FN_ID, + config.CHAIN_ID, + config.SUCCINCT_PLATFORM_URL, + config.SUCCINCT_API_KEY, +); +``` + +4. Add the new Service by calling `server.add(...)` by providing the Service ABI, and the handler function. For example: + +```typescript +server.add(ProofsServiceAbi, [proofsService.handler('getProofs')]); +``` diff --git a/typescript/ccip-server/jest.config.js b/typescript/ccip-server/jest.config.js new file mode 100644 index 0000000000..3745fc2237 --- /dev/null +++ b/typescript/ccip-server/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json new file mode 100644 index 0000000000..d604bd65b0 --- /dev/null +++ b/typescript/ccip-server/package.json @@ -0,0 +1,38 @@ +{ + "name": "@hyperlane-xyz/ccip-server", + "version": "3.8.0", + "description": "CCIP server", + "typings": "dist/index.d.ts", + "typedocMain": "src/index.ts", + "private": true, + "files": [ + "src" + ], + "engines": { + "node": ">=14" + }, + "scripts": { + "start": "ts-node src/server.ts", + "dev": "nodemon src/server.ts", + "test": "jest", + "prettier": "prettier --write ./src/* ./tests/" + }, + "author": "brolee", + "license": "Apache-2.0", + "devDependencies": { + "@jest/globals": "^29.7.0", + "@types/node": "^16.9.1", + "jest": "^29.7.0", + "nodemon": "^3.0.3", + "prettier": "^2.8.8", + "ts-jest": "^29.1.2", + "ts-node": "^10.8.0", + "typescript": "5.1.6" + }, + "dependencies": { + "@chainlink/ccip-read-server": "^0.2.1", + "dotenv-flow": "^4.1.0", + "ethers": "5.7.2", + "hyperlane-explorer": "https://github.com/hyperlane-xyz/hyperlane-explorer.git" + } +} diff --git a/typescript/ccip-server/src/abis/ProofsServiceAbi.ts b/typescript/ccip-server/src/abis/ProofsServiceAbi.ts new file mode 100644 index 0000000000..d9960c21da --- /dev/null +++ b/typescript/ccip-server/src/abis/ProofsServiceAbi.ts @@ -0,0 +1,7 @@ +// This is the ABI for the ProofsService. +// This is used to 1) Select the function 2) encode output +const ProofsServiceAbi = [ + 'function getProofs(address, bytes32, uint256) public view returns (string[][])', +]; + +export { ProofsServiceAbi }; diff --git a/typescript/ccip-server/src/abis/TelepathyCcipReadIsmAbi.ts b/typescript/ccip-server/src/abis/TelepathyCcipReadIsmAbi.ts new file mode 100644 index 0000000000..68a61a543f --- /dev/null +++ b/typescript/ccip-server/src/abis/TelepathyCcipReadIsmAbi.ts @@ -0,0 +1,7 @@ +const TelepathyCcipReadIsmAbi = [ + 'function verify(bytes, bytes) public view returns (bool)', + 'function step(uint256) external', + 'function syncCommitteePoseidons(uint256) external view returns (bytes32)', +]; + +export { TelepathyCcipReadIsmAbi }; diff --git a/typescript/ccip-server/src/config.ts b/typescript/ccip-server/src/config.ts new file mode 100644 index 0000000000..f5fa74658a --- /dev/null +++ b/typescript/ccip-server/src/config.ts @@ -0,0 +1,23 @@ +import dotenvFlow from 'dotenv-flow'; + +dotenvFlow.config(); + +const RPC_ADDRESS = process.env.RPC_ADDRESS as string; +const LIGHT_CLIENT_ADDR = process.env.LIGHT_CLIENT_ADDR as string; +const STEP_FN_ID = process.env.STEP_FN_ID as string; +const CHAIN_ID = process.env.CHAIN_ID as string; +const SUCCINCT_PLATFORM_URL = process.env.SUCCINCT_PLATFORM_URL as string; +const SUCCINCT_API_KEY = process.env.SUCCINCT_API_KEY as string; +const SERVER_PORT = process.env.SERVER_PORT as string; +const SERVER_URL_PREFIX = process.env.SERVER_URL_PREFIX as string; + +export { + RPC_ADDRESS, + LIGHT_CLIENT_ADDR, + STEP_FN_ID, + CHAIN_ID, + SUCCINCT_PLATFORM_URL, + SUCCINCT_API_KEY, + SERVER_PORT, + SERVER_URL_PREFIX, +}; diff --git a/typescript/ccip-server/src/server.ts b/typescript/ccip-server/src/server.ts new file mode 100644 index 0000000000..ddd2fa330d --- /dev/null +++ b/typescript/ccip-server/src/server.ts @@ -0,0 +1,28 @@ +import { Server } from '@chainlink/ccip-read-server'; + +import { ProofsServiceAbi } from './abis/ProofsServiceAbi'; +import * as config from './config'; +import { ProofsService } from './services/ProofsService'; + +// Initalize Services +const proofsService = new ProofsService( + config.LIGHT_CLIENT_ADDR, + config.RPC_ADDRESS, + config.STEP_FN_ID, + config.CHAIN_ID, + config.SUCCINCT_PLATFORM_URL, + config.SUCCINCT_API_KEY, +); + +// Initalize Server and add Service handlers +const server = new Server(); + +server.add(ProofsServiceAbi, [ + { type: 'getProofs', func: proofsService.getProofs.bind(this) }, +]); + +// Start Server +const app = server.makeApp(config.SERVER_URL_PREFIX); +app.listen(config.SERVER_PORT, () => + console.log(`Listening on port ${config.SERVER_PORT}`), +); diff --git a/typescript/ccip-server/src/services/HyperlaneService.ts b/typescript/ccip-server/src/services/HyperlaneService.ts new file mode 100644 index 0000000000..2898a386ed --- /dev/null +++ b/typescript/ccip-server/src/services/HyperlaneService.ts @@ -0,0 +1,36 @@ +import { info } from 'console'; +import { Message, MessageTx } from 'hyperlane-explorer/src/types'; + +// These types are copied from hyperlane-explorer. TODO: export them so this file can use them directly. +interface ApiResult { + status: '0' | '1'; + message: string; + result: R; +} + +enum API_ACTION { + GetMessages = 'get-messages', +} + +class HyperlaneService { + constructor(readonly baseUrl: string) {} + + /** + * Makes a request to the Explorer API to get the block info by message Id. Throws if request fails, or no results + * @param id: Message id to look up + */ + async getOriginBlockByMessageId(id: string): Promise { + info(`Fetching block for id: ${id}`); + const response = await fetch( + `${this.baseUrl}?module=message&action=${API_ACTION.GetMessages}&id=${id}`, + ); + const responseAsJson: ApiResult = await response.json(); + if (responseAsJson.status === '1') { + return responseAsJson.result[0]?.origin; + } else { + throw new Error(responseAsJson.message); + } + } +} + +export { HyperlaneService }; diff --git a/typescript/ccip-server/src/services/LightClientService.ts b/typescript/ccip-server/src/services/LightClientService.ts new file mode 100644 index 0000000000..3d5238b94b --- /dev/null +++ b/typescript/ccip-server/src/services/LightClientService.ts @@ -0,0 +1,99 @@ +import { ethers, utils } from 'ethers'; + +import { TelepathyCcipReadIsmAbi } from '../abis/TelepathyCcipReadIsmAbi'; + +import { ProofStatus } from './common/ProofStatusEnum'; + +export type SuccinctConfig = { + readonly lightClientAddress: string; + readonly stepFunctionId: string; + readonly platformUrl: string; + readonly apiKey: string; +}; + +// Service that interacts with the LightClient/ISM +class LightClientService { + constructor( + private readonly lightClientContract: ethers.Contract, // TODO USE TYPECHAIN + private succinctConfig: SuccinctConfig, + ) {} + + private getSyncCommitteePeriod(slot: bigint): bigint { + return slot / 8192n; // Slots Per Period + } + + /** + * Gets syncCommitteePoseidons from ISM/LightClient + * @param slot + * @returns + */ + async getSyncCommitteePoseidons(slot: bigint): Promise { + return await this.lightClientContract.syncCommitteePoseidons( + this.getSyncCommitteePeriod(slot), + ); + } + + /** + * Calculates the slot given a timestamp, and the LightClient's configured Genesis Time and Secods Per Slot + * @param timestamp timestamp to calculate slot with + */ + async calculateSlot(timestamp: bigint): Promise { + return ( + (timestamp - (await this.lightClientContract.GENESIS_TIME())) / + (await this.lightClientContract.SECONDS_PER_SLOT()) + ); + } + + /** + * Request the proof from Succinct. + * @param slot + * @param syncCommitteePoseidon + */ + async requestProof( + syncCommitteePoseidon: string, + slot: bigint, + ): Promise { + console.log(`Requesting proof for${slot}`); + + // Note that Succinct will asynchronously call step() on the ISM/LightClient + const telepathyIface = new utils.Interface(TelepathyCcipReadIsmAbi); + + const body = { + chainId: this.lightClientContract.chainId, + to: this.lightClientContract.address, + data: telepathyIface.encodeFunctionData('step', [slot]), + functionId: this.lightClientContract.stepFunctionId, + input: utils.defaultAbiCoder.encode( + ['bytes32', 'uint64'], + [syncCommitteePoseidon, slot], + ), + retry: true, + }; + + const response = await fetch( + `${this.lightClientContract.platformUrl}/new`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.succinctConfig.apiKey}`, + }, + body: JSON.stringify(body), + }, + ); + const responseAsJson = await response.json(); + + return responseAsJson.proof_id; + } + + // @dev in the case of when a proof doesn't exist, the request returns an object of { error: 'failed to get proof' }. + // Example: GET https://alpha.succinct.xyz/api/proof/4dfd2802-4edf-4c4f-91db-b2d05eb69791 + async getProofStatus(proofId: string): Promise { + const response = await fetch( + `${this.lightClientContract.platformUrl}/${proofId}`, + ); + const responseAsJson = await response.json(); + return responseAsJson.status ?? ProofStatus.error; + } +} + +export { LightClientService, ProofStatus }; diff --git a/typescript/ccip-server/src/services/ProofsService.ts b/typescript/ccip-server/src/services/ProofsService.ts new file mode 100644 index 0000000000..1d05e7a3f7 --- /dev/null +++ b/typescript/ccip-server/src/services/ProofsService.ts @@ -0,0 +1,149 @@ +import { ethers } from 'ethers'; + +import { TelepathyCcipReadIsmAbi } from '../abis/TelepathyCcipReadIsmAbi'; + +import { HyperlaneService } from './HyperlaneService'; +import { LightClientService, SuccinctConfig } from './LightClientService'; +import { RPCService } from './RPCService'; +import { ProofResult } from './RPCService'; +import { ProofStatus } from './common/ProofStatusEnum'; + +type RPCConfig = { + readonly url: string; + readonly chainId: string; +}; + +type HyperlaneConfig = { + readonly url: string; +}; + +// Service that requests proofs from Succinct and RPC Provider +class ProofsService { + // Maps from pendingProofKey to pendingProofId + pendingProof = new Map(); + + // External Services + rpcService: RPCService; + lightClientService: LightClientService; + hyperlaneService: HyperlaneService; + + constructor( + succinctConfig: Required, + rpcConfig: Required, + hyperlaneConfig: Required, + ) { + this.rpcService = new RPCService(rpcConfig.url); + const lightClientContract = new ethers.Contract( + succinctConfig.lightClientAddress, + TelepathyCcipReadIsmAbi, + this.rpcService.provider, + ); + + this.lightClientService = new LightClientService( + lightClientContract, + succinctConfig, + ); + + this.hyperlaneService = new HyperlaneService(hyperlaneConfig.url); + } + + /** + * Requests the Succinct proof, state proof, and returns account and storage proof + * @dev Upon requesting Succinct Proof, this function will revert to force the relayer to re-check the pending proof + * @param target contract address to get the proof for + * @param storageKeys storage keys to get the proof for + * @param messageId messageId that will be used to get the block info from hyperlane + * @returns The account and a single storage proof + */ + async getProofs([ + target, + storageKey, + messageId, + ]: ethers.utils.Result): Promise> { + const proofs: Array<[string[], string[]]> = []; + const pendingProofKey = this.getPendingProofKey( + target, + storageKey, + messageId, + ); + if (!this.pendingProof.has(pendingProofKey)) { + // Request a Proof from Succinct + const pendingProofId = await this.requestProofFromSuccinct(messageId); + this.pendingProof.set(pendingProofKey, pendingProofId); + this.forceRelayerRecheck(); + } else { + // Proof is being generated, check status + const proofStatus = await this.lightClientService.getProofStatus( + this.pendingProof.get(pendingProofKey)!, + ); + if (proofStatus === ProofStatus.success) { + // Succinct Proof is ready. + // This means that the LightClient should have the latest state root. Fetch and return the storage proofs from eth_getProof + proofs.push(await this.getStorageProofs(target, storageKey, messageId)); + this.pendingProof.delete(pendingProofKey); + } else { + this.forceRelayerRecheck(); + } + } + // TODO Write tests to check proofs + return proofs; + } + + /** + * Requests the Succinct proof + * @param messageId messageId that will be used to get the block info from hyperlane + * @returns the proofId + */ + async requestProofFromSuccinct(messageId: string) { + const { timestamp } = await this.hyperlaneService.getOriginBlockByMessageId( + messageId, + ); + const slot = await this.lightClientService.calculateSlot(BigInt(timestamp)); + const syncCommitteePoseidon = ''; // TODO get from LC + return await this.lightClientService.requestProof( + syncCommitteePoseidon, + slot, + ); + } + + /** + * Gets the account and single storage proof from eth_getProof + * @param target contract address to get the proof for + * @param storageKeys storage keys to get the proof for + * @param messageId messageId that will be used to get the block info from hyperlane + * @returns The account and a single storage proof + */ + async getStorageProofs( + target: string, + storageKey: string, + messageId: string, + ): Promise<[string[], string[]]> { + const { blockNumber } = + await this.hyperlaneService.getOriginBlockByMessageId(messageId); + const { accountProof, storageProof }: ProofResult = + await this.rpcService.getProofs( + target, + [storageKey], + new Number(blockNumber).toString(16), // Converts to hexstring + ); + + return [accountProof, storageProof[0].proof]; // Since we only expect one storage key, we only return the first proof + } + + getPendingProofKey( + target: string, + storageKey: string, + messageId: string, + ): string { + return ethers.utils.defaultAbiCoder.encode( + ['string', 'string', 'string'], + [target, storageKey, messageId], + ); + } + + forceRelayerRecheck(): void { + throw new Error('Proof is not ready'); + } +} + +export { ProofsService }; diff --git a/typescript/ccip-server/src/services/RPCService.ts b/typescript/ccip-server/src/services/RPCService.ts new file mode 100644 index 0000000000..353e5af707 --- /dev/null +++ b/typescript/ccip-server/src/services/RPCService.ts @@ -0,0 +1,47 @@ +import { ethers } from 'ethers'; + +type ProofResultStorageProof = { + key: string; + proof: Array; + value: string; +}; + +type ProofResult = { + accountProof: Array; + storageProof: Array; + address: string; + balance: string; + codeHash: string; + nonce: string; + storageHash: string; +}; + +class RPCService { + provider: ethers.providers.JsonRpcProvider; + constructor(private readonly providerAddress: string) { + this.provider = new ethers.providers.JsonRpcProvider(this.providerAddress); + } + + /** + * Request state proofs using eth_getProofs + * @param address + * @param storageKeys + * @param block + * @returns + */ + async getProofs( + address: string, + storageKeys: string[], + block: string, + ): Promise { + const results = await this.provider.send('eth_getProof', [ + address, + storageKeys, + block, + ]); + + return results; + } +} + +export { RPCService, ProofResult }; diff --git a/typescript/ccip-server/src/services/__mocks__/HyperlaneService.ts b/typescript/ccip-server/src/services/__mocks__/HyperlaneService.ts new file mode 100644 index 0000000000..b197fc3f87 --- /dev/null +++ b/typescript/ccip-server/src/services/__mocks__/HyperlaneService.ts @@ -0,0 +1,25 @@ +import { MessageTx } from 'hyperlane-explorer/src/types'; + +class HyperlaneService { + async getOriginBlockByMessageId(id: string): Promise { + return { + timestamp: 123456789, + hash: '0x123abc456def789', + from: '0x9876543210abcdef', + to: '0xabcdef0123456789', + blockHash: '0x456789abc123def', + blockNumber: 12345, + mailbox: '0xabcdef0123456789', + nonce: 0, + gasLimit: 1000000, + gasPrice: 100, + effectiveGasPrice: 90, + gasUsed: 50000, + cumulativeGasUsed: 1234567, + maxFeePerGas: 150, + maxPriorityPerGas: 100, + }; + } +} + +export { HyperlaneService }; diff --git a/typescript/ccip-server/src/services/__mocks__/LightClientService.ts b/typescript/ccip-server/src/services/__mocks__/LightClientService.ts new file mode 100644 index 0000000000..097cb57ff1 --- /dev/null +++ b/typescript/ccip-server/src/services/__mocks__/LightClientService.ts @@ -0,0 +1,27 @@ +// TODO figure out why I cannot import this from LightClientService. +enum ProofStatus { + running = 'running', + success = 'success', + error = 'error', +} + +class LightClientService { + proofStatus: ProofStatus = ProofStatus.running; + async calculateSlot(timestamp: bigint): Promise { + return ( + (timestamp - 1606824023n) / 12n // (timestamp - GENESIS TIME) / SLOTS_PER_SECOND + ); + } + + async requestProof( + syncCommitteePoseidon: string, + slot: BigInt, + ): Promise { + return 'pendingProofId12'; + } + async getProofStatus(pendingProofId: string): Promise { + return ProofStatus.success; + } +} + +export { LightClientService }; diff --git a/typescript/ccip-server/src/services/__mocks__/RPCService.ts b/typescript/ccip-server/src/services/__mocks__/RPCService.ts new file mode 100644 index 0000000000..801c93bdbb --- /dev/null +++ b/typescript/ccip-server/src/services/__mocks__/RPCService.ts @@ -0,0 +1,13 @@ +import ETH_GET_PROOFS from '../../../../../solidity/test/test-data/getProof-data.json'; + +class RPCService { + getProofs = async ( + address: string, + storageKeys: string[], + block: string, + ): Promise => { + return ETH_GET_PROOFS; + }; +} + +export { RPCService }; diff --git a/typescript/ccip-server/src/services/common/ProofStatusEnum.ts b/typescript/ccip-server/src/services/common/ProofStatusEnum.ts new file mode 100644 index 0000000000..aa1b593bd7 --- /dev/null +++ b/typescript/ccip-server/src/services/common/ProofStatusEnum.ts @@ -0,0 +1,8 @@ +// Needs to be in its own file because Mocks will mock the entire file +enum ProofStatus { + running = 'running', + success = 'success', + error = 'error', +} + +export { ProofStatus }; diff --git a/typescript/ccip-server/tests/services/HyperlaneService.test.ts b/typescript/ccip-server/tests/services/HyperlaneService.test.ts new file mode 100644 index 0000000000..894f377107 --- /dev/null +++ b/typescript/ccip-server/tests/services/HyperlaneService.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from '@jest/globals'; + +import { HyperlaneService } from '../../src/services/HyperlaneService'; + +describe('HyperlaneServiceTest', () => { + let hyperlaneService: HyperlaneService; + beforeEach(() => { + hyperlaneService = new HyperlaneService( + 'https://explorer.hyperlane.xyz/api', + ); + }); + test('should get the block by messageId', async () => { + await hyperlaneService.getOriginBlockByMessageId( + '0xb0430e396f4014883c01bb3ee43df17ce93d8257a0a0b5778d9d3229a1bf02bb', + ); + expect(true).toBe(true); + }); +}); diff --git a/typescript/ccip-server/tests/services/LightClientService.test.ts b/typescript/ccip-server/tests/services/LightClientService.test.ts new file mode 100644 index 0000000000..4541762096 --- /dev/null +++ b/typescript/ccip-server/tests/services/LightClientService.test.ts @@ -0,0 +1,29 @@ +import { describe, expect, jest, test } from '@jest/globals'; +import { ethers } from 'ethers'; + +import { TelepathyCcipReadIsmAbi } from '../../src/abis/TelepathyCcipReadIsmAbi'; +import { LightClientService } from '../../src/services/LightClientService'; +import { RPCService } from '../../src/services/RPCService'; + +describe('LightClientService', () => { + let lightClientService: LightClientService; + beforeEach(() => { + const rpcService = new RPCService('http://localhost:8545'); + const lightClientContract = new ethers.Contract( + 'lightClientAddress', + TelepathyCcipReadIsmAbi, + rpcService.provider, + ); + lightClientService = new LightClientService(lightClientContract, { + lightClientAddress: ethers.constants.AddressZero, + stepFunctionId: ethers.constants.HashZero, + platformUrl: 'http://localhost:8080', + apiKey: 'apiKey', + }); + + jest.resetModules(); + }); + test('should return the correct proof status', () => { + expect(lightClientService.calculateSlot(1n)).toBeGreaterThan(0); + }); +}); diff --git a/typescript/ccip-server/tests/services/ProofsService.test.ts b/typescript/ccip-server/tests/services/ProofsService.test.ts new file mode 100644 index 0000000000..be063e9185 --- /dev/null +++ b/typescript/ccip-server/tests/services/ProofsService.test.ts @@ -0,0 +1,77 @@ +import { describe, expect, jest, test } from '@jest/globals'; +import { ethers } from 'ethers'; + +// import { LightClientService } from '../../src/services/LightClientService'; +import { ProofsService } from '../../src/services/ProofsService'; + +// Fixtures +jest.mock('../../src/services/HyperlaneService'); +jest.mock('../../src/services/LightClientService'); +jest.mock('../../src/services/RPCService'); + +describe('ProofsService', () => { + const TARGET_ADDR = 'targetAddress'; + const MESSAGE_ID = 'msgId'; + const STORAGE_KEY = ethers.utils.formatBytes32String('10'); + let proofsService: ProofsService; + let pendingProofKey: string; + + beforeEach(() => { + proofsService = new ProofsService( + { + lightClientAddress: ethers.constants.AddressZero, + stepFunctionId: ethers.constants.HashZero, + platformUrl: 'http://localhost:8080', + apiKey: 'apiKey', + }, + { + url: 'http://localhost:8545', + chainId: '1337', + }, + { + url: 'http://localhost:8545', + }, + ); + pendingProofKey = proofsService.getPendingProofKey( + TARGET_ADDR, + STORAGE_KEY, + MESSAGE_ID, + ); + jest.clearAllMocks(); + }); + + test('should set currentProofId, if proof is not ready', async () => { + try { + await proofsService.getProofs([TARGET_ADDR, STORAGE_KEY, MESSAGE_ID]); + } catch (e) { + expect(proofsService.pendingProof.get(pendingProofKey)).toEqual( + 'pendingProofId12', + ); + } + }); + + test('should reset currentProofId, if proof is ready', async () => { + const pendingProofKey = proofsService.getPendingProofKey( + TARGET_ADDR, + STORAGE_KEY, + MESSAGE_ID, + ); + try { + await proofsService.getProofs([TARGET_ADDR, STORAGE_KEY, MESSAGE_ID]); + expect(proofsService.pendingProof.get(pendingProofKey)).toEqual( + 'pendingProofId12', + ); + } catch (e) { + // Try to get the proof again + const proofs = await proofsService.getProofs([ + TARGET_ADDR, + STORAGE_KEY, + MESSAGE_ID, + ]); + expect(proofs[0][1]).toEqual([ + '0xf844a120443dd0be11dd8e645a2e5675fd62011681443445ea8b04c77d2cdeb1326739eca1a031ede38d2e93c5aee49c836f329a626d8c6322abfbff3783e82e5759f870d7e9', + ]); + expect(proofsService.pendingProof.get(pendingProofKey)).toBeUndefined(); + } + }); +}); diff --git a/typescript/ccip-server/tests/services/RPCService.test.ts b/typescript/ccip-server/tests/services/RPCService.test.ts new file mode 100644 index 0000000000..fde1373f2d --- /dev/null +++ b/typescript/ccip-server/tests/services/RPCService.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from '@jest/globals'; + +import * as config from '../../src/config'; +import { RPCService } from '../../src/services/RPCService'; + +describe('RPCService', () => { + const rpcService = new RPCService(config.RPC_ADDRESS); + + test('should return the proofs from api', async () => { + const proofs = await rpcService.getProofs( + '0x3ef546f04a1b24eaf9dce2ed4338a1b5c32e2a56', + ['0x02c1eed75677f1bd39cc3abdd3042974bf12ab4a12ecc40df73fe3aa103e5e0e'], + '0x1221E88', + ); + + expect(proofs).not.toBeNull(); + }); +}); diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index ca7c0a81ea..a55e2caaf5 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,65 @@ # @hyperlane-xyz/cli +## 3.8.0 + +### Patch Changes + +- 9681df08d: TestRecipient as part of core deployer +- 9681df08d: Update CLI Warp route deployment output shape to new WarpCore config +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] + - @hyperlane-xyz/sdk@3.8.0 + - @hyperlane-xyz/utils@3.8.0 + +## 3.7.0 + +### Minor Changes + +- 84e508039: Improve send transfer ergonomics by omitting token type flag +- 7ff826a8f: Merged agent addresses will now include igp as the zero address if not configured as the hook + +### Patch Changes + +- ab17af5f7: Updating HyperlaneIgpDeployer to configure storage gas oracles as part of deployment +- Updated dependencies [6f464eaed] +- Updated dependencies [87151c62b] +- Updated dependencies [ab17af5f7] +- Updated dependencies [7b40232af] +- Updated dependencies [54aeb6420] + - @hyperlane-xyz/sdk@3.7.0 + - @hyperlane-xyz/utils@3.7.0 + +## 3.6.2 + +### Patch Changes + +- 99fe93a5b: Removed IGP from preset hook config + - @hyperlane-xyz/sdk@3.6.2 + - @hyperlane-xyz/utils@3.6.2 + +## 3.6.1 + +### Patch Changes + +- Updated dependencies [3c298d064] +- Updated dependencies [ae4476ad0] +- Updated dependencies [f3b7ddb69] +- Updated dependencies [df24eec8b] +- Updated dependencies [78e50e7da] +- Updated dependencies [e4e4f93fc] + - @hyperlane-xyz/utils@3.6.1 + - @hyperlane-xyz/sdk@3.6.1 + ## 3.6.0 ### Patch Changes diff --git a/typescript/cli/ci-test.sh b/typescript/cli/ci-test.sh index 00e61f55c1..d0c49ad75b 100755 --- a/typescript/cli/ci-test.sh +++ b/typescript/cli/ci-test.sh @@ -1,10 +1,47 @@ #!/usr/bin/env bash -# NOTE: This script is intended to be run from the root of the repo +# set script location as repo root +cd "$(dirname "$0")/../.." + +TEST_TYPE_PRESET_HOOK="preset_hook_enabled" +TEST_TYPE_CONFIGURED_HOOK="configure_hook_enabled" +TEST_TYPE_PI_CORE="pi_with_core_chain" + +# set the first arg to 'configured_hook' to set the hook config as part of core deployment +# motivation is to test both the bare bone deployment (included in the docs) and the deployment +# with the routing over igp hook (which is closer to production deployment) +TEST_TYPE=$1 +if [ -z "$TEST_TYPE" ]; then + echo "Usage: ci-test.sh " + exit 1 +fi + +HOOK_FLAG=false +if [ "$TEST_TYPE" == $TEST_TYPE_CONFIGURED_HOOK ]; then + HOOK_FLAG=true +fi + +CHAIN1=anvil1 +CHAIN2=anvil2 +EXAMPLES_PATH=./examples + +# use different chain names and config for pi<>core test +if [ "$TEST_TYPE" == $TEST_TYPE_PI_CORE ]; then + CHAIN1=anvil + CHAIN2=ethereum + EXAMPLES_PATH=./examples/fork +fi + +CHAIN1_CAPS=$(echo "${CHAIN1}" | tr '[:lower:]' '[:upper:]') +CHAIN2_CAPS=$(echo "${CHAIN2}" | tr '[:lower:]' '[:upper:]') + +CHAIN1_PORT=8545 +CHAIN2_PORT=8555 # Optional cleanup for previous runs, useful when running locally pkill -f anvil -rm -rf /tmp/anvil* +rm -rf /tmp/${CHAIN1}* +rm -rf /tmp/${CHAIN2}* rm -rf /tmp/relayer if [[ $OSTYPE == 'darwin'* ]]; then @@ -14,16 +51,36 @@ if [[ $OSTYPE == 'darwin'* ]]; then fi # Setup directories for anvil chains -for CHAIN in anvil1 anvil2 +for CHAIN in ${CHAIN1} ${CHAIN2} do mkdir -p /tmp/$CHAIN /tmp/$CHAIN/state /tmp/$CHAIN/validator /tmp/relayer chmod -R 777 /tmp/relayer /tmp/$CHAIN done -anvil --chain-id 31337 -p 8545 --state /tmp/anvil1/state --gas-price 1 > /dev/null & -anvil --chain-id 31338 -p 8555 --state /tmp/anvil2/state --gas-price 1 > /dev/null & +# run the PI chain +anvil --chain-id 31337 -p ${CHAIN1_PORT} --state /tmp/${CHAIN1}/state --gas-price 1 > /dev/null & sleep 1 +# use different chain names for pi<>core test +if [ "$TEST_TYPE" == $TEST_TYPE_PI_CORE ]; then + # Fetch the RPC of chain to fork + cd typescript/infra + RPC_URL=$(yarn ts-node scripts/print-chain-metadatas.ts -e mainnet3 | jq -r ".${CHAIN2}.rpcUrls[0].http") + cd ../../ + + # run the fork chain + anvil -p ${CHAIN2_PORT} --state /tmp/${CHAIN2}/state --gas-price 1 --fork-url $RPC_URL --fork-retry-backoff 3 --compute-units-per-second 200 > /dev/null & + + # wait for fork to be ready + while ! cast bn --rpc-url http://127.0.0.1:${CHAIN2_PORT} &> /dev/null; do + sleep 1 + done +else + # run a second PI chain + anvil --chain-id 31338 -p ${CHAIN2_PORT} --state /tmp/${CHAIN2}/state --gas-price 1 > /dev/null & + sleep 1 +fi + set -e echo "{}" > /tmp/empty-artifacts.json @@ -31,73 +88,75 @@ echo "{}" > /tmp/empty-artifacts.json export DEBUG=hyperlane:* DEPLOYER=$(cast rpc eth_accounts | jq -r '.[0]') -BEFORE=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +BEFORE=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT}) -echo "Deploying contracts to anvil1 and anvil2" +echo "Deploying contracts to ${CHAIN1} and ${CHAIN2}" yarn workspace @hyperlane-xyz/cli run hyperlane deploy core \ - --targets anvil1,anvil2 \ - --chains ./examples/anvil-chains.yaml \ + --targets ${CHAIN1},${CHAIN2} \ + --chains ${EXAMPLES_PATH}/anvil-chains.yaml \ --artifacts /tmp/empty-artifacts.json \ - --ism ./examples/ism.yaml \ - --hook ./examples/hooks.yaml \ + $(if [ "$HOOK_FLAG" == "true" ]; then echo "--hook ${EXAMPLES_PATH}/hooks.yaml"; fi) \ + --ism ${EXAMPLES_PATH}/ism.yaml \ --out /tmp \ --key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --yes -AFTER_CORE=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) -GAS_PRICE=$(cast gas-price --rpc-url http://localhost:8545) +AFTER_CORE=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT}) +GAS_PRICE=$(cast gas-price --rpc-url http://127.0.0.1:${CHAIN1_PORT}) CORE_MIN_GAS=$(bc <<< "($BEFORE - $AFTER_CORE) / $GAS_PRICE") echo "Gas used: $CORE_MIN_GAS" CORE_ARTIFACTS_PATH=`find /tmp/core-deployment* -type f -exec ls -t1 {} + | head -1` echo "Core artifacts:" echo $CORE_ARTIFACTS_PATH +cat $CORE_ARTIFACTS_PATH AGENT_CONFIG_FILENAME=`ls -t1 /tmp | grep agent-config | head -1` echo "Deploying warp routes" yarn workspace @hyperlane-xyz/cli run hyperlane deploy warp \ - --chains ./examples/anvil-chains.yaml \ + --chains ${EXAMPLES_PATH}/anvil-chains.yaml \ --core $CORE_ARTIFACTS_PATH \ - --config ./examples/warp-tokens.yaml \ + --config ${EXAMPLES_PATH}/warp-route-deployment.yaml \ --out /tmp \ --key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --yes -AFTER_WARP=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) -GAS_PRICE=$(cast gas-price --rpc-url http://localhost:8545) +AFTER_WARP=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT}) +GAS_PRICE=$(cast gas-price --rpc-url http://127.0.0.1:${CHAIN1_PORT}) WARP_MIN_GAS=$(bc <<< "($AFTER_CORE - $AFTER_WARP) / $GAS_PRICE") echo "Gas used: $WARP_MIN_GAS" echo "Sending test message" yarn workspace @hyperlane-xyz/cli run hyperlane send message \ - --origin anvil1 \ - --destination anvil2 \ - --chains ./examples/anvil-chains.yaml \ + --origin ${CHAIN1} \ + --destination ${CHAIN2} \ + --messageBody "Howdy!" \ + --chains ${EXAMPLES_PATH}/anvil-chains.yaml \ --core $CORE_ARTIFACTS_PATH \ --quick \ --key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ | tee /tmp/message1 -AFTER_MSG=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) -GAS_PRICE=$(cast gas-price --rpc-url http://localhost:8545) +AFTER_MSG=$(cast balance $DEPLOYER --rpc-url http://127.0.0.1:${CHAIN1_PORT}) +GAS_PRICE=$(cast gas-price --rpc-url http://127.0.0.1:${CHAIN1_PORT}) MSG_MIN_GAS=$(bc <<< "($AFTER_WARP - $AFTER_MSG) / $GAS_PRICE") echo "Gas used: $MSG_MIN_GAS" MESSAGE1_ID=`cat /tmp/message1 | grep "Message ID" | grep -E -o '0x[0-9a-f]+'` echo "Message 1 ID: $MESSAGE1_ID" -WARP_ARTIFACTS_FILE=`find /tmp/warp-deployment* -type f -exec ls -t1 {} + | head -1` -ANVIL1_ROUTER=`cat $WARP_ARTIFACTS_FILE | jq -r ".anvil1.router"` +WARP_ARTIFACTS_FILE=`find /tmp/warp-route-deployment* -type f -exec ls -t1 {} + | head -1` +CHAIN1_ROUTER="${CHAIN1_CAPS}_ROUTER" +declare $CHAIN1_ROUTER=$(cat $WARP_ARTIFACTS_FILE | jq -r ".${CHAIN1}.router") echo "Sending test warp transfer" yarn workspace @hyperlane-xyz/cli run hyperlane send transfer \ - --origin anvil1 \ - --destination anvil2 \ - --chains ./examples/anvil-chains.yaml \ + --origin ${CHAIN1} \ + --destination ${CHAIN2} \ + --chains ${EXAMPLES_PATH}/anvil-chains.yaml \ --core $CORE_ARTIFACTS_PATH \ - --router $ANVIL1_ROUTER \ - --type native \ + --router ${!CHAIN1_ROUTER} \ --quick \ --key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ | tee /tmp/message2 @@ -109,24 +168,36 @@ cd ./rust echo "Pre-building validator with cargo" cargo build --bin validator -ANVIL_CONNECTION_URL="http://127.0.0.1" -for i in "anvil1 8545 ANVIL1" "anvil2 8555 ANVIL2" +# set some default agent env vars, used by both validators and relayer +export HYP_CHAINS_${CHAIN1_CAPS}_BLOCKS_REORGPERIOD=0 +export HYP_CHAINS_${CHAIN1_CAPS}_CUSTOMRPCURLS="http://127.0.0.1:${CHAIN1_PORT}" +export HYP_CHAINS_${CHAIN2_CAPS}_BLOCKS_REORGPERIOD=0 +export HYP_CHAINS_${CHAIN2_CAPS}_CUSTOMRPCURLS="http://127.0.0.1:${CHAIN2_PORT}" + +VALIDATOR_PORT=9091 + +for CHAIN in ${CHAIN1} ${CHAIN2} do - set -- $i - echo "Running validator on $1" + # don't need the second validator for pi<>core test + if [ "$CHAIN" == "$CHAIN2" ] && [ "$TEST_TYPE" == "$TEST_TYPE_PI_CORE" ]; then + echo "Skipping validator for $CHAIN2 due to $TEST_TYPE_PI_CORE test type" + continue + fi + + VALIDATOR_PORT=$((VALIDATOR_PORT+1)) + echo "Running validator on $CHAIN on port $VALIDATOR_PORT" export CONFIG_FILES=/tmp/${AGENT_CONFIG_FILENAME} - export HYP_ORIGINCHAINNAME=$1 - export HYP_CHAINS_${3}_BLOCKS_REORGPERIOD=0 + export HYP_ORIGINCHAINNAME=${CHAIN} export HYP_VALIDATOR_INTERVAL=1 - export HYP_CHAINS_${3}_CUSTOMRPCURLS=${ANVIL_CONNECTION_URL}:${2} export HYP_VALIDATOR_TYPE=hexKey export HYP_VALIDATOR_KEY=0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6 export HYP_CHECKPOINTSYNCER_TYPE=localStorage - export HYP_CHECKPOINTSYNCER_PATH=/tmp/${1}/validator + export HYP_CHECKPOINTSYNCER_PATH=/tmp/${CHAIN}/validator export HYP_TRACING_LEVEL=debug export HYP_TRACING_FMT=compact + export HYP_METRICSPORT=$VALIDATOR_PORT - cargo run --bin validator > /tmp/${1}/validator-logs.txt & + cargo run --bin validator > /tmp/${CHAIN}/validator-logs.txt & done echo "Validator running, sleeping to let it sync" @@ -134,24 +205,37 @@ echo "Validator running, sleeping to let it sync" sleep 15 echo "Done sleeping" -echo "Validator Announcement:" -cat /tmp/anvil1/validator/announcement.json +for CHAIN in ${CHAIN1} ${CHAIN2} +do + # only have one validator announce in pi<>core test + if [ "$CHAIN" == "$CHAIN2" ] && [ "$TEST_TYPE" == "$TEST_TYPE_PI_CORE" ]; then + echo "Skipping validator for $CHAIN2 due to $TEST_TYPE_PI_CORE test type" + continue + fi + + echo "Validator Announcement for ${CHAIN}:" + cat /tmp/${CHAIN}/validator/announcement.json +done echo "Pre-building relayer with cargo" cargo build --bin relayer echo "Running relayer" -export HYP_RELAYCHAINS=anvil1,anvil2 +export CONFIG_FILES=/tmp/${AGENT_CONFIG_FILENAME} +export HYP_RELAYCHAINS=${CHAIN1},${CHAIN2} export HYP_ALLOWLOCALCHECKPOINTSYNCERS=true export HYP_DB=/tmp/relayer export HYP_GASPAYMENTENFORCEMENT='[{"type":"none"}]' -export HYP_CHAINS_ANVIL1_SIGNER_TYPE=hexKey -export HYP_CHAINS_ANVIL1_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 -export HYP_CHAINS_ANVIL2_SIGNER_TYPE=hexKey -export HYP_CHAINS_ANVIL2_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 +export HYP_CHAINS_${CHAIN1_CAPS}_SIGNER_TYPE=hexKey +export HYP_CHAINS_${CHAIN1_CAPS}_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 +export HYP_CHAINS_${CHAIN2_CAPS}_SIGNER_TYPE=hexKey +export HYP_CHAINS_${CHAIN2_CAPS}_SIGNER_KEY=0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97 +export HYP_METRICSPORT=9090 + cargo run --bin relayer > /tmp/relayer/relayer-logs.txt & # This needs to be long to allow time for the cargo build to finish +echo "Waiting for relayer..." sleep 20 echo "Done running relayer, checking message delivery statuses" @@ -161,8 +245,8 @@ do echo "Checking delivery status of $1: $2" yarn workspace @hyperlane-xyz/cli run hyperlane status \ --id $2 \ - --destination anvil2 \ - --chains ./examples/anvil-chains.yaml \ + --destination ${CHAIN2} \ + --chains ${EXAMPLES_PATH}/anvil-chains.yaml \ --core $CORE_ARTIFACTS_PATH \ | tee /tmp/message-status-$1 if ! grep -q "$2 was delivered" /tmp/message-status-$1; then diff --git a/typescript/cli/cli.ts b/typescript/cli/cli.ts index fbffcfe33e..7218cf884e 100644 --- a/typescript/cli/cli.ts +++ b/typescript/cli/cli.ts @@ -8,6 +8,7 @@ import { configCommand } from './src/commands/config.js'; import { deployCommand } from './src/commands/deploy.js'; import { sendCommand } from './src/commands/send.js'; import { statusCommand } from './src/commands/status.js'; +import { checkVersion } from './src/utils/version-check.js'; import { VERSION } from './src/version.js'; // From yargs code: @@ -15,6 +16,8 @@ const MISSING_PARAMS_ERROR = 'Not enough non-option arguments'; console.log(chalk.blue('Hyperlane'), chalk.magentaBright('CLI')); +await checkVersion(); + try { await yargs(process.argv.slice(2)) .scriptName('hyperlane') diff --git a/typescript/cli/examples/fork/anvil-chains.yaml b/typescript/cli/examples/fork/anvil-chains.yaml new file mode 100644 index 0000000000..70b889af38 --- /dev/null +++ b/typescript/cli/examples/fork/anvil-chains.yaml @@ -0,0 +1,21 @@ +# Configs for describing chain metadata for use in Hyperlane deployments or apps +# Consists of a map of chain names to metadata +# Schema here: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/metadata/chainMetadataTypes.ts +--- +anvil: + chainId: 31337 + domainId: 31337 + name: anvil + protocol: ethereum + rpcUrls: + - http: http://127.0.0.1:8545 + nativeToken: + name: Ether + symbol: ETH + decimals: 18 +ethereum: + rpcUrls: + - http: http://127.0.0.1:8555 + blocks: + confirmations: 1 + estimateBlockTime: 1 diff --git a/typescript/cli/examples/fork/ism.yaml b/typescript/cli/examples/fork/ism.yaml new file mode 100644 index 0000000000..f5eab57405 --- /dev/null +++ b/typescript/cli/examples/fork/ism.yaml @@ -0,0 +1,9 @@ +# A config for a multisig Interchain Security Module (ISM) +# Schema: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/sdk/src/ism/types.ts +# + +--- +anvil: + threshold: 1 # Number: Signatures required to approve a message + validators: # Array: List of validator addresses + - '0xa0ee7a142d267c1f36714e4a8f75612f20a79720' diff --git a/typescript/cli/examples/fork/warp-route-deployment.yaml b/typescript/cli/examples/fork/warp-route-deployment.yaml new file mode 100644 index 0000000000..95755bd191 --- /dev/null +++ b/typescript/cli/examples/fork/warp-route-deployment.yaml @@ -0,0 +1,26 @@ +# A config for a Warp Route deployment +# Typically used with the 'hyperlane deploy warp' command +# +# Token Types: +# native +# collateral +# synthetic +# collateralUri +# syntheticUri +# fastCollateral +# fastSynthetic +--- +base: + chainName: anvil + type: native + # address: 0x123... # Required for collateral types + # isNft: true # If the token is an NFT (ERC721), set to true + # owner: 0x123 # Optional owner address for synthetic token + # mailbox: 0x123 # Optional mailbox address route + # interchainGasPaymaster: 0x123 # Optional interchainGasPaymaster address +synthetics: + - chainName: ethereum + # You can optionally set the token metadata, otherwise the base token's will be used + # name: "MySyntheticToken" + # symbol: "MST" + # totalSupply: 10000000 diff --git a/typescript/cli/examples/warp-tokens.yaml b/typescript/cli/examples/warp-route-deployment.yaml similarity index 100% rename from typescript/cli/examples/warp-tokens.yaml rename to typescript/cli/examples/warp-route-deployment.yaml diff --git a/typescript/cli/logger.ts b/typescript/cli/logger.ts index 5f2c220d31..3e0a20e162 100644 --- a/typescript/cli/logger.ts +++ b/typescript/cli/logger.ts @@ -50,6 +50,8 @@ export const logPink = (...args: any) => export const logGray = (...args: any) => console.log(chalk.gray(...args)); export const logGreen = (...args: any) => console.log(chalk.green(...args)); export const logRed = (...args: any) => console.log(chalk.red(...args)); +export const logBoldUnderlinedRed = (...args: any) => + console.log(chalk.red.bold.underline(...args)); export const logTip = (...args: any) => console.log(chalk.bgYellow(...args)); export const errorRed = (...args: any) => console.error(chalk.red(...args)); export const log = (...args: any) => console.log(...args); diff --git a/typescript/cli/package.json b/typescript/cli/package.json index f5bc09c70a..8e26bad268 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,14 +1,15 @@ { "name": "@hyperlane-xyz/cli", - "version": "3.6.0", + "version": "3.8.0", "description": "A command-line utility for common Hyperlane operations", "dependencies": { - "@hyperlane-xyz/sdk": "3.6.0", - "@hyperlane-xyz/utils": "3.6.0", + "@hyperlane-xyz/sdk": "3.8.0", + "@hyperlane-xyz/utils": "3.8.0", "@inquirer/prompts": "^3.0.0", "bignumber.js": "^9.1.1", "chalk": "^5.3.0", "ethers": "^5.7.2", + "latest-version": "^8.0.0", "terminal-link": "^3.0.0", "yaml": "^2.3.1", "yargs": "^17.7.2", diff --git a/typescript/cli/src/commands/chains.ts b/typescript/cli/src/commands/chains.ts index 539163654a..23a0748075 100644 --- a/typescript/cli/src/commands/chains.ts +++ b/typescript/cli/src/commands/chains.ts @@ -32,7 +32,21 @@ export const chainsCommand: CommandModule = { const listCommand: CommandModule = { command: 'list', describe: 'List all core chains included in the Hyperlane SDK', - handler: () => { + builder: (yargs) => + yargs + .option('mainnet', { + alias: 'm', + describe: 'Only list mainnet chains', + }) + .option('testnet', { + alias: 't', + describe: 'Only list testnet chains', + }) + .conflicts('mainnet', 'testnet'), + handler: (args) => { + const mainnet = args.mainnet as string | undefined; + const testnet = args.testnet as string | undefined; + const serializer = (chains: string[]) => chains.reduce((result, chain) => { result[chain] = { @@ -41,13 +55,22 @@ const listCommand: CommandModule = { }; return result; }, {}); + const logMainnet = () => { + logBlue('\nHyperlane core mainnet chains:'); + logGray('------------------------------'); + logTable(serializer(Mainnets)); + }; + const logTestnet = () => { + logBlue('\nHyperlane core testnet chains:'); + logGray('------------------------------'); + logTable(serializer(Testnets)); + }; + + if (mainnet) return logMainnet(); + else if (testnet) return logTestnet(); - logBlue('Hyperlane core mainnet chains:'); - logGray('------------------------------'); - logTable(serializer(Mainnets)); - logBlue('\nHyperlane core testnet chains:'); - logGray('------------------------------'); - logTable(serializer(Testnets)); + logMainnet(); + logTestnet(); }, }; diff --git a/typescript/cli/src/commands/config.ts b/typescript/cli/src/commands/config.ts index 74b3c45646..5823f3a6ab 100644 --- a/typescript/cli/src/commands/config.ts +++ b/typescript/cli/src/commands/config.ts @@ -8,7 +8,10 @@ import { createMultisigConfig, readMultisigConfig, } from '../config/multisig.js'; -import { createWarpConfig, readWarpRouteConfig } from '../config/warp.js'; +import { + createWarpRouteDeployConfig, + readWarpRouteDeployConfig, +} from '../config/warp.js'; import { FileFormat } from '../utils/files.js'; import { @@ -43,7 +46,7 @@ const createCommand: CommandModule = { .command(createChainConfigCommand) .command(createIsmConfigCommand) .command(createHookConfigCommand) - .command(createWarpConfigCommand) + .command(createWarpRouteDeployConfigCommand) .version(false) .demandCommand(), handler: () => log('Command required'), @@ -113,12 +116,12 @@ const createHookConfigCommand: CommandModule = { }, }; -const createWarpConfigCommand: CommandModule = { +const createWarpRouteDeployConfigCommand: CommandModule = { command: 'warp', - describe: 'Create a new Warp Route tokens config', + describe: 'Create a new Warp Route deployment config', builder: (yargs) => yargs.options({ - output: outputFileOption('./configs/warp-tokens.yaml'), + output: outputFileOption('./configs/warp-route-deployment.yaml'), format: fileFormatOption, chains: chainsCommandOption, }), @@ -126,7 +129,7 @@ const createWarpConfigCommand: CommandModule = { const format: FileFormat = argv.format; const outPath: string = argv.output; const chainConfigPath: string = argv.chains; - await createWarpConfig({ format, outPath, chainConfigPath }); + await createWarpRouteDeployConfig({ format, outPath, chainConfigPath }); process.exit(0); }, }; @@ -217,7 +220,7 @@ const validateWarpCommand: CommandModule = { }), handler: async (argv) => { const path = argv.path as string; - readWarpRouteConfig(path); + readWarpRouteDeployConfig(path); logGreen('Config is valid'); process.exit(0); }, diff --git a/typescript/cli/src/commands/deploy.ts b/typescript/cli/src/commands/deploy.ts index fffcb96d72..54a34a2533 100644 --- a/typescript/cli/src/commands/deploy.ts +++ b/typescript/cli/src/commands/deploy.ts @@ -3,7 +3,8 @@ import { CommandModule } from 'yargs'; import { log, logGray } from '../../logger.js'; import { runKurtosisAgentDeploy } from '../deploy/agent.js'; import { runCoreDeploy } from '../deploy/core.js'; -import { runWarpDeploy } from '../deploy/warp.js'; +import { runWarpRouteDeploy } from '../deploy/warp.js'; +import { ENV } from '../utils/env.js'; import { agentConfigurationOption, @@ -98,7 +99,7 @@ const coreCommand: CommandModule = { handler: async (argv: any) => { logGray('Hyperlane permissionless core deployment'); logGray('----------------------------------------'); - const key: string = argv.key || process.env.HYP_KEY; + const key: string = argv.key || ENV.HYP_KEY; const chainConfigPath: string = argv.chains; const outPath: string = argv.out; const chains: string[] | undefined = argv.targets @@ -132,8 +133,9 @@ const warpCommand: CommandModule = { yargs.options({ config: { type: 'string', - description: 'A path to a JSON or YAML file with a warp config.', - default: './configs/warp-tokens.yaml', + description: + 'A path to a JSON or YAML file with a warp route deployment config.', + default: './configs/warp-route-deployment.yaml', }, core: coreArtifactsOption, chains: chainsCommandOption, @@ -142,16 +144,16 @@ const warpCommand: CommandModule = { yes: skipConfirmationOption, }), handler: async (argv: any) => { - const key: string = argv.key || process.env.HYP_KEY; + const key: string = argv.key || ENV.HYP_KEY; const chainConfigPath: string = argv.chains; - const warpConfigPath: string | undefined = argv.config; + const warpRouteDeploymentConfigPath: string | undefined = argv.config; const coreArtifactsPath: string | undefined = argv.core; const outPath: string = argv.out; const skipConfirmation: boolean = argv.yes; - await runWarpDeploy({ + await runWarpRouteDeploy({ key, chainConfigPath, - warpConfigPath, + warpRouteDeploymentConfigPath, coreArtifactsPath, outPath, skipConfirmation, diff --git a/typescript/cli/src/commands/send.ts b/typescript/cli/src/commands/send.ts index b1ce305981..9ae02d438e 100644 --- a/typescript/cli/src/commands/send.ts +++ b/typescript/cli/src/commands/send.ts @@ -1,10 +1,10 @@ +import { ethers } from 'ethers'; import { CommandModule, Options } from 'yargs'; -import { TokenType } from '@hyperlane-xyz/sdk'; - import { log } from '../../logger.js'; import { sendTestMessage } from '../send/message.js'; import { sendTestTransfer } from '../send/transfer.js'; +import { ENV } from '../utils/env.js'; import { chainsCommandOption, @@ -57,21 +57,31 @@ const messageOptions: { [k: string]: Options } = { const messageCommand: CommandModule = { command: 'message', describe: 'Send a test message to a remote chain', - builder: (yargs) => yargs.options(messageOptions), + builder: (yargs) => + yargs.options({ + ...messageOptions, + messageBody: { + type: 'string', + description: 'Optional Message body', + default: 'Hello!', + }, + }), handler: async (argv: any) => { - const key: string = argv.key || process.env.HYP_KEY; + const key: string = argv.key || ENV.HYP_KEY; const chainConfigPath: string = argv.chains; const coreArtifactsPath: string | undefined = argv.core; const origin: string | undefined = argv.origin; const destination: string | undefined = argv.destination; const timeoutSec: number = argv.timeout; const skipWaitForDelivery: boolean = argv.quick; + const messageBody: string = argv.messageBody; await sendTestMessage({ key, chainConfigPath, coreArtifactsPath, origin, destination, + messageBody: ethers.utils.hexlify(ethers.utils.toUtf8Bytes(messageBody)), timeoutSec, skipWaitForDelivery, }); @@ -92,12 +102,6 @@ const transferCommand: CommandModule = { type: 'string', description: 'The address of the token router contract', }, - type: { - type: 'string', - description: 'Warp token type (native or collateral)', - default: TokenType.collateral, - choices: [TokenType.collateral, TokenType.native], - }, wei: { type: 'string', description: 'Amount in wei to send', @@ -109,14 +113,13 @@ const transferCommand: CommandModule = { }, }), handler: async (argv: any) => { - const key: string = argv.key || process.env.HYP_KEY; + const key: string = argv.key || ENV.HYP_KEY; const chainConfigPath: string = argv.chains; const coreArtifactsPath: string | undefined = argv.core; const origin: string | undefined = argv.origin; const destination: string | undefined = argv.destination; const timeoutSec: number = argv.timeout; const routerAddress: string | undefined = argv.router; - const tokenType: TokenType = argv.type; const wei: string = argv.wei; const recipient: string | undefined = argv.recipient; const skipWaitForDelivery: boolean = argv.quick; @@ -127,7 +130,6 @@ const transferCommand: CommandModule = { origin, destination, routerAddress, - tokenType, wei, recipient, timeoutSec, diff --git a/typescript/cli/src/config/chain.ts b/typescript/cli/src/config/chain.ts index f3499fde7a..28e5507762 100644 --- a/typescript/cli/src/config/chain.ts +++ b/typescript/cli/src/config/chain.ts @@ -1,4 +1,4 @@ -import { input } from '@inquirer/prompts'; +import { confirm, input } from '@inquirer/prompts'; import { ChainMap, @@ -90,6 +90,63 @@ export async function createChainConfig({ protocol: ProtocolType.Ethereum, rpcUrls: [{ http: rpcUrl }], }; + const wantAdvancedConfig = await confirm({ + message: + 'Do you want to set block or gas properties for this chain config?(optional)', + }); + if (wantAdvancedConfig) { + const wantBlockConfig = await confirm({ + message: 'Do you want to add block config for this chain?', + }); + if (wantBlockConfig) { + const blockConfirmation = await input({ + message: + 'Enter no. of blocks to wait before considering a transaction confirmed(0-500)', + validate: (value) => parseInt(value) >= 0 && parseInt(value) <= 500, + }); + const blockReorgPeriod = await input({ + message: + 'Enter no. of blocks before a transaction has a near-zero chance of reverting(0-500)', + validate: (value) => parseInt(value) >= 0 && parseInt(value) <= 500, + }); + const blockTimeEstimate = await input({ + message: 'Enter the rough estimate of time per block in seconds(0-20)', + validate: (value) => parseInt(value) >= 0 && parseInt(value) <= 20, + }); + metadata.blocks = { + confirmations: parseInt(blockConfirmation, 10), + reorgPeriod: parseInt(blockReorgPeriod, 10), + estimateBlockTime: parseInt(blockTimeEstimate, 10), + }; + } + const wantGasConfig = await confirm({ + message: 'Do you want to add gas config for this chain?', + }); + if (wantGasConfig) { + const isEIP1559 = await confirm({ + message: 'Is your chain an EIP1559 enabled?', + }); + if (isEIP1559) { + const maxFeePerGas = await input({ + message: 'Enter the max fee per gas in gwei', + }); + const maxPriorityFeePerGas = await input({ + message: 'Enter the max priority fee per gas in gwei', + }); + metadata.transactionOverrides = { + maxFeePerGas: BigInt(maxFeePerGas) * BigInt(10 ** 9), + maxPriorityFeePerGas: BigInt(maxPriorityFeePerGas) * BigInt(10 ** 9), + }; + } else { + const gasPrice = await input({ + message: 'Enter the gas price in gwei', + }); + metadata.transactionOverrides = { + gasPrice: BigInt(gasPrice) * BigInt(10 ** 9), + }; + } + } + } const parseResult = ChainMetadataSchema.safeParse(metadata); if (parseResult.success) { logGreen(`Chain config is valid, writing to file ${outPath}`); diff --git a/typescript/cli/src/config/hooks.ts b/typescript/cli/src/config/hooks.ts index 6392d60f95..0c0f49111c 100644 --- a/typescript/cli/src/config/hooks.ts +++ b/typescript/cli/src/config/hooks.ts @@ -9,10 +9,7 @@ import { GasOracleContractType, HookType, HooksConfig, - MultisigConfig, chainMetadata, - defaultMultisigConfigs, - multisigIsmVerificationCost, } from '@hyperlane-xyz/sdk'; import { Address, @@ -83,41 +80,7 @@ export function isValidHookConfigMap(config: any) { return HooksConfigMapSchema.safeParse(config).success; } -export function presetHookConfigs( - owner: Address, - local: ChainName, - destinationChains: ChainName[], - multisigConfig?: MultisigConfig, -): HooksConfig { - const gasOracleType = destinationChains.reduce< - ChainMap - >((acc, chain) => { - acc[chain] = GasOracleContractType.StorageGasOracle; - return acc; - }, {}); - const overhead = destinationChains.reduce>((acc, chain) => { - let validatorThreshold: number; - let validatorCount: number; - if (multisigConfig) { - validatorThreshold = multisigConfig.threshold; - validatorCount = multisigConfig.validators.length; - } else if (local in defaultMultisigConfigs) { - validatorThreshold = defaultMultisigConfigs[local].threshold; - validatorCount = defaultMultisigConfigs[local].validators.length; - } else { - // default values - // fix here: https://github.com/hyperlane-xyz/issues/issues/773 - validatorThreshold = 2; - validatorCount = 3; - } - acc[chain] = multisigIsmVerificationCost( - validatorThreshold, - validatorCount, - ); - return acc; - }, {}); - - // TODO improve types here to avoid need for `as` casts +export function presetHookConfigs(owner: Address): HooksConfig { return { required: { type: HookType.PROTOCOL_FEE, @@ -127,20 +90,7 @@ export function presetHookConfigs( owner: owner, }, default: { - type: HookType.AGGREGATION, - hooks: [ - { - type: HookType.MERKLE_TREE, - }, - { - type: HookType.INTERCHAIN_GAS_PAYMASTER, - owner: owner, - beneficiary: owner, - gasOracleType, - overhead, - oracleKey: owner, - }, - ], + type: HookType.MERKLE_TREE, }, }; } diff --git a/typescript/cli/src/config/ism.ts b/typescript/cli/src/config/ism.ts index 45a111ee26..d035bec83d 100644 --- a/typescript/cli/src/config/ism.ts +++ b/typescript/cli/src/config/ism.ts @@ -3,7 +3,14 @@ import { z } from 'zod'; import { ChainMap, ChainName, IsmType, ZHash } from '@hyperlane-xyz/sdk'; -import { errorRed, log, logBlue, logGreen } from '../../logger.js'; +import { + errorRed, + log, + logBlue, + logBoldUnderlinedRed, + logGreen, + logRed, +} from '../../logger.js'; import { runMultiChainSelectionStep } from '../utils/chains.js'; import { FileFormat, mergeYamlOrJson, readYamlOrJson } from '../utils/files.js'; @@ -108,7 +115,11 @@ export async function createIsmConfigMap({ outPath: string; chainConfigPath: string; }) { - logBlue('Creating a new ISM config'); + logBlue('Creating a new advanced ISM config'); + logBoldUnderlinedRed('WARNING: USE AT YOUR RISK.'); + logRed( + 'Advanced ISM configs require knowledge of different ISM types and how they work together topologically. If possible, use the basic ISM configs are recommended.', + ); const customChains = readChainConfigsIfExists(chainConfigPath); const chains = await runMultiChainSelectionStep( customChains, diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index d187e9b9e7..794d9bdef6 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -15,12 +15,11 @@ import { readChainConfigsIfExists } from './chain.js'; const ConnectionConfigSchema = { mailbox: ZHash.optional(), - interchainGasPaymaster: ZHash.optional(), interchainSecurityModule: ZHash.optional(), foreignDeployment: z.string().optional(), }; -export const WarpRouteConfigSchema = z.object({ +export const WarpRouteDeployConfigSchema = z.object({ base: z.object({ type: z.literal(TokenType.native).or(z.literal(TokenType.collateral)), chainName: z.string(), @@ -44,17 +43,18 @@ export const WarpRouteConfigSchema = z.object({ .nonempty(), }); -type InferredType = z.infer; +type InferredType = z.infer; // A workaround for Zod's terrible typing for nonEmpty arrays -export type WarpRouteConfig = { +export type WarpRouteDeployConfig = { base: InferredType['base']; synthetics: Array; }; -export function readWarpRouteConfig(filePath: string) { +export function readWarpRouteDeployConfig(filePath: string) { const config = readYamlOrJson(filePath); - if (!config) throw new Error(`No warp config found at ${filePath}`); - const result = WarpRouteConfigSchema.safeParse(config); + if (!config) + throw new Error(`No warp route deploy config found at ${filePath}`); + const result = WarpRouteDeployConfigSchema.safeParse(config); if (!result.success) { const firstIssue = result.error.issues[0]; throw new Error( @@ -64,11 +64,11 @@ export function readWarpRouteConfig(filePath: string) { return result.data; } -export function isValidWarpRouteConfig(config: any) { - return WarpRouteConfigSchema.safeParse(config).success; +export function isValidWarpRouteDeployConfig(config: any) { + return WarpRouteDeployConfigSchema.safeParse(config).success; } -export async function createWarpConfig({ +export async function createWarpRouteDeployConfig({ format, outPath, chainConfigPath, @@ -77,7 +77,7 @@ export async function createWarpConfig({ outPath: string; chainConfigPath: string; }) { - logBlue('Creating a new warp route config'); + logBlue('Creating a new warp route deployment config'); const customChains = readChainConfigsIfExists(chainConfigPath); const baseChain = await runSingleChainSelectionStep( customChains, @@ -104,7 +104,7 @@ export async function createWarpConfig({ // TODO add more prompts here to support customizing the token metadata - const result: WarpRouteConfig = { + const result: WarpRouteDeployConfig = { base: { chainName: baseChain, type: baseType, @@ -114,12 +114,12 @@ export async function createWarpConfig({ synthetics: syntheticChains.map((chain) => ({ chainName: chain })), }; - if (isValidWarpRouteConfig(result)) { + if (isValidWarpRouteDeployConfig(result)) { logGreen(`Warp Route config is valid, writing to file ${outPath}`); writeYamlOrJson(outPath, result, format); } else { errorRed( - `Warp config is invalid, please see https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/cli/examples/warp-tokens.yaml for an example`, + `Warp route deployment config is invalid, please see https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/main/typescript/cli/examples/warp-route-deployment.yaml for an example`, ); throw new Error('Invalid multisig config'); } diff --git a/typescript/cli/src/deploy/agent.ts b/typescript/cli/src/deploy/agent.ts index 0680eaf0bc..a3d66865d3 100644 --- a/typescript/cli/src/deploy/agent.ts +++ b/typescript/cli/src/deploy/agent.ts @@ -69,6 +69,7 @@ export async function runKurtosisAgentDeploy({ const kurtosisCloudLink = terminalLink( 'Cmd+Click or Ctrl+Click here', kurtosisCloudUrl, + { fallback: () => kurtosisCloudUrl }, ); logGreen( diff --git a/typescript/cli/src/deploy/core.ts b/typescript/cli/src/deploy/core.ts index 06a8537161..2ae0b5c359 100644 --- a/typescript/cli/src/deploy/core.ts +++ b/typescript/cli/src/deploy/core.ts @@ -21,8 +21,6 @@ import { MultiProvider, MultisigConfig, RoutingIsmConfig, - TestRecipientConfig, - TestRecipientDeployer, buildAgentConfig, buildAggregationIsmConfigs, defaultMultisigConfigs, @@ -31,7 +29,14 @@ import { } from '@hyperlane-xyz/sdk'; import { Address, objFilter, objMerge } from '@hyperlane-xyz/utils'; -import { log, logBlue, logGray, logGreen, logRed } from '../../logger.js'; +import { + log, + logBlue, + logBoldUnderlinedRed, + logGray, + logGreen, + logRed, +} from '../../logger.js'; import { runDeploymentArtifactStep } from '../config/artifacts.js'; import { presetHookConfigs, readHooksConfigMap } from '../config/hooks.js'; import { readIsmConfig } from '../config/ism.js'; @@ -158,9 +163,15 @@ async function runIsmStep( ); } - const isIsm = isZODISMConfig(ismConfigPath); + const isAdvancedIsm = isZODISMConfig(ismConfigPath); // separate flow for 'ism' and 'ism-advanced' options - if (isIsm) { + if (isAdvancedIsm) { + logBoldUnderlinedRed( + 'WARNING: YOU ARE DEPLOYING WITH AN ADVANCED ISM CONFIG', + ); + logRed( + 'Advanced ISM configs require knowledge of different ISM types and how they work together topologically. If possible, use the basic ISM configs are recommended.', + ); const ismConfig = readIsmConfig(ismConfigPath); const requiredIsms = objFilter( ismConfig, @@ -315,7 +326,6 @@ async function executeDeploy({ chains, defaultIsms, hooksConfig, - multisigConfigs, ); const coreContracts = await coreDeployer.deploy(coreConfigs); @@ -331,21 +341,6 @@ async function executeDeploy({ artifacts = writeMergedAddresses(contractsFilePath, artifacts, coreContracts); logGreen('Core contracts deployed'); - // 5. Deploy TestRecipients to all deployable chains - log('Deploying test recipient contracts'); - const testRecipientConfig = buildTestRecipientConfigMap(chains, artifacts); - const testRecipientDeployer = new TestRecipientDeployer(multiProvider); - testRecipientDeployer.cacheAddressesMap(mergedContractAddrs); - const testRecipients = await testRecipientDeployer.deploy( - testRecipientConfig, - ); - artifacts = writeMergedAddresses( - contractsFilePath, - artifacts, - testRecipients, - ); - logGreen('Test recipient contracts deployed'); - log('Writing agent configs'); await writeAgentConfig(agentFilePath, artifacts, chains, multiProvider); logGreen('Agent configs written'); @@ -378,17 +373,9 @@ function buildCoreConfigMap( chains: ChainName[], defaultIsms: ChainMap, hooksConfig: ChainMap, - multisigConfigs: ChainMap, ): ChainMap { return chains.reduce>((config, chain) => { - const hooks = - hooksConfig[chain] ?? - presetHookConfigs( - owner, - chain, - chains.filter((c) => c !== chain), - multisigConfigs[chain], // if no multisig config, uses default 2/3 - ); + const hooks = hooksConfig[chain] ?? presetHookConfigs(owner); config[chain] = { owner, defaultIsm: defaultIsms[chain], @@ -399,22 +386,6 @@ function buildCoreConfigMap( }, {}); } -export function buildTestRecipientConfigMap( - chains: ChainName[], - addressesMap: HyperlaneAddressesMap, -): ChainMap { - return chains.reduce>((config, chain) => { - const interchainSecurityModule = - addressesMap[chain].interchainSecurityModule ?? - ethers.constants.AddressZero; - if (interchainSecurityModule === ethers.constants.AddressZero) { - logRed('Error: No ISM for TestRecipient, deploying with zero address'); - } - config[chain] = { interchainSecurityModule }; - return config; - }, {}); -} - export function buildIgpConfigMap( owner: Address, chains: ChainName[], @@ -468,18 +439,26 @@ async function writeAgentConfig( multiProvider: MultiProvider, ) { const startBlocks: ChainMap = {}; + const core = HyperlaneCore.fromAddressesMap(artifacts, multiProvider); + for (const chain of chains) { - const core = HyperlaneCore.fromAddressesMap(artifacts, multiProvider); const mailbox = core.getContracts(chain).mailbox; startBlocks[chain] = (await mailbox.deployedBlock()).toNumber(); } + const mergedAddressesMap = objMerge( sdkContractAddressesMap, artifacts, ) as ChainMap; + for (const chain of chains) { + if (!mergedAddressesMap[chain].interchainGasPaymaster) { + mergedAddressesMap[chain].interchainGasPaymaster = + ethers.constants.AddressZero; + } + } const agentConfig = buildAgentConfig( - Object.keys(mergedAddressesMap), + chains, // Use only the chains that were deployed to multiProvider, mergedAddressesMap, startBlocks, diff --git a/typescript/cli/src/deploy/types.ts b/typescript/cli/src/deploy/types.ts deleted file mode 100644 index 48517a63d6..0000000000 --- a/typescript/cli/src/deploy/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ERC20Metadata, TokenType } from '@hyperlane-xyz/sdk'; -import type { Address } from '@hyperlane-xyz/utils'; - -export type MinimalTokenMetadata = Omit; - -// Types below must match the Warp UI token config schema -// It is used to generate the configs for the Warp UI -// https://github.com/hyperlane-xyz/hyperlane-warp-ui-template/blob/main/src/features/tokens/types.ts -interface BaseWarpUITokenConfig extends MinimalTokenMetadata { - type: TokenType.collateral | TokenType.native; - chainId: number; - logoURI?: string; - isNft?: boolean; -} - -interface CollateralTokenConfig extends BaseWarpUITokenConfig { - type: TokenType.collateral; - address: Address; - hypCollateralAddress: Address; -} - -interface NativeTokenConfig extends BaseWarpUITokenConfig { - type: TokenType.native; - hypNativeAddress: Address; -} - -export type WarpUITokenConfig = CollateralTokenConfig | NativeTokenConfig; diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index adc70844d6..392bcf7be4 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -1,26 +1,32 @@ import { confirm, input } from '@inquirer/prompts'; import { ethers } from 'ethers'; -import { ERC20__factory, ERC721__factory } from '@hyperlane-xyz/core'; import { ChainMap, ChainName, ConnectionClientConfig, + EvmHypCollateralAdapter, HypERC20Deployer, HypERC721Deployer, HyperlaneContractsMap, + MinimalTokenMetadata, + MultiProtocolProvider, MultiProvider, RouterConfig, + TOKEN_TYPE_TO_STANDARD, TokenConfig, TokenFactories, TokenType, - chainMetadata as defaultChainMetadata, - getChainIdNumber, + WarpCoreConfig, + getTokenConnectionId, } from '@hyperlane-xyz/sdk'; import { Address, ProtocolType, objMap } from '@hyperlane-xyz/utils'; import { log, logBlue, logGray, logGreen } from '../../logger.js'; -import { WarpRouteConfig, readWarpRouteConfig } from '../config/warp.js'; +import { + WarpRouteDeployConfig, + readWarpRouteDeployConfig, +} from '../config/warp.js'; import { MINIMUM_WARP_DEPLOY_GAS } from '../consts.js'; import { getContext, getMergedContractAddresses } from '../context.js'; import { @@ -30,20 +36,19 @@ import { writeJson, } from '../utils/files.js'; -import { MinimalTokenMetadata, WarpUITokenConfig } from './types.js'; import { runPreflightChecks } from './utils.js'; -export async function runWarpDeploy({ +export async function runWarpRouteDeploy({ key, chainConfigPath, - warpConfigPath, + warpRouteDeploymentConfigPath, coreArtifactsPath, outPath, skipConfirmation, }: { key: string; chainConfigPath: string; - warpConfigPath?: string; + warpRouteDeploymentConfigPath?: string; coreArtifactsPath?: string; outPath: string; skipConfirmation: boolean; @@ -55,17 +60,25 @@ export async function runWarpDeploy({ skipConfirmation, }); - if (!warpConfigPath || !isFile(warpConfigPath)) { - if (skipConfirmation) throw new Error('Warp config required'); - warpConfigPath = await runFileSelectionStep( + if ( + !warpRouteDeploymentConfigPath || + !isFile(warpRouteDeploymentConfigPath) + ) { + if (skipConfirmation) + throw new Error('Warp route deployment config required'); + warpRouteDeploymentConfigPath = await runFileSelectionStep( './configs', - 'Warp config', + 'Warp route deployment config', 'warp', ); } else { - log(`Using warp config at ${warpConfigPath}`); + log( + `Using warp route deployment config at ${warpRouteDeploymentConfigPath}`, + ); } - const warpRouteConfig = readWarpRouteConfig(warpConfigPath); + const warpRouteConfig = readWarpRouteDeployConfig( + warpRouteDeploymentConfigPath, + ); const configs = await runBuildConfigStep({ warpRouteConfig, @@ -83,7 +96,7 @@ export async function runWarpDeploy({ skipConfirmation, }; - logBlue('WARP Deployment plan'); + logBlue('Warp route deployment plan'); await runDeployPlanStep(deploymentParams); await runPreflightChecks({ @@ -100,7 +113,7 @@ async function runBuildConfigStep({ coreArtifacts, skipConfirmation, }: { - warpRouteConfig: WarpRouteConfig; + warpRouteConfig: WarpRouteDeployConfig; multiProvider: MultiProvider; signer: ethers.Signer; coreArtifacts?: HyperlaneContractsMap; @@ -239,7 +252,7 @@ async function executeDeploy(params: DeployParams) { const { configMap, isNft, multiProvider, outPath } = params; const [contractsFilePath, tokenConfigPath] = prepNewArtifactsFiles(outPath, [ - { filename: 'warp-deployment', description: 'Contract addresses' }, + { filename: 'warp-route-deployment', description: 'Contract addresses' }, { filename: 'warp-ui-token-config', description: 'Warp UI token config' }, ]); @@ -259,12 +272,11 @@ async function executeDeploy(params: DeployParams) { logBlue(`Warp UI token config is in ${tokenConfigPath}`); } -// TODO move into token classes in the SDK async function fetchBaseTokenMetadata( - base: WarpRouteConfig['base'], + base: WarpRouteDeployConfig['base'], multiProvider: MultiProvider, ): Promise { - const { type, name, symbol, chainName, address, decimals, isNft } = base; + const { type, name, symbol, chainName, address, decimals } = base; // Skip fetching metadata if it's already provided in the config if (name && symbol && decimals) { @@ -272,31 +284,24 @@ async function fetchBaseTokenMetadata( } if (type === TokenType.native) { - return ( - multiProvider.getChainMetadata(base.chainName).nativeToken || - defaultChainMetadata.ethereum.nativeToken! - ); + // If it's a native token, use the chain's native token metadata + const chainNativeToken = + multiProvider.getChainMetadata(chainName).nativeToken; + if (chainNativeToken) return chainNativeToken; + else throw new Error(`No native token metadata for ${chainName}`); } else if (base.type === TokenType.collateral && address) { + // If it's a collateral type, use a TokenAdapter to query for its metadata log(`Fetching token metadata for ${address} on ${chainName}}`); - const provider = multiProvider.getProvider(chainName); - if (isNft) { - const erc721Contract = ERC721__factory.connect(address, provider); - const [name, symbol] = await Promise.all([ - erc721Contract.name(), - erc721Contract.symbol(), - ]); - return { name, symbol, decimals: 0 }; - } else { - const erc20Contract = ERC20__factory.connect(address, provider); - const [name, symbol, decimals] = await Promise.all([ - erc20Contract.name(), - erc20Contract.symbol(), - erc20Contract.decimals(), - ]); - return { name, symbol, decimals }; - } + const adapter = new EvmHypCollateralAdapter( + chainName, + MultiProtocolProvider.fromMultiProvider(multiProvider), + { token: address }, + ); + return adapter.getMetadata(); } else { - throw new Error(`Unsupported token: ${base}`); + throw new Error( + `Unsupported token: ${base}. Consider setting token metadata in your deployment config.`, + ); } } @@ -323,43 +328,46 @@ function writeTokenDeploymentArtifacts( function writeWarpUiTokenConfig( filePath: string, contracts: HyperlaneContractsMap, - { configMap, isNft, metadata, origin, multiProvider }: DeployParams, + { configMap, metadata }: DeployParams, ) { - const baseConfig = configMap[origin]; - const hypTokenAddr = - contracts[origin]?.router?.address || configMap[origin]?.foreignDeployment; - if (!hypTokenAddr) { - throw Error( - 'No base Hyperlane token address deployed and no foreign deployment specified', - ); + const warpCoreConfig: WarpCoreConfig = { tokens: [] }; + + // First pass, create token configs + for (const [chainName, contract] of Object.entries(contracts)) { + const config = configMap[chainName]; + const collateralAddressOrDenom = + config.type === TokenType.collateral ? config.token : undefined; + warpCoreConfig.tokens.push({ + chainName, + standard: TOKEN_TYPE_TO_STANDARD[config.type], + name: metadata.name, + symbol: metadata.symbol, + decimals: metadata.decimals, + addressOrDenom: contract.router.address, + collateralAddressOrDenom, + }); } - const chain = multiProvider.getChainMetadata(origin); - if (chain.protocol !== ProtocolType.Ethereum) throw Error('Unsupported VM'); - const chainMetadata = multiProvider.getChainMetadata(origin); - const commonFields = { - chainId: getChainIdNumber(chainMetadata), - name: metadata.name, - symbol: metadata.symbol, - decimals: metadata.decimals, - }; - let tokenConfig: WarpUITokenConfig; - if (baseConfig.type === TokenType.collateral) { - tokenConfig = { - ...commonFields, - type: TokenType.collateral, - address: baseConfig.token, - hypCollateralAddress: hypTokenAddr, - isNft, - }; - } else if (baseConfig.type === TokenType.native) { - tokenConfig = { - ...commonFields, - type: TokenType.native, - hypNativeAddress: hypTokenAddr, - }; - } else { - throw new Error(`Unsupported token type: ${baseConfig.type}`); + + // Second pass, add connections between tokens + // Assumes full interconnectivity between all tokens for now b.c. that's + // what the deployers do by default. + for (const token1 of warpCoreConfig.tokens) { + for (const token2 of warpCoreConfig.tokens) { + if ( + token1.chainName === token2.chainName && + token1.addressOrDenom === token2.addressOrDenom + ) + continue; + token1.connections ||= []; + token1.connections.push({ + token: getTokenConnectionId( + ProtocolType.Ethereum, + token2.chainName, + token2.addressOrDenom!, + ), + }); + } } - writeJson(filePath, tokenConfig); + writeJson(filePath, warpCoreConfig); } diff --git a/typescript/cli/src/send/message.ts b/typescript/cli/src/send/message.ts index 53051d6c08..563b3de62f 100644 --- a/typescript/cli/src/send/message.ts +++ b/typescript/cli/src/send/message.ts @@ -14,14 +14,13 @@ import { getContext, getMergedContractAddresses } from '../context.js'; import { runPreflightChecks } from '../deploy/utils.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; -const MESSAGE_BODY = '0x48656c6c6f21'; // Hello!' - export async function sendTestMessage({ key, chainConfigPath, coreArtifactsPath, origin, destination, + messageBody, timeoutSec, skipWaitForDelivery, }: { @@ -30,6 +29,7 @@ export async function sendTestMessage({ coreArtifactsPath?: string; origin?: ChainName; destination?: ChainName; + messageBody: string; timeoutSec: number; skipWaitForDelivery: boolean; }) { @@ -67,6 +67,7 @@ export async function sendTestMessage({ executeDelivery({ origin, destination, + messageBody, multiProvider, coreArtifacts, skipWaitForDelivery, @@ -79,12 +80,14 @@ export async function sendTestMessage({ async function executeDelivery({ origin, destination, + messageBody, multiProvider, coreArtifacts, skipWaitForDelivery, }: { origin: ChainName; destination: ChainName; + messageBody: string; multiProvider: MultiProvider; coreArtifacts?: HyperlaneContractsMap; skipWaitForDelivery: boolean; @@ -96,6 +99,14 @@ async function executeDelivery({ ); const mailbox = core.getContracts(origin).mailbox; + let hook = mergedContractAddrs[origin]?.customHook; + if (hook) { + logBlue(`Using custom hook ${hook} for ${origin} -> ${destination}`); + } else { + hook = await mailbox.defaultHook(); + logBlue(`Using default hook ${hook} for ${origin} -> ${destination}`); + } + const destinationDomain = multiProvider.getDomainId(destination); let txReceipt: ethers.ContractReceipt; try { @@ -106,19 +117,29 @@ async function executeDelivery({ const formattedRecipient = addressToBytes32(recipient); log('Getting gas quote'); - const value = await mailbox['quoteDispatch(uint32,bytes32,bytes)']( + const value = await mailbox[ + 'quoteDispatch(uint32,bytes32,bytes,bytes,address)' + ]( destinationDomain, formattedRecipient, - MESSAGE_BODY, + messageBody, + ethers.utils.hexlify([]), + hook, ); log(`Paying for gas with ${value} wei`); log('Dispatching message'); - const messageTx = await mailbox['dispatch(uint32,bytes32,bytes)']( + const messageTx = await mailbox[ + 'dispatch(uint32,bytes32,bytes,bytes,address)' + ]( destinationDomain, formattedRecipient, - MESSAGE_BODY, - { value }, + messageBody, + ethers.utils.hexlify([]), + hook, + { + value, + }, ); txReceipt = await multiProvider.handleTx(origin, messageTx); const message = core.getDispatchedMessages(txReceipt)[0]; diff --git a/typescript/cli/src/send/transfer.ts b/typescript/cli/src/send/transfer.ts index 10a03e47c3..4287e7bca2 100644 --- a/typescript/cli/src/send/transfer.ts +++ b/typescript/cli/src/send/transfer.ts @@ -1,15 +1,19 @@ import { input } from '@inquirer/prompts'; -import { BigNumber, ethers } from 'ethers'; +import { PopulatedTransaction, ethers } from 'ethers'; import { ERC20__factory, HypERC20Collateral__factory, + HypERC20__factory, } from '@hyperlane-xyz/core'; import { ChainName, EvmHypCollateralAdapter, + EvmHypNativeAdapter, + EvmHypSyntheticAdapter, HyperlaneContractsMap, HyperlaneCore, + IHypTokenAdapter, MultiProtocolProvider, MultiProvider, TokenType, @@ -30,7 +34,6 @@ export async function sendTestTransfer({ origin, destination, routerAddress, - tokenType, wei, recipient, timeoutSec, @@ -42,7 +45,6 @@ export async function sendTestTransfer({ origin?: ChainName; destination?: ChainName; routerAddress?: Address; - tokenType: TokenType; wei: string; recipient?: string; timeoutSec: number; @@ -75,20 +77,43 @@ export async function sendTestTransfer({ }); } - if (tokenType === TokenType.collateral) { + // TODO: move to SDK token router app + // deduce TokenType + // 1. decimals() call implies synthetic + // 2. wrappedToken() call implies collateral + // 3. if neither, it's native + let tokenAddress: Address | undefined; + let tokenType: TokenType; + const provider = multiProvider.getProvider(origin); + try { + const synthRouter = HypERC20__factory.connect(routerAddress, provider); + await synthRouter.decimals(); + tokenType = TokenType.synthetic; + tokenAddress = routerAddress; + } catch (error) { + try { + const collateralRouter = HypERC20Collateral__factory.connect( + routerAddress, + provider, + ); + tokenAddress = await collateralRouter.wrappedToken(); + tokenType = TokenType.collateral; + } catch (error) { + tokenType = TokenType.native; + } + } + + if (tokenAddress) { + // checks token balances for collateral and synthetic await assertTokenBalance( multiProvider, signer, origin, - routerAddress, + tokenAddress, wei.toString(), ); - } else if (tokenType === TokenType.native) { - await assertNativeBalances(multiProvider, signer, [origin], wei.toString()); } else { - throw new Error( - 'Only collateral and native token types are currently supported in the CLI. For synthetic transfers, try the Warp UI.', - ); + await assertNativeBalances(multiProvider, signer, [origin], wei.toString()); } await runPreflightChecks({ @@ -154,6 +179,9 @@ async function executeDelivery({ const provider = multiProvider.getProvider(origin); const connectedSigner = signer.connect(provider); + // TODO replace all code below with WarpCore + // https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3259 + if (tokenType === TokenType.collateral) { const wrappedToken = await getWrappedToken(routerAddress, provider); const token = ERC20__factory.connect(wrappedToken, connectedSigner); @@ -164,24 +192,33 @@ async function executeDelivery({ } } - // TODO move next section into MultiProtocolTokenApp when it exists - const adapter = new EvmHypCollateralAdapter( - origin, - MultiProtocolProvider.fromMultiProvider(multiProvider), - { token: routerAddress }, - ); + let adapter: IHypTokenAdapter; + const multiProtocolProvider = + MultiProtocolProvider.fromMultiProvider(multiProvider); + if (tokenType === TokenType.native) { + adapter = new EvmHypNativeAdapter(origin, multiProtocolProvider, { + token: routerAddress, + }); + } else if (tokenType === TokenType.collateral) { + adapter = new EvmHypCollateralAdapter(origin, multiProtocolProvider, { + token: routerAddress, + }); + } else { + adapter = new EvmHypSyntheticAdapter(origin, multiProtocolProvider, { + token: routerAddress, + }); + } + const destinationDomain = multiProvider.getDomainId(destination); - const gasPayment = await adapter.quoteGasPayment(destinationDomain); - const txValue = - tokenType === TokenType.native - ? BigNumber.from(gasPayment).add(wei).toString() - : gasPayment; - const transferTx = await adapter.populateTransferRemoteTx({ + log('Fetching interchain gas quote'); + const interchainGas = await adapter.quoteTransferRemoteGas(destinationDomain); + log('Interchain gas quote:', interchainGas); + const transferTx = (await adapter.populateTransferRemoteTx({ weiAmountOrId: wei, destination: destinationDomain, recipient, - txValue, - }); + interchainGas, + })) as ethers.PopulatedTransaction; const txResponse = await connectedSigner.sendTransaction(transferTx); const txReceipt = await multiProvider.handleTx(origin, txResponse); diff --git a/typescript/cli/src/utils/env.ts b/typescript/cli/src/utils/env.ts new file mode 100644 index 0000000000..6f2fb060a6 --- /dev/null +++ b/typescript/cli/src/utils/env.ts @@ -0,0 +1,9 @@ +import z from 'zod'; + +const envScheme = z.object({ + HYP_KEY: z.string().optional(), +}); + +const parsedEnv = envScheme.safeParse(process.env); + +export const ENV = parsedEnv.success ? parsedEnv.data : {}; diff --git a/typescript/cli/src/utils/version-check.ts b/typescript/cli/src/utils/version-check.ts new file mode 100644 index 0000000000..6d59d77170 --- /dev/null +++ b/typescript/cli/src/utils/version-check.ts @@ -0,0 +1,11 @@ +import latestVersion from 'latest-version'; + +import { log } from '../../logger.js'; +import { VERSION } from '../version.js'; + +export async function checkVersion() { + const currentVersion = await latestVersion('@hyperlane-xyz/cli'); + if (VERSION < currentVersion) { + log(`Your CLI version: ${VERSION}, latest version: ${currentVersion}`); + } +} diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 16f5b96cee..9083009fb7 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.6.0'; +export const VERSION = '3.8.0'; diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index a10ee2c8ec..0cddcd68f5 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,62 @@ # @hyperlane-xyz/helloworld +## 3.8.0 + +### Minor Changes + +- 9681df08d: Enabled verification of contracts as part of the deployment flow. + + - Solidity build artifact is now included as part of the `@hyperlane-xyz/core` package. + - Updated the `HyperlaneDeployer` to perform contract verification immediately after deploying a contract. A default verifier is instantiated using the core build artifact. + - Updated the `HyperlaneIsmFactory` to re-use the `HyperlaneDeployer` for deployment where possible. + - Minor logging improvements throughout deployers. + +### Patch Changes + +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] + - @hyperlane-xyz/sdk@3.8.0 + - @hyperlane-xyz/core@3.8.0 + +## 3.7.0 + +### Patch Changes + +- Updated dependencies [6f464eaed] +- Updated dependencies [87151c62b] +- Updated dependencies [ab17af5f7] +- Updated dependencies [7b40232af] +- Updated dependencies [54aeb6420] + - @hyperlane-xyz/sdk@3.7.0 + - @hyperlane-xyz/core@3.7.0 + +## 3.6.2 + +### Patch Changes + +- @hyperlane-xyz/core@3.6.2 +- @hyperlane-xyz/sdk@3.6.2 + +## 3.6.1 + +### Patch Changes + +- Updated dependencies [ae4476ad0] +- Updated dependencies [f3b7ddb69] +- Updated dependencies [e4e4f93fc] + - @hyperlane-xyz/sdk@3.6.1 + - @hyperlane-xyz/core@3.6.1 + ## 3.6.0 ### Patch Changes diff --git a/typescript/helloworld/hardhat.config.ts b/typescript/helloworld/hardhat.config.ts index 9efa6c0a93..ef7ce035c4 100644 --- a/typescript/helloworld/hardhat.config.ts +++ b/typescript/helloworld/hardhat.config.ts @@ -9,11 +9,7 @@ import 'solidity-coverage'; */ module.exports = { solidity: { - compilers: [ - { - version: '0.8.19', - }, - ], + version: '0.8.19', }, gasReporter: { currency: 'USD', diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 521c7b9c73..cd3b08312e 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,10 +1,10 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "3.6.0", + "version": "3.8.0", "dependencies": { - "@hyperlane-xyz/core": "3.6.0", - "@hyperlane-xyz/sdk": "3.6.0", + "@hyperlane-xyz/core": "3.8.0", + "@hyperlane-xyz/sdk": "3.8.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/helloworld/src/app/app.ts b/typescript/helloworld/src/app/app.ts index 44c1fcceed..be22a55393 100644 --- a/typescript/helloworld/src/app/app.ts +++ b/typescript/helloworld/src/app/app.ts @@ -61,7 +61,7 @@ export class HelloWorldApp extends RouterApp { message, tx, }); - return tx.wait(blocks?.confirmations || 1); + return tx.wait(blocks?.confirmations ?? 1); } async waitForMessageReceipt( diff --git a/typescript/helloworld/src/deploy/deploy.ts b/typescript/helloworld/src/deploy/deploy.ts index 495b46cb57..8b9d67b890 100644 --- a/typescript/helloworld/src/deploy/deploy.ts +++ b/typescript/helloworld/src/deploy/deploy.ts @@ -2,6 +2,7 @@ import { ethers } from 'ethers'; import { ChainName, + ContractVerifier, HyperlaneContracts, HyperlaneIsmFactory, HyperlaneRouterDeployer, @@ -20,8 +21,12 @@ export class HelloWorldDeployer extends HyperlaneRouterDeployer< constructor( multiProvider: MultiProvider, readonly ismFactory?: HyperlaneIsmFactory, + readonly contractVerifier?: ContractVerifier, ) { - super(multiProvider, helloWorldFactories, { ismFactory }); + super(multiProvider, helloWorldFactories, { + ismFactory, + contractVerifier, + }); } router(contracts: HyperlaneContracts): HelloWorld { diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 825e4e99c8..3cf7094b89 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,83 @@ # @hyperlane-xyz/infra +## 3.8.0 + +### Minor Changes + +- 9681df08d: Remove support for goerli networks (including optimismgoerli, arbitrumgoerli, lineagoerli and polygonzkevmtestnet) +- 9681df08d: Enabled verification of contracts as part of the deployment flow. + + - Solidity build artifact is now included as part of the `@hyperlane-xyz/core` package. + - Updated the `HyperlaneDeployer` to perform contract verification immediately after deploying a contract. A default verifier is instantiated using the core build artifact. + - Updated the `HyperlaneIsmFactory` to re-use the `HyperlaneDeployer` for deployment where possible. + - Minor logging improvements throughout deployers. + +### Patch Changes + +- 9681df08d: Removed basegoerli and moonbasealpha testnets +- 9681df08d: Add logos for plume to SDK +- 9681df08d: TestRecipient as part of core deployer +- 9681df08d: Update viction validator set +- 9681df08d: Patch transfer ownership in hook deployer +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] + - @hyperlane-xyz/sdk@3.8.0 + - @hyperlane-xyz/helloworld@3.8.0 + - @hyperlane-xyz/utils@3.8.0 + +## 3.7.0 + +### Minor Changes + +- 54aeb6420: Added warp route artifacts type adopting registry schema + +### Patch Changes + +- 87151c62b: Bumped injective reorg period +- ab17af5f7: Updating HyperlaneIgpDeployer to configure storage gas oracles as part of deployment +- Updated dependencies [6f464eaed] +- Updated dependencies [87151c62b] +- Updated dependencies [ab17af5f7] +- Updated dependencies [7b40232af] +- Updated dependencies [54aeb6420] + - @hyperlane-xyz/sdk@3.7.0 + - @hyperlane-xyz/helloworld@3.7.0 + - @hyperlane-xyz/utils@3.7.0 + +## 3.6.2 + +### Patch Changes + +- @hyperlane-xyz/helloworld@3.6.2 +- @hyperlane-xyz/sdk@3.6.2 +- @hyperlane-xyz/utils@3.6.2 + +## 3.6.1 + +### Patch Changes + +- ae4476ad0: Bumped mantapacific reorgPeriod to 1, a reorg period in chain metadata is now required by infra. +- e4e4f93fc: Support pausable ISM in deployer and checker +- Updated dependencies [3c298d064] +- Updated dependencies [ae4476ad0] +- Updated dependencies [f3b7ddb69] +- Updated dependencies [df24eec8b] +- Updated dependencies [78e50e7da] +- Updated dependencies [e4e4f93fc] + - @hyperlane-xyz/utils@3.6.1 + - @hyperlane-xyz/sdk@3.6.1 + - @hyperlane-xyz/helloworld@3.6.1 + ## 3.6.0 ### Patch Changes diff --git a/typescript/infra/config/aw-multisig.json b/typescript/infra/config/aw-multisig.json new file mode 100644 index 0000000000..0fe535cf90 --- /dev/null +++ b/typescript/infra/config/aw-multisig.json @@ -0,0 +1,172 @@ +{ + "alfajores": { + "validators": [ + "0x2233a5ce12f814bd64c9cdd73410bb8693124d40", + "0xba279f965489d90f90490e3c49e860e0b43c2ae6", + "0x86485dcec5f7bb8478dd251676372d054dea6653" + ] + }, + "arbitrum": { + "validators": [ + "0x4d966438fe9e2b1e7124c87bbb90cb4f0f6c59a1", + "0x6333e110b8a261cab28acb43030bcde59f26978a", + "0x3369e12edd52570806f126eb50be269ba5e65843" + ] + }, + "avalanche": { + "validators": [ + "0x3fb8263859843bffb02950c492d492cae169f4cf", + "0xe58c63ad669b946e7c8211299f22679deecc9c83", + "0x6c754f1e9cd8287088b46a7c807303d55d728b49" + ] + }, + "base": { + "validators": [ + "0xb9453d675e0fa3c178a17b4ce1ad5b1a279b3af9", + "0x4512985a574cb127b2af2d4bb676876ce804e3f8", + "0xb144bb2f599a5af095bc30367856f27ea8a8adc7" + ] + }, + "bsc": { + "validators": [ + "0x570af9b7b36568c8877eebba6c6727aa9dab7268", + "0x7bf928d5d262365d31d64eaa24755d48c3cae313", + "0x03047213365800f065356b4a2fe97c3c3a52296a" + ] + }, + "bsctestnet": { + "validators": [ + "0x242d8a855a8c932dec51f7999ae7d1e48b10c95e", + "0xf620f5e3d25a3ae848fec74bccae5de3edcd8796", + "0x1f030345963c54ff8229720dd3a711c15c554aeb" + ] + }, + "celo": { + "validators": [ + "0x63478422679303c3e4fc611b771fa4a707ef7f4a", + "0x2f4e808744df049d8acc050628f7bdd8265807f9", + "0x7bf30afcb6a7d92146d5a910ea4c154fba38d25e" + ] + }, + "eclipsetestnet": { + "validators": ["0xf344f34abca9a444545b5295066348a0ae22dda3"] + }, + "ethereum": { + "validators": [ + "0x03c842db86a6a3e524d4a6615390c1ea8e2b9541", + "0x4346776b10f5e0d9995d884b7a1dbaee4e24c016", + "0x749d6e7ad949e522c92181dc77f7bbc1c5d71506" + ] + }, + "fuji": { + "validators": [ + "0xd8154f73d04cc7f7f0c332793692e6e6f6b2402e", + "0x895ae30bc83ff1493b9cf7781b0b813d23659857", + "0x43e915573d9f1383cbf482049e4a012290759e7f" + ] + }, + "gnosis": { + "validators": [ + "0xd4df66a859585678f2ea8357161d896be19cc1ca", + "0x06a833508579f8b59d756b3a1e72451fc70840c3", + "0xb93a72cee19402553c9dd7fed2461aebd04e2454" + ] + }, + "inevm": { + "validators": [ + "0xf9e35ee88e4448a3673b4676a4e153e3584a08eb", + "0xae3e6bb6b3ece1c425aa6f47adc8cb0453c1f9a2", + "0xd98c9522cd9d3e3e00bee05ff76c34b91b266ec3" + ] + }, + "injective": { + "validators": [ + "0xbfb8911b72cfb138c7ce517c57d9c691535dc517", + "0x6faa139c33a7e6f53cb101f6b2ae392298283ed2", + "0x0115e3a66820fb99da30d30e2ce52a453ba99d92" + ] + }, + "mantapacific": { + "validators": [ + "0x8e668c97ad76d0e28375275c41ece4972ab8a5bc", + "0x80afdde2a81f3fb056fd088a97f0af3722dbc4f3", + "0x5dda0c4cf18de3b3ab637f8df82b24921082b54c" + ] + }, + "moonbeam": { + "validators": [ + "0x2225e2f4e9221049456da93b71d2de41f3b6b2a8", + "0x4fe067bb455358e295bfcfb92519a6f9de94b98e", + "0xcc4a78aa162482bea43313cd836ba7b560b44fc4" + ] + }, + "mumbai": { + "validators": [ + "0xebc301013b6cd2548e347c28d2dc43ec20c068f2", + "0x315db9868fc8813b221b1694f8760ece39f45447", + "0x17517c98358c5937c5d9ee47ce1f5b4c2b7fc9f5" + ] + }, + "neutron": { + "validators": [ + "0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0", + "0x60e890b34cb44ce3fa52f38684f613f31b47a1a6", + "0x7885fae56dbcf5176657f54adbbd881dc6714132" + ] + }, + "optimism": { + "validators": [ + "0x20349eadc6c72e94ce38268b96692b1a5c20de4f", + "0x04d040cee072272789e2d1f29aef73b3ad098db5", + "0x779a17e035018396724a6dec8a59bda1b5adf738" + ] + }, + "plumetestnet": { + "validators": [ + "0xe765a214849f3ecdf00793b97d00422f2d408ea6", + "0xb59998f71efc65190a85ac5e81b66bd72a192a3b", + "0xc906470a73e6b5aad65a4ceb4acd73e3eaf80e2c" + ] + }, + "polygon": { + "validators": [ + "0x12ecb319c7f4e8ac5eb5226662aeb8528c5cefac", + "0x8dd8f8d34b5ecaa5f66de24b01acd7b8461c3916", + "0xdbf3666de031bea43ec35822e8c33b9a9c610322" + ] + }, + "polygonzkevm": { + "validators": [ + "0x86f2a44592bb98da766e880cfd70d3bbb295e61a", + "0xc84076030bdabaabb9e61161d833dd84b700afda", + "0x6a1da2e0b7ae26aaece1377c0a4dbe25b85fa3ca" + ] + }, + "scroll": { + "validators": [ + "0xad557170a9f2f21c35e03de07cb30dcbcc3dff63", + "0xb37fe43a9f47b7024c2d5ae22526cc66b5261533", + "0x7210fa0a6be39a75cb14d682ebfb37e2b53ecbe5" + ] + }, + "scrollsepolia": { + "validators": [ + "0xbe18dbd758afb367180260b524e6d4bcd1cb6d05", + "0x9a11ed23ae962974018ab45bc133caabff7b3271", + "0x7867bea3c9761fe64e6d124b171f91fd5dd79644" + ] + }, + "sepolia": { + "validators": [ + "0xb22b65f202558adf86a8bb2847b76ae1036686a5", + "0x469f0940684d147defc44f3647146cb90dd0bc8e", + "0xd3c75dcf15056012a4d74c483a0c6ea11d8c2b83" + ] + }, + "solanatestnet": { + "validators": ["0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5"] + }, + "viction": { + "validators": ["0x1f87c368f8e05a85ef9126d984a980a20930cb9c"] + } +} diff --git a/typescript/infra/config/environments/agents.ts b/typescript/infra/config/environments/agents.ts new file mode 100644 index 0000000000..f9f7055aa7 --- /dev/null +++ b/typescript/infra/config/environments/agents.ts @@ -0,0 +1,9 @@ +import { agents as mainnet3Agents } from './mainnet3/agent'; +import { agents as testAgents } from './test/agent'; +import { agents as testnet4Agents } from './testnet4/agent'; + +export const agents = { + mainnet3: mainnet3Agents, + testnet4: testnet4Agents, + test: testAgents, +}; diff --git a/typescript/infra/config/environments/helloworld.ts b/typescript/infra/config/environments/helloworld.ts new file mode 100644 index 0000000000..104b3c01ee --- /dev/null +++ b/typescript/infra/config/environments/helloworld.ts @@ -0,0 +1,7 @@ +import { helloWorld as mainnet3HelloWorld } from './mainnet3/helloworld'; +import { helloWorld as testnet4HelloWorld } from './testnet4/helloworld'; + +export const helloworld = { + mainnet3: mainnet3HelloWorld, + testnet4: testnet4HelloWorld, +}; diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 6c1afac1a2..3d83b0a941 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -1,29 +1,115 @@ import { + Chains, GasPaymentEnforcementPolicyType, RpcConsensusType, chainMetadata, getDomainId, } from '@hyperlane-xyz/sdk'; -import { RootAgentConfig, allAgentChainNames } from '../../../src/config'; -import { GasPaymentEnforcementConfig } from '../../../src/config/agent/relayer'; +import { + AgentChainConfig, + RootAgentConfig, + getAgentChainNamesFromConfig, +} from '../../../src/config'; +import { + GasPaymentEnforcementConfig, + routerMatchingList, +} from '../../../src/config/agent/relayer'; import { ALL_KEY_ROLES, Role } from '../../../src/roles'; import { Contexts } from '../../contexts'; -import { agentChainNames, environment } from './chains'; +import { environment, supportedChainNames } from './chains'; +import { helloWorld } from './helloworld'; import { validatorChainConfig } from './validators'; +import arbitrumTIAAddresses from './warp/arbitrum-TIA-addresses.json'; +import injectiveInevmAddresses from './warp/injective-inevm-addresses.json'; +import mantaTIAAddresses from './warp/manta-TIA-addresses.json'; -// const releaseCandidateHelloworldMatchingList = routerMatchingList( -// helloWorld[Contexts.ReleaseCandidate].addresses, -// ); +const releaseCandidateHelloworldMatchingList = routerMatchingList( + helloWorld[Contexts.ReleaseCandidate].addresses, +); const repo = 'gcr.io/abacus-labs-dev/hyperlane-agent'; +// The chains here must be consistent with the environment's supportedChainNames, which is +// checked / enforced at runtime & in the CI pipeline. +// +// This is intentionally separate and not derived from the environment's supportedChainNames +// to allow for more fine-grained control over which chains are enabled for each agent role. +export const hyperlaneContextAgentChainConfig: AgentChainConfig = { + // Generally, we run all production validators in the Hyperlane context. + [Role.Validator]: { + [Chains.arbitrum]: true, + [Chains.avalanche]: true, + [Chains.bsc]: true, + [Chains.celo]: true, + [Chains.ethereum]: true, + [Chains.neutron]: true, + [Chains.mantapacific]: true, + [Chains.moonbeam]: true, + [Chains.optimism]: true, + [Chains.polygon]: true, + [Chains.gnosis]: true, + [Chains.base]: true, + [Chains.scroll]: true, + [Chains.polygonzkevm]: true, + [Chains.injective]: true, + [Chains.inevm]: true, + [Chains.viction]: true, + }, + [Role.Relayer]: { + [Chains.arbitrum]: true, + [Chains.avalanche]: true, + [Chains.bsc]: true, + [Chains.celo]: true, + [Chains.ethereum]: true, + // At the moment, we only relay between Neutron and Manta Pacific on the neutron context. + [Chains.neutron]: false, + [Chains.mantapacific]: false, + [Chains.moonbeam]: true, + [Chains.optimism]: true, + [Chains.polygon]: true, + [Chains.gnosis]: true, + [Chains.base]: true, + [Chains.scroll]: true, + [Chains.polygonzkevm]: true, + [Chains.injective]: true, + [Chains.inevm]: true, + [Chains.viction]: true, + }, + [Role.Scraper]: { + [Chains.arbitrum]: true, + [Chains.avalanche]: true, + [Chains.bsc]: true, + [Chains.celo]: true, + [Chains.ethereum]: true, + // Cannot scrape non-EVM chains + [Chains.neutron]: false, + [Chains.mantapacific]: true, + [Chains.moonbeam]: true, + [Chains.optimism]: true, + [Chains.polygon]: true, + [Chains.gnosis]: true, + [Chains.base]: true, + [Chains.scroll]: true, + [Chains.polygonzkevm]: true, + // Cannot scrape non-EVM chains + [Chains.injective]: false, + [Chains.inevm]: true, + // Has RPC non-compliance that breaks scraping. + [Chains.viction]: false, + }, +}; + +export const hyperlaneContextAgentChainNames = getAgentChainNamesFromConfig( + hyperlaneContextAgentChainConfig, + supportedChainNames, +); + const contextBase = { namespace: environment, runEnv: environment, - contextChainNames: agentChainNames, - environmentChainNames: allAgentChainNames(agentChainNames), + environmentChainNames: supportedChainNames, aws: { region: 'us-east-1', }, @@ -38,19 +124,41 @@ const gasPaymentEnforcement: GasPaymentEnforcementConfig[] = [ const hyperlane: RootAgentConfig = { ...contextBase, context: Contexts.Hyperlane, + contextChainNames: hyperlaneContextAgentChainNames, rolesWithKeys: ALL_KEY_ROLES, relayer: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + // Includes Cosmos block-by-block indexing. + tag: 'c2bf423-20240308-164604', }, - gasPaymentEnforcement, + gasPaymentEnforcement: [ + // Temporary measure to ensure all inEVM warp route messages are delivered - + // we saw some issues with IGP indexing. + { + type: GasPaymentEnforcementPolicyType.None, + matchingList: routerMatchingList(injectiveInevmAddresses), + }, + ...gasPaymentEnforcement, + ], + metricAppContexts: [ + { + name: 'helloworld', + matchingList: routerMatchingList( + helloWorld[Contexts.Hyperlane].addresses, + ), + }, + { + name: 'injective_inevm_inj', + matchingList: routerMatchingList(injectiveInevmAddresses), + }, + ], }, validators: { docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '9736164-20240307-131918', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -59,7 +167,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '54aeb64-20240206-163119', }, }, }; @@ -67,14 +175,15 @@ const hyperlane: RootAgentConfig = { const releaseCandidate: RootAgentConfig = { ...contextBase, context: Contexts.ReleaseCandidate, + contextChainNames: hyperlaneContextAgentChainNames, rolesWithKeys: [Role.Relayer, Role.Kathy, Role.Validator], relayer: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '9736164-20240307-131918', }, - // whitelist: releaseCandidateHelloworldMatchingList, + whitelist: releaseCandidateHelloworldMatchingList, gasPaymentEnforcement, transactionGasLimit: 750000, // Skipping arbitrum because the gas price estimates are inclusive of L1 @@ -84,7 +193,7 @@ const releaseCandidate: RootAgentConfig = { validators: { docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '9736164-20240307-131918', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.ReleaseCandidate), @@ -108,7 +217,8 @@ const neutron: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '67585a2-20231220-223937', + // Includes Cosmos block-by-block indexing. + tag: '9736164-20240307-131918', }, gasPaymentEnforcement: [ { @@ -130,6 +240,16 @@ const neutron: RootAgentConfig = { }, ...gasPaymentEnforcement, ], + metricAppContexts: [ + { + name: 'manta_tia', + matchingList: routerMatchingList(mantaTIAAddresses), + }, + { + name: 'arbitrum_tia', + matchingList: routerMatchingList(arbitrumTIAAddresses), + }, + ], }, }; diff --git a/typescript/infra/config/environments/mainnet3/chains.ts b/typescript/infra/config/environments/mainnet3/chains.ts index 437fe170c2..73a1681a06 100644 --- a/typescript/infra/config/environments/mainnet3/chains.ts +++ b/typescript/infra/config/environments/mainnet3/chains.ts @@ -1,16 +1,32 @@ -import { ChainMap, ChainMetadata, chainMetadata } from '@hyperlane-xyz/sdk'; +import { + ChainMap, + ChainMetadata, + Mainnets, + chainMetadata, +} from '@hyperlane-xyz/sdk'; -import { AgentChainNames, Role } from '../../../src/roles'; +import { getChainMetadatas } from '../../../src/config/chain'; + +// The `Mainnets` from the SDK are all supported chains for the mainnet3 environment. +// These chains may be any protocol type. +export const supportedChainNames = Mainnets; + +export type MainnetChains = (typeof supportedChainNames)[number]; +export const environment = 'mainnet3'; + +const { + ethereumMetadatas: defaultEthereumMainnetConfigs, + nonEthereumMetadatas: nonEthereumMainnetConfigs, +} = getChainMetadatas(supportedChainNames); export const ethereumMainnetConfigs: ChainMap = { + ...defaultEthereumMainnetConfigs, bsc: { ...chainMetadata.bsc, transactionOverrides: { gasPrice: 7 * 10 ** 9, // 7 gwei }, }, - avalanche: chainMetadata.avalanche, - base: chainMetadata.base, polygon: { ...chainMetadata.polygon, blocks: { @@ -18,16 +34,11 @@ export const ethereumMainnetConfigs: ChainMap = { confirmations: 3, }, transactionOverrides: { - maxFeePerGas: 500 * 10 ** 9, // 500 gwei - maxPriorityFeePerGas: 100 * 10 ** 9, // 100 gwei + maxFeePerGas: 250 * 10 ** 9, // 250 gwei + maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei // gasPrice: 50 * 10 ** 9, // 50 gwei }, }, - polygonzkevm: chainMetadata.polygonzkevm, - scroll: chainMetadata.scroll, - celo: chainMetadata.celo, - arbitrum: chainMetadata.arbitrum, - optimism: chainMetadata.optimism, ethereum: { ...chainMetadata.ethereum, blocks: { @@ -39,43 +50,13 @@ export const ethereumMainnetConfigs: ChainMap = { maxPriorityFeePerGas: 5 * 10 ** 9, // gwei }, }, - moonbeam: chainMetadata.moonbeam, - gnosis: chainMetadata.gnosis, - mantapacific: chainMetadata.mantapacific, -}; - -// Blessed non-Ethereum chains. -export const nonEthereumMainnetConfigs: ChainMap = { - // solana: chainMetadata.solana, - neutron: chainMetadata.neutron, }; export const mainnetConfigs: ChainMap = { ...ethereumMainnetConfigs, - // ...nonEthereumMainnetConfigs, + ...nonEthereumMainnetConfigs, }; -export type MainnetChains = keyof typeof mainnetConfigs; -export const supportedChainNames = Object.keys( - mainnetConfigs, -) as MainnetChains[]; -export const environment = 'mainnet3'; - export const ethereumChainNames = Object.keys( ethereumMainnetConfigs, ) as MainnetChains[]; - -// Remove mantapacific, as it's not considered a "blessed" -// chain. It's not included in the scraper domains table, -// and we don't relay to mantapacific on the Hyperlane or RC contexts. -const hyperlaneContextRelayChains = ethereumChainNames.filter( - (chainName) => chainName !== chainMetadata.mantapacific.name, -); - -// Hyperlane & RC context agent chain names. -export const agentChainNames: AgentChainNames = { - // Run validators for all chains. - [Role.Validator]: supportedChainNames, - [Role.Relayer]: hyperlaneContextRelayChains, - [Role.Scraper]: hyperlaneContextRelayChains, -}; diff --git a/typescript/infra/config/environments/mainnet3/core.ts b/typescript/infra/config/environments/mainnet3/core.ts index c228b2d94e..8332de9250 100644 --- a/typescript/infra/config/environments/mainnet3/core.ts +++ b/typescript/infra/config/environments/mainnet3/core.ts @@ -5,6 +5,7 @@ import { AggregationIsmConfig, ChainMap, CoreConfig, + FallbackRoutingHookConfig, HookType, IgpHookConfig, IsmType, @@ -21,7 +22,7 @@ import { objMap } from '@hyperlane-xyz/utils'; import { supportedChainNames } from './chains'; import { igp } from './igp'; -import { owners, safes } from './owners'; +import { DEPLOYER, owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { const originMultisigs: ChainMap = Object.fromEntries( @@ -50,12 +51,12 @@ export const core: ChainMap = objMap(owners, (local, owner) => { threshold: 1, }), ), - owner, + ...owner, }; const pausableIsm: PausableIsmConfig = { type: IsmType.PAUSABLE, - owner, + owner: DEPLOYER, // keep pausable hot }; const defaultIsm: AggregationIsmConfig = { @@ -75,32 +76,34 @@ export const core: ChainMap = objMap(owners, (local, owner) => { const pausableHook: PausableHookConfig = { type: HookType.PAUSABLE, - owner, + owner: DEPLOYER, // keep pausable hot }; - - const defaultHook: AggregationHookConfig = { - type: HookType.AGGREGATION, - hooks: [pausableHook, merkleHook, igpHook], + const aggregationHooks = objMap( + originMultisigs, + (_origin, _): AggregationHookConfig => ({ + type: HookType.AGGREGATION, + hooks: [pausableHook, merkleHook, igpHook], + }), + ); + const defaultHook: FallbackRoutingHookConfig = { + type: HookType.FALLBACK_ROUTING, + ...owner, + domains: aggregationHooks, + fallback: merkleHook, }; const requiredHook: ProtocolFeeHookConfig = { type: HookType.PROTOCOL_FEE, maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token protocolFee: BigNumber.from(0).toString(), // 0 wei - beneficiary: owner, - owner, + beneficiary: owner.owner, + ...owner, }; return { - owner, defaultIsm, defaultHook, requiredHook, - ownerOverrides: { - proxyAdmin: - local === 'arbitrum' - ? `0xAC98b0cD1B64EA4fe133C6D2EDaf842cE5cF4b01` - : safes[local] ?? owner, - }, + ...owner, }; }); diff --git a/typescript/infra/config/environments/mainnet3/core/verification.json b/typescript/infra/config/environments/mainnet3/core/verification.json index bee9dfbdf0..c559233f17 100644 --- a/typescript/infra/config/environments/mainnet3/core/verification.json +++ b/typescript/infra/config/environments/mainnet3/core/verification.json @@ -119,6 +119,54 @@ "address": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false } ], "arbitrum": [ @@ -241,6 +289,54 @@ "address": "0x1df063280C4166AF9a725e3828b4dAC6c7113B08", "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", + "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000748040afb89b8fdbb992799808215419d36a0930", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", + "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000748040afb89b8fdbb992799808215419d36a0930", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", + "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000748040afb89b8fdbb992799808215419d36a0930", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", + "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000748040afb89b8fdbb992799808215419d36a0930", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "constructorArguments": "", + "isProxy": false } ], "base": [ @@ -339,6 +435,54 @@ "address": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000019dc38aeae620380430c200a6e990d5af5480117", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000019dc38aeae620380430c200a6e990d5af5480117", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000019dc38aeae620380430c200a6e990d5af5480117", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000019dc38aeae620380430c200a6e990d5af5480117", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "constructorArguments": "", + "isProxy": false } ], "avalanche": [ @@ -461,6 +605,54 @@ "address": "0x9Cad0eC82328CEE2386Ec14a12E81d070a27712f", "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd6", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", + "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd6000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000084eea61d679f42d92145fa052c89900cbacce95a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", + "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd6000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000084eea61d679f42d92145fa052c89900cbacce95a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", + "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd6000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000084eea61d679f42d92145fa052c89900cbacce95a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", + "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd6000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000084eea61d679f42d92145fa052c89900cbacce95a", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "constructorArguments": "", + "isProxy": false } ], "scroll": [ @@ -619,6 +811,54 @@ "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "constructorArguments": "", + "isProxy": false } ], "polygonzkevm": [ @@ -777,6 +1017,42 @@ "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "constructorArguments": "", + "isProxy": false } ], "bsc": [ @@ -871,6 +1147,54 @@ "address": "0x87ED6926abc9E38b9C7C19f835B41943b622663c", "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", + "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002684c6f89e901987e1fdb7649dc5be0c57c61645", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", + "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002684c6f89e901987e1fdb7649dc5be0c57c61645", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", + "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002684c6f89e901987e1fdb7649dc5be0c57c61645", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", + "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000002684c6f89e901987e1fdb7649dc5be0c57c61645", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "constructorArguments": "", + "isProxy": false } ], "celo": [ @@ -1115,6 +1439,54 @@ "address": "0xCe74905e51497b4adD3639366708b821dcBcff96", "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000048e6c30b97748d1e2e03bf3e9fbe3890ca5f8cca", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000048e6c30b97748d1e2e03bf3e9fbe3890ca5f8cca", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000048e6c30b97748d1e2e03bf3e9fbe3890ca5f8cca", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000048e6c30b97748d1e2e03bf3e9fbe3890ca5f8cca", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "constructorArguments": "", + "isProxy": false } ], "moonbeam": [ @@ -1237,6 +1609,54 @@ "address": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "constructorArguments": "", + "isProxy": false } ], "polygon": [ @@ -1367,6 +1787,358 @@ "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f112", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x7556a0E61d577D921Cba8Fca0d7D6299d36E607E", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "FallbackRoutingHook", + "address": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f112", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0x7556a0E61d577D921Cba8Fca0d7D6299d36E607E", + "constructorArguments": "", + "isProxy": false + } + ], + "viction": [ + { + "name": "ProxyAdmin", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "ProtocolFee", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "ProxyAdmin", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "ProtocolFee", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "ProxyAdmin", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "ProtocolFee", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + } + ], + "inevm": [ + { + "name": "ProxyAdmin", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "Mailbox", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000009dd", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "000000000000000000000000481171eb1aad17ede6a56005b7f1ab00c581ef130000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "MerkleTreeHook", + "address": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "StorageGasOracle", + "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "InterchainGasPaymaster", + "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "TransparentUpgradeableProxy", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "000000000000000000000000481171eb1aad17ede6a56005b7f1ab00c581ef130000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true + }, + { + "name": "ProtocolFee", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false + }, + { + "name": "ValidatorAnnounce", + "address": "0x15ab173bDB6832f9b64276bA128659b0eD77730B", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false + }, + { + "name": "PausableHook", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false } ] } diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index ef558f7dad..84d38631cb 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -9,7 +9,7 @@ import { environment } from './chains'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'f6da03e-20231216-141949', + tag: 'c037206-20240220-152500', }, // We're currently using the same deployer key as mainnet. // To minimize nonce clobbering we offset the key funder cron @@ -21,7 +21,41 @@ export const keyFunderConfig: KeyFunderConfig = { contextFundingFrom: Contexts.Hyperlane, contextsAndRolesToFund: { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], - // [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], + [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], }, connectionType: RpcConsensusType.Single, + // desired balance config + desiredBalancePerChain: { + avalanche: '5', + bsc: '5', + celo: '3', + ethereum: '0.5', + gnosis: '5', + inevm: '3', + moonbeam: '5', + polygon: '20', + viction: '3', + // Funder boosts itself upto 5x balance on L2 before dispersing funds + arbitrum: '0.5', + base: '0.5', + optimism: '0.5', + polygonzkevm: '0.5', + scroll: '0.5', + }, + desiredKathyBalancePerChain: { + arbitrum: '0.1', + avalanche: '6', + base: '0.05', + bsc: '0.35', + celo: '150', + ethereum: '0.4', + gnosis: '100', + inevm: '0.05', + moonbeam: '250', + optimism: '0.1', + polygon: '85', + polygonzkevm: '0.05', + scroll: '0.05', + viction: '0.05', + }, }; diff --git a/typescript/infra/config/environments/mainnet3/gas-oracle.ts b/typescript/infra/config/environments/mainnet3/gas-oracle.ts index 955f5814cf..e27166fbcc 100644 --- a/typescript/infra/config/environments/mainnet3/gas-oracle.ts +++ b/typescript/infra/config/environments/mainnet3/gas-oracle.ts @@ -1,6 +1,7 @@ import { BigNumber, ethers } from 'ethers'; import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; +import { objMap } from '@hyperlane-xyz/utils'; import { AllStorageGasOracleConfigs, @@ -12,87 +13,18 @@ import { } from '../../../src/config/gas-oracle'; import { supportedChainNames } from './chains'; - -// Taken by looking at each network's gas history and overestimating -// Last updated Mar 9, 2023. -const gasPrices: ChainMap = { - // https://bscscan.com/chart/gasprice - bsc: ethers.utils.parseUnits('3', 'gwei'), - // https://snowtrace.io/chart/gasprice - avalanche: ethers.utils.parseUnits('35', 'gwei'), - // https://polygonscan.com/chart/gasprice - polygon: ethers.utils.parseUnits('300', 'gwei'), - // https://celoscan.io/chart/gasprice - // This one is interesting - the average is high (~20 gwei) - // but the median is low (< 10). This is likely because a popular wallet is - // overpaying, but all our txs tend to be < 10 gwei. - celo: ethers.utils.parseUnits('10', 'gwei'), - // https://dune.com/Henrystats/arbitrum-metrics - // A bit higher to try to account for L1 fees - arbitrum: ethers.utils.parseUnits('1', 'gwei'), - // https://dune.com/optimismfnd/optimism-l1-batch-submission-fees-security-costs - // A bit higher to try to account for L1 fees - optimism: ethers.utils.parseUnits('1', 'gwei'), - // https://dune.com/hildobby/Gas - ethereum: ethers.utils.parseUnits('35', 'gwei'), - // https://moonscan.io/chart/gasprice - // Similar to Celo - average is ~200 gwei, but people - // generally are overpaying compared to us - moonbeam: ethers.utils.parseUnits('150', 'gwei'), - // https://gnosisscan.io/chart/gasprice - // People also seem to be overpaying here - gnosis: ethers.utils.parseUnits('10', 'gwei'), - // Arbitrarily chosen as gas prices aren't really a thing - // in Solana. - solana: ethers.BigNumber.from('28'), - base: ethers.utils.parseUnits('1', 'gwei'), - scroll: ethers.utils.parseUnits('1', 'gwei'), - polygonzkevm: ethers.utils.parseUnits('2', 'gwei'), - neutron: ethers.utils.parseUnits('1', 'gwei'), - mantapacific: ethers.utils.parseUnits('1', 'gwei'), -}; - -// Accurate from coingecko as of Mar 9, 2023. -// These aren't overestimates because the exchange rates between -// tokens are what matters. These generally have high beta -const tokenUsdPrices: ChainMap = { - // https://www.coingecko.com/en/coins/bnb - bsc: ethers.utils.parseUnits('230.55', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/avalanche - avalanche: ethers.utils.parseUnits('20.25', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/polygon - polygon: ethers.utils.parseUnits('0.75', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/celo - celo: ethers.utils.parseUnits('0.52', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - arbitrum: ethers.utils.parseUnits('2000.00', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - optimism: ethers.utils.parseUnits('2000.00', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - ethereum: ethers.utils.parseUnits('2000.00', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/moonbeam - moonbeam: ethers.utils.parseUnits('0.266', TOKEN_EXCHANGE_RATE_DECIMALS), - // xDAI - gnosis: ethers.utils.parseUnits('1.00', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/solana - solana: ethers.utils.parseUnits('58.85', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - base: ethers.utils.parseUnits('2000.00', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - scroll: ethers.utils.parseUnits('2000.00', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - polygonzkevm: ethers.utils.parseUnits( - '2000.00', - TOKEN_EXCHANGE_RATE_DECIMALS, - ), - // https://www.coingecko.com/en/coins/neutron - neutron: ethers.utils.parseUnits('0.304396', TOKEN_EXCHANGE_RATE_DECIMALS), - // https://www.coingecko.com/en/coins/ethereum - mantapacific: ethers.utils.parseUnits( - '1619.00', - TOKEN_EXCHANGE_RATE_DECIMALS, - ), -}; +import rawGasPrices from './gasPrices.json'; +import rawTokenPrices from './tokenPrices.json'; + +const gasPrices: ChainMap = objMap(rawGasPrices, (_, gasPrice) => + ethers.utils.parseUnits(gasPrice, 'gwei'), +); + +const tokenUsdPrices: ChainMap = objMap( + rawTokenPrices, + (_, tokenUsdPrice) => + ethers.utils.parseUnits(tokenUsdPrice, TOKEN_EXCHANGE_RATE_DECIMALS), +); // Gets the exchange rate of the remote quoted in local tokens function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber { diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json new file mode 100644 index 0000000000..1d647540cc --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -0,0 +1,19 @@ +{ + "arbitrum": "0.1", + "avalanche": "43.212830197", + "bsc": "1.350070319", + "celo": "10.0", + "ethereum": "26.346912847", + "mantapacific": "0.100000057", + "moonbeam": "125.0", + "optimism": "0.003225814", + "polygon": "61.601287856", + "gnosis": "1.852997796", + "base": "0.0010003", + "scroll": "0.46", + "polygonzkevm": "3.95", + "inevm": "0.1", + "viction": "0.25", + "neutron": "0.1", + "injective": "0.1" +} diff --git a/typescript/infra/config/environments/mainnet3/helloworld.ts b/typescript/infra/config/environments/mainnet3/helloworld.ts index c8e346796f..82cf5692ac 100644 --- a/typescript/infra/config/environments/mainnet3/helloworld.ts +++ b/typescript/infra/config/environments/mainnet3/helloworld.ts @@ -20,12 +20,12 @@ export const hyperlane: HelloWorldConfig = { namespace: environment, runConfig: { mode: HelloWorldKathyRunMode.Service, - fullCycleTime: 1000 * 60 * 60 * 24, // every 24 hours + fullCycleTime: 1000 * 60 * 60 * 24 * 5, // every 5 days, 13 * 12 messages = 156 messages is little less than once an hour }, messageSendTimeout: 1000 * 60 * 8, // 8 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min connectionType: RpcConsensusType.Fallback, - cyclesBetweenEthereumMessages: 3, // Skip 3 cycles of Ethereum, i.e. send/receive Ethereum messages every 32 hours. + cyclesBetweenEthereumMessages: 1, // Skip 1 cycle of Ethereum, i.e. send/receive Ethereum messages every 5 days (not great since we still send like 12 in that cycle) }, }; @@ -34,7 +34,7 @@ export const releaseCandidate: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '86b7f98-20231207-153806', + tag: '0e3f73f-20240206-160718', }, chainsToSkip: [], runEnv: environment, @@ -44,7 +44,7 @@ export const releaseCandidate: HelloWorldConfig = { }, messageSendTimeout: 1000 * 60 * 8, // 8 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min - connectionType: RpcConsensusType.Single, + connectionType: RpcConsensusType.Fallback, }, }; diff --git a/typescript/infra/config/environments/mainnet3/helloworld/hyperlane/verification.json b/typescript/infra/config/environments/mainnet3/helloworld/hyperlane/verification.json index 8df25db64d..37b6b7e5be 100644 --- a/typescript/infra/config/environments/mainnet3/helloworld/hyperlane/verification.json +++ b/typescript/infra/config/environments/mainnet3/helloworld/hyperlane/verification.json @@ -7,13 +7,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xB97d3bF2fC296c2cAC4056bBC8A783ff39408e20", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x22Fd11F93F0303346c9b9070cc67C4Bc7aB2dABB", "constructorArguments": "0000000000000000000000002971b9aec44be4eb673df1b88cdb57b96eefe8a40000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -27,13 +27,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x2A925CD8a5d919c5c6599633090c37fe38A561b6", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xd54fF402ADf0a7CBad9626B1261bF4bEB26A437a", "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd60000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -47,13 +47,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x6c0aC8cEA75232aa7BeD8cbe9C4f820E7a77a9C3", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x42ad84C5d2a90e574678958133B8a13CA9F44AAF", "constructorArguments": "0000000000000000000000005d934f4e2f797775e53561bb72aca21ba36b96bb0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -67,13 +67,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x4151773Db70C0b2D4c43Ea44A5FB5803ff1d3e0B", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x10Add66Db9C837000A43F3601aa9c54c6744F4c8", "constructorArguments": "00000000000000000000000050da3b3907a08a24fe4999f4dcf337e8dc7954bb0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -87,13 +87,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x96271cA0ab9eeFB3Ca481749c0Ca4c705fD4F523", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xb2BFFD0248C7a0029860A29fD4Efe762a22f93Af", "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb90000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -107,13 +107,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xA6f0A37DFDe9C2c8F46F010989C47d9edB3a9FA8", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x3da5fDCcC661c84454f49dB0Cf519561BC7c2729", "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -127,13 +127,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x9311cEE522A7C122B843b66cC31C6a63e2F92641", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x7ff2bF58C38A41AD7C9CbC14e780e8a7EDBbd48D", "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d2390000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -147,13 +147,13 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xAe067C08703508230357025B38c35Cd12793628c", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xE25d0D46a42a106Cdf4943262644DD5E26fa7bfF", "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc30000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -167,19 +167,19 @@ "isProxy": false }, { - "name": "router", + "name": "HelloWorld", "address": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4", "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xA166219dF110BDA97b91e65D4BB4Aae4159978b9", "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -187,7 +187,7 @@ ], "base": [ { - "name": "Router", + "name": "HelloWorld", "address": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162", "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -195,7 +195,7 @@ ], "scroll": [ { - "name": "Router", + "name": "HelloWorld", "address": "0x0be2Ae2f6D02a3e0e00ECB57D3E1fCbb7f8F38F4", "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a70000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -203,7 +203,7 @@ ], "polygonzkevm": [ { - "name": "Router", + "name": "HelloWorld", "address": "0xaad207a0Fd7a4e3C927Ccc78ac8134baF586B852", "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false diff --git a/typescript/infra/config/environments/mainnet3/helloworld/rc/addresses.json b/typescript/infra/config/environments/mainnet3/helloworld/rc/addresses.json index 0967ef424b..d87d332c9a 100644 --- a/typescript/infra/config/environments/mainnet3/helloworld/rc/addresses.json +++ b/typescript/infra/config/environments/mainnet3/helloworld/rc/addresses.json @@ -1 +1,47 @@ -{} +{ + "polygon": { + "router": "0xd5D06f8Ee9cfab362e5758A0A925db7470E7D22f" + }, + "bsc": { + "router": "0xC728F24AA2442d6230c9785635b81fF73C1a16Db" + }, + "arbitrum": { + "router": "0x4D172025D810DDD770e7464A41673ca8e75539b0" + }, + "optimism": { + "router": "0x8Fc3AdBF87c74dF6142f6D65aE0f8BFe042BBDd0" + }, + "moonbeam": { + "router": "0x1dA36d5c79Ae8Bc43eC080FeD9B4Dbb91b509834" + }, + "gnosis": { + "router": "0x9B0C41777A0fC5dd040BC8B043991Eb168f3bD9C" + }, + "base": { + "router": "0x6756189BDE3a29bb56466DECb50BBA76543D8752" + }, + "scroll": { + "router": "0x5366362c41e34869BDa231061603E4356D66079D" + }, + "polygonzkevm": { + "router": "0x03cF708E42C89623bd83B281A56935cB562b9258" + }, + "celo": { + "router": "0xcfacC141f090E5441D8F274659D43ec20F748b19" + }, + "ethereum": { + "router": "0xC6B1e375550343cDA762d2efD4EbdB3B8609a7a4" + }, + "avalanche": { + "router": "0x24fb6e8E7F8298696BaeE10B15bB57295a1f1e35" + }, + "mantapacific": { + "router": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762" + }, + "viction": { + "router": "0x83c2DB237e93Ce52565AB110124f78fdf159E3f4" + }, + "inevm": { + "router": "0x0BD07E3934D1C4cc8Db0eA2a5cDAc8C8d8eb9824" + } +} diff --git a/typescript/infra/config/environments/mainnet3/helloworld/rc/verification.json b/typescript/infra/config/environments/mainnet3/helloworld/rc/verification.json index bd533c7149..a95fa4d775 100644 --- a/typescript/infra/config/environments/mainnet3/helloworld/rc/verification.json +++ b/typescript/infra/config/environments/mainnet3/helloworld/rc/verification.json @@ -1,127 +1,121 @@ { - "bsc": [ - { - "name": "router", - "address": "0x3f4663873A9aC7Ec683a5Bddc0acbC00091c10D0", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", - "isProxy": false - }, + "polygon": [ { - "name": "router", - "address": "0xe5554478F167936dB253f79f57c41770bfa00Bae", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0xd5D06f8Ee9cfab362e5758A0A925db7470E7D22f", + "constructorArguments": "0000000000000000000000005d934f4e2f797775e53561bb72aca21ba36b96bb0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "avalanche": [ - { - "name": "router", - "address": "0xaC5a4925d8aab7B9fb33F0a1722e3b94b6f87dB4", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", - "isProxy": false - }, + "bsc": [ { - "name": "router", - "address": "0xe1De9910fe71cC216490AC7FCF019e13a34481D7", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0xC728F24AA2442d6230c9785635b81fF73C1a16Db", + "constructorArguments": "0000000000000000000000002971b9aec44be4eb673df1b88cdb57b96eefe8a40000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "polygon": [ + "arbitrum": [ { - "name": "router", - "address": "0x3Ee06Da5110117c5B9AD41e2F827B774cBb15CC3", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x4D172025D810DDD770e7464A41673ca8e75539b0", + "constructorArguments": "000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb90000000000000000000000000000000000000000000000000000000000000000", "isProxy": false - }, + } + ], + "optimism": [ { - "name": "router", - "address": "0xAb65C41a1BC580a52f0b166879122EFdce0cB868", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x8Fc3AdBF87c74dF6142f6D65aE0f8BFe042BBDd0", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "celo": [ + "moonbeam": [ { - "name": "router", - "address": "0x8E10405F4D23060b1a75005EFB99d99D537e0A7f", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x1dA36d5c79Ae8Bc43eC080FeD9B4Dbb91b509834", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc30000000000000000000000000000000000000000000000000000000000000000", "isProxy": false - }, + } + ], + "gnosis": [ { - "name": "router", - "address": "0xfE29f6a4468536029Fc9c97d3a9669b9fe38E114", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x9B0C41777A0fC5dd040BC8B043991Eb168f3bD9C", + "constructorArguments": "000000000000000000000000ad09d78f4c6b9da2ae82b1d34107802d380bb74f0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "arbitrum": [ + "base": [ { - "name": "router", - "address": "0xb44a2B834FFdf762051Ee26Eb41531a4A02fA8d0", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x6756189BDE3a29bb56466DECb50BBA76543D8752", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false - }, + } + ], + "scroll": [ { - "name": "router", - "address": "0x414B67F62b143d6db6E9b633168Dd6fd4DA20642", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x5366362c41e34869BDa231061603E4356D66079D", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a70000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "optimism": [ + "polygonzkevm": [ { - "name": "router", - "address": "0xe5F7E241F9bb6A644e88f2ca38fC373196b5392b", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x03cF708E42C89623bd83B281A56935cB562b9258", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false - }, + } + ], + "celo": [ { - "name": "router", - "address": "0xB4caf2CA864B413DAA502fA18A8D48cD0740fC52", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0xcfacC141f090E5441D8F274659D43ec20F748b19", + "constructorArguments": "00000000000000000000000050da3b3907a08a24fe4999f4dcf337e8dc7954bb0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], "ethereum": [ { - "name": "router", - "address": "0x43ae568363c4FA6897EE9dF0c9ca445d3872c906", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", - "isProxy": false - }, - { - "name": "router", - "address": "0xed31c20c5517EaC05decD5F6dCd01Fe6d16fD09D", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0xC6B1e375550343cDA762d2efD4EbdB3B8609a7a4", + "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d2390000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "moonbeam": [ + "avalanche": [ { - "name": "router", - "address": "0x1Efd1EdC42ef6cf2612F75e6C599C081d650c513", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x24fb6e8E7F8298696BaeE10B15bB57295a1f1e35", + "constructorArguments": "000000000000000000000000ff06afcaabaddd1fb08371f9cca15d73d51febd60000000000000000000000000000000000000000000000000000000000000000", "isProxy": false - }, + } + ], + "mantapacific": [ { - "name": "router", - "address": "0x3eB9eE2CFC8DCB6F58B5869D33336CFcBf1dC354", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "gnosis": [ + "viction": [ { - "name": "router", - "address": "0x14aBb1f28B06272c57c37723D0e671d1c3326679", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x83c2DB237e93Ce52565AB110124f78fdf159E3f4", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a70000000000000000000000000000000000000000000000000000000000000000", "isProxy": false - }, + } + ], + "inevm": [ { - "name": "router", - "address": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a", - "constructorArguments": "00000000000000000000000035231d4c2d8b8adcb5617a638a0c4548684c7c7000000000000000000000000056f52c0a1ddcd557285f7cbc782d3d83096ce1cc", + "name": "HelloWorld", + "address": "0x0BD07E3934D1C4cc8Db0eA2a5cDAc8C8d8eb9824", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a70000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ] diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index aee860b461..c6b66f7c25 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -1,6 +1,5 @@ import { ChainMap, - GasOracleContractType, IgpConfig, defaultMultisigConfigs, multisigIsmVerificationCost, @@ -12,34 +11,33 @@ import { ethereumChainNames, supportedChainNames, } from './chains'; -import { owners } from './owners'; +import { storageGasOracleConfig } from './gas-oracle'; +import { DEPLOYER, owners } from './owners'; -// TODO: make this generic -const KEY_FUNDER_ADDRESS = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; -const DEPLOYER_ADDRESS = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; +const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen -function getGasOracles(local: MainnetChains) { - return Object.fromEntries( - exclude(local, supportedChainNames).map((name) => [ - name, - GasOracleContractType.StorageGasOracle, - ]), - ); -} +const remoteOverhead = (remote: MainnetChains) => + ethereumChainNames.includes(remote) + ? multisigIsmVerificationCost( + defaultMultisigConfigs[remote].threshold, + defaultMultisigConfigs[remote].validators.length, + ) + : FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead -export const igp: ChainMap = objMap(owners, (chain, owner) => ({ - owner, - oracleKey: DEPLOYER_ADDRESS, - beneficiary: KEY_FUNDER_ADDRESS, - gasOracleType: getGasOracles(chain), +export const igp: ChainMap = objMap(owners, (local, owner) => ({ + ...owner, + ownerOverrides: { + ...owner.ownerOverrides, + interchainGasPaymaster: DEPLOYER, + storageGasOracle: DEPLOYER, + }, + oracleKey: DEPLOYER, + beneficiary: DEPLOYER, overhead: Object.fromEntries( - // Not setting overhead for non-Ethereum destination chains - exclude(chain, ethereumChainNames).map((remote) => [ + exclude(local, supportedChainNames).map((remote) => [ remote, - multisigIsmVerificationCost( - defaultMultisigConfigs[remote].threshold, - defaultMultisigConfigs[remote].validators.length, - ), + remoteOverhead(remote as MainnetChains), ]), ), + oracleConfig: storageGasOracleConfig[local], })); diff --git a/typescript/infra/config/environments/mainnet3/igp/verification.json b/typescript/infra/config/environments/mainnet3/igp/verification.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/igp/verification.json @@ -0,0 +1 @@ +{} diff --git a/typescript/infra/config/environments/mainnet3/index.ts b/typescript/infra/config/environments/mainnet3/index.ts index 3253309025..127af12260 100644 --- a/typescript/infra/config/environments/mainnet3/index.ts +++ b/typescript/infra/config/environments/mainnet3/index.ts @@ -1,9 +1,10 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; +import { ChainMetadata, RpcConsensusType } from '@hyperlane-xyz/sdk'; +import { ProtocolType, objFilter } from '@hyperlane-xyz/utils'; import { getKeysForRole, getMultiProviderForRole, -} from '../../../scripts/utils'; +} from '../../../scripts/agent-utils'; import { EnvironmentConfig } from '../../../src/config'; import { Role } from '../../../src/roles'; import { Contexts } from '../../contexts'; @@ -12,7 +13,6 @@ import { agents } from './agent'; import { environment as environmentName, mainnetConfigs } from './chains'; import { core } from './core'; import { keyFunderConfig } from './funding'; -import { storageGasOracleConfig } from './gas-oracle'; import { helloWorld } from './helloworld'; import { igp } from './igp'; import { infrastructure } from './infrastructure'; @@ -26,15 +26,22 @@ export const environment: EnvironmentConfig = { context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, connectionType?: RpcConsensusType, - ) => - getMultiProviderForRole( + ) => { + const config = objFilter( mainnetConfigs, + (_, chainMetadata): chainMetadata is ChainMetadata => + chainMetadata.protocol === ProtocolType.Ethereum, + ); + + return getMultiProviderForRole( + config, environmentName, context, role, undefined, connectionType, - ), + ); + }, getKeys: ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, @@ -46,7 +53,6 @@ export const environment: EnvironmentConfig = { infra: infrastructure, helloWorld, keyFunderConfig, - storageGasOracleConfig, liquidityLayerConfig: { bridgeAdapters: bridgeAdapterConfigs, relayer: relayerConfig, diff --git a/typescript/infra/config/environments/mainnet3/ism/verification.json b/typescript/infra/config/environments/mainnet3/ism/verification.json index 658a2085a9..1b8f42b360 100644 --- a/typescript/infra/config/environments/mainnet3/ism/verification.json +++ b/typescript/infra/config/environments/mainnet3/ism/verification.json @@ -2177,82 +2177,132 @@ "isProxy": true }, { - "name": "RoutingIsmFactory", - "address": "0x8061Af3A459093540d17823D651BC5E2A92669a7", + "name": "DomaingRoutingIsm", + "address": "0xE8d610DC4Baf01070FD2f223d45f84d8801D90B1", + "constructorArguments": "", + "isProxy": true + } + ], + "viction": [ + { + "name": "MerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "constructorArguments": "", "isProxy": false }, { - "name": "DomaingRoutingIsm", - "address": "0x2214b2AC96215d2FF1fffC84E4Af295d7497B013", + "name": "StaticMerkleRootMultisigIsm", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", "constructorArguments": "", "isProxy": true }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x28336d2b8783f2373bCFc173058EA932bf3b901C", + "name": "MessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "constructorArguments": "", - "isProxy": true + "isProxy": false }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x76FD8c164F380107631160d8Fd1f4Edc2719004D", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", "constructorArguments": "", "isProxy": true }, + { + "name": "AggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, { "name": "StaticAggregationIsm", - "address": "0xd5FF00DD9E737c9d1a197246738876fAF43e4aC0", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", "constructorArguments": "", "isProxy": true }, + { + "name": "AggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, { "name": "StaticAggregationHook", - "address": "0x568De5f1639Fe7c9eba67f1191DE19eeCc77985B", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", "constructorArguments": "", "isProxy": true }, + { + "name": "RoutingIsmFactory", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false + }, { "name": "DomaingRoutingIsm", - "address": "0x2214b2AC96215d2FF1fffC84E4Af295d7497B013", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", "constructorArguments": "", "isProxy": true } ], - "mantapacific": [ + "inevm": [ + { + "name": "MerkleRootMultisigIsmFactory", + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false + }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", "constructorArguments": "", "isProxy": true }, + { + "name": "MessageIdMultisigIsmFactory", + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false + }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", "constructorArguments": "", "isProxy": true }, + { + "name": "AggregationIsmFactory", + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false + }, { "name": "StaticAggregationIsm", - "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", "constructorArguments": "", "isProxy": true }, + { + "name": "AggregationHookFactory", + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false + }, { "name": "StaticAggregationHook", - "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", "constructorArguments": "", "isProxy": true }, { "name": "RoutingIsmFactory", - "address": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "constructorArguments": "", "isProxy": false }, { "name": "DomaingRoutingIsm", - "address": "0x6C360932873629929ff3D9ffB70bD4cbC1606541", + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", "constructorArguments": "", "isProxy": true } diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index e78ad2003a..8f9c21615a 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -1,7 +1,14 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; -import { Address, objMap } from '@hyperlane-xyz/utils'; +import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; -export const safes: ChainMap

= { +import { ethereumChainNames } from './chains'; + +export const timelocks: ChainMap
= { + arbitrum: '0xAC98b0cD1B64EA4fe133C6D2EDaf842cE5cF4b01', +}; + +export const safes: ChainMap
= { + mantapacific: '0x03ed2D65f2742193CeD99D48EbF1F1D6F12345B6', // does not have a UI celo: '0x1DE69322B55AC7E0999F8e7738a1428C8b130E4d', ethereum: '0x12C5AB61Fe17dF9c65739DBa73dF294708f78d23', avalanche: '0xDF9B28B76877f1b1B4B8a11526Eb7D8D7C49f4f3', @@ -11,16 +18,21 @@ export const safes: ChainMap
= { optimism: '0xb523CFAf45AACF472859f8B793CB0BFDB16bD257', moonbeam: '0xF0cb1f968Df01fc789762fddBfA704AE0F952197', gnosis: '0x36b0AA0e7d04e7b825D7E409FEa3c9A3d57E4C22', + inevm: '0x77F3863ea99F2360D84d4BA1A2E441857D0357fa', // caldera + injective + // injective: 'inj1632x8j35kenryam3mkrsez064sqg2y2fr0frzt', // solana: 'EzppBFV2taxWw8kEjxNYvby6q7W1biJEqwP3iC7YgRe3', - // TODO: create gnosis safes here - base: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - scroll: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - polygonzkevm: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', - mantapacific: '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba', }; -// export const owners = safes; +export const DEPLOYER = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; -// temporarily keep ownership on deployer key -const deployer = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; -export const owners = objMap(safes, (_, __) => deployer); +export const owners: ChainMap = Object.fromEntries( + ethereumChainNames.map((local) => [ + local, + { + owner: safes[local] ?? DEPLOYER, + ownerOverrides: { + proxyAdmin: timelocks[local] ?? safes[local] ?? DEPLOYER, + }, + }, + ]), +); diff --git a/typescript/infra/config/environments/mainnet3/token-bridge.ts b/typescript/infra/config/environments/mainnet3/token-bridge.ts index 16e568a413..7dc12ce63f 100644 --- a/typescript/infra/config/environments/mainnet3/token-bridge.ts +++ b/typescript/infra/config/environments/mainnet3/token-bridge.ts @@ -8,22 +8,11 @@ import { } from '@hyperlane-xyz/sdk'; const circleDomainMapping = [ - { - hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), - circleDomain: 0, - }, { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), circleDomain: 1 }, ]; // Circle deployed contracts export const circleBridgeAdapterConfig: ChainMap = { - [Chains.goerli]: { - type: BridgeAdapterType.Circle, - tokenMessengerAddress: '0xdabec94b97f7b5fca28f050cc8eeac2dc9920476', - messageTransmitterAddress: '0x40a61d3d2afcf5a5d31fcdf269e575fb99dd87f7', - usdcAddress: '0x07865c6e87b9f70255377e024ace6630c1eaa37f', - circleDomainMapping, - }, [Chains.fuji]: { type: BridgeAdapterType.Circle, tokenMessengerAddress: '0x0fc1103927af27af808d03135214718bcedbe9ad', diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json new file mode 100644 index 0000000000..c7365ff84d --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -0,0 +1,19 @@ +{ + "arbitrum": "2914.61", + "avalanche": "35.71", + "bsc": "373.19", + "celo": "0.772619", + "ethereum": "2914.61", + "mantapacific": "2914.61", + "moonbeam": "0.419658", + "optimism": "2914.61", + "polygon": "0.982687", + "gnosis": "1.012", + "base": "2914.61", + "scroll": "2914.61", + "polygonzkevm": "2914.61", + "inevm": "32.77", + "viction": "0.750231", + "neutron": "1.76", + "injective": "32.77" +} diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index 61cf6f3f1b..c6c44d6a6c 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -1,4 +1,4 @@ -import { chainMetadata } from '@hyperlane-xyz/sdk'; +import { chainMetadata, getReorgPeriod } from '@hyperlane-xyz/sdk'; import { ValidatorBaseChainConfigMap } from '../../../src/config/agent'; import { Contexts } from '../../contexts'; @@ -13,7 +13,7 @@ export const validatorChainConfig = ( return { celo: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.celo), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -21,7 +21,11 @@ export const validatorChainConfig = ( '0x2f4e808744df049d8acc050628f7bdd8265807f9', '0x7bf30afcb6a7d92146d5a910ea4c154fba38d25e', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0xb51768c1388e976486a43dbbbbf9ce04cf45e990', + '0x6325de37b33e20089c091950518a471e29c52883', + '0xd796c1d4fcfb3c63acfa6e4113aa6ae1399b337c', + ], [Contexts.Neutron]: [], }, 'celo', @@ -29,7 +33,7 @@ export const validatorChainConfig = ( }, ethereum: { interval: 5, - reorgPeriod: 20, + reorgPeriod: getReorgPeriod(chainMetadata.ethereum), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -37,7 +41,11 @@ export const validatorChainConfig = ( '0x4346776b10f5e0d9995d884b7a1dbaee4e24c016', '0x749d6e7ad949e522c92181dc77f7bbc1c5d71506', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x0580884289890805802012b9872afa5ae41a5fa6', + '0xa5465cb5095a2e6093587e644d6121d6ed55c632', + '0x87cf8a85465118aff9ec728ca157798201b1e368', + ], [Contexts.Neutron]: [], }, 'ethereum', @@ -45,7 +53,7 @@ export const validatorChainConfig = ( }, avalanche: { interval: 5, - reorgPeriod: 3, + reorgPeriod: getReorgPeriod(chainMetadata.avalanche), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -54,7 +62,9 @@ export const validatorChainConfig = ( '0x6c754f1e9cd8287088b46a7c807303d55d728b49', ], [Contexts.ReleaseCandidate]: [ - '0x706976391e23dea28152e0207936bd942aba01ce', + '0x2c7cf6d1796e37676ba95f056ff21bf536c6c2d3', + '0xcd250d48d16e2ce4b939d44b5215f9e978975152', + '0x26691cd3e9c1b8a82588606b31d9d69b14cb2729', ], [Contexts.Neutron]: [], }, @@ -63,7 +73,7 @@ export const validatorChainConfig = ( }, polygon: { interval: 5, - reorgPeriod: 256, + reorgPeriod: getReorgPeriod(chainMetadata.polygon), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -71,7 +81,11 @@ export const validatorChainConfig = ( '0x8dd8f8d34b5ecaa5f66de24b01acd7b8461c3916', '0xdbf3666de031bea43ec35822e8c33b9a9c610322', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0xf0a990959f833ccde624c8bcd4c7669286a57a0f', + '0x456b636bdde99d69176261d7a4fba42c16f57f56', + '0xe78d3681d4f59e0768be8b1171f920ed4d52409f', + ], [Contexts.Neutron]: [], }, 'polygon', @@ -79,7 +93,7 @@ export const validatorChainConfig = ( }, bsc: { interval: 5, - reorgPeriod: 15, + reorgPeriod: getReorgPeriod(chainMetadata.bsc), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -87,7 +101,11 @@ export const validatorChainConfig = ( '0x7bf928d5d262365d31d64eaa24755d48c3cae313', '0x03047213365800f065356b4a2fe97c3c3a52296a', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x911dfcc19dd5b723e84be452f6af52adef020bc8', + '0xee2d4fd5fe2170e51c6279552297117feaeb19e1', + '0x50ff94984161976a13e9ec3b2a7647da5319448f', + ], [Contexts.Neutron]: [], }, 'bsc', @@ -95,7 +113,7 @@ export const validatorChainConfig = ( }, arbitrum: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.arbitrum), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -103,7 +121,11 @@ export const validatorChainConfig = ( '0x6333e110b8a261cab28acb43030bcde59f26978a', '0x3369e12edd52570806f126eb50be269ba5e65843', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0xb4c18167c163391facb345bb069d12d0430a6a89', + '0x2f6dc057ae079997f76205903b85c8302164a78c', + '0x229d4dc6a740212da746b0e35314419a24bc2a5b', + ], [Contexts.Neutron]: [], }, 'arbitrum', @@ -111,7 +133,7 @@ export const validatorChainConfig = ( }, optimism: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.optimism), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -120,7 +142,9 @@ export const validatorChainConfig = ( '0x779a17e035018396724a6dec8a59bda1b5adf738', ], [Contexts.ReleaseCandidate]: [ - '0x60e938bf280bbc21bacfd8bf435459d9003a8f98', + '0x7e4391786e0b5b0cbaada12d32c931e46e44f104', + '0x138ca73e805afa14e85d80f6e35c46e6f235429e', + '0x2d58cdb2bed9aac57b488b1bad06839ddc280a78', ], [Contexts.Neutron]: [], }, @@ -129,7 +153,7 @@ export const validatorChainConfig = ( }, moonbeam: { interval: 5, - reorgPeriod: 2, + reorgPeriod: getReorgPeriod(chainMetadata.moonbeam), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -137,7 +161,11 @@ export const validatorChainConfig = ( '0x4fe067bb455358e295bfcfb92519a6f9de94b98e', '0xcc4a78aa162482bea43313cd836ba7b560b44fc4', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x75e3cd4e909089ae6c9f3a42b1468b33eec84161', + '0xc28418d0858a82a46a11e07db75f8bf4eed43881', + '0xcaa9c6e6efa35e4a8b47565f3ce98845fa638bf3', + ], [Contexts.Neutron]: [], }, 'moonbeam', @@ -145,7 +173,7 @@ export const validatorChainConfig = ( }, gnosis: { interval: 5, - reorgPeriod: 14, + reorgPeriod: getReorgPeriod(chainMetadata.gnosis), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -153,7 +181,11 @@ export const validatorChainConfig = ( '0x06a833508579f8b59d756b3a1e72451fc70840c3', '0xb93a72cee19402553c9dd7fed2461aebd04e2454', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0xd5122daa0c3dfc94a825ae928f3ea138cdb6a2e1', + '0x2d1f367e942585f8a1c25c742397dc8be9a61dee', + '0x2111141b7f985d305f392c502ad52dd74ef9c569', + ], [Contexts.Neutron]: [], }, 'gnosis', @@ -161,7 +193,7 @@ export const validatorChainConfig = ( }, base: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.base), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -169,15 +201,55 @@ export const validatorChainConfig = ( '0x4512985a574cb127b2af2d4bb676876ce804e3f8', '0xb144bb2f599a5af095bc30367856f27ea8a8adc7', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0xa8363570749080c7faa1de714e0782ff444af4cc', + '0x3b55d9febe02a9038ef8c867fa8bbfdd8d70f9b8', + '0xed7703e06572768bb09e03d88e6b788d8800b9fb', + ], [Contexts.Neutron]: [], }, 'base', ), }, + injective: { + interval: 5, + reorgPeriod: getReorgPeriod(chainMetadata.injective), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: [ + '0xbfb8911b72cfb138c7ce517c57d9c691535dc517', + '0x6faa139c33a7e6f53cb101f6b2ae392298283ed2', + '0x0115e3a66820fb99da30d30e2ce52a453ba99d92', + ], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'injective', + ), + }, + inevm: { + interval: 5, + reorgPeriod: getReorgPeriod(chainMetadata.inevm), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: [ + '0xf9e35ee88e4448a3673b4676a4e153e3584a08eb', + '0xae3e6bb6b3ece1c425aa6f47adc8cb0453c1f9a2', + '0xd98c9522cd9d3e3e00bee05ff76c34b91b266ec3', + ], + [Contexts.ReleaseCandidate]: [ + '0x52a0376903294c796c091c785a66c62943d99aa8', + '0xc2ea1799664f753bedb9872d617e3ebc60b2e0ab', + '0xe83d36fd00d9ef86243d9f7147b29e98d11df0ee', + ], + [Contexts.Neutron]: [], + }, + 'inevm', + ), + }, scroll: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.scroll), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -185,7 +257,11 @@ export const validatorChainConfig = ( '0xb37fe43a9f47b7024c2d5ae22526cc66b5261533', '0x7210fa0a6be39a75cb14d682ebfb37e2b53ecbe5', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x11387d89856219cf685f22781bf4e85e00468d54', + '0x64b98b96ccae6e660ecf373b5dd61bcc34fd19ee', + '0x07c2f32a402543badc3141f6b98969d75ef2ac28', + ], [Contexts.Neutron]: [], }, 'scroll', @@ -193,7 +269,7 @@ export const validatorChainConfig = ( }, polygonzkevm: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.polygonzkevm), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -201,7 +277,11 @@ export const validatorChainConfig = ( '0xc84076030bdabaabb9e61161d833dd84b700afda', '0x6a1da2e0b7ae26aaece1377c0a4dbe25b85fa3ca', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x75cffb90391d7ecf58a84e9e70c67e7b306211c0', + '0x82c10acb56f3d7ed6738b61668111a6b5250283e', + '0x1cd73544c000fd519784f56e59bc380a5fef53d6', + ], [Contexts.Neutron]: [], }, 'polygonzkevm', @@ -209,7 +289,7 @@ export const validatorChainConfig = ( }, neutron: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.neutron), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -217,7 +297,11 @@ export const validatorChainConfig = ( '0x60e890b34cb44ce3fa52f38684f613f31b47a1a6', '0x7885fae56dbcf5176657f54adbbd881dc6714132', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x307a8fe091b8273c7ce3d277b161b4a2167279b1', + '0xb825c1bd020cb068f477b320f591b32e26814b5b', + '0x0a5b31090d4c3c207b9ea6708f938e328f895fce', + ], [Contexts.Neutron]: [], }, 'neutron', @@ -225,7 +309,7 @@ export const validatorChainConfig = ( }, mantapacific: { interval: 5, - reorgPeriod: 0, + reorgPeriod: getReorgPeriod(chainMetadata.mantapacific), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -233,11 +317,31 @@ export const validatorChainConfig = ( '0x80afdde2a81f3fb056fd088a97f0af3722dbc4f3', '0x5dda0c4cf18de3b3ab637f8df82b24921082b54c', ], - [Contexts.ReleaseCandidate]: [], + [Contexts.ReleaseCandidate]: [ + '0x84fcb05e6e5961df2dfd9f36e8f2b3e87ede7d76', + '0x45f3e2655a08feda821ee7b495cf2595401e1569', + '0x4cfccfd66dbb702b643b56f6986a928ed1b50c7e', + ], [Contexts.Neutron]: [], }, 'mantapacific', ), }, + viction: { + interval: 5, + reorgPeriod: 0, + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x1f87c368f8e05a85ef9126d984a980a20930cb9c'], + [Contexts.ReleaseCandidate]: [ + '0xe858971cd865b11d3e8fb6b6af72db0d85881baf', + '0xad94659e2383214e4a1c4e8d3c17caffb75bc31b', + '0x0f9e5775ac4d3b73dd28e5a3f8394443186cb70c', + ], + [Contexts.Neutron]: [], + }, + 'viction', + ), + }, }; }; diff --git a/typescript/infra/config/environments/mainnet3/warp/addresses.json b/typescript/infra/config/environments/mainnet3/warp/addresses.json index 79b01185bb..c0f9371c34 100644 --- a/typescript/infra/config/environments/mainnet3/warp/addresses.json +++ b/typescript/infra/config/environments/mainnet3/warp/addresses.json @@ -1,5 +1,9 @@ { - "arbitrum": { - "router": "0x93ca0d85837FF83158Cd14D65B169CdB223b1921" + "injective": { + "router": "inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k" + }, + "inevm": { + "HypNative": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4", + "router": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4" } } diff --git a/typescript/infra/config/environments/mainnet3/warp/arbitrum-TIA-addresses.json b/typescript/infra/config/environments/mainnet3/warp/arbitrum-TIA-addresses.json new file mode 100644 index 0000000000..b578857016 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/arbitrum-TIA-addresses.json @@ -0,0 +1,8 @@ +{ + "neutron": { + "router": "neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq" + }, + "arbitrum": { + "router": "0xd56734d7f9979dd94fae3d67c7e928234e71cd4c" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml new file mode 100644 index 0000000000..0d8ff77557 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDC-inevm-deployments.yaml @@ -0,0 +1,22 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-02-06T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0xED56728fb977b0bBdacf65bCdD5e17Bb7e84504f' + tokenAddress: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' # USDC + name: USDC + symbol: USDC + decimals: 6 + inevm: + protocolType: ethereum + type: synthetic + hypAddress: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147' + name: USDC + symbol: USDC + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml new file mode 100644 index 0000000000..25ec599f73 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/ethereumUSDT-inevm-deployments.yaml @@ -0,0 +1,22 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-02-06T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0xab852e67bf03E74C89aF67C4BA97dd1088D3dA19' + tokenAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7' # USDT + name: Tether USD + symbol: USDT + decimals: 6 + inevm: + protocolType: ethereum + type: synthetic + hypAddress: '0x97423A68BAe94b5De52d767a17aBCc54c157c0E5' + name: Tether USD + symbol: USDT + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/inevm-USDC-addresses.json b/typescript/infra/config/environments/mainnet3/warp/inevm-USDC-addresses.json new file mode 100644 index 0000000000..2e38c664a0 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/inevm-USDC-addresses.json @@ -0,0 +1,8 @@ +{ + "inevm": { + "HypERC20": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147" + }, + "ethereum": { + "HypERC20Collateral": "0xED56728fb977b0bBdacf65bCdD5e17Bb7e84504f" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/inevm-USDT-addresses.json b/typescript/infra/config/environments/mainnet3/warp/inevm-USDT-addresses.json new file mode 100644 index 0000000000..b9e1b65223 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/inevm-USDT-addresses.json @@ -0,0 +1,8 @@ +{ + "inevm": { + "HypERC20": "0x97423A68BAe94b5De52d767a17aBCc54c157c0E5" + }, + "ethereum": { + "HypERC20Collateral": "0xab852e67bf03E74C89aF67C4BA97dd1088D3dA19" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/injective-inevm-addresses.json b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-addresses.json new file mode 100644 index 0000000000..d92b53abf8 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-addresses.json @@ -0,0 +1,8 @@ +{ + "injective": { + "router": "inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k" + }, + "inevm": { + "router": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml new file mode 100644 index 0000000000..efabaf59da --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/injective-inevm-deployments.yaml @@ -0,0 +1,22 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between injective and inevm +description: Hyperlane Warp Route artifacts +timestamp: '2024-01-31T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + injective: + protocolType: cosmos + type: native + hypAddress: inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k + name: Injective Coin + symbol: INJ + decimals: 18 + ibcDenom: inj + inevm: + protocolType: ethereum + type: native + hypAddress: '0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4' + name: Injective coin + symbol: INJ + decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/manta-TIA-addresses.json b/typescript/infra/config/environments/mainnet3/warp/manta-TIA-addresses.json new file mode 100644 index 0000000000..cfe967bb17 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/manta-TIA-addresses.json @@ -0,0 +1,8 @@ +{ + "neutron": { + "router": "neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa" + }, + "mantapacific": { + "router": "0x6fae4d9935e2fcb11fc79a64e917fb2bf14dafaa" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/nautilus-solana-bsc-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/nautilus-solana-bsc-deployments.yaml new file mode 100644 index 0000000000..e3e8770078 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/nautilus-solana-bsc-deployments.yaml @@ -0,0 +1,31 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between nautilus and bsc, solana +description: Hyperlane Warp Route artifacts +timestamp: '2023-09-23T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + bsc: + protocolType: ethereum + type: collateral + hypAddress: '0xC27980812E2E66491FD457D488509b7E04144b98' + tokenAddress: '0x37a56cdcD83Dce2868f721De58cB3830C44C6303' + name: Zebec + symbol: ZBC + decimals: 9 + nautilus: + protocolType: ethereum + type: native + hypAddress: '0x4501bBE6e731A4bC5c60C03A77435b2f6d5e9Fe7' + name: Zebec + symbol: ZBC + decimals: 18 + solana: + protocolType: sealevel + type: collateral + tokenAddress: 'wzbcJyhGhQDLTV1S99apZiiBdE4jmYfbw99saMMdP59' + hypAddress: 'EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa' + name: Zebec + symbol: ZBC + decimals: 9 + isSpl2022: true diff --git a/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml new file mode 100644 index 0000000000..ae13acf3d7 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/neutron-mantapacific-deployments.yaml @@ -0,0 +1,22 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between neutron and mantapacific +description: Hyperlane Warp Route artifacts +timestamp: '2023-09-23T16:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + neutron: + protocolType: cosmos + type: collateral + hypAddress: neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa + tokenAddress: ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7 + name: Celestia + symbol: TIA + decimals: 6 + mantapacific: + protocolType: ethereum + type: synthetic + hypAddress: '0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa' + name: Celestia + symbol: TIA + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/verification.json b/typescript/infra/config/environments/mainnet3/warp/verification.json index 023a2a53da..a48214e7e4 100644 --- a/typescript/infra/config/environments/mainnet3/warp/verification.json +++ b/typescript/infra/config/environments/mainnet3/warp/verification.json @@ -5,11 +5,71 @@ "address": "0x93ca0d85837FF83158Cd14D65B169CdB223b1921", "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9", "isProxy": false + } + ], + "viction": [ + { + "name": "HypERC20", + "address": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000120000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false }, { "name": "HypERC20", - "address": "0x93ca0d85837FF83158Cd14D65B169CdB223b1921", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000979ca5202784112f4738403dbec5d0f3b9daabb9", + "address": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000120000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "HypERC20", + "address": "0x61DDB465eEA5bc3708Cf8B53156aC91a77A2f029", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000120000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + } + ], + "ethereum": [ + { + "name": "HypNative", + "address": "0x15b5D6B614242B118AA404528A7f3E2Ad241e4A4", + "constructorArguments": "000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239", + "isProxy": false + }, + { + "name": "HypERC20Collateral", + "address": "0x31Dca7762930f56D81292f85E65c9D67575804fE", + "constructorArguments": "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239", + "isProxy": false + }, + { + "name": "HypERC20Collateral", + "address": "0x4221a16A01F61c2b38A03C52d828a7041f6AAA49", + "constructorArguments": "000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239", + "isProxy": false + }, + { + "name": "HypERC20Collateral", + "address": "0xab852e67bf03E74C89aF67C4BA97dd1088D3dA19", + "constructorArguments": "000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239", + "isProxy": false + }, + { + "name": "HypERC20Collateral", + "address": "0xED56728fb977b0bBdacf65bCdD5e17Bb7e84504f", + "constructorArguments": "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239", + "isProxy": false + } + ], + "inevm": [ + { + "name": "HypERC20", + "address": "0x97423A68BAe94b5De52d767a17aBCc54c157c0E5", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000060000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false + }, + { + "name": "HypERC20", + "address": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000000060000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false } ] diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ETH-addresses.json b/typescript/infra/config/environments/mainnet3/warp/viction-ETH-addresses.json new file mode 100644 index 0000000000..c5842c1768 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ETH-addresses.json @@ -0,0 +1,8 @@ +{ + "viction": { + "router": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B" + }, + "ethereum": { + "router": "0x15b5D6B614242B118AA404528A7f3E2Ad241e4A4" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-USDC-addresses.json b/typescript/infra/config/environments/mainnet3/warp/viction-USDC-addresses.json new file mode 100644 index 0000000000..c567193098 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/viction-USDC-addresses.json @@ -0,0 +1,10 @@ +{ + "ethereum": { + "HypERC20Collateral": "0x31Dca7762930f56D81292f85E65c9D67575804fE", + "router": "0x31Dca7762930f56D81292f85E65c9D67575804fE" + }, + "viction": { + "HypERC20": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "router": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-USDT-addresses.json b/typescript/infra/config/environments/mainnet3/warp/viction-USDT-addresses.json new file mode 100644 index 0000000000..49c09c3356 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/viction-USDT-addresses.json @@ -0,0 +1,10 @@ +{ + "ethereum": { + "router": "0x4221a16A01F61c2b38A03C52d828a7041f6AAA49", + "HypERC20Collateral": "0x4221a16A01F61c2b38A03C52d828a7041f6AAA49" + }, + "viction": { + "router": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4", + "HypERC20": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4" + } +} diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml new file mode 100644 index 0000000000..8923facc00 --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-ETH-deployments.yaml @@ -0,0 +1,21 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between viction and ethereum, ETH +description: Hyperlane Warp Route artifacts +timestamp: '2023-02-14T20:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: native + hypAddress: '0x15b5D6B614242B118AA404528A7f3E2Ad241e4A4' + name: Ether + symbol: ETH + decimals: 18 + viction: + protocolType: ethereum + type: synthetic + hypAddress: '0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B' + name: ETH + symbol: ETH + decimals: 18 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml new file mode 100644 index 0000000000..a278a93e8c --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDC-deployments.yaml @@ -0,0 +1,22 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between viction and ethereum, USDC +description: Hyperlane Warp Route artifacts +timestamp: '2023-02-14T20:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0x31Dca7762930f56D81292f85E65c9D67575804fE' + tokenAddress: '0x31Dca7762930f56D81292f85E65c9D67575804fE' # USDC + name: USD Coin + symbol: USDC + decimals: 6 + viction: + protocolType: ethereum + type: synthetic + hypAddress: '0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0' + name: USDC + symbol: USDC + decimals: 6 diff --git a/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml new file mode 100644 index 0000000000..20dd075f8d --- /dev/null +++ b/typescript/infra/config/environments/mainnet3/warp/viction-ethereum-USDT-deployments.yaml @@ -0,0 +1,22 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between viction and ethereum, USDT +description: Hyperlane Warp Route artifacts +timestamp: '2023-02-14T20:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + ethereum: + protocolType: ethereum + type: collateral + hypAddress: '0x4221a16A01F61c2b38A03C52d828a7041f6AAA49' + tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7' # USDT + name: Tether USD + symbol: USDT + decimals: 6 + viction: + protocolType: ethereum + type: synthetic + hypAddress: '0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4' + name: USDT + symbol: USDT + decimals: 6 diff --git a/typescript/infra/config/environments/test/core.ts b/typescript/infra/config/environments/test/core.ts index 397059c52c..5bfefd1456 100644 --- a/typescript/infra/config/environments/test/core.ts +++ b/typescript/infra/config/environments/test/core.ts @@ -22,7 +22,7 @@ import { owners } from './owners'; export const core: ChainMap = objMap(owners, (local, owner) => { const defaultIsm: RoutingIsmConfig = { type: IsmType.ROUTING, - owner, + ...owner, domains: Object.fromEntries( Object.entries(chainToValidator) .filter(([chain, _]) => chain !== local) @@ -46,7 +46,7 @@ export const core: ChainMap = objMap(owners, (local, owner) => { const defaultHook: FallbackRoutingHookConfig = { type: HookType.FALLBACK_ROUTING, - owner, + ...owner, fallback: merkleHook, domains: Object.fromEntries( Object.entries(chainToValidator) @@ -59,14 +59,14 @@ export const core: ChainMap = objMap(owners, (local, owner) => { type: HookType.PROTOCOL_FEE, maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token protocolFee: BigNumber.from(1).toString(), // 1 wei - beneficiary: owner, - owner, + beneficiary: owner.owner, + ...owner, }; return { - owner, defaultIsm, defaultHook, requiredHook, + ...owner, }; }); diff --git a/typescript/infra/config/environments/test/igp.ts b/typescript/infra/config/environments/test/igp.ts index 6590c933ab..cb7ffa4bf6 100644 --- a/typescript/infra/config/environments/test/igp.ts +++ b/typescript/infra/config/environments/test/igp.ts @@ -19,7 +19,7 @@ function getGasOracles(local: TestChains) { ); } -export const igp: ChainMap = objMap(owners, (chain, owner) => { +export const igp: ChainMap = objMap(owners, (chain, ownerConfig) => { const overhead = Object.fromEntries( exclude(chain, chainNames).map((remote) => [ remote, @@ -30,10 +30,10 @@ export const igp: ChainMap = objMap(owners, (chain, owner) => { ]), ); return { - owner, - oracleKey: owner, - beneficiary: owner, + oracleKey: ownerConfig.owner, + beneficiary: ownerConfig.owner, gasOracleType: getGasOracles(chain), overhead, + ...ownerConfig, }; }); diff --git a/typescript/infra/config/environments/test/index.ts b/typescript/infra/config/environments/test/index.ts index a458f47a08..c11ab549f0 100644 --- a/typescript/infra/config/environments/test/index.ts +++ b/typescript/infra/config/environments/test/index.ts @@ -7,7 +7,6 @@ import { EnvironmentConfig } from '../../../src/config'; import { agents } from './agent'; import { testConfigs } from './chains'; import { core } from './core'; -import { storageGasOracleConfig } from './gas-oracle'; import { igp } from './igp'; import { infra } from './infra'; import { owners } from './owners'; @@ -31,5 +30,4 @@ export const environment: EnvironmentConfig = { getKeys: async () => { throw new Error('Not implemented'); }, - storageGasOracleConfig, }; diff --git a/typescript/infra/config/environments/test/middleware/accounts/addresses.json b/typescript/infra/config/environments/test/middleware/accounts/addresses.json index 0efdde8771..aed8d78a7b 100644 --- a/typescript/infra/config/environments/test/middleware/accounts/addresses.json +++ b/typescript/infra/config/environments/test/middleware/accounts/addresses.json @@ -10,17 +10,5 @@ }, "bsctestnet": { "router": "0xc011170d9795a7a2d065E384EAd1CA3394A7d35E" - }, - "goerli": { - "router": "0xc011170d9795a7a2d065E384EAd1CA3394A7d35E" - }, - "moonbasealpha": { - "router": "0xc011170d9795a7a2d065E384EAd1CA3394A7d35E" - }, - "optimismgoerli": { - "router": "0xc011170d9795a7a2d065E384EAd1CA3394A7d35E" - }, - "arbitrumgoerli": { - "router": "0xc011170d9795a7a2d065E384EAd1CA3394A7d35E" } } diff --git a/typescript/infra/config/environments/test/middleware/liquidity-layer/addresses.json b/typescript/infra/config/environments/test/middleware/liquidity-layer/addresses.json index 8fe20c0ff6..038c6d75da 100644 --- a/typescript/infra/config/environments/test/middleware/liquidity-layer/addresses.json +++ b/typescript/infra/config/environments/test/middleware/liquidity-layer/addresses.json @@ -4,11 +4,6 @@ "portalAdapter": "0xe589a05be1304b43A6FEb9c5D6a6EEEA35656271", "router": "0x3428e12EfDb2446c1E7feC3f1CED099A8a7cD541" }, - "goerli": { - "circleBridgeAdapter": "0x17EB33454AAEF8E91510540a0ebF4a8213dd740D", - "portalAdapter": "0xe589a05be1304b43A6FEb9c5D6a6EEEA35656271", - "router": "0x3428e12EfDb2446c1E7feC3f1CED099A8a7cD541" - }, "mumbai": { "portalAdapter": "0xe589a05be1304b43A6FEb9c5D6a6EEEA35656271", "router": "0x3428e12EfDb2446c1E7feC3f1CED099A8a7cD541" diff --git a/typescript/infra/config/environments/test/middleware/liquidity-layer/verification.json b/typescript/infra/config/environments/test/middleware/liquidity-layer/verification.json index 20d474c4ad..2a43d68ca7 100644 --- a/typescript/infra/config/environments/test/middleware/liquidity-layer/verification.json +++ b/typescript/infra/config/environments/test/middleware/liquidity-layer/verification.json @@ -31,38 +31,6 @@ "constructorArguments": "" } ], - "goerli": [ - { - "name": "LiquidityLayerRouter", - "address": "0x3428e12EfDb2446c1E7feC3f1CED099A8a7cD541", - "isProxy": false, - "constructorArguments": "" - }, - { - "name": "CircleBridgeAdapter", - "address": "0xb54AD7AE42B7c505100594365CdBC4b28Ef51FE6", - "isProxy": false, - "constructorArguments": "" - }, - { - "name": "PortalAdapter", - "address": "0xe589a05be1304b43A6FEb9c5D6a6EEEA35656271", - "isProxy": false, - "constructorArguments": "" - }, - { - "name": "CircleBridgeAdapter", - "address": "0x54FCA26E5FF828847D8caF471e44cD5727C73B0d", - "isProxy": false, - "constructorArguments": "" - }, - { - "name": "CircleBridgeAdapter", - "address": "0x17EB33454AAEF8E91510540a0ebF4a8213dd740D", - "isProxy": false, - "constructorArguments": "" - } - ], "mumbai": [ { "name": "LiquidityLayerRouter", diff --git a/typescript/infra/config/environments/test/middleware/queries/addresses.json b/typescript/infra/config/environments/test/middleware/queries/addresses.json index e09eed8422..9cb16369a3 100644 --- a/typescript/infra/config/environments/test/middleware/queries/addresses.json +++ b/typescript/infra/config/environments/test/middleware/queries/addresses.json @@ -10,17 +10,5 @@ }, "bsctestnet": { "router": "0x6141e7E7fA2c1beB8be030B0a7DB4b8A10c7c3cd" - }, - "goerli": { - "router": "0x6141e7E7fA2c1beB8be030B0a7DB4b8A10c7c3cd" - }, - "moonbasealpha": { - "router": "0x6141e7E7fA2c1beB8be030B0a7DB4b8A10c7c3cd" - }, - "optimismgoerli": { - "router": "0x6141e7E7fA2c1beB8be030B0a7DB4b8A10c7c3cd" - }, - "arbitrumgoerli": { - "router": "0x6141e7E7fA2c1beB8be030B0a7DB4b8A10c7c3cd" } } diff --git a/typescript/infra/config/environments/test/owners.ts b/typescript/infra/config/environments/test/owners.ts index 3134145ed0..4156bd0ecb 100644 --- a/typescript/infra/config/environments/test/owners.ts +++ b/typescript/infra/config/environments/test/owners.ts @@ -1,10 +1,9 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; import { chainNames } from './chains'; // Owner is hardhat account 0 const OWNER_ADDRESS = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'; -export const owners: ChainMap
= Object.fromEntries( - chainNames.map((chain) => [chain, OWNER_ADDRESS]), +export const owners: ChainMap = Object.fromEntries( + chainNames.map((chain) => [chain, { owner: OWNER_ADDRESS }]), ); diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 4621e0a1e1..cd4529fb14 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -2,21 +2,22 @@ import { Chains, GasPaymentEnforcementPolicyType, RpcConsensusType, - chainMetadata, } from '@hyperlane-xyz/sdk'; import { + AgentChainConfig, RootAgentConfig, - allAgentChainNames, + getAgentChainNamesFromConfig, routerMatchingList, } from '../../../src/config'; import { GasPaymentEnforcementConfig } from '../../../src/config/agent/relayer'; import { ALL_KEY_ROLES, Role } from '../../../src/roles'; import { Contexts } from '../../contexts'; -import { agentChainNames, environment } from './chains'; +import { environment, supportedChainNames } from './chains'; import { helloWorld } from './helloworld'; import { validatorChainConfig } from './validators'; +import plumetestnetSepoliaAddresses from './warp/plumetestnet-sepolia-addresses.json'; const releaseCandidateHelloworldMatchingList = routerMatchingList( helloWorld[Contexts.ReleaseCandidate].addresses, @@ -24,11 +25,58 @@ const releaseCandidateHelloworldMatchingList = routerMatchingList( const repo = 'gcr.io/abacus-labs-dev/hyperlane-agent'; +// The chains here must be consistent with the environment's supportedChainNames, which is +// checked / enforced at runtime & in the CI pipeline. +// +// This is intentionally separate and not derived from the environment's supportedChainNames +// to allow for more fine-grained control over which chains are enabled for each agent role. +export const hyperlaneContextAgentChainConfig: AgentChainConfig = { + [Role.Validator]: { + [Chains.alfajores]: true, + [Chains.bsctestnet]: true, + [Chains.eclipsetestnet]: true, + [Chains.fuji]: true, + [Chains.mumbai]: true, + [Chains.plumetestnet]: true, + [Chains.scrollsepolia]: true, + [Chains.sepolia]: true, + [Chains.solanatestnet]: true, + }, + [Role.Relayer]: { + [Chains.alfajores]: true, + [Chains.bsctestnet]: true, + [Chains.eclipsetestnet]: true, + [Chains.fuji]: true, + [Chains.mumbai]: true, + [Chains.plumetestnet]: true, + [Chains.scrollsepolia]: true, + [Chains.sepolia]: true, + [Chains.solanatestnet]: true, + }, + [Role.Scraper]: { + [Chains.alfajores]: true, + [Chains.bsctestnet]: true, + // Cannot scrape non-EVM chains + [Chains.eclipsetestnet]: false, + [Chains.fuji]: true, + [Chains.mumbai]: true, + [Chains.plumetestnet]: true, + [Chains.scrollsepolia]: true, + [Chains.sepolia]: true, + // Cannot scrape non-EVM chains + [Chains.solanatestnet]: false, + }, +}; + +export const hyperlaneContextAgentChainNames = getAgentChainNamesFromConfig( + hyperlaneContextAgentChainConfig, + supportedChainNames, +); + const contextBase = { namespace: environment, runEnv: environment, - contextChainNames: agentChainNames, - environmentChainNames: allAgentChainNames(agentChainNames), + environmentChainNames: supportedChainNames, aws: { region: 'us-east-1', }, @@ -43,14 +91,14 @@ const gasPaymentEnforcement: GasPaymentEnforcementConfig[] = [ const hyperlane: RootAgentConfig = { ...contextBase, - contextChainNames: agentChainNames, + contextChainNames: hyperlaneContextAgentChainNames, context: Contexts.Hyperlane, rolesWithKeys: ALL_KEY_ROLES, relayer: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '7d1f975-20240312-113101', }, blacklist: [ ...releaseCandidateHelloworldMatchingList, @@ -62,12 +110,24 @@ const hyperlane: RootAgentConfig = { }, ], gasPaymentEnforcement, + metricAppContexts: [ + { + name: 'helloworld', + matchingList: routerMatchingList( + helloWorld[Contexts.Hyperlane].addresses, + ), + }, + { + name: 'plumetestnet_sepolia_eth', + matchingList: routerMatchingList(plumetestnetSepoliaAddresses), + }, + ], }, validators: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '7d1f975-20240312-113101', }, chains: validatorChainConfig(Contexts.Hyperlane), }, @@ -75,7 +135,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '7d1f975-20240312-113101', }, }, }; @@ -83,25 +143,23 @@ const hyperlane: RootAgentConfig = { const releaseCandidate: RootAgentConfig = { ...contextBase, context: Contexts.ReleaseCandidate, + contextChainNames: hyperlaneContextAgentChainNames, rolesWithKeys: [Role.Relayer, Role.Kathy, Role.Validator], relayer: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '7d1f975-20240312-113101', }, whitelist: [...releaseCandidateHelloworldMatchingList], gasPaymentEnforcement, transactionGasLimit: 750000, - // Skipping arbitrum because the gas price estimates are inclusive of L1 - // fees which leads to wildly off predictions. - skipTransactionGasLimitFor: [chainMetadata.arbitrumgoerli.name], }, validators: { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: '86b7f98-20231207-153805', + tag: '7d1f975-20240312-113101', }, chains: validatorChainConfig(Contexts.ReleaseCandidate), }, diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 2c5fed6008..d4d7d4672a 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -1,12 +1,30 @@ -import { ChainMap, ChainMetadata, chainMetadata } from '@hyperlane-xyz/sdk'; +import { + ChainMap, + ChainMetadata, + Chains, + chainMetadata, +} from '@hyperlane-xyz/sdk'; -import { AgentChainNames, Role } from '../../../src/roles'; +// All supported chains for the testnet4 environment. +// These chains may be any protocol type. +export const supportedChainNames = [ + Chains.alfajores, + Chains.bsctestnet, + Chains.eclipsetestnet, + Chains.fuji, + Chains.mumbai, + Chains.plumetestnet, + Chains.scrollsepolia, + Chains.sepolia, + Chains.solanatestnet, +]; -// Blessed -export const ethereumTestnetConfigs: ChainMap = { - alfajores: chainMetadata.alfajores, - basegoerli: chainMetadata.basegoerli, - fuji: chainMetadata.fuji, +export const environment = 'testnet4'; + +export const testnetConfigs: ChainMap = { + ...Object.fromEntries( + supportedChainNames.map((chain) => [chain, chainMetadata[chain]]), + ), mumbai: { ...chainMetadata.mumbai, transactionOverrides: { @@ -20,40 +38,4 @@ export const ethereumTestnetConfigs: ChainMap = { gasPrice: 80 * 10 ** 9, // 8 gwei }, }, - goerli: chainMetadata.goerli, - scrollsepolia: chainMetadata.scrollsepolia, - sepolia: chainMetadata.sepolia, - moonbasealpha: chainMetadata.moonbasealpha, - optimismgoerli: chainMetadata.optimismgoerli, - arbitrumgoerli: chainMetadata.arbitrumgoerli, - polygonzkevmtestnet: chainMetadata.polygonzkevmtestnet, -}; - -// Blessed non-Ethereum chains. -export const nonEthereumTestnetConfigs: ChainMap = { - // solanadevnet: chainMetadata.solanadevnet, -}; - -export const testnetConfigs: ChainMap = { - ...ethereumTestnetConfigs, - ...nonEthereumTestnetConfigs, -}; - -export type TestnetChains = keyof typeof testnetConfigs; -export const supportedChainNames = Object.keys( - testnetConfigs, -) as TestnetChains[]; -export const environment = 'testnet4'; - -export const ethereumChainNames = Object.keys( - ethereumTestnetConfigs, -) as TestnetChains[]; - -// Hyperlane & RC context agent chain names. -export const agentChainNames: AgentChainNames = { - // Run validators for all chains. - [Role.Validator]: supportedChainNames, - // Only run relayers for Ethereum chains at the moment. - [Role.Relayer]: ethereumChainNames, - [Role.Scraper]: ethereumChainNames, }; diff --git a/typescript/infra/config/environments/testnet4/core.ts b/typescript/infra/config/environments/testnet4/core.ts index 5d23ef799a..3c05099bce 100644 --- a/typescript/infra/config/environments/testnet4/core.ts +++ b/typescript/infra/config/environments/testnet4/core.ts @@ -5,6 +5,7 @@ import { AggregationIsmConfig, ChainMap, CoreConfig, + FallbackRoutingHookConfig, HookType, IgpHookConfig, IsmType, @@ -17,94 +18,97 @@ import { RoutingIsmConfig, defaultMultisigConfigs, } from '@hyperlane-xyz/sdk'; -import { DomainRoutingHookConfig } from '@hyperlane-xyz/sdk/src/hook/types'; import { objMap } from '@hyperlane-xyz/utils'; import { supportedChainNames } from './chains'; import { igp } from './igp'; import { owners } from './owners'; -export const core: ChainMap = objMap(owners, (local, owner) => { - const originMultisigs: ChainMap = Object.fromEntries( - supportedChainNames - .filter((chain) => chain !== local) - .map((origin) => [origin, defaultMultisigConfigs[origin]]), - ); +export const core: ChainMap = objMap( + owners, + (local, ownerConfig) => { + const originMultisigs: ChainMap = Object.fromEntries( + supportedChainNames + .filter((chain) => chain !== local) + .map((origin) => [origin, defaultMultisigConfigs[origin]]), + ); - const merkleRoot = (multisig: MultisigConfig): MultisigIsmConfig => ({ - type: IsmType.MERKLE_ROOT_MULTISIG, - ...multisig, - }); + const merkleRoot = (multisig: MultisigConfig): MultisigIsmConfig => ({ + type: IsmType.MERKLE_ROOT_MULTISIG, + ...multisig, + }); - const messageIdIsm = (multisig: MultisigConfig): MultisigIsmConfig => ({ - type: IsmType.MESSAGE_ID_MULTISIG, - ...multisig, - }); + const messageIdIsm = (multisig: MultisigConfig): MultisigIsmConfig => ({ + type: IsmType.MESSAGE_ID_MULTISIG, + ...multisig, + }); - const routingIsm: RoutingIsmConfig = { - type: IsmType.ROUTING, - domains: objMap( - originMultisigs, - (_, multisig): AggregationIsmConfig => ({ - type: IsmType.AGGREGATION, - modules: [messageIdIsm(multisig), merkleRoot(multisig)], - threshold: 1, - }), - ), - owner, - }; + const routingIsm: RoutingIsmConfig = { + type: IsmType.ROUTING, + domains: objMap( + originMultisigs, + (_, multisig): AggregationIsmConfig => ({ + type: IsmType.AGGREGATION, + modules: [messageIdIsm(multisig), merkleRoot(multisig)], + threshold: 1, + }), + ), + ...ownerConfig, + }; - const pausableIsm: PausableIsmConfig = { - type: IsmType.PAUSABLE, - owner, - }; + const pausableIsm: PausableIsmConfig = { + type: IsmType.PAUSABLE, + ...ownerConfig, + }; - const defaultIsm: AggregationIsmConfig = { - type: IsmType.AGGREGATION, - modules: [routingIsm, pausableIsm], - threshold: 2, - }; + const defaultIsm: AggregationIsmConfig = { + type: IsmType.AGGREGATION, + modules: [routingIsm, pausableIsm], + threshold: 2, + }; - const merkleHook: MerkleTreeHookConfig = { - type: HookType.MERKLE_TREE, - }; + const merkleHook: MerkleTreeHookConfig = { + type: HookType.MERKLE_TREE, + }; - const igpHook: IgpHookConfig = { - type: HookType.INTERCHAIN_GAS_PAYMASTER, - ...igp[local], - }; + const igpHook: IgpHookConfig = { + type: HookType.INTERCHAIN_GAS_PAYMASTER, + ...igp[local], + }; - const pausableHook: PausableHookConfig = { - type: HookType.PAUSABLE, - owner, - }; + const pausableHook: PausableHookConfig = { + type: HookType.PAUSABLE, + ...ownerConfig, + }; - const aggregationHooks = objMap( - originMultisigs, - (_origin, _): AggregationHookConfig => ({ - type: HookType.AGGREGATION, - hooks: [pausableHook, merkleHook, igpHook], - }), - ); + const aggregationHooks = objMap( + originMultisigs, + (_origin, _): AggregationHookConfig => ({ + type: HookType.AGGREGATION, + hooks: [pausableHook, merkleHook, igpHook], + }), + ); - const defaultHook: DomainRoutingHookConfig = { - type: HookType.ROUTING, - owner, - domains: aggregationHooks, - }; + const defaultHook: FallbackRoutingHookConfig = { + type: HookType.FALLBACK_ROUTING, + ...ownerConfig, + domains: aggregationHooks, + fallback: merkleHook, + }; - const requiredHook: ProtocolFeeHookConfig = { - type: HookType.PROTOCOL_FEE, - maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token - protocolFee: BigNumber.from(1).toString(), // 1 wei of native token - beneficiary: owner, - owner, - }; + const requiredHook: ProtocolFeeHookConfig = { + type: HookType.PROTOCOL_FEE, + maxProtocolFee: ethers.utils.parseUnits('1', 'gwei').toString(), // 1 gwei of native token + protocolFee: BigNumber.from(1).toString(), // 1 wei of native token + beneficiary: ownerConfig.owner, + ...ownerConfig, + }; - return { - owner, - defaultIsm, - defaultHook, - requiredHook, - }; -}); + return { + defaultIsm, + defaultHook, + requiredHook, + ...ownerConfig, + }; + }, +); diff --git a/typescript/infra/config/environments/testnet4/core/verification.json b/typescript/infra/config/environments/testnet4/core/verification.json index 31eb4ee72a..b5a1e1101c 100644 --- a/typescript/infra/config/environments/testnet4/core/verification.json +++ b/typescript/infra/config/environments/testnet4/core/verification.json @@ -1,4130 +1,2802 @@ { "alfajores": [ { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", "address": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", "constructorArguments": "0x000000000000000000000000592248baa7e27ed50a99cf821c61bf2ac8d6f2b70000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", "address": "0x592248Baa7e27ed50A99cf821C61Bf2ac8D6F2B7", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000aef3", - "isProxy": false + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", "address": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", "address": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", "address": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", "address": "0x1246529edDcA523AfE5c6b9414299633d2E16697", "constructorArguments": "0x00000000000000000000000076a1aae73e9d837cef10ac5af5afdd30d7612f980000000000000000000000004edbf5846d973c53af478cf62ab5bc92807521e300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", "address": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", "address": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "constructorArguments": "0x000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59", - "isProxy": false + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "FallbackRoutingHook", "address": "0xE1386148385275A27D29fC39Bd58a969CD5dCAF0", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000221fa9cbafcd6c1c3d206571cf4427703e023ffa", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0xE1386148385275A27D29fC39Bd58a969CD5dCAF0", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e59000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000221fa9cbafcd6c1c3d206571cf4427703e023ffa", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" } ], - "basegoerli": [ + "bsctestnet": [ { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", + "constructorArguments": "0x000000000000000000000000bfb3eb3bcc00a17d04237c98f2d1061548f8ac38000000000000000000000000b12282d2e838aa5f2a4f9ee5f624a77b7199a07800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0xbfB3EB3Bcc00A17d04237C98F2D1061548f8AC38", + "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000000061", + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", + "constructorArguments": "0x000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", + "constructorArguments": "0x000000000000000000000000795b9b7aa901c8b999b62b8c80299e79a5c96057000000000000000000000000b12282d2e838aa5f2a4f9ee5f624a77b7199a07800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x795B9b7AA901C8B999b62B8c80299e79a5c96057", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", + "constructorArguments": "0x000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", + "constructorArguments": "0x000000000000000000000000795b9b7aa901c8b999b62b8c80299e79a5c96057000000000000000000000000b12282d2e838aa5f2a4f9ee5f624a77b7199a07800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x795B9b7AA901C8B999b62B8c80299e79a5c96057", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "address": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xf09701B0a93210113D175461b6135a96773B5465", + "constructorArguments": "0x000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d", + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc278DDe83018F0e8c624b208e6D9E6251d263B1d", + "constructorArguments": "000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000c6cbf39a747f5e28d1bdc8d9dfdab2960abd5a8f", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, + "address": "0xc278DDe83018F0e8c624b208e6D9E6251d263B1d", + "constructorArguments": "000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000c6cbf39a747f5e28d1bdc8d9dfdab2960abd5a8f", + "isProxy": false, + "name": "FallbackRoutingHook" + } + ], + "fuji": [ { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", + "constructorArguments": "0x", + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", + "constructorArguments": "0x", + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", + "constructorArguments": "0x", + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", + "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", + "constructorArguments": "0x0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", + "constructorArguments": "0x00000000000000000000000004be088a7aab0e1b6ec7d73af9e152344fbc8a0c000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x04BE088A7aAb0E1B6Ec7d73af9E152344Fbc8A0C", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", + "constructorArguments": "0x0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", + "constructorArguments": "0x00000000000000000000000004be088a7aab0e1b6ec7d73af9e152344fbc8a0c000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x04BE088A7aAb0E1B6Ec7d73af9E152344Fbc8A0C", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "address": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", + "constructorArguments": "0x0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0", + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x50897eDCb3f1bB2A90f20DA5a8dF0e5c57A146e3", + "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009ff6ac3daf63103620bbf76136ea1aff43c2f612", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x50897eDCb3f1bB2A90f20DA5a8dF0e5c57A146e3", + "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009ff6ac3daf63103620bbf76136ea1aff43c2f612", + "isProxy": false, + "name": "FallbackRoutingHook" + } + ], + "moonbasealpha": [ + { + "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x76189acFA212298d7022624a4633411eE0d2f26F", + "constructorArguments": "0x00000000000000000000000096ee22fb5cad052bef57f8ee9d5050b45204cc92000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x96ee22Fb5Cad052bEf57f8EE9D5050B45204cc92", + "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000000507", + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x155B1CD2f7Cbc58d403B9BE341FaB6CD77425175", + "constructorArguments": "0x00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x62fA20dE68Dbe425f0bc474b12235a4F8449E608", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x92F05669A354a032A84FcfABfD13beE1aBc5bFd0", + "constructorArguments": "0x000000000000000000000000700eb93ae3a4cfccfefd2f17d92b79cd3ff202e4000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x700EB93Ae3A4CFcCfEFd2F17d92b79CD3FF202e4", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x155B1CD2f7Cbc58d403B9BE341FaB6CD77425175", + "constructorArguments": "0x00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x62fA20dE68Dbe425f0bc474b12235a4F8449E608", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x92F05669A354a032A84FcfABfD13beE1aBc5bFd0", + "constructorArguments": "0x000000000000000000000000700eb93ae3a4cfccfefd2f17d92b79cd3ff202e4000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x700EB93Ae3A4CFcCfEFd2F17d92b79CD3FF202e4", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "address": "0xe2A73F106902983452713F24Bd019F6eb8712986", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x07543860AE9E72aBcF2Bae9827b23621A64Fa416", + "constructorArguments": "0x00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x6c9EB73793F9Cd535DB1bF86dC307f6d899b2fE3", + "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000155b1cd2f7cbc58d403b9be341fab6cd77425175", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x6c9EB73793F9Cd535DB1bF86dC307f6d899b2fE3", + "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000155b1cd2f7cbc58d403b9be341fab6cd77425175", + "isProxy": false, + "name": "FallbackRoutingHook" + } + ], + "mumbai": [ + { + "address": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x2d1889fe5B092CD988972261434F7E5f26041115", + "constructorArguments": "0x000000000000000000000000de1973894d1418ae463013cb6415cb24fce15575000000000000000000000000a99ad6b1c10e92db8d3510f1865a6d2ab43ead5800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0xde1973894d1418ae463013CB6415Cb24fcE15575", + "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000013881", + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", + "constructorArguments": "0x0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x8aB67CAF605c6ee83cbFeFb0D8d67FDd3BF7B591", + "constructorArguments": "0x00000000000000000000000098f44ea5b9ca6aa02a5b75f31e0621083d9096a2000000000000000000000000a99ad6b1c10e92db8d3510f1865a6d2ab43ead5800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", + "constructorArguments": "0x0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x8aB67CAF605c6ee83cbFeFb0D8d67FDd3BF7B591", + "constructorArguments": "0x00000000000000000000000098f44ea5b9ca6aa02a5b75f31e0621083d9096a2000000000000000000000000a99ad6b1c10e92db8d3510f1865a6d2ab43ead5800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "address": "0x244d1F7e30Be144A87602905baBF86630e8f39DC", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x99303EFF09332cDd93E8BC8b2F07b2416e4501e5", + "constructorArguments": "0x0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115", + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xFA005A892EbDACFcc3f1EF0111A7406c779c3647", + "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009af85731edd41e2e50f81ef8a0a69d2fb836edf9", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false - }, + "address": "0xFA005A892EbDACFcc3f1EF0111A7406c779c3647", + "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009af85731edd41e2e50f81ef8a0a69d2fb836edf9", + "isProxy": false, + "name": "FallbackRoutingHook" + } + ], + "plumetestnet": [ { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000099c0a0f", + "isProxy": false, + "name": "Mailbox" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "constructorArguments": "000000000000000000000000ddcfecf17586d08a5740b7d91735fcce3dfe3eed000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "constructorArguments": "00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "address": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "constructorArguments": "00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000ddf4c3e791cacafd26d7fb275549739b38ae6e75", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "MerkleTreeHook", "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "constructorArguments": "00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "constructorArguments": "00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000ddf4c3e791cacafd26d7fb275549739b38ae6e75", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "MerkleTreeHook", "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "StorageGasOracle", "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "constructorArguments": "0x", - "isProxy": false + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "constructorArguments": "0x", - "isProxy": false + "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "TransparentUpgradeableProxy", "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "InterchainGasPaymaster", "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", - "constructorArguments": "0x", - "isProxy": false + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "constructorArguments": "000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e8000000000000000000000000589c201a07c26b4725a4a829d772f24423da480b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", + "constructorArguments": "00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065", + "isProxy": false, + "name": "ValidatorAnnounce" + } + ], + "scrollsepolia": [ + { + "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", + "constructorArguments": "0x0000000000000000000000007914a3349107a7295bbf2374db5a973d73d1b324000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x7914A3349107A7295Bbf2374db5A973d73D1b324", + "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000008274f", + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x863E8c26621c52ACa1849C53500606e73BA272F0", + "constructorArguments": "0x0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "constructorArguments": "0x000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x863E8c26621c52ACa1849C53500606e73BA272F0", + "constructorArguments": "0x0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "constructorArguments": "0x000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x527768930D889662Fe7ACF64294871e86e4C2381", + "constructorArguments": "0x0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", + "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", + "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", + "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", + "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", + "isProxy": false, + "name": "FallbackRoutingHook" + } + ], + "sepolia": [ + { + "address": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "constructorArguments": "0x000000000000000000000000cf5baaf976c80a66fa7839715c45788f60041a3300000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", + "constructorArguments": "0x00000000000000000000000062b2203d0757d1845ab2422eb61ab30d4ad3c51500000000000000000000000097bbc6bbafa5ce3b2fa966c121af63bd09e940f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "Mailbox", - "address": "0xcf5BaaF976C80a66Fa7839715C45788f60041A33", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000014a33", - "isProxy": false + "address": "0x62b2203d0757d1845Ab2422Eb61ab30D4Ad3c515", + "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000aa36a7", + "isProxy": false, + "name": "Mailbox" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", + "constructorArguments": "0x000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", + "constructorArguments": "0x0000000000000000000000003f09709b9865993f0320c3e836f3a3f4ff83ea9b00000000000000000000000097bbc6bbafa5ce3b2fa966c121af63bd09e940f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x3f09709b9865993f0320C3e836f3a3F4ff83eA9b", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "MerkleTreeHook", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", + "constructorArguments": "0x000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", + "isProxy": false, + "name": "MerkleTreeHook" }, { - "name": "ProxyAdmin", - "address": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", + "address": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "ProxyAdmin" }, { - "name": "StorageGasOracle", - "address": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "address": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "StorageGasOracle" }, { - "name": "TransparentUpgradeableProxy", - "address": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "constructorArguments": "0x000000000000000000000000c756cfc1b7d0d4646589edf10ed54b201237f5e800000000000000000000000005ea36caee7d92c173334c9d97dcd39abdcb2b6900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true + "address": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", + "constructorArguments": "0x0000000000000000000000003f09709b9865993f0320c3e836f3a3f4ff83ea9b00000000000000000000000097bbc6bbafa5ce3b2fa966c121af63bd09e940f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "name": "InterchainGasPaymaster", - "address": "0xc756cFc1b7d0d4646589EDf10eD54b201237F5e8", + "address": "0x3f09709b9865993f0320C3e836f3a3F4ff83eA9b", "constructorArguments": "0x", - "isProxy": false + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "name": "ProtocolFee", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", + "address": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false + "isProxy": false, + "name": "ProtocolFee" }, { - "name": "ValidatorAnnounce", - "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "constructorArguments": "0x00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a", - "isProxy": false + "address": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", + "constructorArguments": "0x000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", + "isProxy": false, + "name": "ValidatorAnnounce" }, { - "name": "FallbackRoutingHook", - "address": "0x4Ece7b15ba5dCA2708dCE2812016683193102b9F", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000005821f3b6ee05f3dc62b43b74ab1c8f8e6904b1c8", - "isProxy": false + "address": "0x7CE74319699165430fC651F6b99406eEbee95493", + "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", - "address": "0x4Ece7b15ba5dCA2708dCE2812016683193102b9F", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000005821f3b6ee05f3dc62b43b74ab1c8f8e6904b1c8", - "isProxy": false - } - ], - "fuji": [ - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "constructorArguments": "0x000000000000000000000000f28969a6a930c68a2758d55c291de595670961fe000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xf28969A6A930c68a2758d55c291DE595670961FE", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000000a869", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", - "constructorArguments": "0x0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "constructorArguments": "0x00000000000000000000000004be088a7aab0e1b6ec7d73af9e152344fbc8a0c000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x04BE088A7aAb0E1B6Ec7d73af9E152344Fbc8A0C", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", - "constructorArguments": "0x0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "constructorArguments": "0x00000000000000000000000004be088a7aab0e1b6ec7d73af9e152344fbc8a0c000000000000000000000000378da02f7dc3c23a8b5ece32b8056cdf01e8d47700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x04BE088A7aAb0E1B6Ec7d73af9E152344Fbc8A0C", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", - "constructorArguments": "0x0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x50897eDCb3f1bB2A90f20DA5a8dF0e5c57A146e3", - "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009ff6ac3daf63103620bbf76136ea1aff43c2f612", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x50897eDCb3f1bB2A90f20DA5a8dF0e5c57A146e3", - "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b0000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009ff6ac3daf63103620bbf76136ea1aff43c2f612", - "isProxy": false - } - ], - "mumbai": [ - { - "name": "ProxyAdmin", - "address": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x2d1889fe5B092CD988972261434F7E5f26041115", - "constructorArguments": "0x000000000000000000000000de1973894d1418ae463013cb6415cb24fce15575000000000000000000000000a99ad6b1c10e92db8d3510f1865a6d2ab43ead5800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xde1973894d1418ae463013CB6415Cb24fcE15575", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000013881", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", - "constructorArguments": "0x0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x8aB67CAF605c6ee83cbFeFb0D8d67FDd3BF7B591", - "constructorArguments": "0x00000000000000000000000098f44ea5b9ca6aa02a5b75f31e0621083d9096a2000000000000000000000000a99ad6b1c10e92db8d3510f1865a6d2ab43ead5800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", - "constructorArguments": "0x0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x8aB67CAF605c6ee83cbFeFb0D8d67FDd3BF7B591", - "constructorArguments": "0x00000000000000000000000098f44ea5b9ca6aa02a5b75f31e0621083d9096a2000000000000000000000000a99ad6b1c10e92db8d3510f1865a6d2ab43ead5800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x244d1F7e30Be144A87602905baBF86630e8f39DC", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x99303EFF09332cDd93E8BC8b2F07b2416e4501e5", - "constructorArguments": "0x0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xFA005A892EbDACFcc3f1EF0111A7406c779c3647", - "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009af85731edd41e2e50f81ef8a0a69d2fb836edf9", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xFA005A892EbDACFcc3f1EF0111A7406c779c3647", - "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f26041115000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000009af85731edd41e2e50f81ef8a0a69d2fb836edf9", - "isProxy": false - } - ], - "bsctestnet": [ - { - "name": "ProxyAdmin", - "address": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", - "constructorArguments": "0x000000000000000000000000bfb3eb3bcc00a17d04237c98f2d1061548f8ac38000000000000000000000000b12282d2e838aa5f2a4f9ee5f624a77b7199a07800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xbfB3EB3Bcc00A17d04237C98F2D1061548f8AC38", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000000061", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", - "constructorArguments": "0x000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", - "constructorArguments": "0x000000000000000000000000795b9b7aa901c8b999b62b8c80299e79a5c96057000000000000000000000000b12282d2e838aa5f2a4f9ee5f624a77b7199a07800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x795B9b7AA901C8B999b62B8c80299e79a5c96057", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", - "constructorArguments": "0x000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", - "constructorArguments": "0x000000000000000000000000795b9b7aa901c8b999b62b8c80299e79a5c96057000000000000000000000000b12282d2e838aa5f2a4f9ee5f624a77b7199a07800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x795B9b7AA901C8B999b62B8c80299e79a5c96057", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0xf09701B0a93210113D175461b6135a96773B5465", - "constructorArguments": "0x000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xc278DDe83018F0e8c624b208e6D9E6251d263B1d", - "constructorArguments": "000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000c6cbf39a747f5e28d1bdc8d9dfdab2960abd5a8f", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xc278DDe83018F0e8c624b208e6D9E6251d263B1d", - "constructorArguments": "000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000c6cbf39a747f5e28d1bdc8d9dfdab2960abd5a8f", - "isProxy": false - } - ], - "goerli": [ - { - "name": "ProxyAdmin", - "address": "0x0EdB3604D230963ecE9d83963164CFe2fDef576B", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x49cfd6Ef774AcAb14814D699e3F7eE36Fdfba932", - "constructorArguments": "0x0000000000000000000000004a67be3a7db40a8ae110f85ab66b0203f9401a770000000000000000000000000edb3604d230963ece9d83963164cfe2fdef576b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0x4a67bE3a7DB40a8Ae110F85ab66b0203f9401a77", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000000005", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x28c294C61D3dE053462d2Cfa5d5f8c8D70605A59", - "constructorArguments": "0x00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x0EdB3604D230963ecE9d83963164CFe2fDef576B", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0xeC34c715ee6d050b2172E8aF650Db779561266C1", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x0cD26594ea6c6526927C0F5225AC09F6288e7140", - "constructorArguments": "0x000000000000000000000000ee99ea7cc4035e942917cd444e0a653a4b9d3e1a0000000000000000000000000edb3604d230963ece9d83963164cfe2fdef576b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0xee99eA7cC4035E942917cd444e0A653A4B9d3e1A", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x28c294C61D3dE053462d2Cfa5d5f8c8D70605A59", - "constructorArguments": "0x00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x0EdB3604D230963ecE9d83963164CFe2fDef576B", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0xeC34c715ee6d050b2172E8aF650Db779561266C1", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x0cD26594ea6c6526927C0F5225AC09F6288e7140", - "constructorArguments": "0x000000000000000000000000ee99ea7cc4035e942917cd444e0a653a4b9d3e1a0000000000000000000000000edb3604d230963ece9d83963164cfe2fdef576b00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0xee99eA7cC4035E942917cd444e0A653A4B9d3e1A", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x9293B8dAcA7933765de499C992B0Fa86Bb104b0f", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x3c182AD9cA8A71bc107Ef440C2667E8360e1158E", - "constructorArguments": "0x00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x66F356393a9d66C7757dE475d02969783796d54c", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000028c294c61d3de053462d2cfa5d5f8c8d70605a59", - "isProxy": false - }, - { - "name": "OpStackHook", - "address": "0x7523AE9fAebf49749a0E7148403c8d26C23a53da", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000014a330000000000000000000000001681cc382e08a72d4b64a123080896e30f96b7400000000000000000000000008e5693140ea606bceb98761d9beb1bc87383706d", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x66F356393a9d66C7757dE475d02969783796d54c", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000028c294c61d3de053462d2cfa5d5f8c8d70605a59", - "isProxy": false - }, - { - "name": "OpStackHook", - "address": "0x7523AE9fAebf49749a0E7148403c8d26C23a53da", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000014a330000000000000000000000001681cc382e08a72d4b64a123080896e30f96b7400000000000000000000000008e5693140ea606bceb98761d9beb1bc87383706d", - "isProxy": false - }, - { - "name": "OpStackHook", - "address": "0x108FD05a2c0Ba834506167ef8f9FD715B3319d8F", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000014a330000000000000000000000003212977fbe6464c2bb60fdb85ab0a5e06e25cdfb0000000000000000000000008e5693140ea606bceb98761d9beb1bc87383706d", - "isProxy": false - }, - { - "name": "OpStackHook", - "address": "0x108FD05a2c0Ba834506167ef8f9FD715B3319d8F", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000014a330000000000000000000000003212977fbe6464c2bb60fdb85ab0a5e06e25cdfb0000000000000000000000008e5693140ea606bceb98761d9beb1bc87383706d", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x8BAB125B823ee5E55797394c03B15874bF176A53", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000028c294c61d3de053462d2cfa5d5f8c8d70605a59", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x8BAB125B823ee5E55797394c03B15874bF176A53", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000028c294c61d3de053462d2cfa5d5f8c8d70605a59", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xDd66CB60D4Ffb7f0d8FB91CB1D20aBcaBC82900a", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000028c294c61d3de053462d2cfa5d5f8c8d70605a59", - "isProxy": false - }, - { - "name": "OpStackHook", - "address": "0xce59701919507F2d379270657A4e410F570aBe0D", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000014a33000000000000000000000000e0c5bdafee7f7065402337040e426a42b5c336500000000000000000000000008e5693140ea606bceb98761d9beb1bc87383706d", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xDd66CB60D4Ffb7f0d8FB91CB1D20aBcaBC82900a", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba932000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000028c294c61d3de053462d2cfa5d5f8c8d70605a59", - "isProxy": false - }, - { - "name": "OpStackHook", - "address": "0xce59701919507F2d379270657A4e410F570aBe0D", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000014a33000000000000000000000000e0c5bdafee7f7065402337040e426a42b5c336500000000000000000000000008e5693140ea606bceb98761d9beb1bc87383706d", - "isProxy": false - } - ], - "scrollsepolia": [ - { - "name": "ProxyAdmin", - "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", - "constructorArguments": "0x0000000000000000000000007914a3349107a7295bbf2374db5a973d73d1b324000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0x7914A3349107A7295Bbf2374db5A973d73D1b324", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000000008274f", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "constructorArguments": "0x0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "constructorArguments": "0x000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "constructorArguments": "0x0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "constructorArguments": "0x000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x527768930D889662Fe7ACF64294871e86e4C2381", - "constructorArguments": "0x0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", - "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xfBeaF07855181f8476B235Cf746A7DF3F9e386Fb", - "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", - "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x7c115c16E34c74afdb88bd268EaB19bC705891FE", - "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a68000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0", - "isProxy": false - } - ], - "sepolia": [ - { - "name": "ProxyAdmin", - "address": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", - "constructorArguments": "0x00000000000000000000000062b2203d0757d1845ab2422eb61ab30d4ad3c51500000000000000000000000097bbc6bbafa5ce3b2fa966c121af63bd09e940f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0x62b2203d0757d1845Ab2422Eb61ab30D4Ad3c515", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000aa36a7", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", - "constructorArguments": "0x000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", - "constructorArguments": "0x0000000000000000000000003f09709b9865993f0320c3e836f3a3f4ff83ea9b00000000000000000000000097bbc6bbafa5ce3b2fa966c121af63bd09e940f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x3f09709b9865993f0320C3e836f3a3F4ff83eA9b", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", - "constructorArguments": "0x000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", - "constructorArguments": "0x0000000000000000000000003f09709b9865993f0320c3e836f3a3f4ff83ea9b00000000000000000000000097bbc6bbafa5ce3b2fa966c121af63bd09e940f800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x3f09709b9865993f0320C3e836f3a3F4ff83eA9b", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", - "constructorArguments": "0x000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x7CE74319699165430fC651F6b99406eEbee95493", - "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", "address": "0x7CE74319699165430fC651F6b99406eEbee95493", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", "address": "0x977837C7bf2863403d08a57Ee952d63fA1ae279E", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c0000000000000000000000004917a9746a7b6e0a57159ccb7f5a6744247f2d0d", - "isProxy": false - } - ], - "moonbasealpha": [ - { - "name": "ProxyAdmin", - "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "constructorArguments": "0x00000000000000000000000096ee22fb5cad052bef57f8ee9d5050b45204cc92000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0x96ee22Fb5Cad052bEf57f8EE9D5050B45204cc92", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000000507", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x155B1CD2f7Cbc58d403B9BE341FaB6CD77425175", - "constructorArguments": "0x00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x62fA20dE68Dbe425f0bc474b12235a4F8449E608", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x92F05669A354a032A84FcfABfD13beE1aBc5bFd0", - "constructorArguments": "0x000000000000000000000000700eb93ae3a4cfccfefd2f17d92b79cd3ff202e4000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x700EB93Ae3A4CFcCfEFd2F17d92b79CD3FF202e4", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x155B1CD2f7Cbc58d403B9BE341FaB6CD77425175", - "constructorArguments": "0x00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x62fA20dE68Dbe425f0bc474b12235a4F8449E608", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x92F05669A354a032A84FcfABfD13beE1aBc5bFd0", - "constructorArguments": "0x000000000000000000000000700eb93ae3a4cfccfefd2f17d92b79cd3ff202e4000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x700EB93Ae3A4CFcCfEFd2F17d92b79CD3FF202e4", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0xe2A73F106902983452713F24Bd019F6eb8712986", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x07543860AE9E72aBcF2Bae9827b23621A64Fa416", - "constructorArguments": "0x00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x6c9EB73793F9Cd535DB1bF86dC307f6d899b2fE3", - "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000155b1cd2f7cbc58d403b9be341fab6cd77425175", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x6c9EB73793F9Cd535DB1bF86dC307f6d899b2fE3", - "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000155b1cd2f7cbc58d403b9be341fab6cd77425175", - "isProxy": false - } - ], - "optimismgoerli": [ - { - "name": "ProxyAdmin", - "address": "0x800b4be4Dc91E56DE934D9f16888d113eFf89Ebb", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xB5f021728Ea6223E3948Db2da61d612307945eA2", - "constructorArguments": "0x000000000000000000000000a04b18c7e45f41cb28590d37784017ea1bbce052000000000000000000000000800b4be4dc91e56de934d9f16888d113eff89ebb00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xA04b18c7E45F41CB28590D37784017Ea1bbCe052", - "constructorArguments": "0x00000000000000000000000000000000000000000000000000000000000001a4", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xFEe074B31B5B259eB3109737bE13D39B853b47b9", - "constructorArguments": "0x000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea2", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x800b4be4Dc91E56DE934D9f16888d113eFf89Ebb", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x4927C33299091033D935C15DE6b6073164e99BE0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x02A7661273528EfF3d78CBE7CbD1a717b28B89fC", - "constructorArguments": "0x00000000000000000000000003c39954c2dc91d32f68461300391e8605e83176000000000000000000000000800b4be4dc91e56de934d9f16888d113eff89ebb00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x03C39954C2DC91d32f68461300391E8605e83176", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xFEe074B31B5B259eB3109737bE13D39B853b47b9", - "constructorArguments": "0x000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea2", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x800b4be4Dc91E56DE934D9f16888d113eFf89Ebb", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x4927C33299091033D935C15DE6b6073164e99BE0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x02A7661273528EfF3d78CBE7CbD1a717b28B89fC", - "constructorArguments": "0x00000000000000000000000003c39954c2dc91d32f68461300391e8605e83176000000000000000000000000800b4be4dc91e56de934d9f16888d113eff89ebb00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x03C39954C2DC91d32f68461300391E8605e83176", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x962e30F6A3ECDA85c7fa1FcF38cD04efA991Ee20", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x24D31e12E4d3bc2C46C994FcE0c828b218A1aeAb", - "constructorArguments": "0x000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea2", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xba962f31B8DE02238fDdf8CE6a21260Af8C5Dd2F", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea2000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fee074b31b5b259eb3109737be13d39b853b47b9", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0xba962f31B8DE02238fDdf8CE6a21260Af8C5Dd2F", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea2000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fee074b31b5b259eb3109737be13d39b853b47b9", - "isProxy": false - } - ], - "arbitrumgoerli": [ - { - "name": "ProxyAdmin", - "address": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x13dABc0351407d5aAa0A50003a166A73b4febfDc", - "constructorArguments": "0x000000000000000000000000385c7f179168f5da92c72e17ae8ef50f3874077f00000000000000000000000000dfb81bfc45fa03060b605273147f274ea807e500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0x385C7f179168f5Da92c72E17AE8EF50F3874077f", - "constructorArguments": "0x0000000000000000000000000000000000000000000000000000000000066eed", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xf0A38e1eEA49dAc7968F470c3aA0BDE2565A5d80", - "constructorArguments": "0x00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0xFc8229ADB46D96056A6e451Fb3c55d60FFeD056f", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "constructorArguments": "0x000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000dfb81bfc45fa03060b605273147f274ea807e500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0xf0A38e1eEA49dAc7968F470c3aA0BDE2565A5d80", - "constructorArguments": "0x00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0xFc8229ADB46D96056A6e451Fb3c55d60FFeD056f", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "constructorArguments": "0x000000000000000000000000b241991527f1c21ade14f55589e5940ac4852fa000000000000000000000000000dfb81bfc45fa03060b605273147f274ea807e500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0x0358ba0D90ED2d90fB8cBb610F27C274D8077a0B", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x4a01EEBa1CC20F47A2e60aE4ec932051601FcB9e", - "constructorArguments": "0x00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x3Ce607F6FcE5Dfb9821f33504d86E04A4CD0C75f", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000f0a38e1eea49dac7968f470c3aa0bde2565a5d80", - "isProxy": false - }, - { - "name": "FallbackRoutingHook", - "address": "0x3Ce607F6FcE5Dfb9821f33504d86E04A4CD0C75f", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000f0a38e1eea49dac7968f470c3aa0bde2565a5d80", - "isProxy": false - } - ], - "polygonzkevmtestnet": [ - { - "name": "ProxyAdmin", - "address": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "constructorArguments": "0x000000000000000000000000ef48bd850e5827b96b55c4d28fb32bbaa73616f2000000000000000000000000666a24f62f7a97ba33c151776eb3d9441a059eb800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "Mailbox", - "address": "0xef48bd850E5827B96B55C4D28FB32Bbaa73616F2", - "constructorArguments": "0x00000000000000000000000000000000000000000000000000000000000005a2", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x68311418D79fE8d96599384ED767d225635d88a8", - "constructorArguments": "0x000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x3707bc8C7342aA6f693bCe1Bd7671Fca146F7F0A", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "constructorArguments": "0x000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0000000000000000000000000666a24f62f7a97ba33c151776eb3d9441a059eb800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "MerkleTreeHook", - "address": "0x68311418D79fE8d96599384ED767d225635d88a8", - "constructorArguments": "0x000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8", - "isProxy": false - }, - { - "name": "ProxyAdmin", - "address": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "StorageGasOracle", - "address": "0x3707bc8C7342aA6f693bCe1Bd7671Fca146F7F0A", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "constructorArguments": "0x000000000000000000000000863e8c26621c52aca1849c53500606e73ba272f0000000000000000000000000666a24f62f7a97ba33c151776eb3d9441a059eb800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainGasPaymaster", - "address": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "ProtocolFee", - "address": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "constructorArguments": "0x000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", - "isProxy": false - }, - { - "name": "ValidatorAnnounce", - "address": "0x7914A3349107A7295Bbf2374db5A973d73D1b324", - "constructorArguments": "0x000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8", - "isProxy": false + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "name": "FallbackRoutingHook", - "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000068311418d79fe8d96599384ed767d225635d88a8", - "isProxy": false + "address": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" }, { - "name": "FallbackRoutingHook", - "address": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000068311418d79fe8d96599384ed767d225635d88a8", - "isProxy": false + "address": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" } ] } diff --git a/typescript/infra/config/environments/testnet4/create2/addresses.json b/typescript/infra/config/environments/testnet4/create2/addresses.json index 8325277590..740ba7abf1 100644 --- a/typescript/infra/config/environments/testnet4/create2/addresses.json +++ b/typescript/infra/config/environments/testnet4/create2/addresses.json @@ -11,18 +11,6 @@ "bsctestnet": { "Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a" }, - "goerli": { - "Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a" - }, - "moonbasealpha": { - "Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a" - }, - "optimismgoerli": { - "Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a" - }, - "arbitrumgoerli": { - "Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a" - }, "sepolia": { "Create2Factory": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a" } diff --git a/typescript/infra/config/environments/testnet4/create2/verification.json b/typescript/infra/config/environments/testnet4/create2/verification.json index 53f6de7133..acd051fa91 100644 --- a/typescript/infra/config/environments/testnet4/create2/verification.json +++ b/typescript/infra/config/environments/testnet4/create2/verification.json @@ -55,14 +55,6 @@ "isProxy": false } ], - "goerli": [ - { - "name": "Create2Factory", - "address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", - "constructorArguments": "", - "isProxy": false - } - ], "moonbasealpha": [ { "name": "Create2Factory", @@ -71,22 +63,6 @@ "isProxy": false } ], - "optimismgoerli": [ - { - "name": "Create2Factory", - "address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", - "constructorArguments": "", - "isProxy": false - } - ], - "arbitrumgoerli": [ - { - "name": "Create2Factory", - "address": "0xc97D8e6f57b0d64971453dDc6EB8483fec9d163a", - "constructorArguments": "", - "isProxy": false - } - ], "sepolia": [ { "name": "Create2Factory", diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 75ec4ddf00..69948bbc3c 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -9,7 +9,7 @@ import { environment } from './chains'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'f6da03e-20231216-141949', + tag: '7d1f975-20240312-113049', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron @@ -24,4 +24,18 @@ export const keyFunderConfig: KeyFunderConfig = { [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], }, connectionType: RpcConsensusType.Quorum, + // desired balance config + desiredBalancePerChain: { + alfajores: '5', + bsctestnet: '5', + fuji: '5', + mumbai: '5', + plumetestnet: '0.2', + sepolia: '5', + // Funder boosts itself upto 5x balance on L2 before dispersing funds + scrollsepolia: '1', + }, + desiredKathyBalancePerChain: { + plumetestnet: '0.05', + }, }; diff --git a/typescript/infra/config/environments/testnet4/gas-oracle.ts b/typescript/infra/config/environments/testnet4/gas-oracle.ts index b173f4d3a5..5dfe45fcb8 100644 --- a/typescript/infra/config/environments/testnet4/gas-oracle.ts +++ b/typescript/infra/config/environments/testnet4/gas-oracle.ts @@ -11,7 +11,7 @@ import { getTokenExchangeRateFromValues, } from '../../../src/config/gas-oracle'; -import { TestnetChains, supportedChainNames } from './chains'; +import { supportedChainNames } from './chains'; // Taken by looking at each testnet and overestimating gas prices const gasPrices: ChainMap = { @@ -19,17 +19,12 @@ const gasPrices: ChainMap = { fuji: ethers.utils.parseUnits('30', 'gwei'), mumbai: ethers.utils.parseUnits('45', 'gwei'), bsctestnet: ethers.utils.parseUnits('15', 'gwei'), - goerli: ethers.utils.parseUnits('5', 'gwei'), sepolia: ethers.utils.parseUnits('5', 'gwei'), - moonbasealpha: ethers.utils.parseUnits('5', 'gwei'), - optimismgoerli: ethers.utils.parseUnits('0.5', 'gwei'), - arbitrumgoerli: ethers.utils.parseUnits('0.5', 'gwei'), - basegoerli: ethers.utils.parseUnits('0.2', 'gwei'), scrollsepolia: ethers.utils.parseUnits('0.5', 'gwei'), - lineagoerli: ethers.utils.parseUnits('1', 'gwei'), - polygonzkevmtestnet: ethers.utils.parseUnits('1', 'gwei'), chiado: ethers.utils.parseUnits('2', 'gwei'), - // solanadevnet: ethers.BigNumber.from('28'), + solanatestnet: ethers.BigNumber.from('28'), + eclipsetestnet: ethers.BigNumber.from('28'), + plumetestnet: ethers.utils.parseUnits('0.01', 'gwei'), }; // Used to categorize rarity of testnet tokens & approximate exchange rates. @@ -53,21 +48,16 @@ const chainTokenRarity: ChainMap = { fuji: Rarity.Rare, mumbai: Rarity.Rare, bsctestnet: Rarity.Rare, - goerli: Rarity.Mythic, sepolia: Rarity.Mythic, - moonbasealpha: Rarity.Common, - optimismgoerli: Rarity.Mythic, - arbitrumgoerli: Rarity.Mythic, - basegoerli: Rarity.Mythic, scrollsepolia: Rarity.Rare, - lineagoerli: Rarity.Rare, - polygonzkevmtestnet: Rarity.Common, chiado: Rarity.Common, - // solanadevnet: Rarity.Common, + solanatestnet: Rarity.Common, + eclipsetestnet: Rarity.Common, + plumetestnet: Rarity.Common, }; // Gets the "value" of a testnet chain -function getApproximateValue(chain: TestnetChains): BigNumber { +function getApproximateValue(chain: ChainName): BigNumber { const rarity = chainTokenRarity[chain]; return RARITY_APPROXIMATE_VALUE[rarity]; } diff --git a/typescript/infra/config/environments/testnet4/helloworld.ts b/typescript/infra/config/environments/testnet4/helloworld.ts index c375be27c9..08f637ec78 100644 --- a/typescript/infra/config/environments/testnet4/helloworld.ts +++ b/typescript/infra/config/environments/testnet4/helloworld.ts @@ -13,14 +13,14 @@ export const hyperlaneHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '86b7f98-20231207-153806', + tag: '7d1f975-20240312-113049', }, chainsToSkip: [], runEnv: environment, namespace: environment, runConfig: { mode: HelloWorldKathyRunMode.Service, - fullCycleTime: 1000 * 60 * 60 * 48, // every 48 hours + fullCycleTime: 1000 * 60 * 60 * 24 * 6, // every 6 days. At 12 chains it 12 * 11 messages = 132 messages its a bit less than once an hour }, messageSendTimeout: 1000 * 60 * 10, // 10 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min @@ -33,7 +33,7 @@ export const releaseCandidateHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '86b7f98-20231207-153806', + tag: '7d1f975-20240312-113049', }, chainsToSkip: [], runEnv: environment, @@ -43,7 +43,7 @@ export const releaseCandidateHelloworld: HelloWorldConfig = { }, messageSendTimeout: 1000 * 60 * 8, // 8 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min - connectionType: RpcConsensusType.Single, + connectionType: RpcConsensusType.Fallback, }, }; diff --git a/typescript/infra/config/environments/testnet4/helloworld/hyperlane/addresses.json b/typescript/infra/config/environments/testnet4/helloworld/hyperlane/addresses.json index 00719dfd49..fc8b407c17 100644 --- a/typescript/infra/config/environments/testnet4/helloworld/hyperlane/addresses.json +++ b/typescript/infra/config/environments/testnet4/helloworld/hyperlane/addresses.json @@ -1,28 +1,13 @@ { - "basegoerli": { - "router": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE" - }, - "arbitrumgoerli": { - "router": "0xFd1e3710347659703962934d2381970ae0022227" - }, - "optimismgoerli": { - "router": "0x372Cf98E673C235C1abD78DEAA61480e6fe55e46" - }, "scrollsepolia": { "router": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c" }, "alfajores": { "router": "0xD0Ef694E96Bb695DC829f71956227eD141e3089F" }, - "polygonzkevmtestnet": { - "router": "0x783c4a0bB6663359281aD4a637D5af68F83ae213" - }, "fuji": { "router": "0x0B1C1B54f45e02552331D3106e71f5e0b573D5D4" }, - "moonbasealpha": { - "router": "0x3Ce607F6FcE5Dfb9821f33504d86E04A4CD0C75f" - }, "mumbai": { "router": "0x04980C17e2CE26578C82f81207e706e4505FaE3B" }, @@ -31,8 +16,5 @@ }, "bsctestnet": { "router": "0xEF6DE81100B9314B45A1bdfA5C9148aFC4DdbDeE" - }, - "goerli": { - "router": "0x31b6a75ef30C8612738CC55eA3CB2d5DD3694DC7" } } diff --git a/typescript/infra/config/environments/testnet4/helloworld/hyperlane/verification.json b/typescript/infra/config/environments/testnet4/helloworld/hyperlane/verification.json index 06f1f6badf..2f369a2193 100644 --- a/typescript/infra/config/environments/testnet4/helloworld/hyperlane/verification.json +++ b/typescript/infra/config/environments/testnet4/helloworld/hyperlane/verification.json @@ -7,37 +7,37 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x477D860f8F41bC69dDD32821F2Bf2C2Af0243F16", "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x76bDE8069b3467A459262192509Ad5c00AcbdaF0", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e590000000000000000000000001246529eddca523afe5c6b9414299633d2e16697", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x39e08602570237433673B1340Da17105cA098EE7", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e590000000000000000000000001246529eddca523afe5c6b9414299633d2e16697", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x0231C1A2CfDbC2d2FA8363c3eC60c85a458088aE", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e590000000000000000000000000000000000000000000000000000000000000000", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xb58F3D0CA2B26803eA6a64696989102cE301Fd23", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e590000000000000000000000000000000000000000000000000000000000000000", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xD0Ef694E96Bb695DC829f71956227eD141e3089F", "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e590000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -51,25 +51,25 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x5da3b8d6F73dF6003A490072106730218c475AAd", "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xE04b1D70d487eFCa454EEBf3821E42Dd41e66609", "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b00000000000000000000000006895d3916b94b386faa6ec9276756e16dae7480e", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x6F7A1B7868f14a2f4c36cc561d910aE7958bc8D8", "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b00000000000000000000000006895d3916b94b386faa6ec9276756e16dae7480e", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x0B1C1B54f45e02552331D3106e71f5e0b573D5D4", "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b00000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -83,25 +83,25 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x1A4d8a5eD6C93Af828655e15C44eeE2c2851F0D6", "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x79B6151129780f5A80DFEF7c1E967b21A9674A7D", "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f260411150000000000000000000000008ab67caf605c6ee83cbfefb0d8d67fdd3bf7b591", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xBEadC848cAB77875d066466b9F3509f069a0a4F5", "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f260411150000000000000000000000008ab67caf605c6ee83cbfefb0d8d67fdd3bf7b591", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x04980C17e2CE26578C82f81207e706e4505FaE3B", "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f260411150000000000000000000000000000000000000000000000000000000000000000", "isProxy": false @@ -115,74 +115,36 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xE09BF59dCA6e622efC33f6fbd8EF85dE45233388", "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x5b297d6BBE080B66E6acece8d378F70F02c0Bc41", "constructorArguments": "00000000000000000000000089280d0b68a246b276a910a518531e2a861ceb650000000000000000000000000dd20e410bdb95404f71c5a4e7fa67b892a5f949", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x90230E8cf53f4F9aFC2A9777eB0093C0B1C81b59", "constructorArguments": "00000000000000000000000089280d0b68a246b276a910a518531e2a861ceb650000000000000000000000000dd20e410bdb95404f71c5a4e7fa67b892a5f949", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xd0e07a58BC91a686235E542350E63211C7A34c17", "constructorArguments": "00000000000000000000000089280d0b68a246b276a910a518531e2a861ceb650000000000000000000000000000000000000000000000000000000000000000", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xEF6DE81100B9314B45A1bdfA5C9148aFC4DdbDeE", "constructorArguments": "000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "goerli": [ - { - "name": "HelloWorld", - "address": "0x0637A1360Ea44602DAe5c4ba515c2BCb6C762fbc", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f857706ce59cb7ae6df81bbd0b0a656db3e6beda", - "isProxy": false - }, - { - "name": "Router", - "address": "0x405BFdEcB33230b4Ad93C29ba4499b776CfBa189", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", - "isProxy": false - }, - { - "name": "Router", - "address": "0xBF5b89eBdF86560F872603e2063f17b78dFbae02", - "constructorArguments": "000000000000000000000000c4796aabce2a9d774ce4603f5db5fb1605791dbd0000000000000000000000000cd26594ea6c6526927c0f5225ac09f6288e7140", - "isProxy": false - }, - { - "name": "Router", - "address": "0xA5F234545270cea8f31A8760b8b7ABcaA3654951", - "constructorArguments": "000000000000000000000000c4796aabce2a9d774ce4603f5db5fb1605791dbd0000000000000000000000000cd26594ea6c6526927c0f5225ac09f6288e7140", - "isProxy": false - }, - { - "name": "Router", - "address": "0x33AA9cFBC7E222B3eC231EcAa58dd846cC393C28", - "constructorArguments": "000000000000000000000000c4796aabce2a9d774ce4603f5db5fb1605791dbd0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x31b6a75ef30C8612738CC55eA3CB2d5DD3694DC7", - "constructorArguments": "00000000000000000000000049cfd6ef774acab14814d699e3f7ee36fdfba9320000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - } - ], "moonbasealpha": [ { "name": "HelloWorld", @@ -191,262 +153,98 @@ "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x89e02C3C7b97bCBa63279E10E2a44e6cEF69E6B2", "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x36a53DDa7626e5eb62e59a0192Af07b67AF434D8", "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f00000000000000000000000092f05669a354a032a84fcfabfd13bee1abc5bfd0", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x893b57DE7E3dd885F827c74E74F3099C1c250c52", "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f00000000000000000000000092f05669a354a032a84fcfabfd13bee1abc5bfd0", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x3Ce607F6FcE5Dfb9821f33504d86E04A4CD0C75f", "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "optimismgoerli": [ - { - "name": "HelloWorld", - "address": "0xa76A3E719E5ff7159a29B8876272052b89B3589F", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f857706ce59cb7ae6df81bbd0b0a656db3e6beda", - "isProxy": false - }, - { - "name": "Router", - "address": "0x3582d1238cBC812165981E4fFaB0E8D9a4518910", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", - "isProxy": false - }, - { - "name": "Router", - "address": "0x050500E43Fa7F245f3012527DE29d22176c594c7", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea200000000000000000000000002a7661273528eff3d78cbe7cbd1a717b28b89fc", - "isProxy": false - }, - { - "name": "Router", - "address": "0x7Dc5cE596995af8e5D568563038109B9bCA7F7c6", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea200000000000000000000000002a7661273528eff3d78cbe7cbd1a717b28b89fc", - "isProxy": false - }, - { - "name": "Router", - "address": "0xe7B2e23A9A77556736DBB040A8a5159a80Db73D5", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea20000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x138e726C327E9dd0779104B8d42331757CAeeB9F", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea20000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x372Cf98E673C235C1abD78DEAA61480e6fe55e46", - "constructorArguments": "000000000000000000000000b5f021728ea6223e3948db2da61d612307945ea20000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - } - ], - "arbitrumgoerli": [ - { - "name": "HelloWorld", - "address": "0xa76A3E719E5ff7159a29B8876272052b89B3589F", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f857706ce59cb7ae6df81bbd0b0a656db3e6beda", - "isProxy": false - }, - { - "name": "Router", - "address": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", - "isProxy": false - }, - { - "name": "Router", - "address": "0xB8D70C9352AA59f5EB138e045117841910c107a3", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", - "isProxy": false - }, - { - "name": "Router", - "address": "0xA3bAaC7d60d10abA54cC2Ffb4b63469810C5aDc0", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f", - "isProxy": false - }, - { - "name": "Router", - "address": "0x9C63cE44d595cfd97215fcb97d58CFD07a9D6BD7", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x22C7d91A4533536FE6006EFA46074E2Df8f82e82", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0xFd1e3710347659703962934d2381970ae0022227", - "constructorArguments": "00000000000000000000000013dabc0351407d5aaa0a50003a166a73b4febfdc0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - } - ], "sepolia": [ { - "name": "Router", + "name": "HelloWorld", "address": "0x5d56B8a669F50193b54319442c6EEE5edD662381", "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f987d7edcb5890cb321437d8145e3d51131298b6", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x8FF62Cd16C689C6541A284c0e2389D4371255Bc2", "constructorArguments": "00000000000000000000000033abaf6708be03bdf0595da0745a7111b01db8c70000000000000000000000006f2756380fd49228ae25aa7f2817993cb74ecc56", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x73A7bDa325Ad8E5F591179C4ccA61b0CeF70d05C", "constructorArguments": "00000000000000000000000033abaf6708be03bdf0595da0745a7111b01db8c70000000000000000000000006f2756380fd49228ae25aa7f2817993cb74ecc56", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xE14FE2a74Ba1E1bD0EE14B780e58fCcc3c95C013", "constructorArguments": "00000000000000000000000033abaf6708be03bdf0595da0745a7111b01db8c70000000000000000000000000000000000000000000000000000000000000000", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x37590302D4E60fbCAdF708E8Fc1DCd903a5880F8", "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec28847660000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "basegoerli": [ - { - "name": "Router", - "address": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a00000000000000000000000028b02b97a850872c4d33c3e024fab6499ad96564", - "isProxy": false - }, - { - "name": "Router", - "address": "0xae7a78916Ba4c507aCB2F0e474ace545Ff4bF841", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a00000000000000000000000028b02b97a850872c4d33c3e024fab6499ad96564", - "isProxy": false - }, - { - "name": "Router", - "address": "0x04438ef7622f5412f82915F59caD4f704C61eA48", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a00000000000000000000000028b02b97a850872c4d33c3e024fab6499ad96564", - "isProxy": false - }, - { - "name": "Router", - "address": "0xb94F96D398eA5BAB5CA528EE9Fdc19afaA825818", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x51A0a100e7BC63Ea7821A3a023B6F17fb94FF011", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x086E902d2f99BcCEAa28B31747eC6Dc5fd43B1bE", - "constructorArguments": "00000000000000000000000058483b754abb1e8947be63d6b95df75b8249543a0000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - } - ], "scrollsepolia": [ { - "name": "Router", + "name": "HelloWorld", "address": "0x433f7d6d0cB9eb8FF2902Ad01C1BEd6C09934a33", "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a6800000000000000000000000086fb9f1c124fb20ff130c41a79a432f770f67afd", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a6800000000000000000000000086fb9f1c124fb20ff130c41a79a432f770f67afd", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a6800000000000000000000000086fb9f1c124fb20ff130c41a79a432f770f67afd", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a680000000000000000000000000000000000000000000000000000000000000000", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0xeAEfB1458b032e75de3e9A3a480d005c426FB1c5", "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a680000000000000000000000000000000000000000000000000000000000000000", "isProxy": false }, { - "name": "Router", + "name": "HelloWorld", "address": "0x66b71A4e18FbE09a6977A6520B47fEDdffA82a1c", "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a680000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } - ], - "polygonzkevmtestnet": [ - { - "name": "Router", - "address": "0xD0680F80F4f947968206806C2598Cbc5b6FE5b03", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c", - "isProxy": false - }, - { - "name": "Router", - "address": "0x433f7d6d0cB9eb8FF2902Ad01C1BEd6C09934a33", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c", - "isProxy": false - }, - { - "name": "Router", - "address": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f8000000000000000000000000ad34a66bf6db18e858f6b686557075568c6e031c", - "isProxy": false - }, - { - "name": "Router", - "address": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f80000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f80000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - }, - { - "name": "Router", - "address": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", - "constructorArguments": "000000000000000000000000598face78a4302f11e3de0bee1894da0b2cb71f80000000000000000000000000000000000000000000000000000000000000000", - "isProxy": false - } ] } diff --git a/typescript/infra/config/environments/testnet4/helloworld/rc/addresses.json b/typescript/infra/config/environments/testnet4/helloworld/rc/addresses.json index 5a90998c34..fcf6137411 100644 --- a/typescript/infra/config/environments/testnet4/helloworld/rc/addresses.json +++ b/typescript/infra/config/environments/testnet4/helloworld/rc/addresses.json @@ -1,32 +1,20 @@ { + "scrollsepolia": { + "router": "0xA9425D5cBcD2c83EB2a5BF453EAA18968db3ef77" + }, "alfajores": { - "router": "0x40Adcb03F3C58170b4751c4140636FC6085Ff475" + "router": "0x4D1d8394cBb445A75aE63fDd24421A353B73FF25" }, - "fuji": { - "router": "0xAc003FcDD0EE223664F2A000B5A59D082745700b" + "sepolia": { + "router": "0xEB25e6e42B743a815E5C0409007993a828a0565f" }, - "mumbai": { - "router": "0xaB0892029C3E7dD4c0235590dc296E618A7b4d03" + "fuji": { + "router": "0x29d70a6753D3F3E756502dE6dCd393fE85a97b73" }, "bsctestnet": { - "router": "0xd259b0e793535325786675542aB296c451535c27" - }, - "goerli": { - "router": "0x03e9531ae74e8F0f96DE26788a22d35bdaD24185" + "router": "0x643C7A37FB191A8a63BAB40264B251714F527AED" }, - "moonbasealpha": { - "router": "0xE9D6317a10860340f035f3d09052D9d376855bE8" - }, - "optimismgoerli": { - "router": "0x057d38d184d74192B96840D8FbB37e584dDb569A" - }, - "arbitrumgoerli": { - "router": "0xaAF1BF6f2BfaE290ea8615066fd167e396a2f578" - }, - "sepolia": { - "router": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217" - }, - "solanadevnet": { - "router": "DdTMkk9nuqH5LnD56HLkPiKMV3yB3BNEYSQfgmJHa5i7" + "mumbai": { + "router": "0x4d8323Bb5cD72148e826fCAb9B4A9dd09f77C905" } } diff --git a/typescript/infra/config/environments/testnet4/helloworld/rc/verification.json b/typescript/infra/config/environments/testnet4/helloworld/rc/verification.json index cd0fd9f388..db68e97550 100644 --- a/typescript/infra/config/environments/testnet4/helloworld/rc/verification.json +++ b/typescript/infra/config/environments/testnet4/helloworld/rc/verification.json @@ -1,73 +1,57 @@ { - "alfajores": [ + "scrollsepolia": [ { - "name": "router", - "address": "0x40Adcb03F3C58170b4751c4140636FC6085Ff475", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", + "name": "HelloWorld", + "address": "0xA9425D5cBcD2c83EB2a5BF453EAA18968db3ef77", + "constructorArguments": "0000000000000000000000003c5154a193d6e2955650f9305c8d80c18c814a680000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "fuji": [ + "alfajores": [ { - "name": "router", - "address": "0xAc003FcDD0EE223664F2A000B5A59D082745700b", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", + "name": "HelloWorld", + "address": "0x4D1d8394cBb445A75aE63fDd24421A353B73FF25", + "constructorArguments": "000000000000000000000000ef9f292fcebc3848bf4bb92a96a04f9ecbb78e590000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "mumbai": [ + "sepolia": [ { - "name": "router", - "address": "0xaB0892029C3E7dD4c0235590dc296E618A7b4d03", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", + "name": "HelloWorld", + "address": "0xEB25e6e42B743a815E5C0409007993a828a0565f", + "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec28847660000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "bsctestnet": [ + "fuji": [ { - "name": "router", - "address": "0xd259b0e793535325786675542aB296c451535c27", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", + "name": "HelloWorld", + "address": "0x29d70a6753D3F3E756502dE6dCd393fE85a97b73", + "constructorArguments": "0000000000000000000000005b6cff85442b851a8e6eabd2a4e4507b5135b3b00000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "goerli": [ + "bsctestnet": [ { - "name": "router", - "address": "0x03e9531ae74e8F0f96DE26788a22d35bdaD24185", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", + "name": "HelloWorld", + "address": "0x643C7A37FB191A8a63BAB40264B251714F527AED", + "constructorArguments": "000000000000000000000000f9f6f5646f478d5ab4e20b0f910c92f1ccc9cc6d0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], "moonbasealpha": [ { - "name": "router", - "address": "0xE9D6317a10860340f035f3d09052D9d376855bE8", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", - "isProxy": false - } - ], - "optimismgoerli": [ - { - "name": "router", - "address": "0x057d38d184d74192B96840D8FbB37e584dDb569A", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", + "name": "HelloWorld", + "address": "0xabB6e0A30acEB8327EcC6D25bABf409081fDF2DA", + "constructorArguments": "00000000000000000000000076189acfa212298d7022624a4633411ee0d2f26f0000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ], - "arbitrumgoerli": [ - { - "name": "router", - "address": "0xaAF1BF6f2BfaE290ea8615066fd167e396a2f578", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac1", - "isProxy": false - } - ], - "sepolia": [ + "mumbai": [ { - "name": "Router", - "address": "0x6AD4DEBA8A147d000C09de6465267a9047d1c217", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f987d7edcb5890cb321437d8145e3d51131298b6", + "name": "HelloWorld", + "address": "0x4d8323Bb5cD72148e826fCAb9B4A9dd09f77C905", + "constructorArguments": "0000000000000000000000002d1889fe5b092cd988972261434f7e5f260411150000000000000000000000000000000000000000000000000000000000000000", "isProxy": false } ] diff --git a/typescript/infra/config/environments/testnet4/hooks.ts b/typescript/infra/config/environments/testnet4/hooks.ts deleted file mode 100644 index b88082c4b9..0000000000 --- a/typescript/infra/config/environments/testnet4/hooks.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Chains, HookType, OpStackHookConfig } from '@hyperlane-xyz/sdk'; - -export const opHookConfig: OpStackHookConfig = { - type: HookType.OP_STACK, - nativeBridge: '0xDa2332D0a7608919Cd331B1304Cd179129a90495', - destinationChain: Chains.optimismgoerli, -}; - -export const baseHookConfig: OpStackHookConfig = { - type: HookType.OP_STACK, - nativeBridge: '0x8e5693140eA606bcEB98761d9beB1BC87383706D', - destinationChain: Chains.basegoerli, -}; diff --git a/typescript/infra/config/environments/testnet4/igp.ts b/typescript/infra/config/environments/testnet4/igp.ts index c098d0af61..2294bff2da 100644 --- a/typescript/infra/config/environments/testnet4/igp.ts +++ b/typescript/infra/config/environments/testnet4/igp.ts @@ -1,30 +1,21 @@ import { ChainMap, - GasOracleContractType, IgpConfig, defaultMultisigConfigs, multisigIsmVerificationCost, } from '@hyperlane-xyz/sdk'; import { exclude, objMap } from '@hyperlane-xyz/utils'; -import { TestnetChains, supportedChainNames } from './chains'; +import { supportedChainNames } from './chains'; +import { storageGasOracleConfig } from './gas-oracle'; import { owners } from './owners'; -function getGasOracles(local: TestnetChains) { - return Object.fromEntries( - exclude(local, supportedChainNames).map((name) => [ - name, - GasOracleContractType.StorageGasOracle, - ]), - ); -} - -export const igp: ChainMap = objMap(owners, (chain, owner) => { +export const igp: ChainMap = objMap(owners, (chain, ownerConfig) => { return { - owner, - oracleKey: owner, - beneficiary: owner, - gasOracleType: getGasOracles(chain), + ...ownerConfig, + oracleKey: ownerConfig.owner, + beneficiary: ownerConfig.owner, + oracleConfig: storageGasOracleConfig[chain], overhead: Object.fromEntries( exclude(chain, supportedChainNames).map((remote) => [ remote, diff --git a/typescript/infra/config/environments/testnet4/index.ts b/typescript/infra/config/environments/testnet4/index.ts index 69ca175aed..b522e7eef3 100644 --- a/typescript/infra/config/environments/testnet4/index.ts +++ b/typescript/infra/config/environments/testnet4/index.ts @@ -3,7 +3,7 @@ import { RpcConsensusType } from '@hyperlane-xyz/sdk'; import { getKeysForRole, getMultiProviderForRole, -} from '../../../scripts/utils'; +} from '../../../scripts/agent-utils'; import { EnvironmentConfig } from '../../../src/config'; import { Role } from '../../../src/roles'; import { Contexts } from '../../contexts'; @@ -12,7 +12,6 @@ import { agents } from './agent'; import { environment as environmentName, testnetConfigs } from './chains'; import { core } from './core'; import { keyFunderConfig } from './funding'; -import { storageGasOracleConfig } from './gas-oracle'; import { helloWorld } from './helloworld'; import { igp } from './igp'; import { infrastructure } from './infrastructure'; @@ -51,5 +50,4 @@ export const environment: EnvironmentConfig = { bridgeAdapters: bridgeAdapterConfigs, relayer: liquidityLayerRelayerConfig, }, - storageGasOracleConfig, }; diff --git a/typescript/infra/config/environments/testnet4/ism/verification.json b/typescript/infra/config/environments/testnet4/ism/verification.json index 71c1a20704..9769ccad24 100644 --- a/typescript/infra/config/environments/testnet4/ism/verification.json +++ b/typescript/infra/config/environments/testnet4/ism/verification.json @@ -1067,204 +1067,6 @@ "isProxy": true } ], - "goerli": [ - { - "name": "StaticMultisigIsmFactory", - "address": "0xFa13bd2BD45644bAF0797e41D1a4D56601a4059a", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsmFactory", - "address": "0x11413a6Ea76a55eAec950894ba8fdd7683E41b06", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomainRoutingIsmFactory", - "address": "0x129A80Fe557153B5F48B4292F8C177bACdcf7dB3", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x4dD7716b876441355657a18c7E7b02129F88E3c0", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0x14b0F0c0a59704E92f95252cE24Ef6aB1d679733", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x8e43aCfb338B137A3befd9b92BfD84E128adE0B8", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xcc493EB29FC244004436a69354771F2C38aFC4c9" - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0xDdB54502A8e2a31C48148C62A8a9E83a693d6173", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x6A995F21a74332BCc280597807DF6Af3e4833f83" - }, - { - "name": "AggregationIsmFactory", - "address": "0x8a176773d54292123d271FA0B9C7C8Def4c3a31b", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsm", - "address": "0x8D090384691b7a5ED7f418Ddf8135438913EFDcc" - }, - { - "name": "AggregationHookFactory", - "address": "0x6bc243963f80AEa80948e8538bB114d4122DD9c5", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationHook", - "address": "0xDd4b9980b52A5e786fA903b2190aaFDdA461dED5" - }, - { - "name": "RoutingIsmFactory", - "address": "0xd16c3f34d6A2e62185aC61f76F83D3AA1E969018", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0x84e9B533555E76a8E9B95daCCbdeE8d3cFA2F5D4" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xcc493EB29FC244004436a69354771F2C38aFC4c9" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x6A995F21a74332BCc280597807DF6Af3e4833f83" - }, - { - "name": "StaticAggregationIsm", - "address": "0x8D090384691b7a5ED7f418Ddf8135438913EFDcc" - }, - { - "name": "StaticAggregationHook", - "address": "0xDd4b9980b52A5e786fA903b2190aaFDdA461dED5" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x84e9B533555E76a8E9B95daCCbdeE8d3cFA2F5D4" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xcc493EB29FC244004436a69354771F2C38aFC4c9" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x6A995F21a74332BCc280597807DF6Af3e4833f83" - }, - { - "name": "StaticAggregationIsm", - "address": "0x8D090384691b7a5ED7f418Ddf8135438913EFDcc" - }, - { - "name": "StaticAggregationHook", - "address": "0xDd4b9980b52A5e786fA903b2190aaFDdA461dED5" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x84e9B533555E76a8E9B95daCCbdeE8d3cFA2F5D4" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xcc493EB29FC244004436a69354771F2C38aFC4c9" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x6A995F21a74332BCc280597807DF6Af3e4833f83" - }, - { - "name": "StaticAggregationIsm", - "address": "0x8D090384691b7a5ED7f418Ddf8135438913EFDcc" - }, - { - "name": "StaticAggregationHook", - "address": "0xDd4b9980b52A5e786fA903b2190aaFDdA461dED5" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x84e9B533555E76a8E9B95daCCbdeE8d3cFA2F5D4" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xcc493EB29FC244004436a69354771F2C38aFC4c9" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x6A995F21a74332BCc280597807DF6Af3e4833f83" - }, - { - "name": "StaticAggregationIsm", - "address": "0x8D090384691b7a5ED7f418Ddf8135438913EFDcc" - }, - { - "name": "StaticAggregationHook", - "address": "0xDd4b9980b52A5e786fA903b2190aaFDdA461dED5" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x84e9B533555E76a8E9B95daCCbdeE8d3cFA2F5D4" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xcc493EB29FC244004436a69354771F2C38aFC4c9", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x6A995F21a74332BCc280597807DF6Af3e4833f83", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0x8D090384691b7a5ED7f418Ddf8135438913EFDcc", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0xDd4b9980b52A5e786fA903b2190aaFDdA461dED5", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "RoutingIsmFactory", - "address": "0xeB998dC788E2c1e772d198d32e50890544776e75", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0xDF3dbca700227925C39cAF902919f94571A50755", - "constructorArguments": "", - "isProxy": true - } - ], "sepolia": [ { "name": "StaticMultisigIsmFactory", @@ -1661,1068 +1463,242 @@ "isProxy": true } ], - "optimismgoerli": [ - { - "name": "StaticMultisigIsmFactory", - "address": "0x0bA75fBC56FEb7e4F5EF4a8F042272cfec152ABc", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsmFactory", - "address": "0x919AD6f6DFE1C17b89DAC2eE526F153C08afE305", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomainRoutingIsmFactory", - "address": "0x1D7b8aBa2494Ec82e80D6206d2B3df9f8C3F1862", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0xC5Bb8CDD44B6c56695df45c7AA8012a97dD6ED13", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0x39a8711BF44165A2292Cb5cB43229659c2Bb11c9", - "constructorArguments": "", - "isProxy": false - }, + "scrollsepolia": [ { "name": "MerkleRootMultisigIsmFactory", - "address": "0x8a02315924C172e8bb558E39e3DF5bfDD6270648", + "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", "constructorArguments": "", "isProxy": false }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x738b1071c86a747834619Ca710c694E4Dc1646B9" + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5" }, { "name": "MerkleRootMultisigIsmFactory", - "address": "0xAbC25d7daDD748948F5cC912A807b0f8FcBb56a9", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", "constructorArguments": "", "isProxy": false }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x76E4295E6d4bB5Be6314da5759fD0a9003BA522E" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" }, { "name": "MessageIdMultisigIsmFactory", - "address": "0x7868B6026E36C4b6E2ca6a0CaBDb1A6D0CcC443B", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", "constructorArguments": "", "isProxy": false }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x12D2e2B9bB44a34eb6a284861be742EE852b4Fc1" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" }, { "name": "AggregationIsmFactory", - "address": "0xf666A33C451E8371907aD22dd545E1678fCa1582", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "constructorArguments": "", "isProxy": false }, { "name": "StaticAggregationIsm", - "address": "0x4287827a2F143ab05B708D54f1c47585D4F2aFaB" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409" }, { "name": "AggregationHookFactory", - "address": "0x00cE81F7B02e0673815a8b0A54e62AeabDE78685", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", "constructorArguments": "", "isProxy": false }, { "name": "StaticAggregationHook", - "address": "0xAA3A04799E5Fb6daf989d952802FC8d0dADA44fe" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" }, { "name": "RoutingIsmFactory", - "address": "0x1807e7d67F00393a49c445E367face82D65d86c7", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", "constructorArguments": "", "isProxy": false }, { "name": "DomaingRoutingIsm", - "address": "0xE2B83e0E3fd5Cd37b969aB3cA4Fcb92b9bc59612" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x76E4295E6d4bB5Be6314da5759fD0a9003BA522E" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x12D2e2B9bB44a34eb6a284861be742EE852b4Fc1" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" }, { "name": "StaticAggregationIsm", - "address": "0x4287827a2F143ab05B708D54f1c47585D4F2aFaB" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409" }, { "name": "StaticAggregationHook", - "address": "0xAA3A04799E5Fb6daf989d952802FC8d0dADA44fe" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" }, { "name": "DomaingRoutingIsm", - "address": "0xE2B83e0E3fd5Cd37b969aB3cA4Fcb92b9bc59612" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x76E4295E6d4bB5Be6314da5759fD0a9003BA522E" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x12D2e2B9bB44a34eb6a284861be742EE852b4Fc1" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" }, { "name": "StaticAggregationIsm", - "address": "0x4287827a2F143ab05B708D54f1c47585D4F2aFaB" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409" }, { "name": "StaticAggregationHook", - "address": "0xAA3A04799E5Fb6daf989d952802FC8d0dADA44fe" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" }, { "name": "DomaingRoutingIsm", - "address": "0xE2B83e0E3fd5Cd37b969aB3cA4Fcb92b9bc59612" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x76E4295E6d4bB5Be6314da5759fD0a9003BA522E" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x12D2e2B9bB44a34eb6a284861be742EE852b4Fc1" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" }, { "name": "StaticAggregationIsm", - "address": "0x4287827a2F143ab05B708D54f1c47585D4F2aFaB" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409" }, { "name": "StaticAggregationHook", - "address": "0xAA3A04799E5Fb6daf989d952802FC8d0dADA44fe" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" }, { "name": "DomaingRoutingIsm", - "address": "0xE2B83e0E3fd5Cd37b969aB3cA4Fcb92b9bc59612" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x76E4295E6d4bB5Be6314da5759fD0a9003BA522E" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x12D2e2B9bB44a34eb6a284861be742EE852b4Fc1" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" }, { "name": "StaticAggregationIsm", - "address": "0x4287827a2F143ab05B708D54f1c47585D4F2aFaB" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409" }, { "name": "StaticAggregationHook", - "address": "0xAA3A04799E5Fb6daf989d952802FC8d0dADA44fe" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" }, { "name": "DomaingRoutingIsm", - "address": "0xE2B83e0E3fd5Cd37b969aB3cA4Fcb92b9bc59612" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" }, { "name": "StaticMerkleRootMultisigIsm", - "address": "0x76E4295E6d4bB5Be6314da5759fD0a9003BA522E", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", "constructorArguments": "", "isProxy": true }, { "name": "StaticMessageIdMultisigIsm", - "address": "0x12D2e2B9bB44a34eb6a284861be742EE852b4Fc1", + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", "constructorArguments": "", "isProxy": true }, { "name": "StaticAggregationIsm", - "address": "0x4287827a2F143ab05B708D54f1c47585D4F2aFaB", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", "constructorArguments": "", "isProxy": true }, { "name": "StaticAggregationHook", - "address": "0xAA3A04799E5Fb6daf989d952802FC8d0dADA44fe", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", "constructorArguments": "", "isProxy": true }, { "name": "RoutingIsmFactory", - "address": "0xce8E9D701A1DFfe672c1d8dB20De2B3fa6F4437D", + "address": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", "constructorArguments": "", "isProxy": false }, { "name": "DomaingRoutingIsm", - "address": "0xEDff0A731Fb99a85181d84F46a473d5754dA236C", + "address": "0xea80345322520d37770dbDeD3FE9c53ba93E70D8", "constructorArguments": "", "isProxy": true } ], - "arbitrumgoerli": [ + "plumetestnet": [ { - "name": "StaticMultisigIsmFactory", - "address": "0x71cCc98689B48Dd50Fd5950815A56d76477349B2", + "name": "MerkleRootMultisigIsmFactory", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "constructorArguments": "", "isProxy": false }, { - "name": "StaticAggregationIsmFactory", - "address": "0xa27F4a2bD624CF1cB3605c532E95E27BB0AC0BB3", + "name": "StaticMerkleRootMultisigIsm", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", "constructorArguments": "", - "isProxy": false + "isProxy": true }, { - "name": "DomainRoutingIsmFactory", - "address": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", + "name": "MessageIdMultisigIsmFactory", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", "constructorArguments": "", "isProxy": false }, { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x0502Be39aE255D022013DC0aeAa52fDBCD5f0331", + "name": "StaticMessageIdMultisigIsm", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", "constructorArguments": "", - "isProxy": false + "isProxy": true }, { - "name": "MessageIdMultisigIsmFactory", - "address": "0x71eAD731EBdd1334d80a89a572fDFA67830C504c", + "name": "AggregationIsmFactory", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", "constructorArguments": "", "isProxy": false }, { - "name": "MerkleRootMultisigIsmFactory", - "address": "0xaa5Bc1EF4Ead69e8ef7Ca40e55b7cf68E457226a", + "name": "StaticAggregationIsm", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "constructorArguments": "", + "isProxy": true + }, + { + "name": "AggregationHookFactory", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "constructorArguments": "", "isProxy": false }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xA9c27a55B879E184ad4CAF34DB166231a60Bc39C" + "name": "StaticAggregationHook", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "constructorArguments": "", + "isProxy": true }, { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x17D58eBb5Ea0E2d360c877E119FAef4C4052e6B9", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4186a6C7aa0D0be93Eda994279974CD2a1E59aE2" - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0x922CeEe9e8832a047e6aD68Df4F079F271b73Ac3", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x2f0354820d0Da451be63D20D63AbE028DF3cb9A0" - }, - { - "name": "AggregationIsmFactory", - "address": "0xC5Bb8CDD44B6c56695df45c7AA8012a97dD6ED13", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsm", - "address": "0xc5a060a9aC418FafD4eF03334A9973762157C867" - }, - { - "name": "AggregationHookFactory", - "address": "0x39a8711BF44165A2292Cb5cB43229659c2Bb11c9", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationHook", - "address": "0x4e2F4E576b622E3246df8430CeeCb63eA28749Ef" - }, - { - "name": "RoutingIsmFactory", - "address": "0x735491727b9a1206E16AF4964aF68d5BB9122333", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0xC204a9864A10d52995E71FE5cc1fDaAA9f7D6FCB" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4186a6C7aa0D0be93Eda994279974CD2a1E59aE2" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x2f0354820d0Da451be63D20D63AbE028DF3cb9A0" - }, - { - "name": "StaticAggregationIsm", - "address": "0xc5a060a9aC418FafD4eF03334A9973762157C867" - }, - { - "name": "StaticAggregationHook", - "address": "0x4e2F4E576b622E3246df8430CeeCb63eA28749Ef" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xC204a9864A10d52995E71FE5cc1fDaAA9f7D6FCB" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4186a6C7aa0D0be93Eda994279974CD2a1E59aE2" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x2f0354820d0Da451be63D20D63AbE028DF3cb9A0" - }, - { - "name": "StaticAggregationIsm", - "address": "0xc5a060a9aC418FafD4eF03334A9973762157C867" - }, - { - "name": "StaticAggregationHook", - "address": "0x4e2F4E576b622E3246df8430CeeCb63eA28749Ef" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xC204a9864A10d52995E71FE5cc1fDaAA9f7D6FCB" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4186a6C7aa0D0be93Eda994279974CD2a1E59aE2" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x2f0354820d0Da451be63D20D63AbE028DF3cb9A0" - }, - { - "name": "StaticAggregationIsm", - "address": "0xc5a060a9aC418FafD4eF03334A9973762157C867" - }, - { - "name": "StaticAggregationHook", - "address": "0x4e2F4E576b622E3246df8430CeeCb63eA28749Ef" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xC204a9864A10d52995E71FE5cc1fDaAA9f7D6FCB" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4186a6C7aa0D0be93Eda994279974CD2a1E59aE2" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x2f0354820d0Da451be63D20D63AbE028DF3cb9A0" - }, - { - "name": "StaticAggregationIsm", - "address": "0xc5a060a9aC418FafD4eF03334A9973762157C867" - }, - { - "name": "StaticAggregationHook", - "address": "0x4e2F4E576b622E3246df8430CeeCb63eA28749Ef" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xC204a9864A10d52995E71FE5cc1fDaAA9f7D6FCB" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4186a6C7aa0D0be93Eda994279974CD2a1E59aE2", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x2f0354820d0Da451be63D20D63AbE028DF3cb9A0", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0xc5a060a9aC418FafD4eF03334A9973762157C867", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0x4e2F4E576b622E3246df8430CeeCb63eA28749Ef", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "DomaingRoutingIsm", - "address": "0x6bc6101acBFD309d7B9540c1d6B50c9985742399", - "constructorArguments": "", - "isProxy": true - } - ], - "basegoerli": [ - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953" - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0x54148470292C24345fb828B003461a9444414517", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2" - }, - { - "name": "AggregationIsmFactory", - "address": "0x589C201a07c26b4725A4A829d772f24423da480B", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7" - }, - { - "name": "AggregationHookFactory", - "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61" - }, - { - "name": "RoutingIsmFactory", - "address": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7d5dCb6cbE5391fc041Fdf1ce2CdFC7E28cc39Bb" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2" - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7" - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7d5dCb6cbE5391fc041Fdf1ce2CdFC7E28cc39Bb" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2" - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7" - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7d5dCb6cbE5391fc041Fdf1ce2CdFC7E28cc39Bb" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2" - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7" - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7d5dCb6cbE5391fc041Fdf1ce2CdFC7E28cc39Bb" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2" - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7" - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7d5dCb6cbE5391fc041Fdf1ce2CdFC7E28cc39Bb" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2" - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7" - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7d5dCb6cbE5391fc041Fdf1ce2CdFC7E28cc39Bb" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "DomaingRoutingIsm", - "address": "0xFa0dF1B35c4328BdE032e0F850693355C381c42d", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "DomaingRoutingIsm", - "address": "0xFa0dF1B35c4328BdE032e0F850693355C381c42d", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "DomaingRoutingIsm", - "address": "0xFa0dF1B35c4328BdE032e0F850693355C381c42d", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "DomaingRoutingIsm", - "address": "0xFa0dF1B35c4328BdE032e0F850693355C381c42d", - "constructorArguments": "", - "isProxy": true - } - ], - "polygonzkevmtestnet": [ - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5" - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF" - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "AggregationIsmFactory", - "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "AggregationHookFactory", - "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "RoutingIsmFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "DomaingRoutingIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x99B304925A08aba9305bC0A8FccBf71B4290c5EF", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "RoutingIsmFactory", - "address": "0xc08675806BA844467E559E45E4bB59e66778bDcd", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0x772E6fa7a868C94e6031683AD13E821e6649d60C", - "constructorArguments": "", - "isProxy": true - } - ], - "scrollsepolia": [ - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5" - }, - { - "name": "MerkleRootMultisigIsmFactory", - "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "MessageIdMultisigIsmFactory", - "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "AggregationIsmFactory", - "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "AggregationHookFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "RoutingIsmFactory", - "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, - { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" - }, - { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" - }, - { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" - }, - { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", - "constructorArguments": "", - "isProxy": true - }, - { - "name": "RoutingIsmFactory", - "address": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", + "name": "RoutingIsmFactory", + "address": "0x54148470292C24345fb828B003461a9444414517", "constructorArguments": "", "isProxy": false }, { "name": "DomaingRoutingIsm", - "address": "0xea80345322520d37770dbDeD3FE9c53ba93E70D8", + "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", "constructorArguments": "", "isProxy": true } diff --git a/typescript/infra/config/environments/testnet4/liquidityLayer.ts b/typescript/infra/config/environments/testnet4/liquidityLayer.ts index 0ae83c4a03..9f0960b4d7 100644 --- a/typescript/infra/config/environments/testnet4/liquidityLayer.ts +++ b/typescript/infra/config/environments/testnet4/liquidityLayer.ts @@ -8,18 +8,10 @@ import { } from '@hyperlane-xyz/sdk'; const circleDomainMapping = [ - { - hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), - circleDomain: 0, - }, { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), circleDomain: 1 }, ]; const wormholeDomainMapping = [ - { - hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), - wormholeDomain: 2, - }, { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), wormholeDomain: 6, @@ -39,20 +31,6 @@ const wormholeDomainMapping = [ ]; export const bridgeAdapterConfigs: ChainMap = { - [Chains.goerli]: { - portal: { - type: BridgeAdapterType.Portal, - portalBridgeAddress: '0xF890982f9310df57d00f659cf4fd87e65adEd8d7', - wormholeDomainMapping, - }, - circle: { - type: BridgeAdapterType.Circle, - tokenMessengerAddress: '0xd0c3da58f55358142b8d3e06c1c30c5c6114efe8', - messageTransmitterAddress: '0x26413e8157cd32011e726065a5462e97dd4d03d9', - usdcAddress: '0x07865c6e87b9f70255377e024ace6630c1eaa37f', - circleDomainMapping, - }, - }, [Chains.fuji]: { portal: { type: BridgeAdapterType.Portal, diff --git a/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json b/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json index 8e545d46e7..6504acc5b3 100644 --- a/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json +++ b/typescript/infra/config/environments/testnet4/middleware/accounts/verification.json @@ -199,74 +199,6 @@ "isProxy": false } ], - "goerli": [ - { - "name": "InterchainAccountRouter", - "address": "0xc61Bbf8eAb0b748Ecb532A7ffC49Ab7ca6D3a39D", - "isProxy": false, - "constructorArguments": "0x" - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xCb0ba89F564e31180A101Df54b7971206e03ee9b", - "constructorArguments": "0000000000000000000000008f919348f9c4619a196acb5e377f49e5e2c0b569000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0xA748586f78dD2a4A90b02aD72ddc014Cc05C19b1", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000cb0ba89f564e31180a101df54b7971206e03ee9b", - "isProxy": false - }, - { - "name": "InterchainAccountIsm", - "address": "0x70835F3C2845394842DEA9f40cbe8087b2cE5712", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685", - "isProxy": false - }, - { - "name": "InterchainAccountIsm", - "address": "0xA65d03C7FE8a9C3aAb17638CA64aB892263a3140", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xfC326F5104559772b851c75942a4Daa8A50be771", - "constructorArguments": "0000000000000000000000008f919348f9c4619a196acb5e377f49e5e2c0b569000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0x069D0E4f0E69041c2E73Fc11fB386a082854D58a", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000fc326f5104559772b851c75942a4daa8a50be771", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xA65d03C7FE8a9C3aAb17638CA64aB892263a3140", - "constructorArguments": "0000000000000000000000008f919348f9c4619a196acb5e377f49e5e2c0b569000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0xfC326F5104559772b851c75942a4Daa8A50be771", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000a65d03c7fe8a9c3aab17638ca64ab892263a3140", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x55486284a85d7b51a7bBfd343702414D65276fa6", - "constructorArguments": "0000000000000000000000008f919348f9c4619a196acb5e377f49e5e2c0b569000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0x72fBCe5Ed460D462B0B434210f6591e4A816D3Af", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000005", - "isProxy": false - } - ], "moonbasealpha": [ { "name": "InterchainAccountRouter", @@ -323,106 +255,6 @@ "isProxy": false } ], - "optimismgoerli": [ - { - "name": "InterchainAccountRouter", - "address": "0xc61Bbf8eAb0b748Ecb532A7ffC49Ab7ca6D3a39D", - "isProxy": false, - "constructorArguments": "0x" - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x12EBd8cb561c68b587C392e0c5405B9866FD5820", - "constructorArguments": "000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab42000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0x860EDf9C38875ad7444BD6e6331866450c9589e4", - "constructorArguments": "00000000000000000000000000000000000000000000000000000000000001a400000000000000000000000012ebd8cb561c68b587c392e0c5405b9866fd5820", - "isProxy": false - }, - { - "name": "InterchainAccountIsm", - "address": "0x82F5a960a4e4Cc0614253e4977bb3BE10344a30C", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x4Ba215ECFb198fa2B63b57Eea19978E493EdaA0c", - "constructorArguments": "000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab42000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0x8a557D004d50A95466C508b61EB68768c8371e30", - "constructorArguments": "00000000000000000000000000000000000000000000000000000000000001a40000000000000000000000004ba215ecfb198fa2b63b57eea19978e493edaa0c", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x6f393F8Dfb327d99c946e0Dd2f39F51B1aB446bf", - "constructorArguments": "000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab42000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0xa6B288C520B06EcF2492F8d690616ea0027D1Ce8", - "constructorArguments": "00000000000000000000000000000000000000000000000000000000000001a4", - "isProxy": false - } - ], - "arbitrumgoerli": [ - { - "name": "InterchainAccountRouter", - "address": "0xc61Bbf8eAb0b748Ecb532A7ffC49Ab7ca6D3a39D", - "isProxy": false, - "constructorArguments": "0x" - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x4266D8Dd66D8Eb3934c8942968d1e54214D072d3", - "constructorArguments": "000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab42000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0x759c4Eb4575B651a9f0Fb46653dd7B2F32fD7310", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000066eed0000000000000000000000004266d8dd66d8eb3934c8942968d1e54214d072d3", - "isProxy": false - }, - { - "name": "InterchainAccountIsm", - "address": "0xA8978fdB093b1C5b892291A36e8527b34B749d6f", - "constructorArguments": "000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xbF3055Ae229A241113BeF54f9A4Dd748A326cf85", - "constructorArguments": "000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab42000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0xc1709b3861E348678DAea51A7ce4b5859F2C99B6", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000066eed000000000000000000000000bf3055ae229a241113bef54f9a4dd748a326cf85", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x3C636ccC50222a9eb54849C7a622D60a40928a5E", - "constructorArguments": "000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab42000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainAccountRouter", - "address": "0xa4B02F68588C0A36Fc6bD4ce0Db2E7c2e8506156", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000066eed", - "isProxy": false - } - ], "sepolia": [ { "name": "TransparentUpgradeableProxy", diff --git a/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/addresses.json b/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/addresses.json index 2497ed4a7e..14fe439e30 100644 --- a/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/addresses.json +++ b/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/addresses.json @@ -1,10 +1,4 @@ { - "goerli": { - "circleBridgeAdapter": "0xfe9d88aA85c5917822C804b949BcEDE832C02ce2", - "portalAdapter": "0x68D753982e89CC083917863F6dc9738448B91ef9", - "proxyAdmin": "0x8f919348F9C4619A196Acb5e377f49E5E2C0B569", - "liquidityLayerRouter": "0x2abe0860D81FB4242C748132bD69D125D88eaE26" - }, "fuji": { "circleBridgeAdapter": "0xfe9d88aA85c5917822C804b949BcEDE832C02ce2", "portalAdapter": "0x68D753982e89CC083917863F6dc9738448B91ef9", diff --git a/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/verification.json b/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/verification.json index 1cf8218960..58b148be76 100644 --- a/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/verification.json +++ b/typescript/infra/config/environments/testnet4/middleware/liquidity-layer/verification.json @@ -1,24 +1,4 @@ { - "goerli": [ - { - "name": "LiquidityLayerRouter", - "address": "0x2abe0860D81FB4242C748132bD69D125D88eaE26", - "isProxy": false, - "constructorArguments": "0x" - }, - { - "name": "CircleBridgeAdapter", - "address": "0xfe9d88aA85c5917822C804b949BcEDE832C02ce2", - "isProxy": false, - "constructorArguments": "0x" - }, - { - "name": "PortalAdapter", - "address": "0x68D753982e89CC083917863F6dc9738448B91ef9", - "isProxy": false, - "constructorArguments": "0x" - } - ], "fuji": [ { "name": "LiquidityLayerRouter", diff --git a/typescript/infra/config/environments/testnet4/middleware/queries/verification.json b/typescript/infra/config/environments/testnet4/middleware/queries/verification.json index 1412f7e32e..252f1b63b2 100644 --- a/typescript/infra/config/environments/testnet4/middleware/queries/verification.json +++ b/typescript/infra/config/environments/testnet4/middleware/queries/verification.json @@ -103,32 +103,6 @@ "isProxy": true } ], - "goerli": [ - { - "name": "InterchainQueryRouter", - "address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559", - "constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainQueryRouter", - "address": "0x1a90f8575462aF25B4fA957ecDdFe5738D1a1D36", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x46A2B1C3E8a93C3613Ebf326235FbD3e2f65660F", - "constructorArguments": "0000000000000000000000001a90f8575462af25b4fa957ecddfe5738d1a1d360000000000000000000000008f919348f9c4619a196acb5e377f49e5e2c0b56900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - } - ], "moonbasealpha": [ { "name": "InterchainQueryRouter", @@ -143,58 +117,6 @@ "isProxy": true } ], - "optimismgoerli": [ - { - "name": "InterchainQueryRouter", - "address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559", - "constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainQueryRouter", - "address": "0x347C63AA547676C6f98f40348FEd0413523C9f85", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x6385E09099d889f912F90c47F10E903fe4feBF69", - "constructorArguments": "000000000000000000000000347c63aa547676c6f98f40348fed0413523c9f85000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab4200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - } - ], - "arbitrumgoerli": [ - { - "name": "InterchainQueryRouter", - "address": "0xfFf9dB6C772525B17cd4eB863A09DcD43e085F59", - "constructorArguments": "0x", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0xC87F9a6cADF77995b18FddE5049b6274695Dd559", - "constructorArguments": "0x000000000000000000000000c97d8e6f57b0d64971453ddc6eb8483fec9d163a000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true - }, - { - "name": "InterchainQueryRouter", - "address": "0x942A026b33FEe502AfdF37890D19A6A78b2C4b15", - "constructorArguments": "", - "isProxy": false - }, - { - "name": "TransparentUpgradeableProxy", - "address": "0x5b1E05e1fdDBc0f3d31c4E634ff4D5d84A56deEe", - "constructorArguments": "000000000000000000000000942a026b33fee502afdf37890d19a6a78b2c4b15000000000000000000000000cd19ff7306e04ea6b8f4b5ab1c5a198c186aab4200000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e000000000000000000000000cc737a94fecaec165abcf12ded095bb13f037685000000000000000000000000f90cb82a76492614d07b82a7658917f3ac811ac10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", - "isProxy": true - } - ], "sepolia": [ { "name": "InterchainQueryRouter", diff --git a/typescript/infra/config/environments/testnet4/owners.ts b/typescript/infra/config/environments/testnet4/owners.ts index 2522ea3892..f63732d4c8 100644 --- a/typescript/infra/config/environments/testnet4/owners.ts +++ b/typescript/infra/config/environments/testnet4/owners.ts @@ -1,14 +1,16 @@ -import { ChainMap } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { ChainMap, OwnableConfig } from '@hyperlane-xyz/sdk'; -import { ethereumChainNames } from './chains'; +import { supportedChainNames } from '../testnet4/chains'; const ETHEREUM_DEPLOYER_ADDRESS = '0xfaD1C94469700833717Fa8a3017278BC1cA8031C'; // const SEALEVEL_DEPLOYER_ADDRESS = '6DjHX6Ezjpq3zZMZ8KsqyoFYo1zPSDoiZmLLkxD4xKXS'; -export const owners: ChainMap
= { +export const owners: ChainMap = { ...Object.fromEntries( - ethereumChainNames.map((chain) => [chain, ETHEREUM_DEPLOYER_ADDRESS]), + supportedChainNames.map((chain) => [ + chain, + { owner: ETHEREUM_DEPLOYER_ADDRESS }, + ]), ), // [chainMetadata.solanadevnet.name]: SEALEVEL_DEPLOYER_ADDRESS, }; diff --git a/typescript/infra/config/environments/testnet4/plume-sepolia-ETH-deployments.yaml b/typescript/infra/config/environments/testnet4/plume-sepolia-ETH-deployments.yaml new file mode 100644 index 0000000000..092d070ead --- /dev/null +++ b/typescript/infra/config/environments/testnet4/plume-sepolia-ETH-deployments.yaml @@ -0,0 +1,21 @@ +# Configs and artifacts for the deployment of Hyperlane Warp Routes +# Between Plume Testnet and ethereum, ETH +description: Hyperlane Warp Route artifacts +timestamp: '2023-02-14T20:00:00.000Z' +deployer: Abacus Works (Hyperlane) +data: + config: + sepolia: + protocolType: ethereum + type: native + hypAddress: '0xd99eA1D8b9542D35252504DDd59EDe8C43FB15fd' + name: Ether + symbol: ETH + decimals: 18 + plumetestnet: + protocolType: ethereum + type: synthetic + hypAddress: '0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f' + name: Wrapped Ether + symbol: WETH + decimals: 18 diff --git a/typescript/infra/config/environments/testnet4/testquerysender/addresses.json b/typescript/infra/config/environments/testnet4/testquerysender/addresses.json index 124b301448..9f05f6c4e5 100644 --- a/typescript/infra/config/environments/testnet4/testquerysender/addresses.json +++ b/typescript/infra/config/environments/testnet4/testquerysender/addresses.json @@ -10,17 +10,5 @@ }, "bsctestnet": { "TestQuerySender": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683" - }, - "goerli": { - "TestQuerySender": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683" - }, - "moonbasealpha": { - "TestQuerySender": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683" - }, - "optimismgoerli": { - "TestQuerySender": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683" - }, - "arbitrumgoerli": { - "TestQuerySender": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683" } } diff --git a/typescript/infra/config/environments/testnet4/testquerysender/verification.json b/typescript/infra/config/environments/testnet4/testquerysender/verification.json index 440029f9f0..2c579379b5 100644 --- a/typescript/infra/config/environments/testnet4/testquerysender/verification.json +++ b/typescript/infra/config/environments/testnet4/testquerysender/verification.json @@ -31,14 +31,6 @@ "constructorArguments": "0x" } ], - "goerli": [ - { - "name": "TestQuerySender", - "address": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683", - "isProxy": false, - "constructorArguments": "0x" - } - ], "moonbasealpha": [ { "name": "TestQuerySender", @@ -46,21 +38,5 @@ "isProxy": false, "constructorArguments": "0x" } - ], - "optimismgoerli": [ - { - "name": "TestQuerySender", - "address": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683", - "isProxy": false, - "constructorArguments": "0x" - } - ], - "arbitrumgoerli": [ - { - "name": "TestQuerySender", - "address": "0x96D7D6Eba6C635e3EaC12b593Ef8B2eE1F6E6683", - "isProxy": false, - "constructorArguments": "0x" - } ] } diff --git a/typescript/infra/config/environments/testnet4/token-bridge.ts b/typescript/infra/config/environments/testnet4/token-bridge.ts index 0ae83c4a03..9f0960b4d7 100644 --- a/typescript/infra/config/environments/testnet4/token-bridge.ts +++ b/typescript/infra/config/environments/testnet4/token-bridge.ts @@ -8,18 +8,10 @@ import { } from '@hyperlane-xyz/sdk'; const circleDomainMapping = [ - { - hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), - circleDomain: 0, - }, { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), circleDomain: 1 }, ]; const wormholeDomainMapping = [ - { - hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), - wormholeDomain: 2, - }, { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), wormholeDomain: 6, @@ -39,20 +31,6 @@ const wormholeDomainMapping = [ ]; export const bridgeAdapterConfigs: ChainMap = { - [Chains.goerli]: { - portal: { - type: BridgeAdapterType.Portal, - portalBridgeAddress: '0xF890982f9310df57d00f659cf4fd87e65adEd8d7', - wormholeDomainMapping, - }, - circle: { - type: BridgeAdapterType.Circle, - tokenMessengerAddress: '0xd0c3da58f55358142b8d3e06c1c30c5c6114efe8', - messageTransmitterAddress: '0x26413e8157cd32011e726065a5462e97dd4d03d9', - usdcAddress: '0x07865c6e87b9f70255377e024ace6630c1eaa37f', - circleDomainMapping, - }, - }, [Chains.fuji]: { portal: { type: BridgeAdapterType.Portal, diff --git a/typescript/infra/config/environments/testnet4/validators.ts b/typescript/infra/config/environments/testnet4/validators.ts index 44ddc20054..936784cd1a 100644 --- a/typescript/infra/config/environments/testnet4/validators.ts +++ b/typescript/infra/config/environments/testnet4/validators.ts @@ -1,4 +1,4 @@ -import { chainMetadata } from '@hyperlane-xyz/sdk'; +import { chainMetadata, getReorgPeriod } from '@hyperlane-xyz/sdk'; import { ValidatorBaseChainConfigMap } from '../../../src/config/agent'; import { Contexts } from '../../contexts'; @@ -13,7 +13,7 @@ export const validatorChainConfig = ( return { alfajores: { interval: 5, - reorgPeriod: chainMetadata.alfajores.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.alfajores), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -31,29 +31,9 @@ export const validatorChainConfig = ( 'alfajores', ), }, - basegoerli: { - interval: 5, - reorgPeriod: chainMetadata.basegoerli.blocks!.reorgPeriod!, - validators: validatorsConfig( - { - [Contexts.Hyperlane]: [ - '0xf6eddda696dcd3bf10f7ce8a02db31ef2e775a03', - '0x5a7d05cebf5db4dde9b2fedcefa76fb58fa05071', - '0x9260a6c7d54cbcbed28f8668679cd1fa3a203b25', - ], - [Contexts.ReleaseCandidate]: [ - '0x81983e03363351b63848867bd76687cc80b9ff37', - '0x36de434527b8f83851d83f1b1d72ec11a5903533', - '0x4b65f7527c267e420bf62a0c5a139cb8c3906277', - ], - [Contexts.Neutron]: [], - }, - 'basegoerli', - ), - }, fuji: { interval: 5, - reorgPeriod: chainMetadata.fuji.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.alfajores), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -71,47 +51,9 @@ export const validatorChainConfig = ( 'fuji', ), }, - // chiado: { - // interval: 5, - // reorgPeriod: chainMetadata.chiado.blocks!.reorgPeriod!, - // validators: validatorsConfig( - // { - // [Contexts.Hyperlane]: [ - // '0x12b1d1354441b900e0a36659ae54c3a9d5d22c57', - // '0x06c3757a4b7a912828e523bb8a5f980ddc297356', - // '0x0874967a145d70b799ebe9ed861ab7c93faef95a', - // ], - // [Contexts.ReleaseCandidate]: [ - // '0x7572ffd8af1abc02cc1d234ac750d387fd6768a0', - // '0x31b37a32657cf2915d434b409ee86978058fa91c', - // '0x32495780512fce64a45aca55ccc02202e9018dc5', - // ], - // }, - // 'chiado', - // ), - // }, - // lineagoerli: { - // interval: 5, - // reorgPeriod: chainMetadata.lineagoerli.blocks!.reorgPeriod!, - // validators: validatorsConfig( - // { - // [Contexts.Hyperlane]: [ - // '0xd767ea1206b8295d7e1267ddd00e56d34f278db6', - // '0x4a5d7085ca93c22fbc994dd97857c98fcc745674', - // '0x8327779c3c31fa1ffc7f0c9ffae33e4d804bbd8f', - // ], - // [Contexts.ReleaseCandidate]: [ - // '0x52e2c6db923124e646011d172dea644e1cafe583', - // '0x48d540e94ff1acb886df6bfed2b7a92568639364', - // '0xe99e3acc543a535b8eeae98f3d6f39015efe0cd0', - // ], - // }, - // 'lineagoerli', - // ), - // }, mumbai: { interval: 5, - reorgPeriod: chainMetadata.mumbai.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.mumbai), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -131,7 +73,7 @@ export const validatorChainConfig = ( }, bsctestnet: { interval: 5, - reorgPeriod: chainMetadata.bsctestnet.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.bsctestnet), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -149,29 +91,9 @@ export const validatorChainConfig = ( 'bsctestnet', ), }, - goerli: { - interval: 5, - reorgPeriod: chainMetadata.goerli.blocks!.reorgPeriod!, - validators: validatorsConfig( - { - [Contexts.Hyperlane]: [ - '0x05a9b5efe9f61f9142453d8e9f61565f333c6768', - '0x43a96c7dfbd8187c95013d6ee8665650cbdb2673', - '0x7940a12c050e24e1839c21ecb12f65afd84e8c5b', - ], - [Contexts.ReleaseCandidate]: [ - '0x6b32af7592948cbec6893363f77c08252d0ce0d7', - '0x4711d476a5929840196def397a156c5253b44b96', - '0xb0add42f2a4b824ba5fab2628f930dc1dcfc40f8', - ], - [Contexts.Neutron]: [], - }, - 'goerli', - ), - }, scrollsepolia: { interval: 5, - reorgPeriod: chainMetadata.scrollsepolia.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.scrollsepolia), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -191,7 +113,7 @@ export const validatorChainConfig = ( }, sepolia: { interval: 5, - reorgPeriod: chainMetadata.sepolia.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.sepolia), validators: validatorsConfig( { [Contexts.Hyperlane]: [ @@ -209,89 +131,41 @@ export const validatorChainConfig = ( 'sepolia', ), }, - moonbasealpha: { + plumetestnet: { interval: 5, - reorgPeriod: chainMetadata.moonbasealpha.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.plumetestnet), validators: validatorsConfig( { [Contexts.Hyperlane]: [ - '0x521877064bd7ac7500d300f162c8c47c256a2f9c', - '0xbc1c70f58ae0459d4b8a013245420a893837d568', - '0x01e42c2c44af81dda1ac16fec76fea2a7a54a44c', + '0xe765a214849f3ecdf00793b97d00422f2d408ea6', + '0xb59998f71efc65190a85ac5e81b66bd72a192a3b', + '0xc906470a73e6b5aad65a4ceb4acd73e3eaf80e2c', ], [Contexts.ReleaseCandidate]: [ - '0x376260b40b2ba2100890f27de1eb18a2774f54d1', - '0x776623e8be8d7218940b7c77d02162af4ff97985', - '0xb4c81facd992a6c7c4a187bcce35a6fc968399a0', + '0xe6e6aeecbf7755cdbc50c2683df9f2d100f6399d', + '0x27946c13a475233a3b1eb47f0bd0f7cdec3a3983', + '0x2596413213368475c96ddfb1ae26666d22093a8b', ], [Contexts.Neutron]: [], }, - 'moonbasealpha', + 'plumetestnet', ), }, - optimismgoerli: { + injective: { interval: 5, - reorgPeriod: chainMetadata.optimismgoerli.blocks!.reorgPeriod!, + reorgPeriod: getReorgPeriod(chainMetadata.injective), validators: validatorsConfig( { - [Contexts.Hyperlane]: [ - '0x79e58546e2faca865c6732ad5f6c4951051c4d67', - '0x7bbfe1bb7146aad7df309c637987d856179ebbc1', - '0xf3d2fb4d53c2bb6a88cec040e0d87430fcee4e40', - ], - [Contexts.ReleaseCandidate]: [ - '0xed4cf9bf144457c927d7a39613c812c53f296283', - '0xec6b5ddfd20ee64ff0dcbc7472ad757dce151685', - '0x4acd2983a51f1c33c2ab41669184c7679e0316f1', - ], - [Contexts.Neutron]: [], - }, - 'optimismgoerli', - ), - }, - arbitrumgoerli: { - interval: 5, - reorgPeriod: chainMetadata.arbitrumgoerli.blocks!.reorgPeriod!, - validators: validatorsConfig( - { - [Contexts.Hyperlane]: [ - '0x071c8d135845ae5a2cb73f98d681d519014c0a8b', - '0x1bcf03360989f15cbeb174c188288f2c6d2760d7', - '0xc1590eaaeaf380e7859564c5ebcdcc87e8369e0d', - ], - [Contexts.ReleaseCandidate]: [ - '0x869f67e89b5c0826a3c2f2ba72e6ae1d8a1952ff', - '0x9be82c7a063b47b2d04c890daabcb666b670a9a4', - '0x92c62f4b9cd60a7fe4216d1f12134d34cf827c41', - ], - [Contexts.Neutron]: [], - }, - 'arbitrumgoerli', - ), - }, - polygonzkevmtestnet: { - interval: 5, - reorgPeriod: chainMetadata.polygonzkevmtestnet.blocks!.reorgPeriod!, - validators: validatorsConfig( - { - [Contexts.Hyperlane]: [ - '0x3f06b725bc9648917eb11c414e9f8d76fd959550', - '0x27bfc57679d9dd4ab2e870f5ed7ec0b339a0b636', - '0xd476548222f43206d0abaa30e46e28670aa7859c', - ], - [Contexts.ReleaseCandidate]: [ - '0x2d0214068e5d8e49c638b5a4c70c75080204be21', - '0x989bbbfa753431169556f69be1b0a496b252e8a6', - '0x292d5788587bb5efd5c2c911115527e57f50cd05', - ], + [Contexts.Hyperlane]: ['0x10686BEe585491A0DA5bfCd5ABfbB95Ab4d6c86d'], + [Contexts.ReleaseCandidate]: [], [Contexts.Neutron]: [], }, - 'polygonzkevmtestnet', + 'injective', ), }, // proteustestnet: { // interval: 5, - // reorgPeriod: chainMetadata.proteustestnet.blocks!.reorgPeriod!, + // reorgPeriod: getReorgPeriod(chainMetadata.proteustestnet), // validators: validatorsConfig( // { // [Contexts.Hyperlane]: [ @@ -306,22 +180,29 @@ export const validatorChainConfig = ( // 'proteustestnet', // ), // }, - // solanadevnet: { - // interval: 10, - // reorgPeriod: chainMetadata.solanadevnet.blocks!.reorgPeriod!, - // validators: validatorsConfig( - // { - // [Contexts.Hyperlane]: [ - // '0xec0f73dbc5b1962a20f7dcbe07c98414025b0c43', - // '0x9c20a149dfa09ea9f77f5a7ca09ed44f9c025133', - // '0x967c5ecdf2625ae86580bd203b630abaaf85cd62', - // ], - // [Contexts.ReleaseCandidate]: [ - // '0x21b9eff4d1a6d3122596c7fb80315bf094b6e5c2', - // ], - // }, - // 'solanadevnet', - // ), - // }, + solanatestnet: { + interval: 1, + reorgPeriod: getReorgPeriod(chainMetadata.solanatestnet), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'solanatestnet', + ), + }, + eclipsetestnet: { + interval: 1, + reorgPeriod: getReorgPeriod(chainMetadata.eclipsetestnet), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xf344f34abca9a444545b5295066348a0ae22dda3'], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }, + 'eclipsetestnet', + ), + }, }; }; diff --git a/typescript/infra/config/environments/testnet4/warp/addresses.json b/typescript/infra/config/environments/testnet4/warp/addresses.json new file mode 100644 index 0000000000..1b2a0f788b --- /dev/null +++ b/typescript/infra/config/environments/testnet4/warp/addresses.json @@ -0,0 +1,8 @@ +{ + "plumetestnet": { + "router": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f" + }, + "sepolia": { + "router": "0xd99eA1D8b9542D35252504DDd59EDe8C43FB15fd" + } +} diff --git a/typescript/infra/config/environments/testnet4/warp/plumetestnet-sepolia-addresses.json b/typescript/infra/config/environments/testnet4/warp/plumetestnet-sepolia-addresses.json new file mode 100644 index 0000000000..1b2a0f788b --- /dev/null +++ b/typescript/infra/config/environments/testnet4/warp/plumetestnet-sepolia-addresses.json @@ -0,0 +1,8 @@ +{ + "plumetestnet": { + "router": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f" + }, + "sepolia": { + "router": "0xd99eA1D8b9542D35252504DDd59EDe8C43FB15fd" + } +} diff --git a/typescript/infra/config/environments/testnet4/warp/verification.json b/typescript/infra/config/environments/testnet4/warp/verification.json new file mode 100644 index 0000000000..cda68c826b --- /dev/null +++ b/typescript/infra/config/environments/testnet4/warp/verification.json @@ -0,0 +1,18 @@ +{ + "plumetestnet": [ + { + "name": "HypERC20", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000001200000000000000000000000033db966328ea213b0f76ef96ca368ab37779f065", + "isProxy": false + } + ], + "sepolia": [ + { + "name": "HypNative", + "address": "0xd99eA1D8b9542D35252504DDd59EDe8C43FB15fd", + "constructorArguments": "000000000000000000000000ffaef09b3cd11d9b20d1a19becca54eec2884766", + "isProxy": false + } + ] +} diff --git a/typescript/infra/config/kathy.json b/typescript/infra/config/kathy.json new file mode 100644 index 0000000000..bbec88e8e5 --- /dev/null +++ b/typescript/infra/config/kathy.json @@ -0,0 +1,17 @@ +{ + "mainnet3": { + "hyperlane": "0x5fb02f40f56d15f0442a39d11a23f73747095b20", + "neutron": "", + "rc": "0x7691f88dccc1554788ba8f226a4a31e5f3ead7c3" + }, + "test": { + "hyperlane": "", + "neutron": "", + "rc": "" + }, + "testnet4": { + "hyperlane": "0x1e8834ff0669b13cf5d37685c5327b82dbae1144", + "neutron": "", + "rc": "0xa623055727c59697961Ee5e35391D2483b26465e" + } +} diff --git a/typescript/infra/config/rcMultisigIsmConfigs.ts b/typescript/infra/config/rcMultisigIsmConfigs.ts index e5b89e1f71..6a2b2ebf50 100644 --- a/typescript/infra/config/rcMultisigIsmConfigs.ts +++ b/typescript/infra/config/rcMultisigIsmConfigs.ts @@ -61,10 +61,6 @@ export const rcMultisigIsmConfigs: ChainMap = { threshold: 1, validators: ['0xace978aaa61d9ee44fe3ab147fd227e0e66b8909'], }, - basegoerli: { - threshold: 1, - validators: ['0x81983e03363351b63848867bd76687cc80b9ff37'], - }, fuji: { threshold: 1, validators: ['0xfc419f9ba3c56c55e28844ade491d428f5a77d55'], @@ -73,10 +69,6 @@ export const rcMultisigIsmConfigs: ChainMap = { threshold: 1, validators: ['0x7572ffd8af1abc02cc1d234ac750d387fd6768a0'], }, - lineagoerli: { - threshold: 1, - validators: ['0x52e2c6db923124e646011d172dea644e1cafe583'], - }, mumbai: { threshold: 1, validators: ['0x7fc2981964427f09e317eda559f506bfd37f1ccb'], @@ -85,10 +77,6 @@ export const rcMultisigIsmConfigs: ChainMap = { threshold: 1, validators: ['0x6353c7402626054c824bd0eca721f82b725e2b4d'], }, - goerli: { - threshold: 1, - validators: ['0x6b32af7592948cbec6893363f77c08252d0ce0d7'], - }, scrollsepolia: { threshold: 1, validators: ['0x50d939d66f114350f322eb8b2e9f01fbc401d4c9'], @@ -97,20 +85,4 @@ export const rcMultisigIsmConfigs: ChainMap = { threshold: 1, validators: ['0x49f253c0dab33be1573d6c2769b3d9e584d91f82'], }, - moonbasealpha: { - threshold: 1, - validators: ['0x376260b40b2ba2100890f27de1eb18a2774f54d1'], - }, - optimismgoerli: { - threshold: 1, - validators: ['0xed4cf9bf144457c927d7a39613c812c53f296283'], - }, - arbitrumgoerli: { - threshold: 1, - validators: ['0x869f67e89b5c0826a3c2f2ba72e6ae1d8a1952ff'], - }, - polygonzkevmtestnet: { - threshold: 1, - validators: ['0x2d0214068e5d8e49c638b5a4c70c75080204be21'], - }, }; diff --git a/typescript/infra/config/relayer.json b/typescript/infra/config/relayer.json new file mode 100644 index 0000000000..0d943716a7 --- /dev/null +++ b/typescript/infra/config/relayer.json @@ -0,0 +1,17 @@ +{ + "mainnet3": { + "hyperlane": "0x74cae0ecc47b02ed9b9d32e000fd70b9417970c5", + "neutron": "0x03787bc64a4f352b4ad172947473342028513ef3", + "rc": "0x09b96417602ed6ac76651f7a8c4860e60e3aa6d0" + }, + "test": { + "hyperlane": "", + "neutron": "", + "rc": "" + }, + "testnet4": { + "hyperlane": "0x16626cd24fd1f228a031e48b77602ae25f8930db", + "neutron": "", + "rc": "0x7fe8c60ead4ab10be736f4de2b3090db5a851f16" + } +} diff --git a/typescript/infra/config/routingIsm.ts b/typescript/infra/config/routingIsm.ts index 41271f5207..7763ef1f84 100644 --- a/typescript/infra/config/routingIsm.ts +++ b/typescript/infra/config/routingIsm.ts @@ -12,23 +12,15 @@ import { import { DeployEnvironment } from '../src/config'; import { Contexts } from './contexts'; +import { environments } from './environments'; import { ethereumChainNames as mainnet3Chains } from './environments/mainnet3/chains'; -import { owners as mainnet3Owners } from './environments/mainnet3/owners'; -import { owners as testOwners } from './environments/test/owners'; import { supportedChainNames as testnet4Chains } from './environments/testnet4/chains'; -import { owners as testnet4Owners } from './environments/testnet4/owners'; import { multisigIsm } from './multisigIsm'; -const owners = { - test: testOwners, - testnet4: testnet4Owners, - mainnet3: mainnet3Owners, -}; - const chains = { test: TestChains, testnet4: testnet4Chains, - mainnet3: mainnet3Chains.filter((_) => _ !== 'mantapacific'), + mainnet3: mainnet3Chains, }; // Intended to be the "entrypoint" ISM. @@ -55,7 +47,7 @@ export const routingIsm = ( return { type: IsmType.ROUTING, domains: aggregationIsms, - owner: owners[environment][local], + owner: environments[environment].core[local].owner, }; }; diff --git a/typescript/infra/fork-all.sh b/typescript/infra/fork-all.sh new file mode 100755 index 0000000000..6f39861b45 --- /dev/null +++ b/typescript/infra/fork-all.sh @@ -0,0 +1,18 @@ +ENVIRONMENT=$1 +MODULE=$2 + +if [ -z "$ENVIRONMENT" ] || [ -z "$MODULE" ]; then + echo "Usage: fork-all.sh " + exit 1 +fi + +CHAINS=`yarn ts-node ./scripts/print-chain-metadatas.ts -e $ENVIRONMENT | \ + jq -r 'to_entries | map(select(.value.protocol=="ethereum")) | map(.key) ' | \ + tr -d '\"[],'` + +# echo all subsequent commands +set -x + +for CHAIN in $CHAINS; do + ./fork.sh $ENVIRONMENT $MODULE $CHAIN +done diff --git a/typescript/infra/fork.sh b/typescript/infra/fork.sh index cb909d043b..1e29e4dd55 100755 --- a/typescript/infra/fork.sh +++ b/typescript/infra/fork.sh @@ -1,19 +1,9 @@ ENVIRONMENT=$1 MODULE=$2 +CHAIN=$3 -if [ -z "$ENVIRONMENT" ]; then - echo "Usage: fork.sh " - exit 1 -fi - -if [ "$ENVIRONMENT" == "testnet4" ]; then - FORK_CHAIN="goerli" - RPC_URL="https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161" -elif [ "$ENVIRONMENT" == "mainnet3" ]; then - FORK_CHAIN="arbitrum" - RPC_URL="https://arb1.arbitrum.io/rpc" -else - echo "Unknown environment $ENVIRONMENT" +if [ -z "$ENVIRONMENT" ] || [ -z "$MODULE" ] || [ -z "$CHAIN" ]; then + echo "Usage: fork.sh " exit 1 fi @@ -23,24 +13,33 @@ trap 'jobs -p | xargs -r kill' EXIT # exit 1 on any subsequent failures set -e -anvil --fork-url $RPC_URL --silent > /dev/null & +RPC_URL=`yarn ts-node ./scripts/print-chain-metadatas.ts -e $ENVIRONMENT | jq -r ".$CHAIN.rpcUrls[0].http"` + +anvil --fork-url $RPC_URL --fork-retry-backoff 3 --compute-units-per-second 200 --gas-price 1 --silent & ANVIL_PID=$! while ! cast bn &> /dev/null; do sleep 1 done -echo "=== Run $MODULE checker against forked $ENVIRONMENT ===" -yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE +# echo all subsequent commands +set -x + +yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $CHAIN -m $MODULE + +# get balance +DEPLOYER="0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba" +BEFORE=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) + +yarn ts-node ./scripts/deploy.ts -e $ENVIRONMENT -f $CHAIN -m $MODULE -echo "=== Run $MODULE deployer against forked $ENVIRONMENT ===" -yarn ts-node ./scripts/deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE +AFTER=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +DEPLOY_DELTA="$((BEFORE-AFTER))" -# build SDK to get the latest addresses -yarn --cwd ../sdk build +BEFORE=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $CHAIN --govern -m $MODULE -echo "=== Run $MODULE govern against forked $ENVIRONMENT ===" -yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN --govern -m $MODULE +AFTER=$(cast balance $DEPLOYER --rpc-url http://localhost:8545) +GOVERN_DELTA="$((BEFORE-AFTER))" -echo "=== Run $MODULE checker against forked $ENVIRONMENT after governance ===" -yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $FORK_CHAIN -m $MODULE +yarn ts-node ./scripts/check-deploy.ts -e $ENVIRONMENT -f $CHAIN -m $MODULE diff --git a/typescript/infra/hardhat.config.ts b/typescript/infra/hardhat.config.ts index 404d6e0a00..e6cb48cefa 100644 --- a/typescript/infra/hardhat.config.ts +++ b/typescript/infra/hardhat.config.ts @@ -12,7 +12,7 @@ import { } from '@hyperlane-xyz/sdk'; import { addressToBytes32 } from '@hyperlane-xyz/utils'; -import { Modules, getAddresses } from './scripts/utils'; +import { Modules, getAddresses } from './scripts/agent-utils'; import { sleep } from './src/utils/utils'; enum MailboxHookType { diff --git a/typescript/infra/helm/key-funder/templates/cron-job.yaml b/typescript/infra/helm/key-funder/templates/cron-job.yaml index ed2ce3dce3..d51f079184 100644 --- a/typescript/infra/helm/key-funder/templates/cron-job.yaml +++ b/typescript/infra/helm/key-funder/templates/cron-job.yaml @@ -28,12 +28,18 @@ spec: {{- range $context, $roles := .Values.hyperlane.contextsAndRolesToFund }} - --contexts-and-roles - {{ $context }}={{ join "," $roles }} - - -f - - /addresses-secret/{{ $context }}-addresses.json {{- end }} {{- if .Values.hyperlane.connectionType }} - --connection-type - {{ .Values.hyperlane.connectionType }} +{{- end }} +{{- range $chain, $balance := .Values.hyperlane.desiredBalancePerChain }} + - --desired-balance-per-chain + - {{ $chain }}={{ $balance }} +{{- end }} +{{- range $chain, $balance := .Values.hyperlane.desiredKathyBalancePerChain }} + - --desired-kathy-balance-per-chain + - {{ $chain }}={{ $balance }} {{- end }} env: - name: PROMETHEUS_PUSH_GATEWAY diff --git a/typescript/infra/helm/warp-routes/templates/_helpers.tpl b/typescript/infra/helm/warp-routes/templates/_helpers.tpl index 338d8ad502..285a5842b8 100644 --- a/typescript/infra/helm/warp-routes/templates/_helpers.tpl +++ b/typescript/infra/helm/warp-routes/templates/_helpers.tpl @@ -61,8 +61,8 @@ The warp-routes container command: - ./node_modules/.bin/ts-node - ./typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts - - -l + - -v - "10000" - - -c - - {{ .Values.config }} + - -f + - {{ .Values.configFilePath }} {{- end }} diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 74ff3cda2f..98b814d8ff 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/infra", "description": "Infrastructure utilities for the Hyperlane Network", - "version": "3.6.0", + "version": "3.8.0", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -12,9 +12,9 @@ "@ethersproject/experimental": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@hyperlane-xyz/helloworld": "3.6.0", - "@hyperlane-xyz/sdk": "3.6.0", - "@hyperlane-xyz/utils": "3.6.0", + "@hyperlane-xyz/helloworld": "3.8.0", + "@hyperlane-xyz/sdk": "3.8.0", + "@hyperlane-xyz/utils": "3.8.0", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "^1.3.0", "@safe-global/protocol-kit": "^1.2.0", @@ -22,6 +22,7 @@ "asn1.js": "5.4.1", "aws-kms-ethers-signer": "^0.1.3", "dotenv": "^10.0.0", + "json-stable-stringify": "^1.1.1", "prom-client": "^14.0.1", "prompts": "^2.4.2", "yargs": "^17.7.2" @@ -30,6 +31,7 @@ "@nomiclabs/hardhat-ethers": "^2.2.1", "@nomiclabs/hardhat-waffle": "^2.0.6", "@types/chai": "^4.2.21", + "@types/json-stable-stringify": "^1.0.36", "@types/mocha": "^10.0.1", "@types/node": "^16.9.1", "@types/prompts": "^2.0.14", @@ -68,7 +70,7 @@ "announce": "hardhat announce --network localhost", "node": "hardhat node", "prettier": "prettier --write *.ts ./src ./config ./scripts ./test", - "test": "mocha --config ../sdk/.mocharc.json test/agents.test.ts", + "test": "mocha --config ../sdk/.mocharc.json test/**/*.test.ts", "test:ci": "yarn test" }, "peerDependencies": { diff --git a/typescript/infra/scripts/utils.ts b/typescript/infra/scripts/agent-utils.ts similarity index 61% rename from typescript/infra/scripts/utils.ts rename to typescript/infra/scripts/agent-utils.ts index 1820f98833..f43c7489dd 100644 --- a/typescript/infra/scripts/utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -1,3 +1,4 @@ +import debug from 'debug'; import path from 'path'; import yargs from 'yargs'; @@ -10,12 +11,14 @@ import { CoreConfig, MultiProvider, RpcConsensusType, + chainMetadata, collectValidators, } from '@hyperlane-xyz/sdk'; import { ProtocolType, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts'; -import { environments } from '../config/environments'; +import { agents } from '../config/environments/agents'; +import { validatorBaseConfigsFn } from '../config/environments/utils'; import { getCurrentKubernetesContext } from '../src/agents'; import { getCloudAgentKey } from '../src/agents/key-utils'; import { CloudAgentKey } from '../src/agents/keys'; @@ -29,6 +32,8 @@ import { EnvironmentNames, deployEnvToSdkEnv } from '../src/config/environment'; import { Role } from '../src/roles'; import { assertContext, assertRole, readJSON } from '../src/utils/utils'; +const debugLog = debug('infra:scripts:utils'); + export enum Modules { // TODO: change PROXY_FACTORY = 'ism', @@ -64,18 +69,26 @@ export function getArgs() { export function withModuleAndFork(args: yargs.Argv) { return args .choices('module', Object.values(Modules)) - .demandOption('module') + .demandOption('module', 'hyperlane module to deploy') .alias('m', 'module') .describe('fork', 'network to fork') .choices('fork', Object.values(Chains)) .alias('f', 'fork'); } +export function withNetwork(args: yargs.Argv) { + return args + .describe('network', 'network to target') + .choices('network', Object.values(Chains)) + .alias('n', 'network'); +} + export function withContext(args: yargs.Argv) { return args .describe('context', 'deploy context') .default('context', Contexts.Hyperlane) .coerce('context', assertContext) + .alias('x', 'context') .demandOption('context'); } @@ -113,6 +126,21 @@ export function withKeyRoleAndChain(args: yargs.Argv) { .alias('i', 'index'); } +// missing chains are chains needed which are not as part of defaultMultisigConfigs in sdk/src/consts/ but are in chainMetadata +export function withMissingChains(args: yargs.Argv) { + return args + .describe('newChains', 'new chains to add') + .string('newChains') + .alias('n', 'newChains'); +} + +export function withBuildArtifactPath(args: yargs.Argv) { + return args + .describe('buildArtifactPath', 'path to hardhat build artifact') + .string('buildArtifactPath') + .alias('b', 'buildArtifactPath'); +} + export function assertEnvironment(env: string): DeployEnvironment { if (EnvironmentNames.includes(env)) { return env as DeployEnvironment; @@ -122,39 +150,98 @@ export function assertEnvironment(env: string): DeployEnvironment { ); } -export function getEnvironmentConfig(environment: DeployEnvironment) { - return environments[environment]; -} - -export async function getConfigsBasedOnArgs(argv?: { +// not requiring to build coreConfig to get agentConfig +export async function getAgentConfigsBasedOnArgs(argv?: { environment: DeployEnvironment; context: Contexts; + newChains: string; }) { - const { environment, context = Contexts.Hyperlane } = argv - ? argv - : await withContext(getArgs()).argv; - const envConfig = getEnvironmentConfig(environment); - const agentConfig = getAgentConfig(context, envConfig); - return { envConfig, agentConfig, context, environment }; + const { + environment, + context = Contexts.Hyperlane, + newChains, + } = argv ? argv : await withMissingChains(withContext(getArgs())).argv; + + const newValidatorCounts: ChainMap = {}; + if (newChains) { + const chains = newChains.split(','); + for (const chain of chains) { + const [chainName, newValidatorCount] = chain.split('='); + newValidatorCounts[chainName] = parseInt(newValidatorCount, 10); + } + } + + const agentConfig = getAgentConfig(context, environment); + // check if new chains are needed + const missingChains = checkIfValidatorsArePersisted(agentConfig); + + // if you include a chain in chainMetadata but not in the aw-multisig.json, you need to specify the new chain in new-chains + for (const chain of missingChains) { + if (!Object.keys(newValidatorCounts).includes(chain)) { + throw new Error(`Missing chain ${chain} not specified in new-chains`); + } + const baseConfig = { + [Contexts.Hyperlane]: [], + [Contexts.ReleaseCandidate]: [], + [Contexts.Neutron]: [], + }; + // supplementing with dummy addresses for validator as part of missingChains + const validatorsConfig = validatorBaseConfigsFn(environment, context); + + const validators = validatorsConfig( + { + ...baseConfig, + [context]: Array(newValidatorCounts[chain]).fill('0x0'), + }, + chain as Chains, + ); + // the hardcoded fields are not strictly necessary to be accurate for create-keys.ts + // ideally would still get them from the chainMetadata + if (!agentConfig.validators) { + throw new Error('AgentConfig does not have validators'); + } + + agentConfig.validators.chains[chain] = { + interval: chainMetadata[chain].blocks?.estimateBlockTime ?? 1, // dummy value + reorgPeriod: chainMetadata[chain].blocks?.reorgPeriod ?? 0, // dummy value + validators, + }; + } + + return { + agentConfig, + context, + environment, + }; } // Gets the agent config of a specific context. +// without fetching environment config export function getAgentConfig( context: Contexts, - environment: EnvironmentConfig | DeployEnvironment, + environment: DeployEnvironment, ): RootAgentConfig { - const coreConfig = - typeof environment == 'string' - ? getEnvironmentConfig(environment) - : environment; - const agentConfig = coreConfig.agents[context]; - if (!agentConfig) - throw Error( - `Invalid context ${context} for environment, must be one of ${Object.keys( - coreConfig.agents, - )}.`, + const agentsForEnvironment = agents[environment] as Record< + Contexts, + RootAgentConfig + >; + if (!Object.keys(agents[environment]).includes(context)) { + throw new Error( + `Context ${context} does not exist in agents for environment ${environment}`, ); - return agentConfig; + } + return agentsForEnvironment[context]; +} + +// check if validators are persisted in agentConfig +export function checkIfValidatorsArePersisted( + agentConfig: RootAgentConfig, +): Set { + const supportedChainNames = agentConfig.contextChainNames.validator; + const persistedChainNames = Object.keys(agentConfig.validators?.chains || {}); + return new Set( + supportedChainNames.filter((x) => !persistedChainNames.includes(x)), + ); } export function getKeyForRole( @@ -164,8 +251,8 @@ export function getKeyForRole( role: Role, index?: number, ): CloudAgentKey { - const environmentConfig = environments[environment]; - const agentConfig = getAgentConfig(context, environmentConfig); + debugLog(`Getting key for ${role} role`); + const agentConfig = getAgentConfig(context, environment); return getCloudAgentKey(agentConfig, role, chain, index); } @@ -178,17 +265,25 @@ export async function getMultiProviderForRole( // TODO: rename to consensusType? connectionType?: RpcConsensusType, ): Promise { + debugLog(`Getting multiprovider for ${role} role`); if (process.env.CI === 'true') { + debugLog('Returning multiprovider with default RPCs in CI'); return new MultiProvider(); // use default RPCs } const multiProvider = new MultiProvider(txConfigs); await promiseObjAll( objMap(txConfigs, async (chain, _) => { - const provider = await fetchProvider(environment, chain, connectionType); - const key = getKeyForRole(environment, context, chain, role, index); - const signer = await key.getSigner(provider); - multiProvider.setProvider(chain, provider); - multiProvider.setSigner(chain, signer); + if (multiProvider.getProtocol(chain) === ProtocolType.Ethereum) { + const provider = await fetchProvider( + environment, + chain, + connectionType, + ); + const key = getKeyForRole(environment, context, chain, role, index); + const signer = await key.getSigner(provider); + multiProvider.setProvider(chain, provider); + multiProvider.setSigner(chain, signer); + } }), ); @@ -205,6 +300,7 @@ export async function getKeysForRole( index?: number, ): Promise> { if (process.env.CI === 'true') { + debugLog('No keys to return in CI'); return {}; } @@ -269,6 +365,10 @@ export function getAgentConfigDirectory() { return path.join('../../', 'rust', 'config'); } +export function getAgentConfigJsonPath(environment: DeployEnvironment) { + return path.join(getAgentConfigDirectory(), `${environment}_config.json`); +} + export async function assertCorrectKubeContext(coreConfig: EnvironmentConfig) { const currentKubeContext = await getCurrentKubernetesContext(); if ( diff --git a/typescript/infra/scripts/agents/deploy-agents.ts b/typescript/infra/scripts/agents/deploy-agents.ts index 3802e240b6..f2080b7afd 100644 --- a/typescript/infra/scripts/agents/deploy-agents.ts +++ b/typescript/infra/scripts/agents/deploy-agents.ts @@ -1,6 +1,6 @@ import { createAgentKeysIfNotExists } from '../../src/agents/key-utils'; import { HelmCommand } from '../../src/utils/helm'; -import { getConfigsBasedOnArgs } from '../utils'; +import { getConfigsBasedOnArgs } from '../core-utils'; import { AgentCli } from './utils'; diff --git a/typescript/infra/scripts/agents/utils.ts b/typescript/infra/scripts/agents/utils.ts index 2bd9a3ad7c..c351346807 100644 --- a/typescript/infra/scripts/agents/utils.ts +++ b/typescript/infra/scripts/agents/utils.ts @@ -7,16 +7,13 @@ import { import { EnvironmentConfig, RootAgentConfig } from '../../src/config'; import { Role } from '../../src/roles'; import { HelmCommand } from '../../src/utils/helm'; -import { sleep } from '../../src/utils/utils'; import { assertCorrectKubeContext, getArgs, - getConfigsBasedOnArgs, withAgentRole, withContext, -} from '../utils'; - -type GetConfigsArgv = NonNullable[0]>; +} from '../agent-utils'; +import { getConfigsBasedOnArgs } from '../core-utils'; export class AgentCli { roles!: Role[]; diff --git a/typescript/infra/scripts/announce-validators.ts b/typescript/infra/scripts/announce-validators.ts index 542e9de962..10e6f6181b 100644 --- a/typescript/infra/scripts/announce-validators.ts +++ b/typescript/infra/scripts/announce-validators.ts @@ -8,13 +8,14 @@ import { AllChains, ChainName, HyperlaneCore } from '@hyperlane-xyz/sdk'; import { S3Validator } from '../src/agents/aws/validator'; import { CheckpointSyncerType } from '../src/config'; import { deployEnvToSdkEnv } from '../src/config/environment'; +import { isEthereumProtocolChain } from '../src/utils/utils'; import { getAgentConfig, - getEnvironmentConfig, getArgs as getRootArgs, withContext, -} from './utils'; +} from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; function getArgs() { return withContext(getRootArgs()) @@ -22,7 +23,7 @@ function getArgs() { .choices('chain', AllChains) .describe( 'location', - 'location, e.g. s3://hyperlane-testnet4-goerli-validator-0/us-east-1', + 'location, e.g. s3://hyperlane-testnet4-sepolia-validator-0/us-east-1', ) .string('location') .check(({ context, chain, location }) => { @@ -74,14 +75,15 @@ async function main() { throw new Error(`Unknown location type %{location}`); } } else { - const agentConfig = getAgentConfig(context, config); + const agentConfig = getAgentConfig(context, environment); if (agentConfig.validators == undefined) { console.warn('No validators provided for context'); return; } await Promise.all( - Object.entries(agentConfig.validators.chains).map( - async ([chain, validatorChainConfig]) => { + Object.entries(agentConfig.validators.chains) + .filter(([chain, _]) => isEthereumProtocolChain(chain)) + .map(async ([chain, validatorChainConfig]) => { for (const validatorBaseConfig of validatorChainConfig.validators) { if ( validatorBaseConfig.checkpointSyncer.type == @@ -104,8 +106,7 @@ async function main() { chains.push(chain); } } - }, - ), + }), ); } @@ -125,7 +126,9 @@ async function main() { const announced = announcedLocations[0].includes(location); if (!announced) { const signature = ethers.utils.joinSignature(announcement.signature); - console.log(`Announcing ${address} checkpoints at ${location}`); + console.log( + `[${chain}] Announcing ${address} checkpoints at ${location}`, + ); await validatorAnnounce.announce( address, location, @@ -133,7 +136,9 @@ async function main() { multiProvider.getTransactionOverrides(chain), ); } else { - console.log(`Already announced ${address} checkpoints at ${location}`); + console.log( + `[${chain}] Already announced ${address} checkpoints at ${location}`, + ); } } } diff --git a/typescript/infra/scripts/check-deploy.ts b/typescript/infra/scripts/check-deploy.ts index c09889cf25..81902dc4fe 100644 --- a/typescript/infra/scripts/check-deploy.ts +++ b/typescript/infra/scripts/check-deploy.ts @@ -20,14 +20,14 @@ import { ProxiedRouterGovernor } from '../src/govern/ProxiedRouterGovernor'; import { Role } from '../src/roles'; import { impersonateAccount, useLocalProvider } from '../src/utils/fork'; -import { getHelloWorldApp } from './helloworld/utils'; import { Modules, - getEnvironmentConfig, getArgs as getRootArgs, withContext, withModuleAndFork, -} from './utils'; +} from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; +import { getHelloWorldApp } from './helloworld/utils'; function getArgs() { return withModuleAndFork(withContext(getRootArgs())) @@ -51,7 +51,8 @@ async function check() { }); const owner = config.core[fork].owner; - const signer = await impersonateAccount(owner); + const signer = await impersonateAccount(owner, 1e18); + multiProvider.setSigner(fork, signer); } } @@ -69,11 +70,11 @@ async function check() { config.core, ismFactory, ); - governor = new HyperlaneCoreGovernor(checker, config.owners); + governor = new HyperlaneCoreGovernor(checker); } else if (module === Modules.INTERCHAIN_GAS_PAYMASTER) { const igp = HyperlaneIgp.fromEnvironment(env, multiProvider); const checker = new HyperlaneIgpChecker(multiProvider, igp, config.igp); - governor = new HyperlaneIgpGovernor(checker, config.owners); + governor = new HyperlaneIgpGovernor(checker); } else if (module === Modules.INTERCHAIN_ACCOUNTS) { const ica = InterchainAccount.fromEnvironment(env, multiProvider); const checker = new InterchainAccountChecker( @@ -81,7 +82,7 @@ async function check() { ica, routerConfig, ); - governor = new ProxiedRouterGovernor(checker, config.owners); + governor = new ProxiedRouterGovernor(checker); } else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) { const iqs = InterchainQuery.fromEnvironment(env, multiProvider); const checker = new InterchainQueryChecker( @@ -89,7 +90,7 @@ async function check() { iqs, routerConfig, ); - governor = new ProxiedRouterGovernor(checker, config.owners); + governor = new ProxiedRouterGovernor(checker); } else if (module === Modules.HELLO_WORLD) { const app = await getHelloWorldApp( config, @@ -104,7 +105,7 @@ async function check() { routerConfig, ismFactory, ); - governor = new ProxiedRouterGovernor(checker, config.owners); + governor = new ProxiedRouterGovernor(checker); } else { console.log(`Skipping ${module}, checker or governor unimplemented`); return; diff --git a/typescript/infra/scripts/check-rpc-urls.ts b/typescript/infra/scripts/check-rpc-urls.ts index 48e874de58..daf7f69657 100644 --- a/typescript/infra/scripts/check-rpc-urls.ts +++ b/typescript/infra/scripts/check-rpc-urls.ts @@ -4,7 +4,8 @@ import { debug, error } from '@hyperlane-xyz/utils'; import { getSecretRpcEndpoint } from '../src/agents'; -import { getArgs, getEnvironmentConfig } from './utils'; +import { getArgs } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; // TODO remove this script as part of migration to CLI // It's redundant with metadata-check.ts in the SDK diff --git a/typescript/infra/scripts/core-utils.ts b/typescript/infra/scripts/core-utils.ts new file mode 100644 index 0000000000..4c41ccd83b --- /dev/null +++ b/typescript/infra/scripts/core-utils.ts @@ -0,0 +1,23 @@ +import { Contexts } from '../config/contexts'; +import { environments } from '../config/environments'; +import { DeployEnvironment } from '../src/config'; + +import { getAgentConfig, getArgs, withContext } from './agent-utils'; + +// utils which use both environment configs + +export function getEnvironmentConfig(environment: DeployEnvironment) { + return environments[environment]; +} + +export async function getConfigsBasedOnArgs(argv?: { + environment: DeployEnvironment; + context: Contexts; +}) { + const { environment, context = Contexts.Hyperlane } = argv + ? argv + : await withContext(getArgs()).argv; + const envConfig = getEnvironmentConfig(environment); + const agentConfig = getAgentConfig(context, environment); + return { envConfig, agentConfig, context, environment }; +} diff --git a/typescript/infra/scripts/create-keys.ts b/typescript/infra/scripts/create-keys.ts index 38e1df9dcc..f0778501a8 100644 --- a/typescript/infra/scripts/create-keys.ts +++ b/typescript/infra/scripts/create-keys.ts @@ -1,10 +1,11 @@ import { createAgentKeysIfNotExists } from '../src/agents/key-utils'; -import { getConfigsBasedOnArgs } from './utils'; +import { getAgentConfigsBasedOnArgs } from './agent-utils'; async function main() { - const { agentConfig } = await getConfigsBasedOnArgs(); - return createAgentKeysIfNotExists(agentConfig); + const { agentConfig } = await getAgentConfigsBasedOnArgs(); + await createAgentKeysIfNotExists(agentConfig); + return 'Keys created successfully!'; } main().then(console.log).catch(console.error); diff --git a/typescript/infra/scripts/debug-message.ts b/typescript/infra/scripts/debug-message.ts index d49b13cf60..d39a5f1bb9 100644 --- a/typescript/infra/scripts/debug-message.ts +++ b/typescript/infra/scripts/debug-message.ts @@ -10,7 +10,7 @@ import { bytes32ToAddress, ensure0x, messageId } from '@hyperlane-xyz/utils'; import { deployEnvToSdkEnv } from '../src/config/environment'; import { assertChain } from '../src/utils/utils'; -import { getArgs } from './utils'; +import { getArgs } from './agent-utils'; async function main() { const argv = await getArgs() diff --git a/typescript/infra/scripts/delete-keys.ts b/typescript/infra/scripts/delete-keys.ts index 29dfde2e64..e76aed23dd 100644 --- a/typescript/infra/scripts/delete-keys.ts +++ b/typescript/infra/scripts/delete-keys.ts @@ -1,9 +1,9 @@ import { deleteAgentKeys } from '../src/agents/key-utils'; -import { getConfigsBasedOnArgs } from './utils'; +import { getAgentConfigsBasedOnArgs } from './agent-utils'; async function main() { - const { agentConfig } = await getConfigsBasedOnArgs(); + const { agentConfig } = await getAgentConfigsBasedOnArgs(); return deleteAgentKeys(agentConfig); } diff --git a/typescript/infra/scripts/deploy-infra-external-secrets.ts b/typescript/infra/scripts/deploy-infra-external-secrets.ts index d3ac34dd40..cd20245751 100644 --- a/typescript/infra/scripts/deploy-infra-external-secrets.ts +++ b/typescript/infra/scripts/deploy-infra-external-secrets.ts @@ -1,11 +1,8 @@ import { runExternalSecretsHelmCommand } from '../src/infrastructure/external-secrets/external-secrets'; import { HelmCommand } from '../src/utils/helm'; -import { - assertCorrectKubeContext, - getArgs, - getEnvironmentConfig, -} from './utils'; +import { assertCorrectKubeContext, getArgs } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; async function main() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/deploy-infra-monitoring.ts b/typescript/infra/scripts/deploy-infra-monitoring.ts index 06862883a7..81e638cab0 100644 --- a/typescript/infra/scripts/deploy-infra-monitoring.ts +++ b/typescript/infra/scripts/deploy-infra-monitoring.ts @@ -1,11 +1,8 @@ import { runPrometheusHelmCommand } from '../src/infrastructure/monitoring/prometheus'; import { HelmCommand } from '../src/utils/helm'; -import { - assertCorrectKubeContext, - getArgs, - getEnvironmentConfig, -} from './utils'; +import { assertCorrectKubeContext, getArgs } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; async function main() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index d9d58194fc..e1545c5879 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -1,10 +1,12 @@ +import { ethers } from 'ethers'; import path from 'path'; import { prompt } from 'prompts'; import { HelloWorldDeployer } from '@hyperlane-xyz/helloworld'; import { ChainMap, - Chains, + ContractVerifier, + ExplorerLicenseType, HypERC20Deployer, HyperlaneCore, HyperlaneCoreDeployer, @@ -15,15 +17,20 @@ import { InterchainAccountDeployer, InterchainQueryDeployer, LiquidityLayerDeployer, - TokenConfig, + TestRecipientDeployer, + TokenType, } from '@hyperlane-xyz/sdk'; -import { TokenDecimals, TokenType } from '@hyperlane-xyz/sdk/dist/token/config'; import { objMap } from '@hyperlane-xyz/utils'; import { Contexts } from '../config/contexts'; +import { safes } from '../config/environments/mainnet3/owners'; import { deployEnvToSdkEnv } from '../src/config/environment'; import { deployWithArtifacts } from '../src/deployment/deploy'; import { TestQuerySenderDeployer } from '../src/deployment/testcontracts/testquerysender'; +import { + extractBuildArtifact, + fetchExplorerApiKeys, +} from '../src/deployment/verify'; import { impersonateAccount, useLocalProvider } from '../src/utils/fork'; import { @@ -32,11 +39,13 @@ import { getAddresses, getArgs, getContractAddressesSdkFilepath, - getEnvironmentConfig, getModuleDirectory, + withBuildArtifactPath, withContext, withModuleAndFork, -} from './utils'; + withNetwork, +} from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; async function main() { const { @@ -44,84 +53,104 @@ async function main() { module, fork, environment, - } = await withContext(withModuleAndFork(getArgs())).argv; + network, + buildArtifactPath, + } = await withContext( + withNetwork(withModuleAndFork(withBuildArtifactPath(getArgs()))), + ).argv; const envConfig = getEnvironmentConfig(environment); const env = deployEnvToSdkEnv[environment]; let multiProvider = await envConfig.getMultiProvider(); - // TODO: make this more generic - const deployerAddress = - environment === 'testnet4' - ? '0xfaD1C94469700833717Fa8a3017278BC1cA8031C' - : '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; - if (fork) { multiProvider = multiProvider.extendChainMetadata({ [fork]: { blocks: { confirmations: 0 } }, }); await useLocalProvider(multiProvider, fork); - const signer = await impersonateAccount(deployerAddress); + // const deployers = await envConfig.getKeys( + // Contexts.Hyperlane, + // Role.Deployer, + // ); + // const deployer = deployers[fork].address; + const deployer = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba'; + const signer = await impersonateAccount(deployer); + multiProvider.setSharedSigner(signer); } + let contractVerifier; + if (buildArtifactPath) { + // fetch explorer API keys from GCP + const apiKeys = await fetchExplorerApiKeys(); + // extract build artifact contents + const buildArtifact = extractBuildArtifact(buildArtifactPath); + // instantiate verifier + contractVerifier = new ContractVerifier( + multiProvider, + apiKeys, + buildArtifact, + ExplorerLicenseType.MIT, + ); + } + let config: ChainMap = {}; let deployer: HyperlaneDeployer; if (module === Modules.PROXY_FACTORY) { config = objMap(envConfig.core, (_chain) => true); - deployer = new HyperlaneProxyFactoryDeployer(multiProvider); + deployer = new HyperlaneProxyFactoryDeployer( + multiProvider, + contractVerifier, + ); } else if (module === Modules.CORE) { config = envConfig.core; const ismFactory = HyperlaneIsmFactory.fromAddressesMap( getAddresses(environment, Modules.PROXY_FACTORY), multiProvider, ); - deployer = new HyperlaneCoreDeployer(multiProvider, ismFactory); + deployer = new HyperlaneCoreDeployer( + multiProvider, + ismFactory, + contractVerifier, + ); } else if (module === Modules.WARP) { - const owner = deployerAddress; - const neutronRouter = - '6b04c49fcfd98bc4ea9c05cd5790462a39537c00028333474aebe6ddf20b73a3'; + const core = HyperlaneCore.fromEnvironment(env, multiProvider); const ismFactory = HyperlaneIsmFactory.fromAddressesMap( getAddresses(environment, Modules.PROXY_FACTORY), multiProvider, ); - const tokenConfig: TokenConfig & TokenDecimals = { - type: TokenType.synthetic, - name: 'Eclipse Fi', - symbol: 'ECLIP', - decimals: 6, - totalSupply: 0, + const routerConfig = core.getRouterConfig(envConfig.owners); + const inevm = { + ...routerConfig.inevm, + type: TokenType.native, + interchainSecurityModule: ethers.constants.AddressZero, + owner: safes.inevm, + }; + const injective = { + ...routerConfig.injective, + type: TokenType.native, }; - const core = HyperlaneCore.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - const routerConfig = core.getRouterConfig(owner); - const targetChains = [Chains.arbitrum]; config = { - arbitrum: { - ...routerConfig['arbitrum'], - ...tokenConfig, - interchainSecurityModule: '0x53A5c239d62ff35c98E0EC9612c86517748ffF59', - gas: 600_000, - }, - neutron: { - foreignDeployment: neutronRouter, - }, + inevm, + injective, }; - deployer = new HypERC20Deployer(multiProvider, ismFactory); + deployer = new HypERC20Deployer( + multiProvider, + ismFactory, + contractVerifier, + ); } else if (module === Modules.INTERCHAIN_GAS_PAYMASTER) { config = envConfig.igp; - deployer = new HyperlaneIgpDeployer(multiProvider); + deployer = new HyperlaneIgpDeployer(multiProvider, contractVerifier); } else if (module === Modules.INTERCHAIN_ACCOUNTS) { const core = HyperlaneCore.fromEnvironment(env, multiProvider); config = core.getRouterConfig(envConfig.owners); - deployer = new InterchainAccountDeployer(multiProvider); + deployer = new InterchainAccountDeployer(multiProvider, contractVerifier); } else if (module === Modules.INTERCHAIN_QUERY_SYSTEM) { const core = HyperlaneCore.fromEnvironment(env, multiProvider); config = core.getRouterConfig(envConfig.owners); - deployer = new InterchainQueryDeployer(multiProvider); + deployer = new InterchainQueryDeployer(multiProvider, contractVerifier); } else if (module === Modules.LIQUIDITY_LAYER) { const core = HyperlaneCore.fromEnvironment(env, multiProvider); const routerConfig = core.getRouterConfig(envConfig.owners); @@ -135,9 +164,18 @@ async function main() { ...routerConfig[chain], }), ); - deployer = new LiquidityLayerDeployer(multiProvider); + deployer = new LiquidityLayerDeployer(multiProvider, contractVerifier); } else if (module === Modules.TEST_RECIPIENT) { - throw new Error('Test recipient is not supported. Use CLI instead.'); + const addresses = getAddresses(environment, Modules.CORE); + + for (const chain of Object.keys(addresses)) { + config[chain] = { + interchainSecurityModule: + addresses[chain].interchainSecurityModule ?? + ethers.constants.AddressZero, // ISM is required for the TestRecipientDeployer but onchain if the ISM is zero address, then it uses the mailbox's defaultISM + }; + } + deployer = new TestRecipientDeployer(multiProvider, contractVerifier); } else if (module === Modules.TEST_QUERY_SENDER) { // Get query router addresses const queryAddresses = getAddresses( @@ -147,11 +185,15 @@ async function main() { config = objMap(queryAddresses, (_c, conf) => ({ queryRouterAddress: conf.router, })); - deployer = new TestQuerySenderDeployer(multiProvider); + deployer = new TestQuerySenderDeployer(multiProvider, contractVerifier); } else if (module === Modules.HELLO_WORLD) { const core = HyperlaneCore.fromEnvironment(env, multiProvider); - config = core.getRouterConfig(deployerAddress); - deployer = new HelloWorldDeployer(multiProvider); + config = core.getRouterConfig(envConfig.owners); + deployer = new HelloWorldDeployer( + multiProvider, + undefined, + contractVerifier, + ); } else { console.log(`Skipping ${module}, deployer unimplemented`); return; @@ -176,7 +218,7 @@ async function main() { addresses, verification, read: environment !== 'test', - write: true, + write: !fork, }; // Don't write agent config in fork tests const agentConfig = @@ -188,9 +230,10 @@ async function main() { } : undefined; - // prompt for confirmation - if ((environment === 'mainnet3' || environment === 'testnet4') && !fork) { - console.log(JSON.stringify(config, null, 2)); + // prompt for confirmation in production environments + if (environment !== 'test' && !fork) { + const confirmConfig = network ? config[network] : config; + console.log(JSON.stringify(confirmConfig, null, 2)); const { value: confirmed } = await prompt({ type: 'confirm', name: 'value', @@ -202,7 +245,13 @@ async function main() { } } - await deployWithArtifacts(config, deployer, cache, fork, agentConfig); + await deployWithArtifacts( + config, + deployer, + cache, + network ?? fork, + agentConfig, + ); } main() diff --git a/typescript/infra/scripts/funding/deploy-key-funder.ts b/typescript/infra/scripts/funding/deploy-key-funder.ts index d4cc5d80d6..a73104c924 100644 --- a/typescript/infra/scripts/funding/deploy-key-funder.ts +++ b/typescript/infra/scripts/funding/deploy-key-funder.ts @@ -4,7 +4,8 @@ import { runKeyFunderHelmCommand, } from '../../src/funding/key-funder'; import { HelmCommand } from '../../src/utils/helm'; -import { assertCorrectKubeContext, getConfigsBasedOnArgs } from '../utils'; +import { assertCorrectKubeContext } from '../agent-utils'; +import { getConfigsBasedOnArgs } from '../core-utils'; async function main() { const { agentConfig, envConfig } = await getConfigsBasedOnArgs(); diff --git a/typescript/infra/scripts/funding/fund-deterministic-key-from-deployer.ts b/typescript/infra/scripts/funding/fund-deterministic-key-from-deployer.ts index fbbdff95c3..7aad6f55f3 100644 --- a/typescript/infra/scripts/funding/fund-deterministic-key-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-deterministic-key-from-deployer.ts @@ -10,7 +10,8 @@ import { } from '../../src/funding/deterministic-keys'; import { Role } from '../../src/roles'; import { assertChain } from '../../src/utils/utils'; -import { getArgs, getEnvironmentConfig } from '../utils'; +import { getArgs } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; async function main() { const argv = await getArgs() diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index 75af8d646e..1e50276b28 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -17,29 +17,38 @@ import { log, objFilter, objMap, - promiseObjAll, warn, } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; -import { parseKeyIdentifier } from '../../src/agents/agent'; -import { KeyAsAddress, getRoleKeysPerChain } from '../../src/agents/key-utils'; import { - BaseCloudAgentKey, + KeyAsAddress, + fetchLocalKeyAddresses, + getRoleKeysPerChain, +} from '../../src/agents/key-utils'; +import { + BaseAgentKey, + LocalAgentKey, ReadOnlyCloudAgentKey, } from '../../src/agents/keys'; import { DeployEnvironment } from '../../src/config'; import { deployEnvToSdkEnv } from '../../src/config/environment'; -import { ContextAndRoles, ContextAndRolesMap } from '../../src/config/funding'; -import { Role } from '../../src/roles'; +import { + ContextAndRoles, + ContextAndRolesMap, + KeyFunderConfig, +} from '../../src/config/funding'; +import { FundableRole, Role } from '../../src/roles'; import { submitMetrics } from '../../src/utils/metrics'; import { assertContext, + assertFundableRole, assertRole, isEthereumProtocolChain, readJSONAtPath, } from '../../src/utils/utils'; -import { getAgentConfig, getArgs, getEnvironmentConfig } from '../utils'; +import { getAgentConfig, getArgs } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; import * as L1ETHGateway from './utils/L1ETHGateway.json'; import * as L1MessageQueue from './utils/L1MessageQueue.json'; @@ -51,37 +60,16 @@ const nativeBridges = { l1ETHGateway: '0x8A54A2347Da2562917304141ab67324615e9866d', l1Messenger: '0x50c7d3e7f7c656493D1D76aaa1a836CedfCBB16A', }, - polygonzkevmtestnet: { - l1EVMBridge: '0xF6BEEeBB578e214CA9E23B0e9683454Ff88Ed2A7', - }, }; -type L2Chain = - | Chains.optimism - | Chains.optimismgoerli - | Chains.arbitrum - | Chains.arbitrumgoerli - | Chains.basegoerli - | Chains.base; - -const L2Chains: ChainName[] = [ - Chains.optimism, - Chains.optimismgoerli, - Chains.arbitrum, - Chains.arbitrumgoerli, - Chains.basegoerli, - Chains.base, - Chains.polygonzkevmtestnet, -]; +type L2Chain = Chains.optimism | Chains.arbitrum | Chains.base; + +const L2Chains: ChainName[] = [Chains.optimism, Chains.arbitrum, Chains.base]; const L2ToL1: ChainMap = { - optimismgoerli: 'goerli', - arbitrumgoerli: 'goerli', optimism: 'ethereum', arbitrum: 'ethereum', - basegoerli: 'goerli', base: 'ethereum', - polygonzkevmtestnet: 'goerli', }; // Missing types declaration for bufio @@ -120,55 +108,6 @@ const MIN_DELTA_DENOMINATOR = ethers.BigNumber.from(10); const RC_FUNDING_DISCOUNT_NUMERATOR = ethers.BigNumber.from(2); const RC_FUNDING_DISCOUNT_DENOMINATOR = ethers.BigNumber.from(10); -const desiredBalancePerChain: ChainMap = { - celo: '0.3', - alfajores: '1', - avalanche: '3', - fuji: '1', - ethereum: '0.5', - polygon: '2', - mumbai: '0.8', - optimism: '0.5', - arbitrum: '0.5', - bsc: '0.05', - bsctestnet: '1', - goerli: '0.5', - sepolia: '0.5', - moonbasealpha: '1', - moonbeam: '0.5', - optimismgoerli: '0.5', - arbitrumgoerli: '0.5', - gnosis: '0.1', - basegoerli: '0.05', - scrollsepolia: '0.05', - polygonzkevm: '0.3', - scroll: '0.3', - base: '0.3', - polygonzkevmtestnet: '0.3', - - // unused - test1: '0', - test2: '0', - test3: '0', -}; - -// Used to fund kathy with more tokens such that it's able to pay interchain gas -// on mainnet. The amount is roughly > $100 -const desiredKathyBalancePerChain: ChainMap = { - celo: '150', - avalanche: '6', - polygon: '85', - ethereum: '0.4', - optimism: '0.1', - arbitrum: '0.1', - bsc: '0.35', - moonbeam: '250', - gnosis: '100', - scroll: '0.05', - base: '0.05', - polygonzkevm: '0.05', -}; - // The balance threshold of the IGP contract that must be met for the key funder // to call `claim()` const igpClaimThresholdPerChain: ChainMap = { @@ -183,19 +122,14 @@ const igpClaimThresholdPerChain: ChainMap = { arbitrum: '0.1', bsc: '0.3', bsctestnet: '1', - goerli: '1', sepolia: '1', - moonbasealpha: '2', moonbeam: '5', - optimismgoerli: '1', - arbitrumgoerli: '1', gnosis: '5', - basegoerli: '0.1', scrollsepolia: '0.1', - polygonzkevmtestnet: '0.1', base: '0.1', scroll: '0.1', polygonzkevm: '0.1', + plumetestnet: '0.1', // unused test1: '0', test2: '0', @@ -236,6 +170,23 @@ async function main() { .coerce('contexts-and-roles', parseContextAndRolesMap) .demandOption('contexts-and-roles') + .string('desired-balance-per-chain') + .array('desired-balance-per-chain') + .describe( + 'desired-balance-per-chain', + 'Array indicating target balance to fund for each chain. Each element is expected as =', + ) + .coerce('desired-balance-per-chain', parseBalancePerChain) + .demandOption('desired-balance-per-chain') + + .string('desired-kathy-balance-per-chain') + .array('desired-kathy-balance-per-chain') + .describe( + 'desired-kathy-balance-per-chain', + 'Array indicating target balance to fund Kathy for each chain. Each element is expected as =', + ) + .coerce('desired-kathy-balance-per-chain', parseBalancePerChain) + .string('connection-type') .describe('connection-type', 'The provider connection type to use for RPCs') .default('connection-type', RpcConsensusType.Single) @@ -266,6 +217,8 @@ async function main() { multiProvider, argv.contextsAndRoles, argv.skipIgpClaim, + argv.desiredBalancePerChain, + argv.desiredKathyBalancePerChain ?? {}, path, ), ); @@ -273,12 +226,14 @@ async function main() { const contexts = Object.keys(argv.contextsAndRoles) as Contexts[]; contextFunders = await Promise.all( contexts.map((context) => - ContextFunder.fromContext( + ContextFunder.fromLocal( environment, multiProvider, context, argv.contextsAndRoles[context]!, argv.skipIgpClaim, + argv.desiredBalancePerChain, + argv.desiredKathyBalancePerChain ?? {}, ), ), ); @@ -301,20 +256,22 @@ async function main() { class ContextFunder { igp: HyperlaneIgp; - keysToFundPerChain: ChainMap; + keysToFundPerChain: ChainMap; constructor( public readonly environment: DeployEnvironment, public readonly multiProvider: MultiProvider, - roleKeysPerChain: ChainMap>, + roleKeysPerChain: ChainMap>, public readonly context: Contexts, - public readonly rolesToFund: Role[], + public readonly rolesToFund: FundableRole[], public readonly skipIgpClaim: boolean, + public readonly desiredBalancePerChain: KeyFunderConfig['desiredBalancePerChain'], + public readonly desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'], ) { // At the moment, only blessed EVM chains are supported roleKeysPerChain = objFilter( roleKeysPerChain, - (chain, _roleKeys): _roleKeys is Record => { + (chain, _roleKeys): _roleKeys is Record => { const valid = isEthereumProtocolChain(chain) && multiProvider.tryGetChainName(chain) !== null; @@ -333,12 +290,12 @@ class ContextFunder { ); this.keysToFundPerChain = objMap(roleKeysPerChain, (_chain, roleKeys) => { return Object.keys(roleKeys).reduce((agg, roleStr) => { - const role = roleStr as Role; + const role = roleStr as FundableRole; if (this.rolesToFund.includes(role)) { return [...agg, ...roleKeys[role]]; } return agg; - }, [] as BaseCloudAgentKey[]); + }, [] as BaseAgentKey[]); }); } @@ -347,6 +304,8 @@ class ContextFunder { multiProvider: MultiProvider, contextsAndRolesToFund: ContextAndRolesMap, skipIgpClaim: boolean, + desiredBalancePerChain: KeyFunderConfig['desiredBalancePerChain'], + desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'], filePath: string, ) { log('Reading identifiers and addresses from file', { @@ -412,32 +371,56 @@ class ContextFunder { context, contextsAndRolesToFund[context]!, skipIgpClaim, + desiredBalancePerChain, + desiredKathyBalancePerChain, ); } - // The keys here are not ReadOnlyCloudAgentKeys, instead they are AgentGCPKey or AgentAWSKeys, - // which require credentials to fetch. If you want to avoid requiring credentials, use - // fromSerializedAddressFile instead. - static async fromContext( + // the keys are retrieved from the local artifacts in the infra/config/relayer.json or infra/config/kathy.json + static async fromLocal( environment: DeployEnvironment, multiProvider: MultiProvider, context: Contexts, - rolesToFund: Role[], + rolesToFund: FundableRole[], skipIgpClaim: boolean, + desiredBalancePerChain: KeyFunderConfig['desiredBalancePerChain'], + desiredKathyBalancePerChain: KeyFunderConfig['desiredKathyBalancePerChain'], ) { - const agentConfig = getAgentConfig(context, environment); - const roleKeysPerChain = getRoleKeysPerChain(agentConfig); - // Fetch all the keys - await promiseObjAll( - objMap(roleKeysPerChain, (_chain, roleKeys) => { - return promiseObjAll( - objMap(roleKeys, (_role, keys) => { - return Promise.all(keys.map((key) => key.fetch())); - }), + // only roles that are fundable keys ie. relayer and kathy + const fundableRoleKeys: Record = { + [Role.Relayer]: '', + [Role.Kathy]: '', + }; + const roleKeysPerChain: ChainMap> = {}; + const chains = getEnvironmentConfig(environment).chainMetadataConfigs; + for (const role of rolesToFund) { + assertFundableRole(role); // only the relayer and kathy are fundable keys + const roleAddress = fetchLocalKeyAddresses(role)[environment][context]; + if (!roleAddress) { + throw Error( + `Could not find address for ${role} in ${environment} ${context}`, ); - }), - ); - + } + fundableRoleKeys[role] = roleAddress; + + for (const chain of Object.keys(chains)) { + if (!roleKeysPerChain[chain as ChainName]) { + roleKeysPerChain[chain as ChainName] = { + [Role.Relayer]: [], + [Role.Kathy]: [], + }; + } + roleKeysPerChain[chain][role] = [ + new LocalAgentKey( + environment, + context, + role, + fundableRoleKeys[role as FundableRole], + chain, + ), + ]; + } + } return new ContextFunder( environment, multiProvider, @@ -445,6 +428,8 @@ class ContextFunder { context, rolesToFund, skipIgpClaim, + desiredBalancePerChain, + desiredKathyBalancePerChain, ); } @@ -497,7 +482,7 @@ class ContextFunder { } private async attemptToFundKey( - key: BaseCloudAgentKey, + key: BaseAgentKey, chain: ChainName, ): Promise { const provider = this.multiProvider.tryGetProvider(chain); @@ -535,7 +520,7 @@ class ContextFunder { if (L2Chains.includes(chain)) { const funderAddress = await this.multiProvider.getSignerAddress(chain)!; const desiredBalanceEther = ethers.utils.parseUnits( - desiredBalancePerChain[chain], + this.desiredBalancePerChain[chain], 'ether', ); // Optionally bridge ETH to L2 before funding the desired key. @@ -602,10 +587,10 @@ class ContextFunder { private getDesiredBalanceForRole(chain: ChainName, role: Role): BigNumber { const desiredBalanceEther = - role === Role.Kathy && desiredKathyBalancePerChain[chain] - ? desiredKathyBalancePerChain[chain] - : desiredBalancePerChain[chain]; - let desiredBalance = ethers.utils.parseEther(desiredBalanceEther); + role === Role.Kathy && this.desiredKathyBalancePerChain[chain] + ? this.desiredKathyBalancePerChain[chain] + : this.desiredBalancePerChain[chain]; + let desiredBalance = ethers.utils.parseEther(desiredBalanceEther ?? '0'); if (this.context === Contexts.ReleaseCandidate) { desiredBalance = desiredBalance .mul(RC_FUNDING_DISCOUNT_NUMERATOR) @@ -618,7 +603,7 @@ class ContextFunder { // is lower than the desired balance by the min delta private async fundKeyIfRequired( chain: ChainName, - key: BaseCloudAgentKey, + key: BaseAgentKey, desiredBalance: BigNumber, ) { const fundingAmount = await this.getFundingAmount( @@ -697,8 +682,6 @@ class ContextFunder { tx = await this.bridgeToArbitrum(l2Chain, amount); } else if (l2Chain.includes('scroll')) { tx = await this.bridgeToScroll(l2Chain, amount, to); - } else if (l2Chain.includes('zkevm')) { - tx = await this.bridgeToPolygonCDK(l2Chain, amount, to); } else { throw new Error(`${l2Chain} is not an L2`); } @@ -774,31 +757,6 @@ class ContextFunder { ); } - private async bridgeToPolygonCDK( - l2Chain: L2Chain, - amount: BigNumber, - to: Address, - ) { - const l1Chain = L2ToL1[l2Chain]; - const l1ChainSigner = this.multiProvider.getSigner(l1Chain); - const polygonZkEVMbridge = new ethers.Contract( - nativeBridges.polygonzkevmtestnet.l1EVMBridge, - PolygonZkEVMBridge.abi, - l1ChainSigner, - ); - return polygonZkEVMbridge.bridgeAsset( - 1, // 0 is mainnet, 1 is l2 - to, - amount, - ethers.constants.AddressZero, - true, - [], - { - value: amount, - }, - ); - } - private async updateWalletBalanceGauge(chain: ChainName) { const funderAddress = await this.multiProvider.getSignerAddress(chain); walletBalanceGauge @@ -833,13 +791,13 @@ async function getAddressInfo( } async function getKeyInfo( - key: BaseCloudAgentKey, + key: BaseAgentKey, chain: ChainName, provider: ethers.providers.Provider, ) { return { ...(await getAddressInfo(key.address, chain, provider)), - context: key.context, + context: (key as LocalAgentKey).context, originChain: key.chainName, role: key.role, }; @@ -874,7 +832,9 @@ function parseContextAndRoles(str: string): ContextAndRoles { for (const role of roles) { if (!validRoles.has(role)) { throw Error( - `Invalid role ${role}, must be one of ${Array.from(validRoles)}`, + `Invalid fundable role ${role}, must be one of ${Array.from( + validRoles, + )}`, ); } } @@ -885,6 +845,18 @@ function parseContextAndRoles(str: string): ContextAndRoles { }; } +function parseBalancePerChain(strs: string[]): ChainMap { + const balanceMap: ChainMap = {}; + strs.forEach((str) => { + const [chain, balance] = str.split('='); + if (!chain || !balance) { + throw new Error(`Invalid format for balance entry: ${str}`); + } + balanceMap[chain] = balance; + }); + return balanceMap; +} + // Returns whether an error occurred async function gracefullyHandleError( fn: () => Promise, diff --git a/typescript/infra/scripts/funding/reclaim-from-igp.ts b/typescript/infra/scripts/funding/reclaim-from-igp.ts index f780b579a3..87c836054d 100644 --- a/typescript/infra/scripts/funding/reclaim-from-igp.ts +++ b/typescript/infra/scripts/funding/reclaim-from-igp.ts @@ -4,14 +4,15 @@ import { HyperlaneIgp } from '@hyperlane-xyz/sdk'; import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { deployEnvToSdkEnv } from '../../src/config/environment'; -import { getArgs, getEnvironmentConfig } from '../utils'; +import { getArgs } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; // Some arbitrary threshold for now const RECLAIM_BALANCE_THRESHOLD = BigNumber.from(10).pow(17); async function main() { const { environment } = await getArgs().argv; - const environmentConfig = await getEnvironmentConfig(environment); + const environmentConfig = getEnvironmentConfig(environment); const multiProvider = await environmentConfig.getMultiProvider(); const igp = HyperlaneIgp.fromEnvironment( deployEnvToSdkEnv[environment], diff --git a/typescript/infra/scripts/gas-oracle/compare-token-exchange-rates.ts b/typescript/infra/scripts/gas-oracle/compare-token-exchange-rates.ts deleted file mode 100644 index 23e31ba97e..0000000000 --- a/typescript/infra/scripts/gas-oracle/compare-token-exchange-rates.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ethers } from 'ethers'; - -import { - ChainName, - CoinGeckoTokenPriceGetter, - HyperlaneCore, -} from '@hyperlane-xyz/sdk'; - -import { StorageGasOracleConfig } from '../../src/config'; -import { deployEnvToSdkEnv } from '../../src/config/environment'; -import { - TOKEN_EXCHANGE_RATE_DECIMALS, - TOKEN_EXCHANGE_RATE_SCALE, -} from '../../src/config/gas-oracle'; -import { getArgs, getEnvironmentConfig } from '../utils'; - -import { prettyTokenExchangeRate } from './utils'; - -// Compares the token exchange rate between chains according to the config -// to the exchange rates using current Coingecko prices. The config exchange -// rates apply the 30% spread / fee, so we expect config prices to be ~30% higher. -async function main() { - const tokenPriceGetter = CoinGeckoTokenPriceGetter.withDefaultCoinGecko(); - - const { environment } = await getArgs().argv; - const coreEnvConfig = getEnvironmentConfig(environment); - const multiProvider = await coreEnvConfig.getMultiProvider(); - - const storageGasOracleConfig = coreEnvConfig.storageGasOracleConfig; - if (!storageGasOracleConfig) { - throw Error(`No storage gas oracle config for environment ${environment}`); - } - - const core = HyperlaneCore.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - - for (const chain of core.chains()) { - await compare(tokenPriceGetter, storageGasOracleConfig[chain], chain); - console.log('\n==========='); - } -} - -async function compare( - tokenPriceGetter: CoinGeckoTokenPriceGetter, - localStorageGasOracleConfig: StorageGasOracleConfig, - local: ChainName, -) { - for (const remoteStr in localStorageGasOracleConfig) { - const remote = remoteStr as ChainName; - const configGasData = localStorageGasOracleConfig[remote]!; - const currentTokenExchangeRateNum = - await tokenPriceGetter.getTokenExchangeRate(remote, local); - const currentTokenExchangeRate = ethers.utils.parseUnits( - currentTokenExchangeRateNum.toFixed(TOKEN_EXCHANGE_RATE_DECIMALS), - TOKEN_EXCHANGE_RATE_DECIMALS, - ); - - const diff = configGasData.tokenExchangeRate.sub(currentTokenExchangeRate); - const percentDiff = diff - .mul(TOKEN_EXCHANGE_RATE_SCALE) - .div(currentTokenExchangeRate) - .mul(100); - - console.log(`${local} -> ${remote}`); - console.log( - `\tConfig token exchange rate:\n\t\t${prettyTokenExchangeRate( - configGasData.tokenExchangeRate, - )}`, - ); - console.log( - `\tCurrent token exchange rate:\n\t\t${prettyTokenExchangeRate( - currentTokenExchangeRate, - )}`, - ); - console.log( - `Config tokenExchangeRate is ${ethers.utils.formatUnits( - percentDiff, - TOKEN_EXCHANGE_RATE_DECIMALS, - )}% different from the current value`, - ); - console.log('------'); - } -} - -main().catch((err: any) => console.error('Error:', err)); diff --git a/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts b/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts deleted file mode 100644 index a3b36a5815..0000000000 --- a/typescript/infra/scripts/gas-oracle/update-storage-gas-oracle.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { ChainName, HyperlaneIgp, MultiProvider } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; - -import { RemoteGasData, StorageGasOracleConfig } from '../../src/config'; -import { deployEnvToSdkEnv } from '../../src/config/environment'; -import { RemoteGasDataConfig } from '../../src/config/gas-oracle'; -import { getArgs, getEnvironmentConfig } from '../utils'; - -import { - eqRemoteGasData, - prettyRemoteGasData, - prettyRemoteGasDataConfig, -} from './utils'; - -/** - * Idempotent. Use `--dry-run` to not send any transactions. - * Updates the currently stored gas data on the StorageGasOracle contract - * if the configured data differs from the on-chain data. - * Expects the deployer key to be the owner of the StorageGasOracle contract. - */ -async function main() { - const args = await getArgs() - .boolean('dry-run') - .describe('dry-run', 'If true, will not submit any transactions') - .default('dry-run', false).argv; - - const { environment } = await getArgs().argv; - const coreEnvConfig = getEnvironmentConfig(environment); - const multiProvider = await coreEnvConfig.getMultiProvider(); - - const storageGasOracleConfig = coreEnvConfig.storageGasOracleConfig; - if (!storageGasOracleConfig) { - throw Error(`No storage gas oracle config for environment ${environment}`); - } - - const igp = HyperlaneIgp.fromEnvironment( - deployEnvToSdkEnv[environment], - multiProvider, - ); - - for (const chain of igp.chains()) { - if ( - multiProvider.getChainMetadata(chain).protocol !== ProtocolType.Ethereum - ) { - console.log(`Skipping ${chain} because it is not an Ethereum chain`); - continue; - } - - await setStorageGasOracleValues( - igp, - multiProvider, - storageGasOracleConfig[chain], - chain, - args.dryRun, - ); - console.log('\n==========='); - } -} - -async function setStorageGasOracleValues( - igp: HyperlaneIgp, - // This multiProvider is used instead of the one on the IGP because the IGP's - // multiprovider will have filtered out non-Ethereum chains. - multiProvider: MultiProvider, - localStorageGasOracleConfig: StorageGasOracleConfig, - local: ChainName, - dryRun: boolean, -) { - console.log(`Setting remote gas data on local chain ${local}...`); - const storageGasOracle = igp.getContracts(local).storageGasOracle; - - const configsToSet: RemoteGasDataConfig[] = []; - - for (const remote in localStorageGasOracleConfig) { - const desiredGasData = localStorageGasOracleConfig[remote]!; - const remoteId = multiProvider.getDomainId(remote); - - const existingGasData: RemoteGasData = await storageGasOracle.remoteGasData( - remoteId, - ); - - console.log( - `${local} -> ${remote} existing gas data:\n`, - prettyRemoteGasData(existingGasData), - ); - console.log( - `${local} -> ${remote} desired gas data:\n`, - prettyRemoteGasData(desiredGasData), - ); - - if (eqRemoteGasData(existingGasData, desiredGasData)) { - console.log('Existing and desired gas data are the same, doing nothing'); - } else { - console.log('Existing and desired gas data differ, will update'); - configsToSet.push({ - remoteDomain: remoteId, - ...desiredGasData, - }); - } - console.log('---'); - } - - if (configsToSet.length > 0) { - console.log(`Updating ${configsToSet.length} configs on local ${local}:`); - console.log( - configsToSet - .map((config) => prettyRemoteGasDataConfig(multiProvider, config)) - .join('\n\t--\n'), - ); - - if (dryRun) { - console.log('Running in dry run mode, not sending tx'); - } else { - await igp.multiProvider.sendTransaction( - local, - await storageGasOracle.populateTransaction.setRemoteGasDataConfigs( - configsToSet, - ), - ); - } - } -} - -main().catch((err) => console.error('Error', err)); diff --git a/typescript/infra/scripts/gas-oracle/utils.ts b/typescript/infra/scripts/gas-oracle/utils.ts deleted file mode 100644 index 5711db26ba..0000000000 --- a/typescript/infra/scripts/gas-oracle/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BigNumber, ethers } from 'ethers'; - -import { MultiProvider } from '@hyperlane-xyz/sdk'; - -import { RemoteGasData } from '../../src/config'; -import { RemoteGasDataConfig } from '../../src/config/gas-oracle'; - -export function prettyRemoteGasDataConfig( - multiProvider: MultiProvider, - config: RemoteGasDataConfig, -) { - return `\tRemote: ${config.remoteDomain} (${multiProvider.getChainName( - config.remoteDomain, - )})\n${prettyRemoteGasData(config)}`; -} - -export function prettyRemoteGasData(data: RemoteGasData) { - return `\tToken exchange rate: ${prettyTokenExchangeRate( - data.tokenExchangeRate, - )}\n\tGas price: ${data.gasPrice.toString()} (${ethers.utils.formatUnits( - data.gasPrice, - 'gwei', - )} gwei)`; -} - -export function prettyTokenExchangeRate(tokenExchangeRate: BigNumber) { - return `${tokenExchangeRate.toString()} (${ethers.utils.formatUnits( - tokenExchangeRate, - 10, - )})`; -} - -export function eqRemoteGasData(a: RemoteGasData, b: RemoteGasData): boolean { - return ( - a.tokenExchangeRate.eq(b.tokenExchangeRate) && a.gasPrice.eq(b.gasPrice) - ); -} diff --git a/typescript/infra/scripts/get-key-addresses.ts b/typescript/infra/scripts/get-key-addresses.ts index 6864177d6a..ccbc221060 100644 --- a/typescript/infra/scripts/get-key-addresses.ts +++ b/typescript/infra/scripts/get-key-addresses.ts @@ -1,11 +1,7 @@ import { getAllCloudAgentKeys } from '../src/agents/key-utils'; -import { - getArgs, - getConfigsBasedOnArgs, - withContext, - withProtocol, -} from './utils'; +import { getArgs, withContext, withProtocol } from './agent-utils'; +import { getConfigsBasedOnArgs } from './core-utils'; async function main() { const argv = await withProtocol(withContext(getArgs())).argv; diff --git a/typescript/infra/scripts/helloworld/deploy-kathy.ts b/typescript/infra/scripts/helloworld/deploy-kathy.ts index 45720c1365..2cc7bf4435 100644 --- a/typescript/infra/scripts/helloworld/deploy-kathy.ts +++ b/typescript/infra/scripts/helloworld/deploy-kathy.ts @@ -1,6 +1,7 @@ import { runHelloworldKathyHelmCommand } from '../../src/helloworld/kathy'; import { HelmCommand } from '../../src/utils/helm'; -import { assertCorrectKubeContext, getConfigsBasedOnArgs } from '../utils'; +import { assertCorrectKubeContext } from '../agent-utils'; +import { getConfigsBasedOnArgs } from '../core-utils'; import { getHelloWorldConfig } from './utils'; diff --git a/typescript/infra/scripts/helloworld/kathy.ts b/typescript/infra/scripts/helloworld/kathy.ts index 2599b895f6..cae5e858a2 100644 --- a/typescript/infra/scripts/helloworld/kathy.ts +++ b/typescript/infra/scripts/helloworld/kathy.ts @@ -40,7 +40,8 @@ import { DeployEnvironment } from '../../src/config/environment'; import { Role } from '../../src/roles'; import { startMetricsServer } from '../../src/utils/metrics'; import { assertChain, diagonalize, sleep } from '../../src/utils/utils'; -import { getArgs, getEnvironmentConfig, withContext } from '../utils'; +import { getArgs, withContext } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; import { getHelloWorldMultiProtocolApp } from './utils'; @@ -246,7 +247,7 @@ async function main(): Promise { } chains.map((chain) => - updateWalletBalanceMetricFor(app, chain, coreConfig.owners[chain]), + updateWalletBalanceMetricFor(app, chain, coreConfig.owners[chain].owner), ); // Incremented each time an entire cycle has occurred @@ -365,14 +366,16 @@ async function main(): Promise { messagesSendCount.labels({ ...labels, status: 'failure' }).inc(); errorOccurred = true; } - updateWalletBalanceMetricFor(app, origin, coreConfig.owners[origin]).catch( - (e) => { - warn('Failed to update wallet balance for chain', { - chain: origin, - err: format(e), - }); - }, - ); + updateWalletBalanceMetricFor( + app, + origin, + coreConfig.owners[origin].owner, + ).catch((e) => { + warn('Failed to update wallet balance for chain', { + chain: origin, + err: format(e), + }); + }); // Break if we should stop sending messages if (await nextMessage()) { diff --git a/typescript/infra/scripts/helloworld/utils.ts b/typescript/infra/scripts/helloworld/utils.ts index cfddb9ee37..834d259d60 100644 --- a/typescript/infra/scripts/helloworld/utils.ts +++ b/typescript/infra/scripts/helloworld/utils.ts @@ -162,3 +162,18 @@ export function getHelloWorldConfig( } return config; } + +// for create-key, you don't want to fetch the multisig[chain].validators.threshold for yet to be created multisigs +export function getJustHelloWorldConfig( + helloWorldConfigs: Partial> | undefined, + context: Contexts, +): HelloWorldConfig { + if (!helloWorldConfigs) { + throw new Error(`Environment does not have a HelloWorld config`); + } + const config = helloWorldConfigs[context]; + if (!config) { + throw new Error(`Context ${context} does not have a HelloWorld config`); + } + return config; +} diff --git a/typescript/infra/scripts/list-validator-checkpoint-indices.ts b/typescript/infra/scripts/list-validator-checkpoint-indices.ts index e918cb6027..d423861b34 100644 --- a/typescript/infra/scripts/list-validator-checkpoint-indices.ts +++ b/typescript/infra/scripts/list-validator-checkpoint-indices.ts @@ -4,7 +4,8 @@ import { S3Validator } from '../src/agents/aws/validator'; import { deployEnvToSdkEnv } from '../src/config/environment'; import { concurrentMap } from '../src/utils/utils'; -import { getArgs, getEnvironmentConfig, getValidatorsByChain } from './utils'; +import { getArgs, getValidatorsByChain } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; async function main() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/middleware/circle-relayer.ts b/typescript/infra/scripts/middleware/circle-relayer.ts index 0d874357cb..de9288ee0a 100644 --- a/typescript/infra/scripts/middleware/circle-relayer.ts +++ b/typescript/infra/scripts/middleware/circle-relayer.ts @@ -9,11 +9,8 @@ import { import { objFilter } from '@hyperlane-xyz/utils'; import { readJSON, sleep } from '../../src/utils/utils'; -import { - getArgs, - getEnvironmentConfig, - getEnvironmentDirectory, -} from '../utils'; +import { getArgs, getEnvironmentDirectory } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; async function check() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/middleware/deploy-relayers.ts b/typescript/infra/scripts/middleware/deploy-relayers.ts index e9643e5068..58a95da84e 100644 --- a/typescript/infra/scripts/middleware/deploy-relayers.ts +++ b/typescript/infra/scripts/middleware/deploy-relayers.ts @@ -4,7 +4,8 @@ import { runLiquidityLayerRelayerHelmCommand, } from '../../src/middleware/liquidity-layer-relayer'; import { HelmCommand } from '../../src/utils/helm'; -import { assertCorrectKubeContext, getConfigsBasedOnArgs } from '../utils'; +import { assertCorrectKubeContext } from '../agent-utils'; +import { getConfigsBasedOnArgs } from '../core-utils'; async function main() { const { agentConfig, envConfig, context } = await getConfigsBasedOnArgs(); diff --git a/typescript/infra/scripts/middleware/portal-relayer.ts b/typescript/infra/scripts/middleware/portal-relayer.ts index bb2d7f9749..09164bb791 100644 --- a/typescript/infra/scripts/middleware/portal-relayer.ts +++ b/typescript/infra/scripts/middleware/portal-relayer.ts @@ -9,11 +9,8 @@ import { error, log } from '@hyperlane-xyz/utils'; import { bridgeAdapterConfigs } from '../../config/environments/testnet4/token-bridge'; import { readJSON, sleep } from '../../src/utils/utils'; -import { - getArgs, - getEnvironmentConfig, - getEnvironmentDirectory, -} from '../utils'; +import { getArgs, getEnvironmentDirectory } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; async function relayPortalTransfers() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/module-can-verify.ts b/typescript/infra/scripts/module-can-verify.ts index 7dfb3c70a1..4cffb36bf3 100644 --- a/typescript/infra/scripts/module-can-verify.ts +++ b/typescript/infra/scripts/module-can-verify.ts @@ -3,7 +3,8 @@ import { ProtocolType } from '@hyperlane-xyz/utils'; import { deployEnvToSdkEnv } from '../src/config/environment'; -import { getArgs, getEnvironmentConfig } from './utils'; +import { getArgs } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; async function main() { const args = await getArgs().argv; diff --git a/typescript/infra/scripts/print-chain-metadatas.ts b/typescript/infra/scripts/print-chain-metadatas.ts index 7b96c35807..954d676445 100644 --- a/typescript/infra/scripts/print-chain-metadatas.ts +++ b/typescript/infra/scripts/print-chain-metadatas.ts @@ -1,4 +1,5 @@ -import { getArgs, getEnvironmentConfig } from './utils'; +import { getArgs } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; // This script exists to print the chain metadata configs for a given environment // so they can easily be copied into the Sealevel tooling. :'( diff --git a/typescript/infra/scripts/print-gas-prices.ts b/typescript/infra/scripts/print-gas-prices.ts new file mode 100644 index 0000000000..3cf0d757fc --- /dev/null +++ b/typescript/infra/scripts/print-gas-prices.ts @@ -0,0 +1,39 @@ +import { ethers } from 'ethers'; + +import { MultiProtocolProvider, ProviderType } from '@hyperlane-xyz/sdk'; +import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; + +import { mainnetConfigs } from '../config/environments/mainnet3/chains'; + +async function main() { + const metadata = mainnetConfigs; + + const mpp = new MultiProtocolProvider(metadata); + + const prices = await promiseObjAll( + objMap(metadata, async (chain, _) => { + const provider = mpp.getProvider(chain); + switch (provider.type) { + case ProviderType.EthersV5: + const gasPrice = await provider.provider.getGasPrice(); + return ethers.utils.formatUnits(gasPrice, 'gwei'); + case ProviderType.CosmJsWasm: + // TODO: get default gas price + return '0.1'; + case ProviderType.SolanaWeb3: + return '0.001'; + default: + throw new Error(`Unsupported provider type: ${provider.type}`); + } + }), + ); + + console.log(JSON.stringify(prices, null, 2)); +} + +main() + .then() + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/typescript/infra/scripts/print-multisig-ism-config.ts b/typescript/infra/scripts/print-multisig-ism-config.ts index e05aeaf996..3b27ef8b91 100644 --- a/typescript/infra/scripts/print-multisig-ism-config.ts +++ b/typescript/infra/scripts/print-multisig-ism-config.ts @@ -2,7 +2,7 @@ import { AllChains, IsmType } from '@hyperlane-xyz/sdk'; import { multisigIsms } from '../config/multisigIsm'; -import { getArgs, withContext } from './utils'; +import { getArgs, withContext } from './agent-utils'; // This script exists to print the default multisig ISM validator sets for a given environment // so they can easily be copied into the Sealevel tooling. :'( diff --git a/typescript/infra/scripts/print-token-prices.ts b/typescript/infra/scripts/print-token-prices.ts new file mode 100644 index 0000000000..9b6ba56124 --- /dev/null +++ b/typescript/infra/scripts/print-token-prices.ts @@ -0,0 +1,33 @@ +import { objMap } from '@hyperlane-xyz/utils'; + +import { mainnetConfigs } from '../config/environments/mainnet3/chains'; + +const CURRENCY = 'usd'; + +async function main() { + const metadata = mainnetConfigs; + + const ids = objMap( + metadata, + (_, metadata) => metadata.gasCurrencyCoinGeckoId ?? metadata.name, + ); + + const resp = await fetch( + `https://api.coingecko.com/api/v3/simple/price?ids=${Object.entries( + ids, + ).join(',')}&vs_currencies=${CURRENCY}`, + ); + + const idPrices = await resp.json(); + + const prices = objMap(ids, (_, id) => idPrices[id][CURRENCY].toString()); + + console.log(JSON.stringify(prices, null, 2)); +} + +main() + .then() + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/typescript/infra/scripts/rotate-key.ts b/typescript/infra/scripts/rotate-key.ts index 34f942475e..506288dad4 100644 --- a/typescript/infra/scripts/rotate-key.ts +++ b/typescript/infra/scripts/rotate-key.ts @@ -3,7 +3,7 @@ import { getArgs, withContext, withKeyRoleAndChain, -} from './utils'; +} from './agent-utils'; async function rotateKey() { const argv = await withContext(withKeyRoleAndChain(getArgs())).argv; diff --git a/typescript/infra/scripts/safe-delegate.ts b/typescript/infra/scripts/safe-delegate.ts index 22a4d3e149..cc1f22b883 100644 --- a/typescript/infra/scripts/safe-delegate.ts +++ b/typescript/infra/scripts/safe-delegate.ts @@ -8,7 +8,8 @@ import { AllChains } from '@hyperlane-xyz/sdk'; import { getSafeDelegates, getSafeService } from '../src/utils/safe'; -import { getEnvironmentConfig, getArgs as getRootArgs } from './utils'; +import { getArgs as getRootArgs } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; function getArgs() { return getRootArgs() diff --git a/typescript/infra/scripts/update-key.ts b/typescript/infra/scripts/update-key.ts index e05013ce88..dab4b279c9 100644 --- a/typescript/infra/scripts/update-key.ts +++ b/typescript/infra/scripts/update-key.ts @@ -3,7 +3,7 @@ import { getArgs, withContext, withKeyRoleAndChain, -} from './utils'; +} from './agent-utils'; async function rotateKey() { const argv = await withKeyRoleAndChain(withContext(getArgs())).argv; diff --git a/typescript/infra/scripts/verify-validators.ts b/typescript/infra/scripts/verify-validators.ts index 2433a6b4f3..a9d288cc23 100644 --- a/typescript/infra/scripts/verify-validators.ts +++ b/typescript/infra/scripts/verify-validators.ts @@ -4,7 +4,8 @@ import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { S3Validator } from '../src/agents/aws/validator'; import { deployEnvToSdkEnv } from '../src/config/environment'; -import { getArgs, getEnvironmentConfig, getValidatorsByChain } from './utils'; +import { getArgs, getValidatorsByChain } from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; async function main() { const { environment } = await getArgs().argv; diff --git a/typescript/infra/scripts/verify.ts b/typescript/infra/scripts/verify.ts index a2367f087d..4b75e66500 100644 --- a/typescript/infra/scripts/verify.ts +++ b/typescript/infra/scripts/verify.ts @@ -1,72 +1,76 @@ import { ChainMap, - CompilerOptions, - ContractVerifier, + ExplorerLicenseType, + PostDeploymentContractVerifier, + VerificationInput, } from '@hyperlane-xyz/sdk'; -import { fetchGCPSecret } from '../src/utils/gcloud'; -import { execCmd, readFileAtPath, readJSONAtPath } from '../src/utils/utils'; +import { + extractBuildArtifact, + fetchExplorerApiKeys, +} from '../src/deployment/verify'; +import { readJSONAtPath } from '../src/utils/utils'; -import { assertEnvironment, getArgs, getEnvironmentConfig } from './utils'; +import { + assertEnvironment, + getArgs, + withBuildArtifactPath, + withNetwork, +} from './agent-utils'; +import { getEnvironmentConfig } from './core-utils'; -// Requires https://github.com/crytic/solc-select to be installed and -// present in your $PATH. The current solc compiler version should -// already be installed via `solc-select install $VERSION` async function main() { - const argv = await getArgs() - // This file can be generated by running `$ yarn workspace @hyperlane-xyz/core flatten`, - .string('source') - .describe('source', 'flattened solidity source file') - .demandOption('source') - .string('artifacts') - .describe('artifacts', 'verification artifacts JSON file') - .demandOption('artifacts') - .string('network') - .describe('network', 'optional target network').argv; + const { environment, buildArtifactPath, verificationArtifactPath, network } = + await withNetwork(withBuildArtifactPath(getArgs())) + .string('verificationArtifactPath') + .describe( + 'verificationArtifactPath', + 'path to hyperlane verification artifact', + ) + .alias('v', 'verificationArtifactPath') + .demandOption('verificationArtifactPath') + .demandOption('buildArtifactPath').argv; - const environment = assertEnvironment(argv.e!); + // set up multiprovider + assertEnvironment(environment); const config = getEnvironmentConfig(environment); const multiProvider = await config.getMultiProvider(); - const verification = readJSONAtPath(argv.artifacts!); - - const sourcePath = argv.source!; - const flattenedSource = readFileAtPath(sourcePath); - - // from solidity/core/hardhat.config.ts - const compilerOptions: CompilerOptions = { - codeformat: 'solidity-single-file', - compilerversion: 'v0.8.19+commit.7dd6d404', - optimizationUsed: '1', - runs: '999999', - }; - - const versionRegex = /v(\d.\d.\d+)\+commit.\w+/; - const matches = versionRegex.exec(compilerOptions.compilerversion); - if (!matches) { - throw new Error( - `Invalid compiler version ${compilerOptions.compilerversion}`, - ); - } + // grab verification artifacts + const verification: ChainMap = readJSONAtPath( + verificationArtifactPath, + ); - // ensures flattened source is compilable - await execCmd(`solc-select use ${matches[1]}`); - await execCmd(`solc ${sourcePath}`); + // fetch explorer API keys from GCP + const apiKeys = await fetchExplorerApiKeys(); - const apiKeys: ChainMap = (await fetchGCPSecret( - 'explorer-api-keys', - true, - )) as any; + // extract build artifact contents + const buildArtifact = extractBuildArtifact(buildArtifactPath); - const verifier = new ContractVerifier( + // instantiate verifier + const verifier = new PostDeploymentContractVerifier( verification, multiProvider, apiKeys, - flattenedSource, - compilerOptions, + buildArtifact, + ExplorerLicenseType.MIT, ); - return verifier.verify(argv.network ? [argv.network] : undefined); + // verify all the things + const failedResults = ( + await verifier.verify(network ? [network] : undefined) + ).filter((result) => result.status === 'rejected'); + + // only log the failed verifications to console + if (failedResults.length > 0) { + console.error( + 'Verification failed for the following contracts:', + failedResults.map((result) => result), + ); + process.exit(1); + } + + process.exit(0); } main().then(console.log).catch(console.error); diff --git a/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts b/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts index 534267004f..8c765ba63b 100644 --- a/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts +++ b/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts @@ -1,12 +1,24 @@ +import yargs from 'yargs'; + import { HelmCommand } from '../../src/utils/helm'; import { runWarpRouteHelmCommand } from './helm'; async function main() { + const { filePath } = await yargs(process.argv.slice(2)) + .alias('f', 'filePath') + .describe( + 'filePath', + 'indicate the filepath to the warp route yaml file relative to the monorepo root', + ) + .demandOption('filePath') + .string('filePath') + .parse(); + await runWarpRouteHelmCommand( HelmCommand.InstallOrUpgrade, 'mainnet3', - 'neutron', + filePath, ); } diff --git a/typescript/infra/scripts/warp-routes/helm.ts b/typescript/infra/scripts/warp-routes/helm.ts index 170800e003..bcf1a20c8d 100644 --- a/typescript/infra/scripts/warp-routes/helm.ts +++ b/typescript/infra/scripts/warp-routes/helm.ts @@ -1,37 +1,38 @@ import { DeployEnvironment } from '../../src/config'; import { HelmCommand, helmifyValues } from '../../src/utils/helm'; import { execCmd } from '../../src/utils/utils'; -import { assertCorrectKubeContext, getEnvironmentConfig } from '../utils'; +import { assertCorrectKubeContext } from '../agent-utils'; +import { getEnvironmentConfig } from '../core-utils'; export async function runWarpRouteHelmCommand( helmCommand: HelmCommand, runEnv: DeployEnvironment, - config: string, + configFilePath: string, ) { const envConfig = getEnvironmentConfig(runEnv); await assertCorrectKubeContext(envConfig); - const values = getWarpRoutesHelmValues(config); - + const values = getWarpRoutesHelmValues(configFilePath); + const releaseName = getHelmReleaseName(configFilePath); return execCmd( - `helm ${helmCommand} ${getHelmReleaseName( - config, - )} ./helm/warp-routes --namespace ${runEnv} ${values.join( + `helm ${helmCommand} ${releaseName} ./helm/warp-routes --namespace ${runEnv} ${values.join( ' ', - )} --set fullnameOverride="${getHelmReleaseName(config)}"`, + )} --set fullnameOverride="${releaseName}"`, ); } function getHelmReleaseName(route: string): string { - return `hyperlane-warp-route-${route}`; + const match = route.match(/\/([^/]+)-deployments\.yaml$/); + const name = match ? match[1] : route; + return `hyperlane-warp-route-${name.toLowerCase()}`; // helm requires lower case release names } -function getWarpRoutesHelmValues(config: string) { +function getWarpRoutesHelmValues(configFilePath: string) { const values = { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'ae8ce44-20231101-012032', + tag: '9b69b34-20240221-172841', }, - config: config, // nautilus or neutron + configFilePath: configFilePath, }; return helmifyValues(values); } diff --git a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts index b2631a87cd..64af067dee 100644 --- a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts @@ -7,10 +7,13 @@ import { ERC20__factory } from '@hyperlane-xyz/core'; import { ChainMap, ChainName, + CosmNativeTokenAdapter, CwNativeTokenAdapter, MultiProtocolProvider, SealevelHypCollateralAdapter, TokenType, + WarpRouteConfig, + WarpRouteConfigSchema, } from '@hyperlane-xyz/sdk'; import { ProtocolType, @@ -19,12 +22,8 @@ import { promiseObjAll, } from '@hyperlane-xyz/utils'; -import { - WarpTokenConfig, - nautilusList, - neutronList, -} from '../../src/config/grafana_token_config'; import { startMetricsServer } from '../../src/utils/metrics'; +import { readYaml } from '../../src/utils/utils'; const metricsRegister = new Registry(); const warpRouteTokenBalance = new Gauge({ @@ -40,20 +39,36 @@ const warpRouteTokenBalance = new Gauge({ ], }); +export function readWarpRouteConfig(filePath: string) { + const config = readYaml(filePath); + if (!config) throw new Error(`No warp config found at ${filePath}`); + const result = WarpRouteConfigSchema.safeParse(config); + if (!result.success) { + const errorMessages = result.error.issues.map( + (issue: any) => `${issue.path} => ${issue.message}`, + ); + throw new Error(`Invalid warp config:\n ${errorMessages.join('\n')}`); + } + return result.data; +} + async function main(): Promise { - const { checkFrequency, config } = await yargs(process.argv.slice(2)) + const { checkFrequency, filePath } = await yargs(process.argv.slice(2)) .describe('checkFrequency', 'frequency to check balances in ms') .demandOption('checkFrequency') - .alias('l', 'checkFrequency') + .alias('v', 'checkFrequency') // v as in Greek letter nu .number('checkFrequency') - .alias('c', 'config') - .describe('config', 'choose warp token config') - .demandOption('config') - .choices('config', ['neutron', 'nautilus']) + .alias('f', 'filePath') + .describe( + 'filePath', + 'indicate the filepatch to the warp route yaml file relative to typescript/infra', + ) + .demandOption('filePath') + .string('filePath') .parse(); - const tokenList: WarpTokenConfig = - config === 'neutron' ? neutronList : nautilusList; + const tokenConfig: WarpRouteConfig = + readWarpRouteConfig(filePath).data.config; startMetricsServer(metricsRegister); @@ -63,8 +78,8 @@ async function main(): Promise { setInterval(async () => { try { debug('Checking balances'); - const balances = await checkBalance(tokenList, multiProtocolProvider); - updateTokenBalanceMetrics(tokenList, balances); + const balances = await checkBalance(tokenConfig, multiProtocolProvider); + updateTokenBalanceMetrics(tokenConfig, balances); } catch (e) { console.error('Error checking balances', e); } @@ -74,20 +89,18 @@ async function main(): Promise { // TODO: see issue https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/2708 async function checkBalance( - tokenConfig: WarpTokenConfig, + tokenConfig: WarpRouteConfig, multiProtocolProvider: MultiProtocolProvider, ): Promise> { - const output: ChainMap> = objMap( + const output = objMap( tokenConfig, - async (chain: ChainName, token: WarpTokenConfig[ChainName]) => { + async (chain: ChainName, token: WarpRouteConfig[ChainName]) => { switch (token.type) { case TokenType.native: { switch (token.protocolType) { case ProtocolType.Ethereum: { const provider = multiProtocolProvider.getEthersV5Provider(chain); - const nativeBalance = await provider.getBalance( - token.hypNativeAddress, - ); + const nativeBalance = await provider.getBalance(token.hypAddress); return parseFloat( ethers.utils.formatUnits(nativeBalance, token.decimals), ); @@ -95,9 +108,20 @@ async function checkBalance( case ProtocolType.Sealevel: // TODO - solana native return 0; - case ProtocolType.Cosmos: - // TODO - cosmos native - return 0; + case ProtocolType.Cosmos: { + if (!token.ibcDenom) + throw new Error('IBC denom missing for native token'); + const adapter = new CosmNativeTokenAdapter( + chain, + multiProtocolProvider, + {}, + { ibcDenom: token.ibcDenom }, + ); + const tokenBalance = await adapter.getBalance(token.hypAddress); + return parseFloat( + ethers.utils.formatUnits(tokenBalance, token.decimals), + ); + } } break; } @@ -105,12 +129,14 @@ async function checkBalance( switch (token.protocolType) { case ProtocolType.Ethereum: { const provider = multiProtocolProvider.getEthersV5Provider(chain); + if (!token.tokenAddress) + throw new Error('Token address missing for collateral token'); const tokenContract = ERC20__factory.connect( - token.address, + token.tokenAddress, provider, ); const collateralBalance = await tokenContract.balanceOf( - token.hypCollateralAddress, + token.hypAddress, ); return parseFloat( @@ -118,35 +144,39 @@ async function checkBalance( ); } case ProtocolType.Sealevel: { + if (!token.tokenAddress) + throw new Error('Token address missing for synthetic token'); const adapter = new SealevelHypCollateralAdapter( chain, multiProtocolProvider, { - token: token.address, - warpRouter: token.hypCollateralAddress, + token: token.tokenAddress, + warpRouter: token.hypAddress, // Mailbox only required for transfers, using system as placeholder mailbox: SystemProgram.programId.toBase58(), }, - token.isSpl2022, + token?.isSpl2022 ?? false, ); const collateralBalance = ethers.BigNumber.from( - await adapter.getBalance(token.hypCollateralAddress), + await adapter.getBalance(token.hypAddress), ); return parseFloat( ethers.utils.formatUnits(collateralBalance, token.decimals), ); } case ProtocolType.Cosmos: { + if (!token.tokenAddress) + throw new Error('Token address missing for cosmos token'); const adapter = new CwNativeTokenAdapter( chain, multiProtocolProvider, { - token: token.address, + token: token.hypAddress, }, - token.address, + token.tokenAddress, ); const collateralBalance = ethers.BigNumber.from( - await adapter.getBalance(token.hypCollateralAddress), + await adapter.getBalance(token.hypAddress), ); return parseFloat( ethers.utils.formatUnits(collateralBalance, token.decimals), @@ -160,7 +190,7 @@ async function checkBalance( case ProtocolType.Ethereum: { const provider = multiProtocolProvider.getEthersV5Provider(chain); const tokenContract = ERC20__factory.connect( - token.hypSyntheticAddress, + token.hypAddress, provider, ); const syntheticBalance = await tokenContract.totalSupply(); @@ -178,36 +208,24 @@ async function checkBalance( break; } } + return 0; }, ); return await promiseObjAll(output); } -function updateTokenBalanceMetrics( - tokenConfig: WarpTokenConfig, +export function updateTokenBalanceMetrics( + tokenConfig: WarpRouteConfig, balances: ChainMap, ) { - objMap(tokenConfig, (chain: ChainName, token: WarpTokenConfig[ChainName]) => { - const tokenAddress = - token.type === TokenType.native - ? ethers.constants.AddressZero - : token.type === TokenType.collateral - ? token.address - : token.hypSyntheticAddress; - const walletAddress = - token.type === TokenType.native - ? token.hypNativeAddress - : token.type === TokenType.collateral - ? token.hypCollateralAddress - : token.hypSyntheticAddress; - + objMap(tokenConfig, (chain: ChainName, token: WarpRouteConfig[ChainName]) => { warpRouteTokenBalance .labels({ chain_name: chain, - token_address: tokenAddress, + token_address: token.tokenAddress ?? ethers.constants.AddressZero, token_name: token.name, - wallet_address: walletAddress, + wallet_address: token.hypAddress, token_type: token.type, }) .set(balances[chain]); diff --git a/typescript/infra/src/agents/agent.ts b/typescript/infra/src/agents/agent.ts index 206ede3452..e6afe9f02e 100644 --- a/typescript/infra/src/agents/agent.ts +++ b/typescript/infra/src/agents/agent.ts @@ -48,7 +48,7 @@ export function userIdentifier( return identifier(false, environment, context, role, chainName, index); } -// Doesn't perform any checks on whether the parsed values are valid, +// Does not perform any checks on whether the parsed values are valid, // this is left to the caller. export function parseKeyIdentifier(identifier: string): { environment: string; diff --git a/typescript/infra/src/agents/aws/key.ts b/typescript/infra/src/agents/aws/key.ts index fb42d10aab..a6c47369e5 100644 --- a/typescript/infra/src/agents/aws/key.ts +++ b/typescript/infra/src/agents/aws/key.ts @@ -16,6 +16,7 @@ import { UpdateAliasCommand, } from '@aws-sdk/client-kms'; import { KmsEthersSigner } from 'aws-kms-ethers-signer'; +import { Debugger, debug } from 'debug'; import { ethers } from 'ethers'; import { AgentSignerKeyType, ChainName } from '@hyperlane-xyz/sdk'; @@ -41,6 +42,7 @@ export class AgentAwsKey extends CloudAgentKey { private client: KMSClient | undefined; private region: string; public remoteKey: RemoteKey = { fetched: false }; + protected logger: Debugger; constructor( agentConfig: AgentContextConfig, @@ -53,16 +55,22 @@ export class AgentAwsKey extends CloudAgentKey { throw new Error('Not configured as AWS'); } this.region = agentConfig.aws.region; + this.logger = debug(`infra:agents:key:aws:${this.identifier}`); } get privateKey(): string { + this.logger( + 'Attempting to access private key, which is unavailable for AWS keys', + ); throw new Error('Private key unavailable for AWS keys'); } async getClient(): Promise { if (this.client) { + this.logger('Returning existing KMSClient instance'); return this.client; } + this.logger('Creating new KMSClient instance'); this.client = new KMSClient({ region: this.region, }); @@ -94,6 +102,7 @@ export class AgentAwsKey extends CloudAgentKey { } async fetch() { + this.logger('Fetching key'); const address = await this.fetchAddressFromAws(); this.remoteKey = { fetched: true, @@ -102,24 +111,28 @@ export class AgentAwsKey extends CloudAgentKey { } async createIfNotExists() { + this.logger('Checking if key exists and creating if not'); const keyId = await this.getId(); // If it doesn't exist, create it if (!keyId) { - // TODO should this be awaited? create is async - // eslint-disable-next-line @typescript-eslint/no-floating-promises - this.create(); + this.logger('Key does not exist, creating new key'); + await this.create(); // It can take a moment for the change to propagate await sleep(1000); + } else { + this.logger('Key already exists'); } await this.fetch(); } async delete() { + this.logger('Delete operation called, but not implemented'); throw Error('Not implemented yet'); } // Allows the `userArn` to use the key async putKeyPolicy(userArn: string) { + this.logger(`Putting key policy for user ARN: ${userArn}`); const client = await this.getClient(); const policy = { Version: '2012-10-17', @@ -151,22 +164,29 @@ export class AgentAwsKey extends CloudAgentKey { PolicyName: 'default', // This is the only accepted name }); await client.send(cmd); + this.logger('Key policy put successfully'); } // Gets the Key's ID if it exists, undefined otherwise async getId() { try { + this.logger('Attempting to describe key to get ID'); const keyDescription = await this.describeKey(); - return keyDescription.KeyMetadata?.KeyId; + const keyId = keyDescription.KeyMetadata?.KeyId; + this.logger(`Key ID retrieved: ${keyId}`); + return keyId; } catch (err: any) { if (err.name === 'NotFoundException') { + this.logger('Key not found'); return undefined; } + this.logger(`Error retrieving key ID: ${err}`); throw err; } } create() { + this.logger('Creating new key'); return this._create(false); } @@ -175,6 +195,7 @@ export class AgentAwsKey extends CloudAgentKey { * @returns The address of the new key */ update() { + this.logger('Updating key (creating new key for rotation)'); return this._create(true); } @@ -182,6 +203,7 @@ export class AgentAwsKey extends CloudAgentKey { * Requires update to have been called on this key prior */ async rotate() { + this.logger('Rotating keys'); const canonicalAlias = this.identifier; const newAlias = canonicalAlias + '-new'; const oldAlias = canonicalAlias + '-old'; @@ -226,15 +248,19 @@ export class AgentAwsKey extends CloudAgentKey { // Address should have changed now await this.fetch(); + this.logger('Keys rotated successfully'); } async getSigner( provider?: ethers.providers.Provider, ): Promise { + this.logger('Getting signer'); const keyId = await this.getId(); if (!keyId) { + this.logger('Key ID not defined, cannot get signer'); throw Error('Key ID not defined'); } + this.logger(`Creating KmsEthersSigner with key ID: ${keyId}`); // @ts-ignore We're using a newer version of Provider than // KmsEthersSigner. The return type for getFeeData for this newer // type is a superset of the return type for getFeeData for the older type, @@ -252,12 +278,15 @@ export class AgentAwsKey extends CloudAgentKey { private requireFetched() { if (!this.remoteKey.fetched) { + this.logger('Key has not been fetched yet'); throw new Error('Key not fetched'); } + this.logger('Key has been fetched'); } // Creates a new key and returns its address private async _create(rotate: boolean) { + this.logger(`Creating key with rotation: ${rotate}`); const client = await this.getClient(); const alias = this.identifier; if (!rotate) { @@ -269,6 +298,7 @@ export class AgentAwsKey extends CloudAgentKey { (_) => _.AliasName === alias, ); if (match) { + this.logger(`Alias ${alias} already exists`); throw new Error( `Attempted to create new key but alias ${alias} already exists`, ); @@ -288,6 +318,7 @@ export class AgentAwsKey extends CloudAgentKey { const createResponse = await client.send(command); if (!createResponse.KeyMetadata) { + this.logger('KeyMetadata was not returned when creating the key'); throw new Error('KeyMetadata was not returned when creating the key'); } const keyId = createResponse.KeyMetadata?.KeyId; @@ -298,10 +329,12 @@ export class AgentAwsKey extends CloudAgentKey { ); const address = this.fetchAddressFromAws(keyId); + this.logger(`New key created with ID: ${keyId}`); return address; } private async fetchAddressFromAws(keyId?: string) { + this.logger(`Fetching address from AWS for key ID: ${keyId}`); const client = await this.getClient(); if (!keyId) { @@ -312,10 +345,15 @@ export class AgentAwsKey extends CloudAgentKey { new GetPublicKeyCommand({ KeyId: keyId }), ); - return getEthereumAddress(Buffer.from(publicKeyResponse.PublicKey!)); + const address = getEthereumAddress( + Buffer.from(publicKeyResponse.PublicKey!), + ); + this.logger(`Address fetched: ${address}`); + return address; } private async describeKey(): Promise { + this.logger('Describing key'); const client = await this.getClient(); return client.send( new DescribeKeyCommand({ @@ -325,6 +363,7 @@ export class AgentAwsKey extends CloudAgentKey { } private async getAliases(): Promise { + this.logger('Getting aliases'); const client = await this.getClient(); let aliases: AliasListEntry[] = []; let marker: string | undefined = undefined; @@ -350,6 +389,7 @@ export class AgentAwsKey extends CloudAgentKey { break; } } + this.logger(`Aliases retrieved: ${aliases.length}`); return aliases; } } diff --git a/typescript/infra/src/agents/gcp.ts b/typescript/infra/src/agents/gcp.ts index 4ba314256e..e0fc75874a 100644 --- a/typescript/infra/src/agents/gcp.ts +++ b/typescript/infra/src/agents/gcp.ts @@ -1,9 +1,6 @@ -import { - encodeSecp256k1Pubkey, - pubkeyToAddress, - rawSecp256k1PubkeyToRawAddress, -} from '@cosmjs/amino'; +import { encodeSecp256k1Pubkey, pubkeyToAddress } from '@cosmjs/amino'; import { Keypair } from '@solana/web3.js'; +import { Debugger, debug } from 'debug'; import { Wallet, ethers } from 'ethers'; import { ChainName } from '@hyperlane-xyz/sdk'; @@ -42,6 +39,8 @@ interface FetchedKey { type RemoteKey = UnfetchedKey | FetchedKey; export class AgentGCPKey extends CloudAgentKey { + protected logger: Debugger; + constructor( environment: DeployEnvironment, context: Contexts, @@ -51,18 +50,23 @@ export class AgentGCPKey extends CloudAgentKey { private remoteKey: RemoteKey = { fetched: false }, ) { super(environment, context, role, chainName, index); + this.logger = debug(`infra:agents:key:gcp:${this.identifier}`); } async createIfNotExists() { + this.logger('Checking if key exists and creating if not'); try { await this.fetch(); + this.logger('Key already exists'); } catch (err) { + this.logger('Key does not exist, creating new key'); await this.create(); } } serializeAsAddress() { this.requireFetched(); + this.logger('Serializing key as address'); return { identifier: this.identifier, // @ts-ignore @@ -98,6 +102,7 @@ export class AgentGCPKey extends CloudAgentKey { addressForProtocol(protocol: ProtocolType): string | undefined { this.requireFetched(); + this.logger(`Getting address for protocol: ${protocol}`); switch (protocol) { case ProtocolType.Ethereum: @@ -106,7 +111,7 @@ export class AgentGCPKey extends CloudAgentKey { return Keypair.fromSeed( Buffer.from(strip0x(this.privateKey), 'hex'), ).publicKey.toBase58(); - case ProtocolType.Cosmos: + case ProtocolType.Cosmos: { const compressedPubkey = ethers.utils.computePublicKey( this.privateKey, true, @@ -117,12 +122,15 @@ export class AgentGCPKey extends CloudAgentKey { // TODO support other prefixes? // https://cosmosdrops.io/en/tools/bech32-converter is useful for converting to other prefixes. return pubkeyToAddress(encodedPubkey, 'neutron'); + } default: + this.logger(`Unsupported protocol: ${protocol}`); return undefined; } } async fetch() { + this.logger('Fetching key'); const secret: SecretManagerPersistedKeys = (await fetchGCPSecret( this.identifier, )) as any; @@ -131,25 +139,34 @@ export class AgentGCPKey extends CloudAgentKey { privateKey: secret.privateKey, address: secret.address, }; + this.logger(`Key fetched successfully: ${secret.address}`); } async create() { + this.logger('Creating new key'); this.remoteKey = await this._create(false); + this.logger('Key created successfully'); } async update() { + this.logger('Updating key'); this.remoteKey = await this._create(true); + this.logger('Key updated successfully'); return this.address; } async delete() { + this.logger('Deleting key'); await execCmd(`gcloud secrets delete ${this.identifier} --quiet`); + this.logger('Key deleted successfully'); } async getSigner( provider?: ethers.providers.Provider, ): Promise { + this.logger('Getting signer'); if (!this.remoteKey.fetched) { + this.logger('Key not fetched, fetching now'); await this.fetch(); } return new Wallet(this.privateKey, provider); @@ -157,12 +174,14 @@ export class AgentGCPKey extends CloudAgentKey { private requireFetched() { if (!this.remoteKey.fetched) { + this.logger('Key not fetched, throwing error'); throw new Error("Can't persist without address"); } } // eslint-disable-next-line @typescript-eslint/no-unused-vars private async _create(rotate: boolean) { + this.logger(`Creating key with rotation: ${rotate}`); const wallet = Wallet.createRandom(); const address = await wallet.getAddress(); const identifier = this.identifier; @@ -187,6 +206,7 @@ export class AgentGCPKey extends CloudAgentKey { }), }, ); + this.logger('Key creation data persisted to GCP'); return { fetched: true, diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 346d5a901b..2acbbe4a5b 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -1,7 +1,6 @@ import fs from 'fs'; import { ChainName, RpcConsensusType, chainMetadata } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; import { @@ -113,7 +112,7 @@ export abstract class AgentHelmManager { runEnv: this.environment, context: this.context, aws: !!this.config.aws, - chains: this.config.environmentChainNames.map((chain) => { + chains: this.config.contextChainNames[this.role].map((chain) => { const metadata = chainMetadata[chain]; const reorgPeriod = metadata.blocks?.reorgPeriod; if (reorgPeriod === undefined) { @@ -121,7 +120,6 @@ export abstract class AgentHelmManager { } return { name: chain, - disabled: !this.config.contextChainNames[this.role].includes(chain), rpcConsensusType: this.rpcConsensusType(chain), protocol: metadata.protocol, blocks: { reorgPeriod }, diff --git a/typescript/infra/src/agents/key-utils.ts b/typescript/infra/src/agents/key-utils.ts index 11e669d238..c008b9afbc 100644 --- a/typescript/infra/src/agents/key-utils.ts +++ b/typescript/infra/src/agents/key-utils.ts @@ -1,27 +1,55 @@ +import debug from 'debug'; +import fs from 'fs'; +import path from 'path'; + import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; +import { Address, objMap } from '@hyperlane-xyz/utils'; +import localAWMultisigAddresses from '../../config/aw-multisig.json'; +// AW - Abacus Works import { Contexts } from '../../config/contexts'; -import { getHelloWorldConfig } from '../../scripts/helloworld/utils'; -import { getEnvironmentConfig } from '../../scripts/utils'; +import { helloworld } from '../../config/environments/helloworld'; +import localKathyAddresses from '../../config/kathy.json'; +import localRelayerAddresses from '../../config/relayer.json'; +import { getJustHelloWorldConfig } from '../../scripts/helloworld/utils'; import { AgentContextConfig, DeployEnvironment, RootAgentConfig, } from '../config'; import { Role } from '../roles'; -import { fetchGCPSecret, setGCPSecret } from '../utils/gcloud'; -import { execCmd, isEthereumProtocolChain } from '../utils/utils'; +import { + execCmd, + isEthereumProtocolChain, + readJSON, + writeJSON, + writeJsonAtPath, +} from '../utils/utils'; import { AgentAwsKey } from './aws/key'; import { AgentGCPKey } from './gcp'; import { CloudAgentKey } from './keys'; +export type LocalRoleAddresses = Record< + DeployEnvironment, + Record +>; +export const relayerAddresses: LocalRoleAddresses = + localRelayerAddresses as LocalRoleAddresses; +export const kathyAddresses: LocalRoleAddresses = + localKathyAddresses as LocalRoleAddresses; +export const awMultisigAddresses: ChainMap<{ validators: Address[] }> = + localAWMultisigAddresses as ChainMap<{ validators: Address[] }>; + +const debugLog = debug('infra:agents:key:utils'); + export interface KeyAsAddress { identifier: string; address: string; } +const CONFIG_DIRECTORY_PATH = path.join(__dirname, '../../config'); + // ================== // Functions for getting keys // ================== @@ -65,9 +93,8 @@ function getRoleKeyMapPerChain( const validators = agentConfig.validators; for (const chainName of agentConfig.contextChainNames.validator) { let chainValidatorKeys = {}; - const validatorCount = - validators?.chains[chainName].validators.length ?? 0; + validators?.chains[chainName]?.validators.length ?? 1; for (let index = 0; index < validatorCount; index++) { const { validator, chainSigner } = getValidatorKeysForChain( agentConfig, @@ -100,9 +127,8 @@ function getRoleKeyMapPerChain( }; const setKathyKeys = () => { - const envConfig = getEnvironmentConfig(agentConfig.runEnv); - const helloWorldConfig = getHelloWorldConfig( - envConfig, + const helloWorldConfig = getJustHelloWorldConfig( + helloworld[agentConfig.runEnv as 'mainnet3' | 'testnet4'], // test doesn't have hello world configs agentConfig.context, ); // Kathy is only needed on chains where the hello world contracts are deployed. @@ -156,6 +182,7 @@ function getRoleKeyMapPerChain( export function getAllCloudAgentKeys( agentConfig: RootAgentConfig, ): Array { + debugLog('Retrieving all cloud agent keys'); const keysPerChain = getRoleKeyMapPerChain(agentConfig); const keysByIdentifier = Object.keys(keysPerChain).reduce( @@ -190,21 +217,22 @@ export function getCloudAgentKey( chainName?: ChainName, index?: number, ): CloudAgentKey { + debugLog(`Retrieving cloud agent key for ${role} on ${chainName}`); switch (role) { case Role.Validator: if (chainName === undefined || index === undefined) { - throw Error(`Must provide chainName and index for validator key`); + throw Error('Must provide chainName and index for validator key'); } // For now just get the validator key, and not the chain signer. return getValidatorKeysForChain(agentConfig, chainName, index).validator; case Role.Relayer: if (chainName === undefined) { - throw Error(`Must provide chainName for relayer key`); + throw Error('Must provide chainName for relayer key'); } return getRelayerKeyForChain(agentConfig, chainName); case Role.Kathy: if (chainName === undefined) { - throw Error(`Must provide chainName for kathy key`); + throw Error('Must provide chainName for kathy key'); } return getKathyKeyForChain(agentConfig, chainName); case Role.Deployer: @@ -223,6 +251,7 @@ export function getRelayerKeyForChain( agentConfig: AgentContextConfig, chainName: ChainName, ): CloudAgentKey { + debugLog(`Retrieving relayer key for ${chainName}`); // If AWS is enabled and the chain is an Ethereum-based chain, we want to use // an AWS key. if (agentConfig.aws && isEthereumProtocolChain(chainName)) { @@ -240,6 +269,7 @@ export function getKathyKeyForChain( agentConfig: AgentContextConfig, chainName: ChainName, ): CloudAgentKey { + debugLog(`Retrieving kathy key for ${chainName}`); // If AWS is enabled and the chain is an Ethereum-based chain, we want to use // an AWS key. if (agentConfig.aws && isEthereumProtocolChain(chainName)) { @@ -252,6 +282,7 @@ export function getKathyKeyForChain( // Returns the deployer key. This is always a GCP key, not chain specific, // and in the Hyperlane context. export function getDeployerKey(agentConfig: AgentContextConfig): CloudAgentKey { + debugLog('Retrieving deployer key'); return new AgentGCPKey(agentConfig.runEnv, Contexts.Hyperlane, Role.Deployer); } @@ -267,6 +298,7 @@ export function getValidatorKeysForChain( validator: CloudAgentKey; chainSigner: CloudAgentKey; } { + debugLog(`Retrieving validator keys for ${chainName}`); const validator = agentConfig.aws ? new AgentAwsKey(agentConfig, Role.Validator, chainName, index) : new AgentGCPKey( @@ -279,15 +311,19 @@ export function getValidatorKeysForChain( // If the chain is Ethereum-based, we can just use the validator key (even if it's AWS-based) // as the chain signer. Otherwise, we need to use a GCP key. - const chainSigner = isEthereumProtocolChain(chainName) - ? validator - : new AgentGCPKey( - agentConfig.runEnv, - agentConfig.context, - Role.Validator, - chainName, - index, - ); + let chainSigner; + if (isEthereumProtocolChain(chainName)) { + chainSigner = validator; + } else { + debugLog(`Retrieving GCP key for ${chainName}, as it is not EVM`); + chainSigner = new AgentGCPKey( + agentConfig.runEnv, + agentConfig.context, + Role.Validator, + chainName, + index, + ); + } return { validator, @@ -302,6 +338,7 @@ export function getValidatorKeysForChain( export async function createAgentKeysIfNotExists( agentConfig: AgentContextConfig, ) { + debugLog('Creating agent keys if none exist'); const keys = getAllCloudAgentKeys(agentConfig); await Promise.all( @@ -310,14 +347,12 @@ export async function createAgentKeysIfNotExists( }), ); - await persistAddresses( - agentConfig.runEnv, - agentConfig.context, - keys.map((key) => key.serializeAsAddress()), - ); + await persistAddressesLocally(agentConfig, keys); + return; } export async function deleteAgentKeys(agentConfig: AgentContextConfig) { + debugLog('Deleting agent keys'); const keys = getAllCloudAgentKeys(agentConfig); await Promise.all(keys.map((key) => key.delete())); await execCmd( @@ -333,48 +368,118 @@ export async function rotateKey( role: Role, chainName: ChainName, ) { + debugLog(`Rotating key for ${role} on ${chainName}`); const key = getCloudAgentKey(agentConfig, role, chainName); await key.update(); - const keyIdentifier = key.identifier; - const addresses = await fetchGCPKeyAddresses( - agentConfig.runEnv, - agentConfig.context, + await persistAddressesLocally(agentConfig, [key]); +} + +async function persistAddressesLocally( + agentConfig: AgentContextConfig, + keys: CloudAgentKey[], +) { + debugLog( + `Persisting addresses to GCP for ${agentConfig.context} context in ${agentConfig.runEnv} environment`, ); - const filteredAddresses = addresses.filter((_) => { - return _.identifier !== keyIdentifier; - }); + // recent keys fetched from aws saved to local artifacts + const multisigValidatorKeys: ChainMap<{ validators: Address[] }> = {}; + let relayer, kathy; + for (const key of keys) { + // Some types of keys come in an AWS and a GCP variant. We prefer + // to persist the AWS version of the key if AWS is enabled. + // Note this means we prefer EVM addresses here, as even if AWS + // is enabled, we use the GCP address for non-EVM chains because + // only the EVM has the tooling & cryptographic compatibility with + // our AWS KMS keys. + if (agentConfig.aws && !(key instanceof AgentAwsKey)) { + continue; + } + + if (key.role === Role.Relayer) { + if (relayer) + throw new Error('More than one Relayer found in gcpCloudAgentKeys'); + relayer = key.address; + } + if (key.role === Role.Kathy) { + if (kathy) + throw new Error('More than one Kathy found in gcpCloudAgentKeys'); + kathy = key.address; + } - filteredAddresses.push(key.serializeAsAddress()); - await persistAddresses( + if (key.chainName) { + multisigValidatorKeys[key.chainName] ||= { + validators: [], + }; + + // The validator role always has a chainName. + if (key.role === Role.Validator) { + multisigValidatorKeys[key.chainName].validators.push(key.address); + } + } + } + if (!relayer) throw new Error('No Relayer found in awsCloudAgentKeys'); + if (agentConfig.context === Contexts.Hyperlane) { + if (!kathy) throw new Error('No Kathy found in awsCloudAgentKeys'); + await persistRoleAddressesToLocalArtifacts( + Role.Kathy, + agentConfig.runEnv, + agentConfig.context, + kathy, + kathyAddresses, + ); + } + await persistRoleAddressesToLocalArtifacts( + Role.Relayer, agentConfig.runEnv, agentConfig.context, - filteredAddresses, + relayer, + relayerAddresses, ); + + await persistValidatorAddressesToLocalArtifacts(multisigValidatorKeys); } -async function persistAddresses( +// non-validator roles +export async function persistRoleAddressesToLocalArtifacts( + role: Role, environment: DeployEnvironment, context: Contexts, - keys: KeyAsAddress[], + updated: Address, + addresses: Record>, ) { - await setGCPSecret( - addressesIdentifier(environment, context), - JSON.stringify(keys), - { - environment, - context, - }, - ); + addresses[environment][context] = updated; + + // Resolve the relative path + const filePath = path.resolve(__dirname, `../../config/${role}.json`); + + writeJsonAtPath(filePath, addresses); } -async function fetchGCPKeyAddresses( - environment: DeployEnvironment, - context: Contexts, +// maintaining the multisigIsm schema sans threshold +export async function persistValidatorAddressesToLocalArtifacts( + fetchedValidatorAddresses: ChainMap<{ validators: Address[] }>, ) { - const addresses = await fetchGCPSecret( - addressesIdentifier(environment, context), - ); - return addresses as KeyAsAddress[]; + for (const chain of Object.keys(fetchedValidatorAddresses)) { + awMultisigAddresses[chain] = { + validators: fetchedValidatorAddresses[chain].validators, // fresh from aws + }; + } + // Write the updated object back to the file + writeJSON(CONFIG_DIRECTORY_PATH, 'aw-multisig.json', awMultisigAddresses); +} + +export function fetchLocalKeyAddresses(role: Role): LocalRoleAddresses { + try { + const addresses: LocalRoleAddresses = readJSON( + CONFIG_DIRECTORY_PATH, + `${role}.json`, + ); + + debugLog(`Fetching addresses from GCP for ${role} role ...`); + return addresses; + } catch (e) { + throw new Error(`Error fetching addresses locally for ${role} role: ${e}`); + } } function addressesIdentifier( diff --git a/typescript/infra/src/agents/keys.ts b/typescript/infra/src/agents/keys.ts index 98e4df12e9..a6d817d891 100644 --- a/typescript/infra/src/agents/keys.ts +++ b/typescript/infra/src/agents/keys.ts @@ -73,6 +73,18 @@ export abstract class CloudAgentKey extends BaseCloudAgentKey { } } +export class LocalAgentKey extends BaseAgentKey { + constructor( + public readonly environment: DeployEnvironment, + public readonly context: Contexts, + public readonly role: Role, + public readonly address: string, + public readonly chainName?: ChainName, + ) { + super(environment, role, chainName); + } +} + // A read-only representation of a key managed internally. export class ReadOnlyCloudAgentKey extends BaseCloudAgentKey { constructor( diff --git a/typescript/infra/src/config/agent/agent.ts b/typescript/infra/src/config/agent/agent.ts index d53e68e056..85b379e5e3 100644 --- a/typescript/infra/src/config/agent/agent.ts +++ b/typescript/infra/src/config/agent/agent.ts @@ -2,14 +2,15 @@ import { AgentChainMetadata, AgentSignerAwsKey, AgentSignerKeyType, + ChainMap, ChainName, RpcConsensusType, chainMetadata, } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; +import { ProtocolType, objMap } from '@hyperlane-xyz/utils'; import { Contexts } from '../../../config/contexts'; -import { AgentChainNames, Role } from '../../roles'; +import { AgentChainNames, AgentRole, Role } from '../../roles'; import { DeployEnvironment } from '../environment'; import { HelmImageValues } from '../infrastructure'; @@ -56,7 +57,6 @@ interface HelmHyperlaneValues { export interface HelmAgentChainOverride extends DeepPartial { name: AgentChainMetadata['name']; - disabled?: boolean; } export interface RootAgentConfig extends AgentContextConfig { @@ -207,3 +207,41 @@ export function defaultChainSignerKeyConfig(chainName: ChainName): KeyConfig { return { type: AgentSignerKeyType.Hex }; } } + +export type AgentChainConfig = Record>; + +/// Converts an AgentChainConfig to an AgentChainNames object. +export function getAgentChainNamesFromConfig( + config: AgentChainConfig, + supportedChainNames: ChainName[], +): AgentChainNames { + ensureAgentChainConfigIncludesAllChainNames(config, supportedChainNames); + + return objMap(config, (role, roleConfig) => + Object.entries(roleConfig) + .filter(([_chain, enabled]) => enabled) + .map(([chain]) => chain), + ); +} + +// Throws if any of the roles in the config do not have all the expected chain names. +export function ensureAgentChainConfigIncludesAllChainNames( + config: AgentChainConfig, + expectedChainNames: ChainName[], +) { + for (const [role, roleConfig] of Object.entries(config)) { + const chainNames = Object.keys(roleConfig); + const missingChainNames = expectedChainNames.filter( + (chainName) => !chainNames.includes(chainName), + ); + const unknownChainNames = chainNames.filter( + (chainName) => !expectedChainNames.includes(chainName), + ); + + if (missingChainNames.length > 0 || unknownChainNames.length > 0) { + throw new Error( + `${role} agent chain config incorrect. Missing chain names: ${missingChainNames}, unknown chain names: ${unknownChainNames}`, + ); + } + } +} diff --git a/typescript/infra/src/config/agent/relayer.ts b/typescript/infra/src/config/agent/relayer.ts index b7821c47c7..b02ae1b8a9 100644 --- a/typescript/infra/src/config/agent/relayer.ts +++ b/typescript/infra/src/config/agent/relayer.ts @@ -2,7 +2,6 @@ import { BigNumberish } from 'ethers'; import { AgentConfig, - AgentSignerKeyType, ChainMap, GasPaymentEnforcement, MatchingList, @@ -10,7 +9,7 @@ import { chainMetadata, getDomainId, } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; +import { ProtocolType, addressToBytes32 } from '@hyperlane-xyz/utils'; import { AgentAwsUser } from '../../agents/aws'; import { Role } from '../../roles'; @@ -25,6 +24,11 @@ import { export { GasPaymentEnforcement as GasPaymentEnforcementConfig } from '@hyperlane-xyz/sdk'; +export interface MetricAppContext { + name: string; + matchingList: MatchingList; +} + // Incomplete basic relayer agent config export interface BaseRelayerConfig { gasPaymentEnforcement: GasPaymentEnforcement[]; @@ -32,6 +36,7 @@ export interface BaseRelayerConfig { blacklist?: MatchingList; transactionGasLimit?: BigNumberish; skipTransactionGasLimitFor?: string[]; + metricAppContexts?: MetricAppContext[]; } // Full relayer-specific agent config for a single chain @@ -83,13 +88,18 @@ export class RelayerConfigHelper extends AgentConfigHelper { relayerConfig.skipTransactionGasLimitFor = baseConfig.skipTransactionGasLimitFor.join(','); } + if (baseConfig.metricAppContexts) { + relayerConfig.metricAppContexts = JSON.stringify( + baseConfig.metricAppContexts, + ); + } return relayerConfig; } // Get the signer configuration for each chain by the chain name. async signers(): Promise> { - let chainSigners: ChainMap = {}; + const chainSigners: ChainMap = {}; if (this.aws) { const awsUser = new AgentAwsUser( @@ -159,9 +169,9 @@ export function routerMatchingList( matchingList.push({ originDomain: getDomainId(chainMetadata[source]), - senderAddress: routers[source].router, + senderAddress: addressToBytes32(routers[source].router), destinationDomain: getDomainId(chainMetadata[destination]), - recipientAddress: routers[destination].router, + recipientAddress: addressToBytes32(routers[destination].router), }); } } diff --git a/typescript/infra/src/config/agent/validator.ts b/typescript/infra/src/config/agent/validator.ts index cfc8a57d20..8aeb1e5fe7 100644 --- a/typescript/infra/src/config/agent/validator.ts +++ b/typescript/infra/src/config/agent/validator.ts @@ -8,7 +8,7 @@ import { } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; -import { AgentAwsUser, ValidatorAgentAwsUser } from '../../agents/aws'; +import { ValidatorAgentAwsUser } from '../../agents/aws'; import { Role } from '../../roles'; import { HelmStatefulSetValues } from '../infrastructure'; diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index 9b6ff567d0..dda0edc7c4 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -1,12 +1,17 @@ import { providers } from 'ethers'; import { + ChainMap, + ChainMetadata, + ChainMetadataManager, ChainName, + CoreChainName, HyperlaneSmartProvider, ProviderRetryOptions, RpcConsensusType, chainMetadata, } from '@hyperlane-xyz/sdk'; +import { ProtocolType, objFilter } from '@hyperlane-xyz/utils'; import { getSecretRpcEndpoint } from '../agents'; @@ -22,9 +27,17 @@ export async function fetchProvider( chainName: ChainName, connectionType: RpcConsensusType = RpcConsensusType.Single, ): Promise { - const chainId = chainMetadata[chainName].chainId; + const cmm = new ChainMetadataManager(chainMetadata); + const chainData = cmm.tryGetChainMetadata(chainName); + if (!chainData) { + throw Error(`Unsupported chain: ${chainName}`); + } + const chainId = chainData.chainId; const single = connectionType === RpcConsensusType.Single; - const rpcData = await getSecretRpcEndpoint(environment, chainName, !single); + let rpcData = chainData.rpcUrls.map((url) => url.http); + if (rpcData.length === 0) { + rpcData = await getSecretRpcEndpoint(environment, chainName, !single); + } if (connectionType === RpcConsensusType.Single) { return HyperlaneSmartProvider.fromRpcUrl(chainId, rpcData[0], defaultRetry); @@ -43,3 +56,24 @@ export async function fetchProvider( throw Error(`Unsupported connectionType: ${connectionType}`); } } + +export function getChainMetadatas(chains: Array) { + const allMetadatas = Object.fromEntries( + chains + .map((chain) => chainMetadata[chain]) + .map((metadata) => [metadata.name, metadata]), + ); + + const ethereumMetadatas = objFilter( + allMetadatas, + (_, metadata): metadata is ChainMetadata => + metadata.protocol === ProtocolType.Ethereum, + ); + const nonEthereumMetadatas = objFilter( + allMetadatas, + (_, metadata): metadata is ChainMetadata => + metadata.protocol !== ProtocolType.Ethereum, + ); + + return { ethereumMetadatas, nonEthereumMetadatas }; +} diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index 96f75e5666..3163178592 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -7,9 +7,9 @@ import { HyperlaneEnvironment, IgpConfig, MultiProvider, + OwnableConfig, RpcConsensusType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; import { environments } from '../../config/environments'; @@ -18,7 +18,6 @@ import { Role } from '../roles'; import { RootAgentConfig } from './agent'; import { KeyFunderConfig } from './funding'; -import { AllStorageGasOracleConfigs } from './gas-oracle'; import { HelloWorldConfig } from './helloworld/types'; import { InfrastructureConfig } from './infrastructure'; import { LiquidityLayerRelayerConfig } from './middleware'; @@ -38,7 +37,7 @@ export type EnvironmentConfig = { agents: Partial>; core: ChainMap; igp: ChainMap; - owners: ChainMap
; + owners: ChainMap; infra: InfrastructureConfig; getMultiProvider: ( context?: Contexts, @@ -55,7 +54,6 @@ export type EnvironmentConfig = { bridgeAdapters: ChainMap; relayer: LiquidityLayerRelayerConfig; }; - storageGasOracleConfig?: AllStorageGasOracleConfigs; }; export const deployEnvToSdkEnv: Record< diff --git a/typescript/infra/src/config/funding.ts b/typescript/infra/src/config/funding.ts index 3ad6f48aa0..c0834abc4b 100644 --- a/typescript/infra/src/config/funding.ts +++ b/typescript/infra/src/config/funding.ts @@ -1,7 +1,7 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; +import { ChainMap, RpcConsensusType } from '@hyperlane-xyz/sdk'; import { Contexts } from '../../config/contexts'; -import { Role } from '../roles'; +import { FundableRole, Role } from '../roles'; import { DockerConfig } from './agent'; @@ -10,7 +10,7 @@ export interface ContextAndRoles { roles: Role[]; } -export type ContextAndRolesMap = Partial>; +export type ContextAndRolesMap = Partial>; export interface KeyFunderConfig { docker: DockerConfig; @@ -21,4 +21,6 @@ export interface KeyFunderConfig { cyclesBetweenEthereumMessages?: number; prometheusPushGateway: string; connectionType: RpcConsensusType.Single | RpcConsensusType.Quorum; + desiredBalancePerChain: ChainMap; + desiredKathyBalancePerChain: ChainMap; } diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index 812314220a..a7e91e44be 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -1,22 +1,17 @@ import { BigNumber, ethers } from 'ethers'; -import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; +import { + ChainMap, + ChainName, + StorageGasOracleConfig as DestinationOracleConfig, +} from '@hyperlane-xyz/sdk'; import { convertDecimals } from '@hyperlane-xyz/utils'; import { mustGetChainNativeTokenDecimals } from '../utils/utils'; -export type RemoteGasData = { - tokenExchangeRate: BigNumber; - gasPrice: BigNumber; -}; - -export type RemoteGasDataConfig = RemoteGasData & { - remoteDomain: number; -}; - -// Gas data to configure on a single local chain. Includes RemoteGasData +// Gas data to configure on a single local chain. Includes DestinationOracleConfig // for each remote chain. -export type StorageGasOracleConfig = ChainMap; +export type StorageGasOracleConfig = ChainMap; // StorageGasOracleConfigs for each local chain export type AllStorageGasOracleConfigs = ChainMap; @@ -27,11 +22,8 @@ export const TOKEN_EXCHANGE_RATE_SCALE = ethers.utils.parseUnits( TOKEN_EXCHANGE_RATE_DECIMALS, ); -// Overcharge by 30% to account for market making risk -const TOKEN_EXCHANGE_RATE_MULTIPLIER = ethers.utils.parseUnits( - '1.30', - TOKEN_EXCHANGE_RATE_DECIMALS, -); +// Overcharge by 20% to account for market making risk (when assets are unequal) +const EXCHANGE_RATE_MARGIN_PCT = 20; // Gets the StorageGasOracleConfig for a particular local chain function getLocalStorageGasOracleConfig( @@ -79,9 +71,11 @@ export function getTokenExchangeRateFromValues( remoteValue: BigNumber, ): BigNumber { // This does not yet account for decimals! - const exchangeRate = remoteValue - .mul(TOKEN_EXCHANGE_RATE_MULTIPLIER) - .div(localValue); + let exchangeRate = remoteValue.mul(TOKEN_EXCHANGE_RATE_SCALE).div(localValue); + // use margin if exchange rate is not 1 + if (!exchangeRate.eq(TOKEN_EXCHANGE_RATE_SCALE)) { + exchangeRate = exchangeRate.mul(100 + EXCHANGE_RATE_MARGIN_PCT).div(100); + } return BigNumber.from( convertDecimals( diff --git a/typescript/infra/src/config/grafana_token_config.ts b/typescript/infra/src/config/grafana_token_config.ts deleted file mode 100644 index 9d7e6f4c34..0000000000 --- a/typescript/infra/src/config/grafana_token_config.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { ChainMap, TokenType } from '@hyperlane-xyz/sdk'; -import { ProtocolType } from '@hyperlane-xyz/utils'; - -interface NativeTokenConfig { - symbol: string; - name: string; - type: TokenType.native; - decimals: number; - hypNativeAddress: string; - protocolType: - | ProtocolType.Ethereum - | ProtocolType.Sealevel - | ProtocolType.Cosmos; -} - -interface CollateralTokenConfig { - type: TokenType.collateral; - address: string; - decimals: number; - symbol: string; - name: string; - hypCollateralAddress: string; - isSpl2022?: boolean; - protocolType: - | ProtocolType.Ethereum - | ProtocolType.Sealevel - | ProtocolType.Cosmos; -} - -interface SyntheticTokenConfig { - type: TokenType.synthetic; - hypSyntheticAddress: string; - decimals: number; - symbol: string; - name: string; - protocolType: - | ProtocolType.Ethereum - | ProtocolType.Sealevel - | ProtocolType.Cosmos; -} - -// TODO: migrate and dedupe to SDK from infra and Warp UI -export type WarpTokenConfig = ChainMap< - CollateralTokenConfig | NativeTokenConfig | SyntheticTokenConfig ->; - -/// nautilus configs -export const nautilusList: WarpTokenConfig = { - // bsc - bsc: { - type: TokenType.collateral, - address: '0x37a56cdcD83Dce2868f721De58cB3830C44C6303', - hypCollateralAddress: '0xC27980812E2E66491FD457D488509b7E04144b98', - symbol: 'ZBC', - name: 'Zebec', - decimals: 9, - protocolType: ProtocolType.Ethereum, - }, - - // nautilus - nautilus: { - type: TokenType.native, - hypNativeAddress: '0x4501bBE6e731A4bC5c60C03A77435b2f6d5e9Fe7', - symbol: 'ZBC', - name: 'Zebec', - decimals: 18, - protocolType: ProtocolType.Ethereum, - }, - - // solana - solana: { - type: TokenType.collateral, - address: 'wzbcJyhGhQDLTV1S99apZiiBdE4jmYfbw99saMMdP59', - hypCollateralAddress: 'EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa', - name: 'Zebec', - symbol: 'ZBC', - decimals: 9, - isSpl2022: false, - protocolType: ProtocolType.Sealevel, - }, -}; - -/// neutron configs -export const neutronList: WarpTokenConfig = { - neutron: { - type: TokenType.collateral, - address: - 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7', - hypCollateralAddress: - 'neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa', - name: 'Celestia', - symbol: 'TIA', - decimals: 6, - protocolType: ProtocolType.Cosmos, - }, - mantapacific: { - type: TokenType.synthetic, - hypSyntheticAddress: '0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa', - name: 'Celestia', - symbol: 'TIA', - decimals: 6, - protocolType: ProtocolType.Ethereum, - }, -}; diff --git a/typescript/infra/src/config/index.ts b/typescript/infra/src/config/index.ts index f98fa4b3be..97a3b0c81a 100644 --- a/typescript/infra/src/config/index.ts +++ b/typescript/infra/src/config/index.ts @@ -1,10 +1,9 @@ -export { EnvironmentConfig, DeployEnvironment } from './environment'; +export * from './agent'; +export { DeployEnvironment, EnvironmentConfig } from './environment'; export { AllStorageGasOracleConfigs, - RemoteGasData, StorageGasOracleConfig, getAllStorageGasOracleConfigs, } from './gas-oracle'; export { HelloWorldConfig } from './helloworld/types'; export { InfrastructureConfig } from './infrastructure'; -export * from './agent'; diff --git a/typescript/infra/src/deployment/deploy.ts b/typescript/infra/src/deployment/deploy.ts index df7de5390d..5c2f4c1a8d 100644 --- a/typescript/infra/src/deployment/deploy.ts +++ b/typescript/infra/src/deployment/deploy.ts @@ -11,12 +11,12 @@ import { } from '@hyperlane-xyz/sdk'; import { objMap, objMerge, promiseObjAll } from '@hyperlane-xyz/utils'; -import { getAgentConfigDirectory } from '../../scripts/utils'; +import { getAgentConfigJsonPath } from '../../scripts/agent-utils'; import { DeployEnvironment } from '../config'; import { readJSONAtPath, - writeJSON, writeJsonAtPath, + writeMergedJSON, writeMergedJSONAtPath, } from '../utils/utils'; @@ -29,7 +29,7 @@ export async function deployWithArtifacts( read: boolean; write: boolean; }, - fork?: ChainName, + targetNetwork?: ChainName, agentConfig?: { multiProvider: MultiProvider; addresses: string; @@ -56,11 +56,9 @@ export async function deployWithArtifacts( }); try { - if (fork) { - deployer.deployedContracts[fork] = await deployer.deployContracts( - fork, - configMap[fork], - ); + if (targetNetwork) { + deployer.deployedContracts[targetNetwork] = + await deployer.deployContracts(targetNetwork, configMap[targetNetwork]); } else { await deployer.deploy(configMap); } @@ -90,7 +88,6 @@ export async function postDeploy( const deployedAddresses = serializeContractsMap(deployer.deployedContracts); const cachedAddresses = deployer.cachedAddresses; const addresses = objMerge(deployedAddresses, cachedAddresses); - console.log(addresses); // cache addresses of deployed contracts writeMergedJSONAtPath(cache.addresses, addresses); @@ -146,9 +143,5 @@ export async function writeAgentConfig( addresses as ChainMap, startBlocks, ); - writeJSON( - getAgentConfigDirectory(), - `${environment}_config.json`, - agentConfig, - ); + writeMergedJSONAtPath(getAgentConfigJsonPath(environment), agentConfig); } diff --git a/typescript/infra/src/deployment/testcontracts/testquerysender.ts b/typescript/infra/src/deployment/testcontracts/testquerysender.ts index 17c4c8b28d..e7203856b6 100644 --- a/typescript/infra/src/deployment/testcontracts/testquerysender.ts +++ b/typescript/infra/src/deployment/testcontracts/testquerysender.ts @@ -1,6 +1,7 @@ import { TestQuerySender__factory } from '@hyperlane-xyz/core'; import { ChainName, + ContractVerifier, HyperlaneDeployer, MultiProvider, } from '@hyperlane-xyz/sdk'; @@ -15,8 +16,13 @@ export class TestQuerySenderDeployer extends HyperlaneDeployer< TestQuerySenderConfig, typeof TEST_QUERY_SENDER_FACTORIES > { - constructor(multiProvider: MultiProvider) { - super(multiProvider, TEST_QUERY_SENDER_FACTORIES); + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { + super(multiProvider, TEST_QUERY_SENDER_FACTORIES, { + contractVerifier, + }); } async deployContracts(chain: ChainName, config: TestQuerySenderConfig) { diff --git a/typescript/infra/src/deployment/verify.ts b/typescript/infra/src/deployment/verify.ts new file mode 100644 index 0000000000..7314c16fcd --- /dev/null +++ b/typescript/infra/src/deployment/verify.ts @@ -0,0 +1,20 @@ +import { BuildArtifact, ChainMap } from '@hyperlane-xyz/sdk'; + +import { fetchGCPSecret } from '../utils/gcloud'; +import { readJSONAtPath } from '../utils/utils'; + +// read build artifact from given path +export function extractBuildArtifact(buildArtifactPath: string): BuildArtifact { + // check provided artifact is JSON + if (!buildArtifactPath.endsWith('.json')) { + throw new Error('Source must be a JSON file.'); + } + + // return as BuildArtifact + return readJSONAtPath(buildArtifactPath) as BuildArtifact; +} + +// fetch explorer API keys from GCP +export async function fetchExplorerApiKeys(): Promise> { + return (await fetchGCPSecret('explorer-api-keys', true)) as any; +} diff --git a/typescript/infra/src/funding/key-funder.ts b/typescript/infra/src/funding/key-funder.ts index 142d323be6..78f58e5531 100644 --- a/typescript/infra/src/funding/key-funder.ts +++ b/typescript/infra/src/funding/key-funder.ts @@ -48,6 +48,8 @@ function getKeyFunderHelmValues( contextFundingFrom: keyFunderConfig.contextFundingFrom, contextsAndRolesToFund: keyFunderConfig.contextsAndRolesToFund, connectionType: keyFunderConfig.connectionType, + desiredBalancePerChain: keyFunderConfig.desiredBalancePerChain, + desiredKathyBalancePerChain: keyFunderConfig.desiredKathyBalancePerChain, }, image: { repository: keyFunderConfig.docker.repo, diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index d7680d821a..8ad934facb 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -5,6 +5,7 @@ import { ChainName, HyperlaneApp, HyperlaneAppChecker, + OwnableConfig, OwnerViolation, } from '@hyperlane-xyz/sdk'; import { Address, CallData, objMap } from '@hyperlane-xyz/utils'; @@ -31,19 +32,14 @@ export type AnnotatedCallData = CallData & { export abstract class HyperlaneAppGovernor< App extends HyperlaneApp, - Config, + Config extends OwnableConfig, > { readonly checker: HyperlaneAppChecker; - private owners: ChainMap
; private calls: ChainMap; private canPropose: ChainMap>; - constructor( - checker: HyperlaneAppChecker, - owners: ChainMap
, - ) { + constructor(checker: HyperlaneAppChecker) { this.checker = checker; - this.owners = owners; this.calls = objMap(this.checker.app.contractsMap, () => []); this.canPropose = objMap(this.checker.app.contractsMap, () => new Map()); } @@ -120,7 +116,11 @@ export abstract class HyperlaneAppGovernor< ); await sendCallsForType( SubmissionType.SAFE, - new SafeMultiSend(this.checker.multiProvider, chain, this.owners[chain]), + new SafeMultiSend( + this.checker.multiProvider, + chain, + this.checker.configMap[chain].owner, + ), ); await sendCallsForType(SubmissionType.MANUAL, new ManualMultiSend(chain)); } @@ -162,7 +162,7 @@ export abstract class HyperlaneAppGovernor< } // 2. Check if the call will succeed via Gnosis Safe. - const safeAddress = this.owners[chain]; + const safeAddress = this.checker.configMap[chain].owner; if (!safeAddress) throw new Error(`Owner address not found for ${chain}`); // 2a. Confirm that the signer is a Safe owner or delegate. // This should implicitly check whether or not the owner is a gnosis diff --git a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts index d41d3effd8..21cd659c87 100644 --- a/typescript/infra/src/govern/HyperlaneCoreGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneCoreGovernor.ts @@ -1,15 +1,14 @@ import { - ChainMap, CoreConfig, CoreViolationType, HyperlaneCore, HyperlaneCoreChecker, + HyperlaneCoreDeployer, MailboxViolation, MailboxViolationType, OwnerViolation, ViolationType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from '../govern/HyperlaneAppGovernor'; @@ -17,11 +16,8 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor< HyperlaneCore, CoreConfig > { - constructor( - readonly checker: HyperlaneCoreChecker, - owners: ChainMap
, - ) { - super(checker, owners); + constructor(readonly checker: HyperlaneCoreChecker) { + super(checker); } protected async handleMailboxViolation(violation: MailboxViolation) { @@ -29,6 +25,11 @@ export class HyperlaneCoreGovernor extends HyperlaneAppGovernor< case MailboxViolationType.DefaultIsm: { let ismAddress: string; if (typeof violation.expected === 'object') { + // hack to bind the ISM factory to the deployer for verification + new HyperlaneCoreDeployer( + this.checker.multiProvider, + this.checker.ismFactory, + ); const ism = await this.checker.ismFactory.deploy({ destination: violation.chain, config: violation.expected, diff --git a/typescript/infra/src/govern/HyperlaneIgpGovernor.ts b/typescript/infra/src/govern/HyperlaneIgpGovernor.ts index 46a9ec0ff8..4f1f94ce44 100644 --- a/typescript/infra/src/govern/HyperlaneIgpGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneIgpGovernor.ts @@ -2,18 +2,16 @@ import { BigNumber, ethers } from 'ethers'; import { InterchainGasPaymaster } from '@hyperlane-xyz/core'; import { - ChainMap, ChainName, HyperlaneIgp, - HyperlaneIgpChecker, IgpBeneficiaryViolation, IgpConfig, IgpGasOraclesViolation, IgpOverheadViolation, IgpViolation, IgpViolationType, + OwnerViolation, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from '../govern/HyperlaneAppGovernor'; @@ -21,10 +19,6 @@ export class HyperlaneIgpGovernor extends HyperlaneAppGovernor< HyperlaneIgp, IgpConfig > { - constructor(checker: HyperlaneIgpChecker, owners: ChainMap
) { - super(checker, owners); - } - protected async mapViolationsToCalls() { for (const violation of this.checker.violations) { switch (violation.type) { @@ -32,6 +26,10 @@ export class HyperlaneIgpGovernor extends HyperlaneAppGovernor< this.handleIgpViolation(violation as IgpViolation); break; } + case 'Owner': { + super.handleOwnerViolation(violation as OwnerViolation); + break; + } default: throw new Error(`Unsupported violation type ${violation.type}`); } diff --git a/typescript/infra/src/govern/ProxiedRouterGovernor.ts b/typescript/infra/src/govern/ProxiedRouterGovernor.ts index b2e5eccb99..9d421c62fe 100644 --- a/typescript/infra/src/govern/ProxiedRouterGovernor.ts +++ b/typescript/infra/src/govern/ProxiedRouterGovernor.ts @@ -1,8 +1,6 @@ import { - ChainMap, ConnectionClientViolation, ConnectionClientViolationType, - HyperlaneAppChecker, OwnerViolation, RouterApp, RouterConfig, @@ -10,7 +8,6 @@ import { RouterViolationType, ViolationType, } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneAppGovernor } from './HyperlaneAppGovernor'; @@ -18,13 +15,6 @@ export class ProxiedRouterGovernor< App extends RouterApp, Config extends RouterConfig, > extends HyperlaneAppGovernor { - constructor( - checker: HyperlaneAppChecker, - owners: ChainMap
, - ) { - super(checker, owners); - } - protected async mapViolationsToCalls() { for (const violation of this.checker.violations) { switch (violation.type) { diff --git a/typescript/infra/src/roles.ts b/typescript/infra/src/roles.ts index f9b1f466e5..4953118f37 100644 --- a/typescript/infra/src/roles.ts +++ b/typescript/infra/src/roles.ts @@ -6,6 +6,8 @@ export enum Role { Kathy = 'kathy', } +export type FundableRole = Role.Relayer | Role.Kathy; + export const ALL_KEY_ROLES = [ Role.Validator, Role.Relayer, diff --git a/typescript/infra/src/utils/fork.ts b/typescript/infra/src/utils/fork.ts index 46de55d010..c51d40a8b1 100644 --- a/typescript/infra/src/utils/fork.ts +++ b/typescript/infra/src/utils/fork.ts @@ -1,5 +1,4 @@ import { JsonRpcProvider, JsonRpcSigner } from '@ethersproject/providers'; -import { ethers } from 'ethers'; import { ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; @@ -16,13 +15,13 @@ export const resetFork = async (url: string) => { export const impersonateAccount = async ( account: string, + spoofBalance?: number, ): Promise => { const provider = new JsonRpcProvider('http://127.0.0.1:8545'); await provider.send('hardhat_impersonateAccount', [account]); - await provider.send('hardhat_setBalance', [ - account, - ethers.utils.parseEther('42').toHexString(), - ]); + if (spoofBalance) { + await provider.send('hardhat_setBalance', [account, spoofBalance]); + } return provider.getSigner(account); }; diff --git a/typescript/infra/src/utils/gcloud.ts b/typescript/infra/src/utils/gcloud.ts index ef2650d357..dde9411ed0 100644 --- a/typescript/infra/src/utils/gcloud.ts +++ b/typescript/infra/src/utils/gcloud.ts @@ -1,3 +1,4 @@ +import debug from 'debug'; import fs from 'fs'; import { rm, writeFile } from 'fs/promises'; @@ -9,6 +10,8 @@ interface IamCondition { expression: string; } +const debugLog = debug('infra:utils:gcloud'); + // Allows secrets to be overridden via environment variables to avoid // gcloud calls. This is particularly useful for running commands in k8s, // where we can use external-secrets to fetch secrets from GCP secret manager, @@ -23,7 +26,7 @@ export async function fetchGCPSecret( const envVarOverride = tryGCPSecretFromEnvVariable(secretName); if (envVarOverride !== undefined) { - console.log( + debugLog( `Using environment variable instead of GCP secret with name ${secretName}`, ); output = envVarOverride; @@ -41,7 +44,7 @@ export async function fetchGCPSecret( // If the environment variable GCP_SECRET_OVERRIDES_ENABLED is `true`, // this will attempt to find an environment variable of the form: -// `GCP_SECRET_OVERRIDE_${gcpSecretName..replaceAll('-', '_').toUpperCase()}` +// `GCP_SECRET_OVERRIDE_${gcpSecretName.replaceAll('-', '_').toUpperCase()}` // If found, it's returned, otherwise, undefined is returned. function tryGCPSecretFromEnvVariable(gcpSecretName: string) { const overridingEnabled = @@ -58,9 +61,12 @@ function tryGCPSecretFromEnvVariable(gcpSecretName: string) { export async function gcpSecretExists(secretName: string) { const fullName = `projects/${await getCurrentProjectNumber()}/secrets/${secretName}`; + debugLog(`Checking if GCP secret exists for ${fullName}`); + const matches = await execCmdAndParseJson( `gcloud secrets list --filter name=${fullName} --format json`, ); + debugLog(`Matches: ${matches.length}`); return matches.length > 0; } @@ -80,10 +86,12 @@ export async function setGCPSecret( await execCmd( `gcloud secrets create ${secretName} --data-file=${fileName} --replication-policy=automatic --labels=${labelString}`, ); + debugLog(`Created new GCP secret for ${secretName}`); } else { await execCmd( `gcloud secrets versions add ${secretName} --data-file=${fileName}`, ); + debugLog(`Added new version to existing GCP secret for ${secretName}`); } await rm(fileName); } @@ -95,6 +103,9 @@ export async function createServiceAccountIfNotExists( let serviceAccountInfo = await getServiceAccountInfo(serviceAccountName); if (!serviceAccountInfo) { serviceAccountInfo = await createServiceAccount(serviceAccountName); + debugLog(`Created new service account with name ${serviceAccountName}`); + } else { + debugLog(`Service account with name ${serviceAccountName} already exists`); } return serviceAccountInfo.email; } @@ -110,6 +121,7 @@ export async function grantServiceAccountRoleIfNotExists( matchedBinding && iamConditionsEqual(condition, matchedBinding.condition) ) { + debugLog(`Service account ${serviceAccountEmail} already has role ${role}`); return; } await execCmd( @@ -119,6 +131,7 @@ export async function grantServiceAccountRoleIfNotExists( : '' }`, ); + debugLog(`Granted role ${role} to service account ${serviceAccountEmail}`); } export async function createServiceAccountKey(serviceAccountEmail: string) { @@ -128,12 +141,14 @@ export async function createServiceAccountKey(serviceAccountEmail: string) { ); const key = JSON.parse(fs.readFileSync(localKeyFile, 'utf8')); fs.rmSync(localKeyFile); + debugLog(`Created new service account key for ${serviceAccountEmail}`); return key; } // The alphanumeric project name / ID export async function getCurrentProject() { const [result] = await execCmd('gcloud config get-value project'); + debugLog(`Current GCP project ID: ${result.trim()}`); return result.trim(); } @@ -150,10 +165,12 @@ async function getIamMemberPolicyBindings(memberEmail: string) { const unprocessedRoles = await execCmdAndParseJson( `gcloud projects get-iam-policy $(gcloud config get-value project) --format "json(bindings)" --flatten="bindings[].members" --filter="bindings.members:${memberEmail}"`, ); - return unprocessedRoles.map((unprocessedRoleObject: any) => ({ + const bindings = unprocessedRoles.map((unprocessedRoleObject: any) => ({ role: unprocessedRoleObject.bindings.role, condition: unprocessedRoleObject.bindings.condition, })); + debugLog(`Retrieved IAM policy bindings for ${memberEmail}`); + return bindings; } async function createServiceAccount(serviceAccountName: string) { @@ -169,8 +186,10 @@ async function getServiceAccountInfo(serviceAccountName: string) { `gcloud iam service-accounts list --format json --filter displayName="${serviceAccountName}"`, ); if (matches.length === 0) { + debugLog(`No service account found with name ${serviceAccountName}`); return undefined; } + debugLog(`Found service account with name ${serviceAccountName}`); return matches[0]; } diff --git a/typescript/infra/src/utils/utils.ts b/typescript/infra/src/utils/utils.ts index 003cfed145..84256ba58e 100644 --- a/typescript/infra/src/utils/utils.ts +++ b/typescript/infra/src/utils/utils.ts @@ -3,7 +3,9 @@ import * as asn1 from 'asn1.js'; import { exec } from 'child_process'; import { ethers } from 'ethers'; import fs from 'fs'; +import stringify from 'json-stable-stringify'; import path from 'path'; +import { parse as yamlParse } from 'yaml'; import { AllChains, @@ -14,7 +16,7 @@ import { import { ProtocolType, objMerge } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts'; -import { Role } from '../roles'; +import { FundableRole, Role } from '../roles'; export function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -178,7 +180,7 @@ export function writeJsonAtPath(filepath: string, obj: any) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } - fs.writeFileSync(filepath, JSON.stringify(obj, null, 2) + '\n'); + fs.writeFileSync(filepath, stringify(obj, { space: ' ' }) + '\n'); } export function writeJSON(directory: string, filename: string, obj: any) { @@ -200,6 +202,10 @@ export function readJSON(directory: string, filename: string) { return readJSONAtPath(path.join(directory, filename)); } +export function readYaml(filepath: string): T { + return yamlParse(readFileAtPath(filepath)) as T; +} + export function assertRole(roleStr: string) { const role = roleStr as Role; if (!Object.values(Role).includes(role)) { @@ -208,6 +214,14 @@ export function assertRole(roleStr: string) { return role; } +export function assertFundableRole(roleStr: string): FundableRole { + const role = roleStr as Role; + if (role !== Role.Relayer && role !== Role.Kathy) { + throw Error(`Invalid fundable role ${role}`); + } + return role; +} + export function assertChain(chainStr: string) { const chain = chainStr as ChainName; if (!AllChains.includes(chain as CoreChainName)) { diff --git a/typescript/infra/test/agent-configs.test.ts b/typescript/infra/test/agent-configs.test.ts new file mode 100644 index 0000000000..ea7c6f3777 --- /dev/null +++ b/typescript/infra/test/agent-configs.test.ts @@ -0,0 +1,47 @@ +import { expect } from 'chai'; + +import { hyperlaneContextAgentChainConfig as mainnet3AgentChainConfig } from '../config/environments/mainnet3/agent'; +import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/chains'; +import { hyperlaneContextAgentChainConfig as testnet4AgentChainConfig } from '../config/environments/testnet4/agent'; +import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/chains'; +import { getAgentConfigJsonPath } from '../scripts/agent-utils'; +import { ensureAgentChainConfigIncludesAllChainNames } from '../src/config'; +import { readJSONAtPath } from '../src/utils/utils'; + +const environmentChainConfigs = { + mainnet3: { + agentChainConfig: mainnet3AgentChainConfig, + // We read the agent config from the file system instead of importing + // to get around the agent JSON configs living outside the typescript rootDir + agentJsonConfig: readJSONAtPath(getAgentConfigJsonPath('mainnet3')), + supportedChainNames: mainnet3SupportedChainNames, + }, + testnet4: { + agentChainConfig: testnet4AgentChainConfig, + agentJsonConfig: readJSONAtPath(getAgentConfigJsonPath('testnet4')), + supportedChainNames: testnet4SupportedChainNames, + }, +}; + +describe('Agent configs', () => { + Object.entries(environmentChainConfigs).forEach(([environment, config]) => { + describe(`Environment: ${environment}`, () => { + it('AgentChainConfig specifies all chains for each role in the agent chain config', () => { + // This will throw if there are any inconsistencies + ensureAgentChainConfigIncludesAllChainNames( + config.agentChainConfig, + config.supportedChainNames, + ); + }); + + it('Agent JSON config matches environment chains', () => { + const agentJsonConfigChains = Object.keys( + config.agentJsonConfig.chains, + ); + expect(agentJsonConfigChains).to.have.members( + config.supportedChainNames, + ); + }); + }); + }); +}); diff --git a/typescript/infra/test/agents.test.ts b/typescript/infra/test/cloud-agent-keys.test.ts similarity index 100% rename from typescript/infra/test/agents.test.ts rename to typescript/infra/test/cloud-agent-keys.test.ts diff --git a/typescript/infra/tsconfig.json b/typescript/infra/tsconfig.json index 13290d01d9..4a968b40db 100644 --- a/typescript/infra/tsconfig.json +++ b/typescript/infra/tsconfig.json @@ -3,6 +3,7 @@ "outDir": "./dist/", "rootDir": "./", "noUnusedLocals": false, + "resolveJsonModule": true }, "exclude": ["./node_modules/", "./dist/", "./tmp.ts"], "extends": "../tsconfig.json", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 6e6f10338b..5df94f24e0 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,75 @@ # @hyperlane-xyz/sdk +## 3.8.0 + +### Minor Changes + +- 9681df08d: **New Feature**: Add transaction fee estimators to the SDK + **Breaking change**: Token Adapter `quoteGasPayment` method renamed to `quoteTransferRemoteGas` for clarity. +- 9681df08d: Remove support for goerli networks (including optimismgoerli, arbitrumgoerli, lineagoerli and polygonzkevmtestnet) +- 9681df08d: Enabled verification of contracts as part of the deployment flow. + + - Solidity build artifact is now included as part of the `@hyperlane-xyz/core` package. + - Updated the `HyperlaneDeployer` to perform contract verification immediately after deploying a contract. A default verifier is instantiated using the core build artifact. + - Updated the `HyperlaneIsmFactory` to re-use the `HyperlaneDeployer` for deployment where possible. + - Minor logging improvements throughout deployers. + +- 9681df08d: Add `WarpCore`, `Token`, and `TokenAmount` classes for interacting with Warp Route instances. + + _Breaking change_: The params to the `IHypTokenAdapter` `populateTransferRemoteTx` method have changed. `txValue` has been replaced with `interchainGas`. + +### Patch Changes + +- 9681df08d: Support configuring non-EVM IGP destinations +- 9681df08d: Removed basegoerli and moonbasealpha testnets +- 9681df08d: Add logos for plume to SDK +- 9681df08d: TestRecipient as part of core deployer +- 9681df08d: Update viction validator set +- 9681df08d: Minor fixes for SDK cosmos logos +- 9681df08d: Implement message id extraction for CosmWasmCoreAdapter +- 9681df08d: Patch transfer ownership in hook deployer +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] +- Updated dependencies [9681df08d] + - @hyperlane-xyz/core@3.8.0 + - @hyperlane-xyz/utils@3.8.0 + +## 3.7.0 + +### Minor Changes + +- 54aeb6420: Added warp route artifacts type adopting registry schema + +### Patch Changes + +- 6f464eaed: Add logos for injective and nautilus +- 87151c62b: Bumped injective reorg period +- ab17af5f7: Updating HyperlaneIgpDeployer to configure storage gas oracles as part of deployment +- 7b40232af: Remove unhealthy zkevm rpc + - @hyperlane-xyz/core@3.7.0 + - @hyperlane-xyz/utils@3.7.0 + +## 3.6.2 + +### Patch Changes + +- @hyperlane-xyz/core@3.6.2 +- @hyperlane-xyz/utils@3.6.2 + +## 3.6.1 + +### Patch Changes + +- ae4476ad0: Bumped mantapacific reorgPeriod to 1, a reorg period in chain metadata is now required by infra. +- f3b7ddb69: Add optional grpcUrl field to ChainMetadata +- e4e4f93fc: Support pausable ISM in deployer and checker +- Updated dependencies [3c298d064] +- Updated dependencies [df24eec8b] +- Updated dependencies [78e50e7da] +- Updated dependencies [e4e4f93fc] + - @hyperlane-xyz/utils@3.6.1 + - @hyperlane-xyz/core@3.6.1 + ## 3.6.0 ### Minor Changes diff --git a/typescript/sdk/logos/black/cosmwasm.svg b/typescript/sdk/logos/black/cosmwasm.svg index bef563f624..e2bdd24ce9 100644 --- a/typescript/sdk/logos/black/cosmwasm.svg +++ b/typescript/sdk/logos/black/cosmwasm.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/typescript/sdk/logos/black/inevm.svg b/typescript/sdk/logos/black/inevm.svg new file mode 100644 index 0000000000..a13721a5cc --- /dev/null +++ b/typescript/sdk/logos/black/inevm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/black/injective.svg b/typescript/sdk/logos/black/injective.svg new file mode 100644 index 0000000000..62a30f4aee --- /dev/null +++ b/typescript/sdk/logos/black/injective.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/black/nautilus.svg b/typescript/sdk/logos/black/nautilus.svg new file mode 100644 index 0000000000..a3d3d1cc76 --- /dev/null +++ b/typescript/sdk/logos/black/nautilus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/black/plume.svg b/typescript/sdk/logos/black/plume.svg new file mode 100644 index 0000000000..a051a4cac0 --- /dev/null +++ b/typescript/sdk/logos/black/plume.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/typescript/sdk/logos/black/viction.svg b/typescript/sdk/logos/black/viction.svg new file mode 100644 index 0000000000..911cc7fb51 --- /dev/null +++ b/typescript/sdk/logos/black/viction.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/cosmos.svg b/typescript/sdk/logos/color/cosmos.svg index 6023d57e6a..bdc37656dd 100644 --- a/typescript/sdk/logos/color/cosmos.svg +++ b/typescript/sdk/logos/color/cosmos.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/typescript/sdk/logos/color/cosmwasm.svg b/typescript/sdk/logos/color/cosmwasm.svg index 21df94390a..f9b3558d95 100644 --- a/typescript/sdk/logos/color/cosmwasm.svg +++ b/typescript/sdk/logos/color/cosmwasm.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/typescript/sdk/logos/color/inevm.svg b/typescript/sdk/logos/color/inevm.svg new file mode 100644 index 0000000000..da8638a4ac --- /dev/null +++ b/typescript/sdk/logos/color/inevm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/injective.svg b/typescript/sdk/logos/color/injective.svg new file mode 100644 index 0000000000..56a066e6fd --- /dev/null +++ b/typescript/sdk/logos/color/injective.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/manta.svg b/typescript/sdk/logos/color/manta.svg index 65ea376986..3ece718ca3 100644 --- a/typescript/sdk/logos/color/manta.svg +++ b/typescript/sdk/logos/color/manta.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/typescript/sdk/logos/color/nautilus.svg b/typescript/sdk/logos/color/nautilus.svg new file mode 100644 index 0000000000..6b5daa694b --- /dev/null +++ b/typescript/sdk/logos/color/nautilus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/logos/color/plume.svg b/typescript/sdk/logos/color/plume.svg new file mode 100644 index 0000000000..461e39c54c --- /dev/null +++ b/typescript/sdk/logos/color/plume.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/typescript/sdk/logos/color/viction.svg b/typescript/sdk/logos/color/viction.svg new file mode 100644 index 0000000000..911cc7fb51 --- /dev/null +++ b/typescript/sdk/logos/color/viction.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 9ad145a7eb..6f20bd10d3 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,12 +1,12 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "3.6.0", + "version": "3.8.0", "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", - "@hyperlane-xyz/core": "3.6.0", - "@hyperlane-xyz/utils": "3.6.0", + "@hyperlane-xyz/core": "3.8.0", + "@hyperlane-xyz/utils": "3.8.0", "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.0", "@types/coingecko-api": "^1.0.10", @@ -38,7 +38,8 @@ "prettier": "^2.8.8", "sinon": "^13.0.2", "ts-node": "^10.8.0", - "typescript": "5.1.6" + "typescript": "5.1.6", + "yaml": "^2.3.1" }, "files": [ "/dist", diff --git a/typescript/sdk/src/consts/bytecode.ts b/typescript/sdk/src/consts/bytecode.ts index 640261bae9..fe5605d4e6 100644 --- a/typescript/sdk/src/consts/bytecode.ts +++ b/typescript/sdk/src/consts/bytecode.ts @@ -1,6 +1,7 @@ export enum BytecodeHash { V3_MAILBOX_BYTECODE_HASH = '0x6e853444a6e38bb1d7ac7608b92a70cee83153c891c70ed882b2432134bb23a0', // without optimizer OPT_V3_MAILBOX_BYTECODE_HASH = '0x7cc944e10fa5597f10265bdac4a808e705711451ee7f117ebf9a97193b796136', // with optimizer + TRANSPARENT_PROXY_4_9_3_BYTECODE_HASH = '0xae0fb63adc64a29562a3337ed10b8772f89d5241bc3d8f0a82e9462d421e5e4b', // OZ 4.9.3 TRANSPARENT_PROXY_BYTECODE_HASH = '0x320bda003dfa31828115be5c01b9f3e7eecaf2532afdb89d2b53559f2e7ab86d', // without optimizer OPT_TRANSPARENT_PROXY_BYTECODE_HASH = '0x30aa3b1506a94c0fe2749af099851623685d9a24a65e2e8b3746c272499979d1', // with optimizer PROXY_ADMIN_BYTECODE_HASH = '0x13855ae57da3aadecb9259cecece16e1f434b8850fe95531f422e4e262f3f200', diff --git a/typescript/sdk/src/consts/chainMetadata.ts b/typescript/sdk/src/consts/chainMetadata.ts index 253a67ce94..817163349d 100644 --- a/typescript/sdk/src/consts/chainMetadata.ts +++ b/typescript/sdk/src/consts/chainMetadata.ts @@ -1,6 +1,10 @@ import { ProtocolType } from '@hyperlane-xyz/utils'; -import { ChainMetadata, ExplorerFamily } from '../metadata/chainMetadataTypes'; +import { + ChainMetadata, + ChainTechnicalStack, + ExplorerFamily, +} from '../metadata/chainMetadataTypes'; import { ChainMap } from '../types'; import { Chains, Mainnets, Testnets } from './chains'; @@ -71,30 +75,15 @@ export const arbitrum: ChainMetadata = { nativeToken: etherToken, protocol: ProtocolType.Ethereum, rpcUrls: [{ http: 'https://arb1.arbitrum.io/rpc' }], -}; - -export const arbitrumgoerli: ChainMetadata = { - blocks: { - confirmations: 1, - estimateBlockTime: 3, - reorgPeriod: 1, - }, - chainId: 421613, - displayName: 'Arbitrum Goerli', - displayNameShort: 'Arb. Goerli', - domainId: 421613, - isTestnet: true, - name: Chains.arbitrumgoerli, - nativeToken: etherToken, - protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://goerli-rollup.arbitrum.io/rpc' }], + technicalStack: ChainTechnicalStack.ArbitrumNitro, }; export const avalanche: ChainMetadata = { blockExplorers: [ { - apiUrl: 'https://api.snowtrace.io/api', - family: ExplorerFamily.Other, + apiUrl: + 'https://api.routescan.io/v2/network/mainnet/evm/43114/etherscan/api', + family: ExplorerFamily.Routescan, name: 'SnowTrace', url: 'https://snowtrace.io', }, @@ -154,33 +143,6 @@ export const base: ChainMetadata = { ], }; -export const basegoerli: ChainMetadata = { - blockExplorers: [ - { - apiUrl: 'https://api-goerli.basescan.org/api', - family: ExplorerFamily.Etherscan, - name: 'BaseScan', - url: 'https://goerli.basescan.org', - }, - ], - blocks: { - confirmations: 1, - estimateBlockTime: 3, - reorgPeriod: 1, - }, - chainId: 84531, - displayName: 'Base Goerli', - domainId: 84531, - isTestnet: true, - name: Chains.basegoerli, - nativeToken: etherToken, - protocol: ProtocolType.Ethereum, - rpcUrls: [ - { http: 'https://base-goerli.publicnode.com' }, - { http: 'https://goerli.base.org' }, - ], -}; - export const bsc: ChainMetadata = { blockExplorers: [ { @@ -204,7 +166,11 @@ export const bsc: ChainMetadata = { name: Chains.bsc, nativeToken: bnbToken, protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://rpc.ankr.com/bsc' }], + rpcUrls: [ + { http: 'https://rpc.ankr.com/bsc' }, + { http: 'https://bsc.drpc.org' }, + { http: 'https://bscrpc.com' }, + ], }; export const bsctestnet: ChainMetadata = { @@ -300,7 +266,7 @@ export const ethereum: ChainMetadata = { url: 'https://etherscan.io', }, { - apiUrl: 'https://blockscout.com/eth/mainnet/api', + apiUrl: 'https://eth.blockscout.com/api', family: ExplorerFamily.Blockscout, name: 'Blockscout', url: 'https://blockscout.com/eth/mainnet', @@ -320,7 +286,7 @@ export const ethereum: ChainMetadata = { nativeToken: etherToken, protocol: ProtocolType.Ethereum, rpcUrls: [ - { http: 'https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161' }, + { http: 'https://ethereum.publicnode.com' }, { http: 'https://cloudflare-eth.com' }, ], }; @@ -328,7 +294,8 @@ export const ethereum: ChainMetadata = { export const fuji: ChainMetadata = { blockExplorers: [ { - apiUrl: 'https://api-testnet.snowtrace.io/api', + apiUrl: + 'https://api.routescan.io/v2/network/testnet/evm/43113/etherscan/api', family: ExplorerFamily.Etherscan, name: 'SnowTrace', url: 'https://testnet.snowtrace.io', @@ -354,33 +321,6 @@ export const fuji: ChainMetadata = { ], }; -export const goerli: ChainMetadata = { - blockExplorers: [ - { - apiUrl: 'https://api-goerli.etherscan.io/api', - family: ExplorerFamily.Etherscan, - name: 'Etherscan', - url: 'https://goerli.etherscan.io', - }, - ], - blocks: { - confirmations: 1, - estimateBlockTime: 13, - reorgPeriod: 2, - }, - chainId: 5, - displayName: 'Goerli', - domainId: 5, - isTestnet: true, - name: Chains.goerli, - nativeToken: etherToken, - protocol: ProtocolType.Ethereum, - rpcUrls: [ - { http: 'https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161' }, - { http: 'https://rpc.ankr.com/eth_goerli' }, - ], -}; - export const gnosis: ChainMetadata = { blockExplorers: [ { @@ -415,28 +355,59 @@ export const gnosis: ChainMetadata = { ], }; -export const lineagoerli: ChainMetadata = { +export const inevm: ChainMetadata = { blockExplorers: [ { - apiUrl: 'https://explorer.goerli.linea.build/api', + apiUrl: 'https://inevm.calderaexplorer.xyz/api', family: ExplorerFamily.Blockscout, - name: 'Linea Explorer', - url: 'https://explorer.goerli.linea.build/', + name: 'Caldera inEVM Explorer', + url: 'https://inevm.calderaexplorer.xyz', }, ], blocks: { confirmations: 1, - estimateBlockTime: 12, - reorgPeriod: 2, + estimateBlockTime: 3, + reorgPeriod: 0, + }, + chainId: 2525, + displayName: 'Injective EVM', + displayNameShort: 'inEVM', + domainId: 2525, + gasCurrencyCoinGeckoId: 'injective-protocol', + name: Chains.inevm, + nativeToken: { + decimals: 18, + name: 'Injective', + symbol: 'INJ', }, - chainId: 59140, - displayName: 'Linea Goerli', - domainId: 59140, - isTestnet: true, - name: Chains.lineagoerli, - nativeToken: etherToken, protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://rpc.goerli.linea.build' }], + rpcUrls: [{ http: 'https://inevm.calderachain.xyz/http' }], +}; + +export const injective: ChainMetadata = { + bech32Prefix: 'inj', + blockExplorers: [], + blocks: { + confirmations: 1, + estimateBlockTime: 1, + reorgPeriod: 10, + }, + chainId: 'injective-1', + displayName: 'Injective', + domainId: 6909546, + gasCurrencyCoinGeckoId: 'injective-protocol', + grpcUrls: [{ http: 'sentry.chain.grpc.injective.network:443' }], + name: Chains.injective, + nativeToken: { + decimals: 18, + denom: 'inj', + name: 'Injective', + symbol: 'INJ', + }, + protocol: ProtocolType.Cosmos, + restUrls: [{ http: 'https://sentry.lcd.injective.network:443' }], + rpcUrls: [{ http: 'https://sentry.tm.injective.network:443' }], + slip44: 118, }; export const mantapacific: ChainMetadata = { @@ -451,12 +422,13 @@ export const mantapacific: ChainMetadata = { blocks: { confirmations: 1, estimateBlockTime: 3, - reorgPeriod: 0, + reorgPeriod: 1, }, chainId: 169, displayName: 'Manta Pacific', displayNameShort: 'Manta', domainId: 169, + gasCurrencyCoinGeckoId: 'ethereum', isTestnet: false, name: Chains.mantapacific, nativeToken: { @@ -468,35 +440,6 @@ export const mantapacific: ChainMetadata = { rpcUrls: [{ http: 'https://pacific-rpc.manta.network/http' }], }; -export const moonbasealpha: ChainMetadata = { - blockExplorers: [ - { - apiUrl: 'https://api-moonbase.moonscan.io/api', - family: ExplorerFamily.Etherscan, - name: 'MoonScan', - url: 'https://moonbase.moonscan.io', - }, - ], - blocks: { - confirmations: 1, - estimateBlockTime: 12, - reorgPeriod: 1, - }, - chainId: 1287, - displayName: 'Moonbase Alpha', - displayNameShort: 'Moonbase', - domainId: 1287, - isTestnet: true, - name: Chains.moonbasealpha, - nativeToken: { - decimals: 18, - name: 'DEV', - symbol: 'DEV', - }, - protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://rpc.api.moonbase.moonbeam.network' }], -}; - export const moonbeam: ChainMetadata = { blockExplorers: [ { @@ -601,17 +544,23 @@ export const neutron: ChainMetadata = { chainId: 'neutron-1', displayName: 'Neutron', domainId: 1853125230, + gasCurrencyCoinGeckoId: 'neutron-3', + grpcUrls: [{ http: 'grpc-kralum.neutron-1.neutron.org:80' }], isTestnet: false, name: Chains.neutron, nativeToken: { decimals: 6, + denom: 'untrn', name: 'Neutron', symbol: 'NTRN', }, protocol: ProtocolType.Cosmos, - restUrls: [{ http: 'grpc-kralum.neutron-1.neutron.org:80' }], + restUrls: [{ http: 'https://rest-lb.neutron.org' }], rpcUrls: [{ http: 'https://rpc-kralum.neutron-1.neutron.org' }], slip44: 118, + transactionOverrides: { + gasPrice: '0.0075', + }, }; export const optimism: ChainMetadata = { @@ -641,29 +590,32 @@ export const optimism: ChainMetadata = { rpcUrls: [{ http: 'https://mainnet.optimism.io' }], }; -export const optimismgoerli: ChainMetadata = { +export const plumetestnet: ChainMetadata = { blockExplorers: [ { - apiUrl: 'https://api-goerli-optimism.etherscan.io/api', - family: ExplorerFamily.Etherscan, - name: 'Etherscan', - url: 'https://goerli-optimism.etherscan.io', + apiUrl: 'https://plume-testnet.explorer.caldera.xyz/api', + family: ExplorerFamily.Blockscout, + name: 'Plume Testnet Explorer', + url: 'https://plume-testnet.explorer.caldera.xyz', }, ], blocks: { confirmations: 1, estimateBlockTime: 3, - reorgPeriod: 1, + reorgPeriod: 0, }, - chainId: 420, - displayName: 'Optimism Goerli', - displayNameShort: 'Opt. Goerli', - domainId: 420, + chainId: 161221135, + displayName: 'Plume Testnet', + domainId: 161221135, isTestnet: true, - name: Chains.optimismgoerli, - nativeToken: etherToken, + name: Chains.plumetestnet, + nativeToken: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://goerli.optimism.io' }], + rpcUrls: [{ http: 'https://plume-testnet.rpc.caldera.xyz/http' }], }; export const polygon: ChainMetadata = { @@ -691,14 +643,10 @@ export const polygon: ChainMetadata = { protocol: ProtocolType.Ethereum, rpcUrls: [ { - http: 'https://rpc-mainnet.matic.quiknode.pro', - pagination: { - // Needs to be low to avoid RPC timeouts - maxBlockRange: 10000, - minBlockNumber: 19657100, - }, + http: 'https://polygon-bor.publicnode.com', }, { http: 'https://polygon-rpc.com' }, + { http: 'https://rpc.ankr.com/polygon' }, ], }; @@ -725,32 +673,10 @@ export const polygonzkevm: ChainMetadata = { name: Chains.polygonzkevm, nativeToken: etherToken, protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://rpc.ankr.com/polygon_zkevm' }], -}; - -export const polygonzkevmtestnet: ChainMetadata = { - blockExplorers: [ - { - apiUrl: 'https://api-testnet-zkevm.polygonscan.com/api', - family: ExplorerFamily.Etherscan, - name: 'PolygonScan', - url: 'https://testnet-zkevm.polygonscan.com', - }, + rpcUrls: [ + { http: 'https://zkevm-rpc.com' }, + { http: 'https://rpc.ankr.com/polygon_zkevm' }, ], - blocks: { - confirmations: 1, - estimateBlockTime: 3, - reorgPeriod: 1, - }, - chainId: 1442, - displayName: 'Polygon zkEVM Testnet', - displayNameShort: 'ZkEvm Testnet', - domainId: 1442, - isTestnet: true, - name: Chains.polygonzkevmtestnet, - nativeToken: etherToken, - protocol: ProtocolType.Ethereum, - rpcUrls: [{ http: 'https://rpc.public.zkevm-test.net' }], }; // Testnet for Nautilus @@ -848,6 +774,7 @@ export const sepolia: ChainMetadata = { nativeToken: etherToken, protocol: ProtocolType.Ethereum, rpcUrls: [ + { http: 'https://ethereum-sepolia.publicnode.com' }, { http: 'https://ethereum-sepolia.blockpi.network/v1/rpc/public' }, { http: 'https://rpc.sepolia.org' }, ], @@ -862,7 +789,6 @@ export const solana: ChainMetadata = { url: 'https://explorer.solana.com', }, ], - blocks: { confirmations: 1, estimateBlockTime: 0.4, @@ -928,6 +854,25 @@ export const solanadevnet: ChainMetadata = { rpcUrls: [{ http: 'https://api.devnet.solana.com' }], }; +export const eclipsetestnet: ChainMetadata = { + blocks: { + confirmations: 1, + estimateBlockTime: 0.4, + reorgPeriod: 0, + }, + chainId: 239092742, + displayName: 'Eclipse Testnet', + domainId: 239092742, + isTestnet: true, + name: 'eclipsetestnet', + nativeToken: { + ...etherToken, + decimals: 9, + }, + protocol: ProtocolType.Sealevel, + rpcUrls: [{ http: 'https://testnet.dev2.eclipsenetwork.xyz' }], +}; + export const test1: ChainMetadata = { blockExplorers: [], blocks: { @@ -979,6 +924,41 @@ export const test3: ChainMetadata = { rpcUrls: [{ http: 'http://127.0.0.1:8545' }], }; +export const viction: ChainMetadata = { + blockExplorers: [ + { + apiUrl: 'https://www.vicscan.xyz/api', + family: ExplorerFamily.Other, + name: 'Vicscan', + url: 'https://www.vicscan.xyz', + }, + ], + blocks: { + confirmations: 1, + estimateBlockTime: 2, + reorgPeriod: 0, + }, + chainId: 88, + displayName: 'Viction', + domainId: 88, + gasCurrencyCoinGeckoId: 'tomochain', + name: Chains.viction, + nativeToken: { + decimals: 18, + name: 'Viction', + symbol: 'VIC', + }, + protocol: ProtocolType.Ethereum, + rpcUrls: [ + { + http: 'https://rpc.tomochain.com', + }, + { + http: 'https://viction.blockpi.network/v1/rpc/public', + }, + ], +}; + /** * Collection maps * @@ -988,30 +968,27 @@ export const test3: ChainMetadata = { export const chainMetadata: ChainMap = { alfajores, arbitrum, - arbitrumgoerli, avalanche, base, - basegoerli, bsc, bsctestnet, celo, chiado, + eclipsetestnet, ethereum, fuji, gnosis, - goerli, - lineagoerli, + inevm, + injective, mantapacific, - moonbasealpha, moonbeam, mumbai, nautilus, neutron, optimism, - optimismgoerli, + plumetestnet, polygon, polygonzkevm, - polygonzkevmtestnet, proteustestnet, scroll, scrollsepolia, @@ -1022,6 +999,7 @@ export const chainMetadata: ChainMap = { test1, test2, test3, + viction, }; export const chainIdToMetadata = Object.values(chainMetadata).reduce< diff --git a/typescript/sdk/src/consts/chains.ts b/typescript/sdk/src/consts/chains.ts index 3907ee8c95..5f40016db2 100644 --- a/typescript/sdk/src/consts/chains.ts +++ b/typescript/sdk/src/consts/chains.ts @@ -5,10 +5,8 @@ export enum Chains { alfajores = 'alfajores', arbitrum = 'arbitrum', - arbitrumgoerli = 'arbitrumgoerli', avalanche = 'avalanche', base = 'base', - basegoerli = 'basegoerli', bsc = 'bsc', bsctestnet = 'bsctestnet', celo = 'celo', @@ -16,25 +14,26 @@ export enum Chains { ethereum = 'ethereum', fuji = 'fuji', gnosis = 'gnosis', - goerli = 'goerli', - lineagoerli = 'lineagoerli', + inevm = 'inevm', + injective = 'injective', mantapacific = 'mantapacific', - moonbasealpha = 'moonbasealpha', moonbeam = 'moonbeam', mumbai = 'mumbai', nautilus = 'nautilus', neutron = 'neutron', optimism = 'optimism', - optimismgoerli = 'optimismgoerli', + plumetestnet = 'plumetestnet', polygon = 'polygon', polygonzkevm = 'polygonzkevm', - polygonzkevmtestnet = 'polygonzkevmtestnet', proteustestnet = 'proteustestnet', scroll = 'scroll', scrollsepolia = 'scrollsepolia', sepolia = 'sepolia', solana = 'solana', solanadevnet = 'solanadevnet', + solanatestnet = 'solanatestnet', + eclipsetestnet = 'eclipsetestnet', + viction = 'viction', test1 = 'test1', test2 = 'test2', test3 = 'test3', @@ -68,25 +67,24 @@ export const Mainnets: Array = [ Chains.base, Chains.scroll, Chains.polygonzkevm, + Chains.injective, + Chains.inevm, + Chains.viction, // Chains.solana, ]; export const Testnets: Array = [ Chains.alfajores, - Chains.arbitrumgoerli, - Chains.basegoerli, Chains.bsctestnet, Chains.chiado, Chains.fuji, - Chains.lineagoerli, - Chains.goerli, - Chains.moonbasealpha, Chains.mumbai, - Chains.optimismgoerli, - Chains.polygonzkevmtestnet, + Chains.plumetestnet, Chains.scrollsepolia, Chains.sepolia, Chains.solanadevnet, + Chains.solanatestnet, + Chains.eclipsetestnet, ]; export const TestChains: Array = [ diff --git a/typescript/sdk/src/consts/environments/mainnet.json b/typescript/sdk/src/consts/environments/mainnet.json index 2be1198330..82f22b523a 100644 --- a/typescript/sdk/src/consts/environments/mainnet.json +++ b/typescript/sdk/src/consts/environments/mainnet.json @@ -14,7 +14,10 @@ "validatorAnnounce": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0x0d0E816eE4557689d34fAd5885C53b9393C1D9fA" + "routingIsmFactory": "0x0d0E816eE4557689d34fAd5885C53b9393C1D9fA", + "interchainSecurityModule": "0x9a795fB62f86146ec06e2377e3C95Af65c7C20eB", + "fallbackRoutingHook": "0xca4cCe24E7e06241846F5EA0cda9947F0507C40C", + "pausableHook": "0x748040afB89B8FdBb992799808215419d36A0930" }, "bsc": { "storageGasOracle": "0x91d23D603d60445411C06e6443d81395593B7940", @@ -31,7 +34,10 @@ "validatorAnnounce": "0x7024078130D9c2100fEA474DAD009C2d1703aCcd", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0xe6Af5720d34213C805C08e2470aea979e3F72F75" + "routingIsmFactory": "0xe6Af5720d34213C805C08e2470aea979e3F72F75", + "interchainSecurityModule": "0xab3df354baBee6c2B88E2CeD3b2e030e31aA5e61", + "fallbackRoutingHook": "0x237E81f87F57Badad9e09f13CC676D986cA852e7", + "pausableHook": "0x7DBdAd1b4A922B65d37d7258a4227b6658344b7f" }, "arbitrum": { "storageGasOracle": "0xD3805207b65d99C075ceA938Fa7c0587026a5DF5", @@ -48,7 +54,10 @@ "validatorAnnounce": "0x1df063280C4166AF9a725e3828b4dAC6c7113B08", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0xa2931C37957f3079d3B21b877d56E1db930e02a5" + "routingIsmFactory": "0xa2931C37957f3079d3B21b877d56E1db930e02a5", + "pausableHook": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "fallbackRoutingHook": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", + "interchainSecurityModule": "0xD0DBBF922076352cC50B285A0023536561F00EEa" }, "optimism": { "storageGasOracle": "0x27e88AeB8EA4B159d81df06355Ea3d20bEB1de38", @@ -65,7 +74,10 @@ "validatorAnnounce": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0xD2e905108c5e44dADA680274740f896Ea96Cf2Fb" + "routingIsmFactory": "0xD2e905108c5e44dADA680274740f896Ea96Cf2Fb", + "pausableHook": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "fallbackRoutingHook": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "interchainSecurityModule": "0x04938856bE60c8e734ffDe5f720E2238302BE8D2" }, "moonbeam": { "storageGasOracle": "0x448b7ADB0dA36d41AA2AfDc9d63b97541A7b3819", @@ -82,7 +94,10 @@ "validatorAnnounce": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0x8061Af3A459093540d17823D651BC5E2A92669a7" + "routingIsmFactory": "0x8061Af3A459093540d17823D651BC5E2A92669a7", + "pausableHook": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "fallbackRoutingHook": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "interchainSecurityModule": "0x373836DFa82f2D27ec79Ca32A197Aa1665F0E1e9" }, "gnosis": { "storageGasOracle": "0x5E01d8F34b629E3f92d69546bbc4142A7Adee7e9", @@ -99,7 +114,10 @@ "validatorAnnounce": "0x87ED6926abc9E38b9C7C19f835B41943b622663c", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0xbB5Df000113e767dE11343A16f83De733e5bCC0F" + "routingIsmFactory": "0xbB5Df000113e767dE11343A16f83De733e5bCC0F", + "pausableHook": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "fallbackRoutingHook": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", + "interchainSecurityModule": "0x8e1aa0687B6d939D5a44304D13B7c922ebB012f1" }, "base": { "merkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", @@ -114,7 +132,10 @@ "aggregationHook": "0x13f3d4B0Ee0a713430fded9E18f7fb6c91A6E41F", "protocolFee": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a", "validatorAnnounce": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B", - "routingIsmFactory": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503" + "routingIsmFactory": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", + "pausableHook": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "fallbackRoutingHook": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "interchainSecurityModule": "0x5D1e7D7c5B9e6dDC8439F67F10c578f2A1084f6F" }, "scroll": { "merkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", @@ -129,7 +150,10 @@ "protocolFee": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "validatorAnnounce": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "routingIsmFactory": "0xe03dad16074BC5EEA9A9311257BF02Eb0B6AAA2b" + "routingIsmFactory": "0xe03dad16074BC5EEA9A9311257BF02Eb0B6AAA2b", + "pausableHook": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "fallbackRoutingHook": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "interchainSecurityModule": "0xaDc0cB48E8DB81855A930C0C1165ea3dCe4Ba5C7" }, "polygonzkevm": { "merkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", @@ -144,7 +168,10 @@ "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "mailbox": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", - "routingIsmFactory": "0xe4057c5B0c43Dc18E36b08C39B419F190D29Ac2d" + "routingIsmFactory": "0xe4057c5B0c43Dc18E36b08C39B419F190D29Ac2d", + "interchainSecurityModule": "0xf2BEE9D2c15Ba9D7e06799B5912dE1F05533c141", + "fallbackRoutingHook": "0x01aE937A7B05d187bBCBE80F44F41879D3D335a4", + "pausableHook": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c" }, "celo": { "storageGasOracle": "0xD9A9966E7dA9a7f0032bF449FB12696a638E673C", @@ -161,7 +188,13 @@ "validatorAnnounce": "0xCeF677b65FDaA6804d4403083bb12B8dB3991FE1", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0x2A2c22B0a8615ad24839fA6Af302E896Af32d1a3" + "routingIsmFactory": "0x2A2c22B0a8615ad24839fA6Af302E896Af32d1a3", + "domainRoutingIsm": "0xf18E32428dad0802C5D6F723cB80A6Da889777c4", + "pausableIsm": "0x6Bc4437ce69696C9461Cbc89582c259AC8847A58", + "staticAggregationIsm": "0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E", + "interchainSecurityModule": "0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E", + "fallbackRoutingHook": "0xDC98a856fb9112894c2fE32267DA8bF35645FAF3", + "pausableHook": "0x80672c5D9Fd26B235654C24adc1CFcDeb8d15115" }, "ethereum": { "storageGasOracle": "0xc9a103990A8dB11b4f627bc5CD1D0c2685484Ec5", @@ -178,7 +211,10 @@ "validatorAnnounce": "0xCe74905e51497b4adD3639366708b821dcBcff96", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0x28fA9552F19039b450498B0d8e5DEAe0d0aAc559" + "routingIsmFactory": "0x28fA9552F19039b450498B0d8e5DEAe0d0aAc559", + "pausableHook": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "fallbackRoutingHook": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "interchainSecurityModule": "0x43Ce4Eb4aE3585dDe9Ac6967Db5b06f7f6764C8a" }, "avalanche": { "storageGasOracle": "0x175821F30AdCAA4bbB72Ce98eF76C2E0De2C3f21", @@ -195,7 +231,10 @@ "validatorAnnounce": "0x9Cad0eC82328CEE2386Ec14a12E81d070a27712f", "testRecipient": "0x36FdA966CfffF8a9Cdc814f546db0e6378bFef35", "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", - "routingIsmFactory": "0x28F7907911C7E321c596686AE6D1F20516450037" + "routingIsmFactory": "0x28F7907911C7E321c596686AE6D1F20516450037", + "pausableHook": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "fallbackRoutingHook": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", + "interchainSecurityModule": "0xA36B02a83564f52d9244310Ea439ee6F6AfeFb60" }, "mantapacific": { "merkleRootMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", @@ -211,6 +250,48 @@ "aggregationHook": "0x8464aF853363B8d6844070F68b0AB34Cb6523d0F", "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", - "routingIsmFactory": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147" + "routingIsmFactory": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", + "testRecipient": "0x4E1c88DD261BEe2941e6c1814597e30F53330428", + "testTokenRecipient": "0x5060eCD5dFAD300A90592C04e504600A7cdcF70b", + "pausableHook": "0x7556a0E61d577D921Cba8Fca0d7D6299d36E607E", + "fallbackRoutingHook": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807", + "interchainSecurityModule": "0xDEed16fe4b1c9b2a93483EDFf34C77A9b57D31Ff" + }, + "inevm": { + "merkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "messageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "aggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "aggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "routingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "domainRoutingIsm": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "storageGasOracle": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", + "aggregationHook": "0xe0dDb5dE7D52918237cC1Ae131F29dcAbcb0F62B", + "protocolFee": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "validatorAnnounce": "0x15ab173bDB6832f9b64276bA128659b0eD77730B", + "interchainSecurityModule": "0x3052aD50De54aAAc5D364d80bBE681d29e924597", + "pausableIsm": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "staticAggregationIsm": "0x3052aD50De54aAAc5D364d80bBE681d29e924597", + "pausableHook": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0" + }, + "viction": { + "merkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "messageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "aggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "aggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "routingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "interchainSecurityModule": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "storageGasOracle": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "testRecipient": "0x17E216fBb22dF4ef8A6640ae9Cb147C92710ac84", + "testTokenRecipient": "0xe042D1fbDf59828dd16b9649Ede7abFc856F7a6c" } } diff --git a/typescript/sdk/src/consts/environments/testnet.json b/typescript/sdk/src/consts/environments/testnet.json index 93257a9431..1757affe7e 100644 --- a/typescript/sdk/src/consts/environments/testnet.json +++ b/typescript/sdk/src/consts/environments/testnet.json @@ -1,217 +1,130 @@ { - "basegoerli": { - "merkleRootMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", - "messageIdMultisigIsmFactory": "0x54148470292C24345fb828B003461a9444414517", - "aggregationIsmFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", - "aggregationHookFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", - "proxyAdmin": "0x05Ea36Caee7d92C173334C9D97DcD39ABdCB2b69", - "mailbox": "0x58483b754Abb1E8947BE63d6b95DF75b8249543A", - "validatorAnnounce": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", - "merkleTreeHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", - "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", - "aggregationHook": "0x168e606fE4A9c8d7F83a3aAA132E831f153e4bAa", - "protocolFee": "0xEe421285728284000ec6c6C55C6F9161faeFfa99", - "fallbackRoutingHook": "0x2C6dD6768E669EDB7b53f26067C1C4534862c3de", - "testRecipient": "0x54Bd02f0f20677e9846F8E9FdB1Abc7315C49C38", - "testTokenRecipient": "0x5e65279Fb7293a058776e37587398fcc3E9184b1", - "routingIsmFactory": "0xb6242d549d4b19a20684397790AFa555b16Bc979" - }, - "arbitrumgoerli": { - "merkleRootMultisigIsmFactory": "0x17D58eBb5Ea0E2d360c877E119FAef4C4052e6B9", - "messageIdMultisigIsmFactory": "0x922CeEe9e8832a047e6aD68Df4F079F271b73Ac3", - "aggregationIsmFactory": "0xC5Bb8CDD44B6c56695df45c7AA8012a97dD6ED13", - "aggregationHookFactory": "0x39a8711BF44165A2292Cb5cB43229659c2Bb11c9", - "proxyAdmin": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "mailbox": "0x13dABc0351407d5aAa0A50003a166A73b4febfDc", - "validatorAnnounce": "0x4a01EEBa1CC20F47A2e60aE4ec932051601FcB9e", - "merkleTreeHook": "0xf0A38e1eEA49dAc7968F470c3aA0BDE2565A5d80", - "storageGasOracle": "0xFc8229ADB46D96056A6e451Fb3c55d60FFeD056f", - "interchainGasPaymaster": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "aggregationHook": "0xf852EB6b98d84A4296754043a56759a0Ae0E06df", - "protocolFee": "0x0358ba0D90ED2d90fB8cBb610F27C274D8077a0B", - "fallbackRoutingHook": "0xEdA6f85f4761A1f9e42FD40CA5a4E8Ce1C764015", - "testRecipient": "0x07543860AE9E72aBcF2Bae9827b23621A64Fa416", - "testTokenRecipient": "0x207db41AB053213451f1a71d936353C9056A0205", - "routingIsmFactory": "0x4D6b4fe86cA1B49ea9CcDFA92F97e4EA0C27Cef2" - }, - "optimismgoerli": { - "merkleRootMultisigIsmFactory": "0xAbC25d7daDD748948F5cC912A807b0f8FcBb56a9", - "messageIdMultisigIsmFactory": "0x7868B6026E36C4b6E2ca6a0CaBDb1A6D0CcC443B", - "aggregationIsmFactory": "0xf666A33C451E8371907aD22dd545E1678fCa1582", - "aggregationHookFactory": "0x00cE81F7B02e0673815a8b0A54e62AeabDE78685", - "proxyAdmin": "0x800b4be4Dc91E56DE934D9f16888d113eFf89Ebb", - "mailbox": "0xB5f021728Ea6223E3948Db2da61d612307945eA2", - "validatorAnnounce": "0x24D31e12E4d3bc2C46C994FcE0c828b218A1aeAb", - "merkleTreeHook": "0xFEe074B31B5B259eB3109737bE13D39B853b47b9", - "storageGasOracle": "0x4927C33299091033D935C15DE6b6073164e99BE0", - "interchainGasPaymaster": "0x02A7661273528EfF3d78CBE7CbD1a717b28B89fC", - "aggregationHook": "0x1C8A2588b8038BF9B7b1b60dD0EdF5b995A45599", - "protocolFee": "0x962e30F6A3ECDA85c7fa1FcF38cD04efA991Ee20", - "fallbackRoutingHook": "0xc775c748F8c9F5443151Fd989e8B61375657474d", - "testRecipient": "0x518eA1802407b4b5AAF3aA92c1A803FfbA9FB7fe", - "testTokenRecipient": "0xB9E45eA920DE14e95A16Ed5e1275F893552f2e32", - "routingIsmFactory": "0xce8E9D701A1DFfe672c1d8dB20De2B3fa6F4437D" - }, - "scrollsepolia": { - "merkleRootMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "messageIdMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "aggregationIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "aggregationHookFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "proxyAdmin": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", - "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381", - "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", - "storageGasOracle": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", - "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", - "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", - "protocolFee": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", - "fallbackRoutingHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", - "testRecipient": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", - "testTokenRecipient": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", - "routingIsmFactory": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153" - }, "alfajores": { - "merkleRootMultisigIsmFactory": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", - "messageIdMultisigIsmFactory": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", - "aggregationIsmFactory": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", + "aggregationHook": "0xdBabD76358897E68E4964647C1fb8Bf524f5EFdB", "aggregationHookFactory": "0x71bB34Ee833467443628CEdFAA188B2387827Cee", - "proxyAdmin": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", + "aggregationIsmFactory": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", + "fallbackRoutingHook": "0x3528B1aeF3a3d29E0eae90ad777A2b4A6a48aC3F", + "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", "mailbox": "0xEf9F292fcEBC3848bF4bB92a96a04F9ECBb78E59", - "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", + "merkleRootMultisigIsmFactory": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", "merkleTreeHook": "0x221FA9CBaFcd6c1C3d206571Cf4427703e023FFa", - "storageGasOracle": "0x8356113754C7aCa297Db3089b89F87CC125499fb", - "interchainGasPaymaster": "0x44769b0f4a6f01339e131a691cc2eebbb519d297", - "aggregationHook": "0xdBabD76358897E68E4964647C1fb8Bf524f5EFdB", + "messageIdMultisigIsmFactory": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", "protocolFee": "0xC9D50584F08Bf6cCD1004d14c7062044b45E3b48", - "fallbackRoutingHook": "0x3528B1aeF3a3d29E0eae90ad777A2b4A6a48aC3F", + "proxyAdmin": "0x4eDBf5846D973c53AF478cf62aB5bC92807521e3", + "routingIsmFactory": "0x30d9A03762431F8A917a0C469E7A62Bf55092Ca6", + "storageGasOracle": "0x8356113754C7aCa297Db3089b89F87CC125499fb", "testRecipient": "0x6489d13AcAd3B8dce4c5B31f375DE4f9451E7b38", "testTokenRecipient": "0x92dC0a76452a9D9358D2d2dEd8CddA209DF67c45", - "routingIsmFactory": "0x30d9A03762431F8A917a0C469E7A62Bf55092Ca6" - }, - "polygonzkevmtestnet": { - "merkleRootMultisigIsmFactory": "0xfc6e546510dC9d76057F1f76633FCFfC188CB213", - "messageIdMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", - "aggregationIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", - "aggregationHookFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "proxyAdmin": "0x666a24F62f7A97BA33c151776Eb3D9441a059eB8", - "mailbox": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", - "validatorAnnounce": "0x7914A3349107A7295Bbf2374db5A973d73D1b324", - "merkleTreeHook": "0x68311418D79fE8d96599384ED767d225635d88a8", - "storageGasOracle": "0x3707bc8C7342aA6f693bCe1Bd7671Fca146F7F0A", - "interchainGasPaymaster": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", - "aggregationHook": "0x0Fd2C6F0Ad45e766660b9fDebCF36a2AD69536D1", - "protocolFee": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", - "fallbackRoutingHook": "0xBF2C366530C1269d531707154948494D3fF4AcA7", - "testRecipient": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", - "testTokenRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48", - "routingIsmFactory": "0xc08675806BA844467E559E45E4bB59e66778bDcd" + "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0" }, - "sepolia": { - "merkleRootMultisigIsmFactory": "0x0a71AcC99967829eE305a285750017C4916Ca269", - "messageIdMultisigIsmFactory": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", - "aggregationIsmFactory": "0xC83e12EF2627ACE445C298e6eC418684918a6002", - "aggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", - "proxyAdmin": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", - "storageGasOracle": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", - "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", - "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", - "protocolFee": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", - "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", - "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", - "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", - "fallbackRoutingHook": "0x17Dc724B7a2F09141C13b8AC33B396073785c2BC", - "testRecipient": "0xeDc1A3EDf87187085A3ABb7A9a65E1e7aE370C07", - "testTokenRecipient": "0x031AD9c560D37baC7d6Bd2d27A2443bAfd10101A", - "routingIsmFactory": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad" + "bsctestnet": { + "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", + "aggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", + "aggregationIsmFactory": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", + "fallbackRoutingHook": "0x2670ED2EC08cAd135307556685a96bD4c16b007b", + "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", + "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", + "merkleRootMultisigIsmFactory": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", + "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", + "messageIdMultisigIsmFactory": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", + "protocolFee": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", + "proxyAdmin": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", + "storageGasOracle": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", + "testRecipient": "0xfbcD1c00a3d809f36cC1A15918694B17B32c0b6c", + "testTokenRecipient": "0x260f6024119549a40595d0937471e607411E8ea5", + "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465" }, "fuji": { - "merkleRootMultisigIsmFactory": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", - "messageIdMultisigIsmFactory": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", - "aggregationIsmFactory": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", + "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", "aggregationHookFactory": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", - "proxyAdmin": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", + "aggregationIsmFactory": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", + "fallbackRoutingHook": "0xc684f7F50DB4b2563218512e021fBdd0BeD6b57E", + "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", "mailbox": "0x5b6CFf85442B851A8e6eaBd2A4E4507B5135B3B0", - "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", + "merkleRootMultisigIsmFactory": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", "merkleTreeHook": "0x9ff6ac3dAf63103620BBf76136eA1AFf43c2F612", - "storageGasOracle": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", - "interchainGasPaymaster": "0x6895d3916B94b386fAA6ec9276756e16dAe7480E", - "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", + "messageIdMultisigIsmFactory": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", "protocolFee": "0xEbA64c8a9b4a61a9210d5fe7E4375380999C821b", - "fallbackRoutingHook": "0xc684f7F50DB4b2563218512e021fBdd0BeD6b57E", + "proxyAdmin": "0x378dA02f7dC3c23A8B5ecE32b8056CdF01e8d477", + "routingIsmFactory": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", + "storageGasOracle": "0x9305dE34306886d615B096Bdf23b94a978f6a6c0", "testRecipient": "0x44a7e1d76fD8AfA244AdE7278336E3D5C658D398", "testTokenRecipient": "0x9CC10c844B3Bbae2444E39991aB027C4A05D1F2e", - "routingIsmFactory": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f" - }, - "bsctestnet": { - "merkleRootMultisigIsmFactory": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", - "messageIdMultisigIsmFactory": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", - "aggregationIsmFactory": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", - "aggregationHookFactory": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", - "proxyAdmin": "0xb12282d2E838Aa5f2A4F9Ee5f624a77b7199A078", - "storageGasOracle": "0x124EBCBC018A5D4Efe639f02ED86f95cdC3f6498", - "interchainGasPaymaster": "0x0dD20e410bdB95404f71c5a4e7Fa67B892A5f949", - "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", - "protocolFee": "0x3eF0a63B8976b838704Bcc93C78C56b6653E5a39", - "mailbox": "0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D", - "merkleTreeHook": "0xc6cbF39A747f5E28d1bDc8D9dfDAb2960Abd5A8f", - "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465", - "fallbackRoutingHook": "0x2670ED2EC08cAd135307556685a96bD4c16b007b", - "testRecipient": "0xfbcD1c00a3d809f36cC1A15918694B17B32c0b6c", - "testTokenRecipient": "0x260f6024119549a40595d0937471e607411E8ea5" - }, - "goerli": { - "merkleRootMultisigIsmFactory": "0x8e43aCfb338B137A3befd9b92BfD84E128adE0B8", - "messageIdMultisigIsmFactory": "0xDdB54502A8e2a31C48148C62A8a9E83a693d6173", - "aggregationIsmFactory": "0x8a176773d54292123d271FA0B9C7C8Def4c3a31b", - "aggregationHookFactory": "0x6bc243963f80AEa80948e8538bB114d4122DD9c5", - "proxyAdmin": "0x0EdB3604D230963ecE9d83963164CFe2fDef576B", - "storageGasOracle": "0xeC34c715ee6d050b2172E8aF650Db779561266C1", - "interchainGasPaymaster": "0x0cD26594ea6c6526927C0F5225AC09F6288e7140", - "aggregationHook": "0x2dF77b3efe9B8f9aEDf7bFC86f40B048178d8116", - "protocolFee": "0x9293B8dAcA7933765de499C992B0Fa86Bb104b0f", - "merkleTreeHook": "0x28c294C61D3dE053462d2Cfa5d5f8c8D70605A59", - "mailbox": "0x49cfd6Ef774AcAb14814D699e3F7eE36Fdfba932", - "validatorAnnounce": "0x3c182AD9cA8A71bc107Ef440C2667E8360e1158E", - "fallbackRoutingHook": "0xd9E546CBB9577dC6346EdB40b24E86aE52487ab8", - "testRecipient": "0x4fC0Ac163eFFEb7890937cB89275B2C231880F22", - "testTokenRecipient": "0xd8958706B33E20C88679a22203F0AFa6158c834d", - "routingIsmFactory": "0xeB998dC788E2c1e772d198d32e50890544776e75" - }, - "moonbasealpha": { - "merkleRootMultisigIsmFactory": "0xA59Ba0A8D4ea5A5DC9c8B0101ba7E6eE6C3399A4", - "messageIdMultisigIsmFactory": "0x8f919348F9C4619A196Acb5e377f49E5E2C0B569", - "aggregationIsmFactory": "0x0048FaB53526D9a0478f66D660059E3E3611FE3E", - "aggregationHookFactory": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", - "proxyAdmin": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", - "mailbox": "0x76189acFA212298d7022624a4633411eE0d2f26F", - "merkleTreeHook": "0x155B1CD2f7Cbc58d403B9BE341FaB6CD77425175", - "storageGasOracle": "0x62fA20dE68Dbe425f0bc474b12235a4F8449E608", - "interchainGasPaymaster": "0x92F05669A354a032A84FcfABfD13beE1aBc5bFd0", - "aggregationHook": "0xaA9d918C49Cea0D2a877252aFb7976B6e3A48623", - "protocolFee": "0xe2A73F106902983452713F24Bd019F6eb8712986", - "validatorAnnounce": "0x07543860AE9E72aBcF2Bae9827b23621A64Fa416", - "fallbackRoutingHook": "0xf666A33C451E8371907aD22dd545E1678fCa1582", - "testRecipient": "0x68729446296E413f0b7d6E85F2FD128465F0e5D0", - "testTokenRecipient": "0xEdA6f85f4761A1f9e42FD40CA5a4E8Ce1C764015", - "routingIsmFactory": "0xE8F752e5C4E1A6a2e3eAfa42d44D601A22d78f2b" + "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a" }, "mumbai": { - "merkleRootMultisigIsmFactory": "0xda0780ed3eE577EfE0B856E00f983bF231603307", - "messageIdMultisigIsmFactory": "0x23c2483ab814177bA79DCDCb5dFA1B105387AAB1", - "aggregationIsmFactory": "0x54b0d9AB6a99E9C9425D20fa4D9eE9dbf067e886", + "aggregationHook": "0xD546273418733AcEC8c7A67EfB881c9Ea83851bf", "aggregationHookFactory": "0x54CA9De95B37365909364672D363D2ecFC4e1Af4", + "aggregationIsmFactory": "0x54b0d9AB6a99E9C9425D20fa4D9eE9dbf067e886", + "fallbackRoutingHook": "0x31191BA83143b4745745389fEe64990c65F36829", + "interchainGasPaymaster": "0x8aB67CAF605c6ee83cbFeFb0D8d67FDd3BF7B591", + "mailbox": "0x2d1889fe5B092CD988972261434F7E5f26041115", + "merkleRootMultisigIsmFactory": "0xda0780ed3eE577EfE0B856E00f983bF231603307", "merkleTreeHook": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", + "messageIdMultisigIsmFactory": "0x23c2483ab814177bA79DCDCb5dFA1B105387AAB1", + "protocolFee": "0x244d1F7e30Be144A87602905baBF86630e8f39DC", "proxyAdmin": "0xa99aD6B1c10E92DB8d3510f1865A6d2Ab43EAd58", + "routingIsmFactory": "0x832Ea28749C93C05E5AaF8207E4e61Bd56aE3877", "storageGasOracle": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", - "interchainGasPaymaster": "0x8aB67CAF605c6ee83cbFeFb0D8d67FDd3BF7B591", - "aggregationHook": "0xD546273418733AcEC8c7A67EfB881c9Ea83851bf", - "protocolFee": "0x244d1F7e30Be144A87602905baBF86630e8f39DC", - "mailbox": "0x2d1889fe5B092CD988972261434F7E5f26041115", - "validatorAnnounce": "0x99303EFF09332cDd93E8BC8b2F07b2416e4501e5", - "fallbackRoutingHook": "0x31191BA83143b4745745389fEe64990c65F36829", "testRecipient": "0xF45A4D54223DA32bf7b5D43a9a460Ef3C94C713B", "testTokenRecipient": "0x57d098e6952B6C1c85Ce0B68C9Deada3dCf7D05A", - "routingIsmFactory": "0x832Ea28749C93C05E5AaF8207E4e61Bd56aE3877" + "validatorAnnounce": "0x99303EFF09332cDd93E8BC8b2F07b2416e4501e5" + }, + "plumetestnet": { + "aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", + "aggregationHookFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "aggregationIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "fallbackRoutingHook": "0x19Be55D859368e02d7b9C00803Eb677BDC1359Bd", + "interchainGasPaymaster": "0x28B02B97a850872C4D33C3E024fab6499ad96564", + "interchainSecurityModule": "0x7B40deb01A127E3A5eECdbCDF263e41899a90078", + "mailbox": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "merkleRootMultisigIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "merkleTreeHook": "0xddf4C3e791caCaFd26D7fb275549739B38ae6e75", + "messageIdMultisigIsmFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "pausableHook": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "protocolFee": "0x1b33611fCc073aB0737011d5512EF673Bff74962", + "proxyAdmin": "0x589C201a07c26b4725A4A829d772f24423da480B", + "routingIsmFactory": "0x54148470292C24345fb828B003461a9444414517", + "storageGasOracle": "0x267B6B6eAf6790faE5D5E9070F28a9cE64CbF279", + "testRecipient": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9" + }, + "scrollsepolia": { + "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", + "aggregationHookFactory": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "aggregationIsmFactory": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "fallbackRoutingHook": "0xE1CCB130389f687bf745Dd6dc05E50da17d9ea96", + "interchainGasPaymaster": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "mailbox": "0x3C5154a193D6e2955650f9305c8d80c18C814A68", + "merkleRootMultisigIsmFactory": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "merkleTreeHook": "0x863E8c26621c52ACa1849C53500606e73BA272F0", + "messageIdMultisigIsmFactory": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "protocolFee": "0x5821f3B6eE05F3dC62b43B74AB1C8F8E6904b1C8", + "proxyAdmin": "0x598facE78a4302f11E3de0bee1894Da0b2Cb71F8", + "routingIsmFactory": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", + "storageGasOracle": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", + "testRecipient": "0xa3AB7E6cE24E6293bD5320A53329Ef2f4DE73fCA", + "testTokenRecipient": "0xc76E477437065093D353b7d56c81ff54D167B0Ab", + "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381" + }, + "sepolia": { + "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", + "aggregationHookFactory": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", + "aggregationIsmFactory": "0xC83e12EF2627ACE445C298e6eC418684918a6002", + "fallbackRoutingHook": "0x17Dc724B7a2F09141C13b8AC33B396073785c2BC", + "interchainGasPaymaster": "0x6f2756380FD49228ae25Aa7F2817993cB74Ecc56", + "interchainSecurityModule": "0x958124472b14B7940Ed5317C44a2508791dB1d48", + "mailbox": "0xfFAEF09B3cd11D9b20d1a19bECca54EEC2884766", + "merkleRootMultisigIsmFactory": "0x0a71AcC99967829eE305a285750017C4916Ca269", + "merkleTreeHook": "0x4917a9746A7B6E0A57159cCb7F5a6744247f2d0d", + "messageIdMultisigIsmFactory": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", + "pausableHook": "0xa68022e53Fd28119D07C8336a8eC84A298Fd38Fd", + "protocolFee": "0x13AC3349Cb159fE86A22cf42DdA803D9f7309DB5", + "proxyAdmin": "0x97Bbc6bBaFa5Ce3b2FA966c121Af63bD09e940f8", + "routingIsmFactory": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", + "storageGasOracle": "0x71775B071F77F1ce52Ece810ce084451a3045FFe", + "testRecipient": "0xeDc1A3EDf87187085A3ABb7A9a65E1e7aE370C07", + "testTokenRecipient": "0x031AD9c560D37baC7d6Bd2d27A2443bAfd10101A", + "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9" } } diff --git a/typescript/sdk/src/consts/igp.ts b/typescript/sdk/src/consts/igp.ts new file mode 100644 index 0000000000..913dc50c73 --- /dev/null +++ b/typescript/sdk/src/consts/igp.ts @@ -0,0 +1 @@ +export const TOKEN_EXCHANGE_RATE_EXPONENT = 10; diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index fbd1215a43..e19fad5b9e 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -22,15 +22,6 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - arbitrumgoerli: { - threshold: 2, - validators: [ - '0x071c8d135845ae5a2cb73f98d681d519014c0a8b', - '0x1bcf03360989f15cbeb174c188288f2c6d2760d7', - '0xc1590eaaeaf380e7859564c5ebcdcc87e8369e0d', - ], - }, - avalanche: { threshold: 2, validators: [ @@ -51,15 +42,6 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - basegoerli: { - threshold: 2, - validators: [ - '0xf6eddda696dcd3bf10f7ce8a02db31ef2e775a03', - '0x5a7d05cebf5db4dde9b2fedcefa76fb58fa05071', - '0x9260a6c7d54cbcbed28f8668679cd1fa3a203b25', - ], - }, - bsc: { threshold: 2, validators: [ @@ -99,6 +81,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + eclipsetestnet: { + threshold: 1, + validators: ['0xf344f34abca9a444545b5295066348a0ae22dda3'], + }, + ethereum: { threshold: 3, validators: [ @@ -129,21 +116,21 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - goerli: { + inevm: { threshold: 2, validators: [ - '0x05a9b5efe9f61f9142453d8e9f61565f333c6768', - '0x43a96c7dfbd8187c95013d6ee8665650cbdb2673', - '0x7940a12c050e24e1839c21ecb12f65afd84e8c5b', + '0xf9e35ee88e4448a3673b4676a4e153e3584a08eb', + '0x6B1d09A97b813D53e9D4b7523DA36604C0B52242', // caldera + '0x9ab11f38a609940153850df611c9a2175dcffe0f', // imperator ], }, - lineagoerli: { + injective: { threshold: 2, validators: [ - '0xd767ea1206b8295d7e1267ddd00e56d34f278db6', - '0x4a5d7085ca93c22fbc994dd97857c98fcc745674', - '0x8327779c3c31fa1ffc7f0c9ffae33e4d804bbd8f', + '0xbfb8911b72cfb138c7ce517c57d9c691535dc517', + '0x6B1d09A97b813D53e9D4b7523DA36604C0B52242', // caldera + '0x9e551b6694bbd295d7d6e6a2540c7d41ce70a3b9', // imperator ], }, @@ -160,15 +147,6 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - moonbasealpha: { - threshold: 2, - validators: [ - '0x521877064bd7ac7500d300f162c8c47c256a2f9c', - '0xbc1c70f58ae0459d4b8a013245420a893837d568', - '0x01e42c2c44af81dda1ac16fec76fea2a7a54a44c', - ], - }, - moonbeam: { threshold: 2, validators: [ @@ -189,11 +167,15 @@ export const defaultMultisigConfigs: ChainMap = { }, neutron: { - threshold: 2, + threshold: 4, validators: [ '0xa9b8c1f4998f781f958c63cfcd1708d02f004ff0', - '0x60e890b34cb44ce3fa52f38684f613f31b47a1a6', - '0x7885fae56dbcf5176657f54adbbd881dc6714132', + '0xb65438a014fb05fbadcfe35bc6e25d372b6ba460', // cosmostation + '0x42fa752defe92459370a052b6387a87f7de9b80c', // p2p + '0xc79503a3e3011535a9c60f6d21f76f59823a38bd', // neutron + '0x47aa126e05933b95c5eb90b26e6b668d84f4b25a', // dsrv + '0x54b2cca5091b098a1a993dec03c4d1ee9af65999', // cosmos spaces + '0x42b6de2edbaa62c2ea2309ad85d20b3e37d38acf', // sg-1 ], }, @@ -208,13 +190,9 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - optimismgoerli: { - threshold: 2, - validators: [ - '0x79e58546e2faca865c6732ad5f6c4951051c4d67', - '0x7bbfe1bb7146aad7df309c637987d856179ebbc1', - '0xf3d2fb4d53c2bb6a88cec040e0d87430fcee4e40', - ], + plumetestnet: { + threshold: 1, + validators: ['0xe765a214849f3ecdf00793b97d00422f2d408ea6'], }, polygon: { @@ -236,15 +214,6 @@ export const defaultMultisigConfigs: ChainMap = { ], }, - polygonzkevmtestnet: { - threshold: 2, - validators: [ - '0x3f06b725bc9648917eb11c414e9f8d76fd959550', - '0x27bfc57679d9dd4ab2e870f5ed7ec0b339a0b636', - '0xd476548222f43206d0abaa30e46e28670aa7859c', - ], - }, - scroll: { threshold: 2, validators: [ @@ -281,4 +250,18 @@ export const defaultMultisigConfigs: ChainMap = { '0x967c5ecdf2625ae86580bd203b630abaaf85cd62', ], }, + + solanatestnet: { + threshold: 1, + validators: ['0xd4ce8fa138d4e083fc0e480cca0dbfa4f5f30bd5'], + }, + + viction: { + threshold: 2, + validators: [ + '0x4E53dA92cD5Bf0a032b6B4614b986926456756A7', // blockpi + '0xa3f93fe365bf99f431d8fde740b140615e24f99b', // rockx + '0x1f87c368f8e05a85ef9126d984a980a20930cb9c', + ], + }, }; diff --git a/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts b/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts index 44d2e1d1d0..8bcc44d20f 100644 --- a/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts +++ b/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts @@ -111,8 +111,10 @@ describe('core', async () => { await deployer.deploy(updatedConfig); - // one aggregation ISM deploy and one set ISM transaction per chain - const numTransactions = 2 * TestChains.length; + // 3x1 for aggregation ISM deploy + // 3x1 for setting ISM transaction for mailbox + // 3x1 for setting ISM transaction for test recipient + const numTransactions = 3 * TestChains.length; const nonceAfter = await signer.getTransactionCount(); expect(nonceAfter).to.equal(nonceBefore + numTransactions); }); diff --git a/typescript/sdk/src/core/HyperlaneCore.ts b/typescript/sdk/src/core/HyperlaneCore.ts index 78d922428d..b373874340 100644 --- a/typescript/sdk/src/core/HyperlaneCore.ts +++ b/typescript/sdk/src/core/HyperlaneCore.ts @@ -5,19 +5,23 @@ import { Mailbox__factory } from '@hyperlane-xyz/core'; import { Address, AddressBytes32, + ProtocolType, messageId, + objFilter, objMap, parseMessage, pollAsync, } from '@hyperlane-xyz/utils'; import { HyperlaneApp } from '../app/HyperlaneApp'; +import { chainMetadata } from '../consts/chainMetadata'; import { HyperlaneEnvironment, hyperlaneEnvironments, } from '../consts/environments'; import { appFromAddressesMapHelper } from '../contracts/contracts'; import { HyperlaneAddressesMap } from '../contracts/types'; +import { OwnableConfig } from '../deploy/types'; import { MultiProvider } from '../providers/MultiProvider'; import { RouterConfig } from '../router/types'; import { ChainMap, ChainName } from '../types'; @@ -50,14 +54,20 @@ export class HyperlaneCore extends HyperlaneApp { } getRouterConfig = ( - owners: Address | ChainMap
, - ): ChainMap => - objMap(this.contractsMap, (chain, contracts) => { - return { - mailbox: contracts.mailbox.address, - owner: typeof owners === 'string' ? owners : owners[chain], - }; - }); + owners: Address | ChainMap, + ): ChainMap => { + // get config + const config = objMap(this.contractsMap, (chain, contracts) => ({ + mailbox: contracts.mailbox.address, + owner: typeof owners === 'string' ? owners : owners[chain].owner, + })); + // filter for EVM chains + return objFilter( + config, + (chainName, _): _ is RouterConfig => + chainMetadata[chainName].protocol === ProtocolType.Ethereum, + ); + }; quoteGasPayment = ( origin: ChainName, diff --git a/typescript/sdk/src/core/HyperlaneCoreChecker.ts b/typescript/sdk/src/core/HyperlaneCoreChecker.ts index 8dc0545a7d..b1e3fabd67 100644 --- a/typescript/sdk/src/core/HyperlaneCoreChecker.ts +++ b/typescript/sdk/src/core/HyperlaneCoreChecker.ts @@ -5,11 +5,8 @@ import { Address, assert, eqAddress } from '@hyperlane-xyz/utils'; import { BytecodeHash } from '../consts/bytecode'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker'; import { proxyImplementation } from '../deploy/proxy'; -import { - HyperlaneIsmFactory, - collectValidators, - moduleMatchesConfig, -} from '../ism/HyperlaneIsmFactory'; +import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; +import { collectValidators, moduleMatchesConfig } from '../ism/utils'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; @@ -150,15 +147,8 @@ export class HyperlaneCoreChecker extends HyperlaneAppChecker< ); } - await this.checkBytecode( - chain, - 'Mailbox proxy', - contracts.mailbox.address, - [ - BytecodeHash.TRANSPARENT_PROXY_BYTECODE_HASH, - BytecodeHash.OPT_TRANSPARENT_PROXY_BYTECODE_HASH, - ], - ); + await this.checkProxy(chain, 'Mailbox proxy', contracts.mailbox.address); + await this.checkBytecode( chain, 'ProxyAdmin', diff --git a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts index 6e1226b6fc..41bc9fef92 100644 --- a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts +++ b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts @@ -1,20 +1,28 @@ import debug from 'debug'; -import { Mailbox, ValidatorAnnounce } from '@hyperlane-xyz/core'; +import { + IPostDispatchHook, + Mailbox, + TestRecipient, + ValidatorAnnounce, +} from '@hyperlane-xyz/core'; import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneContracts } from '../contracts/types'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; +import { ContractVerifier } from '../deploy/verify/ContractVerifier'; import { HyperlaneHookDeployer } from '../hook/HyperlaneHookDeployer'; import { HookConfig } from '../hook/types'; -import { - HyperlaneIsmFactory, - moduleMatchesConfig, -} from '../ism/HyperlaneIsmFactory'; +import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; import { IsmConfig } from '../ism/types'; +import { moduleMatchesConfig } from '../ism/utils'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; +import { + TestRecipientConfig, + TestRecipientDeployer, +} from './TestRecipientDeployer'; import { CoreAddresses, CoreFactories, coreFactories } from './contracts'; import { CoreConfig } from './types'; @@ -23,20 +31,28 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< CoreFactories > { hookDeployer: HyperlaneHookDeployer; + testRecipient: TestRecipientDeployer; constructor( multiProvider: MultiProvider, readonly ismFactory: HyperlaneIsmFactory, + contractVerifier?: ContractVerifier, ) { super(multiProvider, coreFactories, { logger: debug('hyperlane:CoreDeployer'), chainTimeoutMs: 1000 * 60 * 10, // 10 minutes ismFactory, + contractVerifier, }); this.hookDeployer = new HyperlaneHookDeployer( multiProvider, {}, ismFactory, + contractVerifier, + ); + this.testRecipient = new TestRecipientDeployer( + multiProvider, + contractVerifier, ); } @@ -100,8 +116,8 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< mailbox.initialize( config.owner, defaultIsm, - defaultHook, - requiredHook, + defaultHook.address, + requiredHook.address, this.multiProvider.getTransactionOverrides(chain), ), ); @@ -119,12 +135,14 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< this.logger('Mailbox already initialized'); + const overrides = this.multiProvider.getTransactionOverrides(chain); await this.configureHook( chain, mailbox, defaultHook, (_mailbox) => _mailbox.defaultHook(), - (_mailbox, _hook) => _mailbox.populateTransaction.setDefaultHook(_hook), + (_mailbox, _hook) => + _mailbox.populateTransaction.setDefaultHook(_hook, { ...overrides }), ); await this.configureHook( @@ -133,7 +151,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< requiredHook, (_mailbox) => _mailbox.requiredHook(), (_mailbox, _hook) => - _mailbox.populateTransaction.setRequiredHook(_hook), + _mailbox.populateTransaction.setRequiredHook(_hook, { ...overrides }), ); await this.configureIsm( @@ -165,7 +183,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< chain: ChainName, config: HookConfig, coreAddresses: Partial, - ): Promise
{ + ): Promise { const hooks = await this.hookDeployer.deployContracts( chain, config, @@ -176,7 +194,7 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< this.hookDeployer.deployedContracts[chain], this.hookDeployer.verificationInputs[chain], ); - return hooks[config.type].address; + return hooks[config.type]; } async deployIsm( @@ -193,6 +211,21 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< return ism.address; } + async deployTestRecipient( + chain: ChainName, + interchainSecurityModule: Address, + ): Promise { + const config: TestRecipientConfig = { + interchainSecurityModule: interchainSecurityModule, + }; + const testRecipient = await this.testRecipient.deployContracts( + chain, + config, + ); + this.addDeployedContracts(chain, testRecipient); + return testRecipient.testRecipient; + } + async deployContracts( chain: ChainName, config: CoreConfig, @@ -211,23 +244,31 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< mailbox.address, ); - let proxyOwner: string; + const testRecipient = await this.deployTestRecipient( + chain, + this.cachedAddresses[chain].interchainSecurityModule, + ); + if (config.upgrade) { const timelockController = await this.deployTimelock( chain, config.upgrade.timelock, ); - proxyOwner = timelockController.address; - } else { - proxyOwner = config.owner; + config.ownerOverrides = { + ...config.ownerOverrides, + proxyAdmin: timelockController.address, + }; } - await this.transferOwnershipOfContracts(chain, proxyOwner, { proxyAdmin }); - - return { + const contracts = { mailbox, proxyAdmin, validatorAnnounce, + testRecipient, }; + + await this.transferOwnershipOfContracts(chain, config, contracts); + + return contracts; } } diff --git a/typescript/sdk/src/core/MultiProtocolCore.ts b/typescript/sdk/src/core/MultiProtocolCore.ts index ee0100bf9a..dcc9c00302 100644 --- a/typescript/sdk/src/core/MultiProtocolCore.ts +++ b/typescript/sdk/src/core/MultiProtocolCore.ts @@ -1,6 +1,6 @@ import debug from 'debug'; -import { ProtocolType } from '@hyperlane-xyz/utils'; +import { HexString, ProtocolType } from '@hyperlane-xyz/utils'; import { AdapterClassType, MultiProtocolApp } from '../app/MultiProtocolApp'; import { @@ -59,6 +59,13 @@ export class MultiProtocolCore extends MultiProtocolApp< throw new Error(`No adapter for protocol ${protocol}`); } + extractMessageIds( + origin: ChainName, + sourceTx: TypedTransactionReceipt, + ): Array<{ messageId: HexString; destination: ChainName }> { + return this.adapter(origin).extractMessageIds(sourceTx); + } + async waitForMessagesProcessed( origin: ChainName, destination: ChainName, diff --git a/typescript/sdk/src/core/TestRecipientDeployer.ts b/typescript/sdk/src/core/TestRecipientDeployer.ts index 6c5dc42463..90f7c12bb5 100644 --- a/typescript/sdk/src/core/TestRecipientDeployer.ts +++ b/typescript/sdk/src/core/TestRecipientDeployer.ts @@ -4,6 +4,7 @@ import { TestRecipient, TestRecipient__factory } from '@hyperlane-xyz/core'; import { Address, eqAddress } from '@hyperlane-xyz/utils'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; +import { ContractVerifier } from '../deploy/verify/ContractVerifier'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainName } from '../types'; @@ -28,9 +29,13 @@ export class TestRecipientDeployer extends HyperlaneDeployer< TestRecipientConfig, typeof testRecipientFactories > { - constructor(multiProvider: MultiProvider) { + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { super(multiProvider, testRecipientFactories, { logger: debug('hyperlane:TestRecipientDeployer'), + contractVerifier, }); } diff --git a/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.test.ts b/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.test.ts new file mode 100644 index 0000000000..686b044f27 --- /dev/null +++ b/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.test.ts @@ -0,0 +1,36 @@ +import { expect } from 'chai'; + +import { Chains } from '../../consts/chains'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; +import { ProviderType } from '../../providers/ProviderType'; + +import { CosmWasmCoreAdapter } from './CosmWasmCoreAdapter'; + +const TX_RECEIPT = JSON.parse( + `{"height":62609917,"transactionHash":"3106A2BECE7FC03EC6F7FD3BC1577B9456C8B481A017A3A51AD7E9D27D9240A7","events":[{"type":"coin_spent","attributes":[{"key":"spender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"},{"key":"amount","value":"948228000000000inj"}]},{"type":"coin_received","attributes":[{"key":"receiver","value":"inj17xpfvakm2amg962yls6f84z3kell8c5l6s5ye9"},{"key":"amount","value":"948228000000000inj"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"inj17xpfvakm2amg962yls6f84z3kell8c5l6s5ye9"},{"key":"sender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"},{"key":"amount","value":"948228000000000inj"}]},{"type":"message","attributes":[{"key":"sender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"}]},{"type":"tx","attributes":[{"key":"fee","value":"948228000000000inj"},{"key":"fee_payer","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"}]},{"type":"tx","attributes":[{"key":"acc_seq","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up/20"}]},{"type":"tx","attributes":[{"key":"signature","value":"9XC7pL/hEa4PvJQxyAKdLpucpA/t+lNKzqfeSgUYTw9Old05Lbfx95GkIaXnuTOspCvYIZIuLesJ5wQHdL1Ljw=="}]},{"type":"message","attributes":[{"key":"action","value":"/cosmwasm.wasm.v1.MsgExecuteContract"},{"key":"sender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"},{"key":"module","value":"wasm"}]},{"type":"coin_spent","attributes":[{"key":"spender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"},{"key":"amount","value":"31000000000000000inj"}]},{"type":"coin_received","attributes":[{"key":"receiver","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"},{"key":"amount","value":"31000000000000000inj"}]},{"type":"transfer","attributes":[{"key":"recipient","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"},{"key":"sender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"},{"key":"amount","value":"31000000000000000inj"}]},{"type":"execute","attributes":[{"key":"_contract_address","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"}]},{"type":"wasm-hpl_warp_native::transfer-remote","attributes":[{"key":"_contract_address","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"},{"key":"sender","value":"inj16paaazy6t2ac02q5t8en099csy7pkyh3hw35up"},{"key":"recipient","value":"0000000000000000000000009a2d8681ffcc45b0c18e72b16fba9b2270b911ed"},{"key":"token","value":"inj"},{"key":"amount","value":"1000000000000000"}]},{"type":"coin_spent","attributes":[{"key":"amount","value":"30000000000000000inj"},{"key":"spender","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"}]},{"type":"coin_received","attributes":[{"key":"amount","value":"30000000000000000inj"},{"key":"receiver","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"}]},{"type":"transfer","attributes":[{"key":"amount","value":"30000000000000000inj"},{"key":"recipient","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"},{"key":"sender","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"}]},{"type":"execute","attributes":[{"key":"_contract_address","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"}]},{"type":"wasm-mailbox_dispatch_id","attributes":[{"key":"_contract_address","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"},{"key":"message_id","value":"afc6cabcf735ac7b13fb4f1a045c4d675ecf8363cac76a21612411e644041af2"}]},{"type":"wasm-mailbox_dispatch","attributes":[{"key":"_contract_address","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"},{"key":"destination","value":"2525"},{"key":"message","value":"030000032e00696e6a000000000000000000000000db0ab932dd778c771c85636070d920ae90a66136000009dd00000000000000000000000026f32245fcf5ad53159e875d5cae62aecf19c2d40000000000000000000000009a2d8681ffcc45b0c18e72b16fba9b2270b911ed00000000000000000000000000000000000000000000000000038d7ea4c68000"},{"key":"recipient","value":"00000000000000000000000026f32245fcf5ad53159e875d5cae62aecf19c2d4"},{"key":"sender","value":"000000000000000000000000db0ab932dd778c771c85636070d920ae90a66136"}]},{"type":"execute","attributes":[{"key":"_contract_address","value":"inj1269dxcuyglc8mmecf95lf63elt3cq2tz57ka6h"}]},{"type":"wasm-hpl_hook_merkle::post_dispatch","attributes":[{"key":"_contract_address","value":"inj1269dxcuyglc8mmecf95lf63elt3cq2tz57ka6h"},{"key":"index","value":"814"},{"key":"message_id","value":"afc6cabcf735ac7b13fb4f1a045c4d675ecf8363cac76a21612411e644041af2"}]},{"type":"wasm-hpl_hook_merkle::inserted_into_tree","attributes":[{"key":"_contract_address","value":"inj1269dxcuyglc8mmecf95lf63elt3cq2tz57ka6h"},{"key":"index","value":"814"}]},{"type":"coin_spent","attributes":[{"key":"amount","value":"30000000000000000inj"},{"key":"spender","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"}]},{"type":"coin_received","attributes":[{"key":"amount","value":"30000000000000000inj"},{"key":"receiver","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"}]},{"type":"transfer","attributes":[{"key":"amount","value":"30000000000000000inj"},{"key":"recipient","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"},{"key":"sender","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"}]},{"type":"execute","attributes":[{"key":"_contract_address","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"}]},{"type":"wasm-igp-core-pay-for-gas","attributes":[{"key":"_contract_address","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"},{"key":"dest_domain","value":"2525"},{"key":"gas_amount","value":"250000"},{"key":"gas_refunded","value":"29999999999997500"},{"key":"gas_required","value":"2500"},{"key":"message_id","value":"afc6cabcf735ac7b13fb4f1a045c4d675ecf8363cac76a21612411e644041af2"},{"key":"payment","value":"30000000000000000"},{"key":"sender","value":"inj1palm2wtp6urg0c6j4f2ukv5u5ahdcrqek0sapt"}]},{"type":"wasm-igp-core-post-dispatch","attributes":[{"key":"_contract_address","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"},{"key":"message","value":"030000032e00696e6a000000000000000000000000db0ab932dd778c771c85636070d920ae90a66136000009dd00000000000000000000000026f32245fcf5ad53159e875d5cae62aecf19c2d40000000000000000000000009a2d8681ffcc45b0c18e72b16fba9b2270b911ed00000000000000000000000000000000000000000000000000038d7ea4c68000"},{"key":"metadata","value":"0x"}]},{"type":"coin_spent","attributes":[{"key":"amount","value":"29999999999997500inj"},{"key":"spender","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"}]},{"type":"coin_received","attributes":[{"key":"amount","value":"29999999999997500inj"},{"key":"receiver","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"}]},{"type":"transfer","attributes":[{"key":"amount","value":"29999999999997500inj"},{"key":"recipient","value":"inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k"},{"key":"sender","value":"inj1y7h9y2vwtdfmxjm6ur9x8czcghp3u86e2wtcxr"}]}],"gasWanted":948228,"gasUsed":695543}`, +); + +describe('CosmWasmCoreAdapter', () => { + let adapter: CosmWasmCoreAdapter; + + it('constructs', () => { + adapter = new CosmWasmCoreAdapter( + Chains.injective, + new MultiProtocolProvider(), + { mailbox: '' }, + ); + expect(adapter).to.be.instanceOf(CosmWasmCoreAdapter); + }); + + it('extracts message IDs', () => { + const messages = adapter.extractMessageIds({ + type: ProviderType.CosmJsWasm, + receipt: TX_RECEIPT, + }); + expect(messages).to.have.length(1); + expect(messages[0].messageId).to.equal( + '0xafc6cabcf735ac7b13fb4f1a045c4d675ecf8363cac76a21612411e644041af2', + ); + expect(messages[0].destination).to.equal('inevm'); + }); +}); diff --git a/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts b/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts index 87517dd6b6..d7187d28a1 100644 --- a/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts +++ b/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts @@ -1,6 +1,6 @@ import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate'; -import { Address, HexString } from '@hyperlane-xyz/utils'; +import { Address, HexString, assert, ensure0x } from '@hyperlane-xyz/utils'; import { BaseCosmWasmAdapter } from '../../app/MultiProtocolApp'; import { @@ -24,6 +24,11 @@ import { ChainName } from '../../types'; import { ICoreAdapter } from './types'; +const MESSAGE_DISPATCH_EVENT_TYPE = 'wasm-mailbox_dispatch'; +const MESSAGE_DISPATCH_ID_EVENT_TYPE = 'wasm-mailbox_dispatch_id'; +const MESSAGE_ID_ATTRIBUTE_KEY = 'message_id'; +const MESSAGE_DESTINATION_ATTRIBUTE_KEY = 'destination'; + type MailboxResponse = | DefaultHookResponse | RequiredHookResponse @@ -169,8 +174,32 @@ export class CosmWasmCoreAdapter `Unsupported provider type for CosmosCoreAdapter ${sourceTx.type}`, ); } - // TODO: parse mailbox logs and extract message ids - throw new Error('Method not implemented.'); + const dispatchIdEvents = sourceTx.receipt.events.filter( + (e) => e.type === MESSAGE_DISPATCH_ID_EVENT_TYPE, + ); + const dispatchEvents = sourceTx.receipt.events.filter( + (e) => e.type === MESSAGE_DISPATCH_EVENT_TYPE, + ); + assert( + dispatchIdEvents.length === dispatchEvents.length, + 'Mismatched dispatch and dispatch id events', + ); + const result: Array<{ messageId: string; destination: ChainName }> = []; + for (let i = 0; i < dispatchIdEvents.length; i++) { + const idAttribute = dispatchIdEvents[i].attributes.find( + (a) => a.key === MESSAGE_ID_ATTRIBUTE_KEY, + ); + const destAttribute = dispatchEvents[i].attributes.find( + (a) => a.key === MESSAGE_DESTINATION_ATTRIBUTE_KEY, + ); + assert(idAttribute, 'No message id attribute found in dispatch event'); + assert(destAttribute, 'No destination attribute found in dispatch event'); + result.push({ + messageId: ensure0x(idAttribute.value), + destination: this.multiProvider.getChainName(destAttribute.value), + }); + } + return result; } async waitForMessageProcessed( diff --git a/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts b/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts index bd5adce441..4c584c2311 100644 --- a/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts +++ b/typescript/sdk/src/core/adapters/SealevelCoreAdapter.ts @@ -45,7 +45,6 @@ export class SealevelCoreAdapter if (!logs) throw new Error('Transaction logs required to check message delivery'); const parsedLogs = SealevelCoreAdapter.parseMessageDispatchLogs(logs); - if (!parsedLogs.length) throw new Error('Message dispatch log not found'); return parsedLogs.map(({ destination, messageId }) => ({ messageId: ensure0x(messageId), destination: this.multiProvider.getChainName(destination), diff --git a/typescript/sdk/src/deploy/HyperlaneAppChecker.ts b/typescript/sdk/src/deploy/HyperlaneAppChecker.ts index f8d5418e05..3f703ef74c 100644 --- a/typescript/sdk/src/deploy/HyperlaneAppChecker.ts +++ b/typescript/sdk/src/deploy/HyperlaneAppChecker.ts @@ -11,6 +11,7 @@ import { } from '@hyperlane-xyz/utils'; import { HyperlaneApp } from '../app/HyperlaneApp'; +import { BytecodeHash } from '../consts/bytecode'; import { filterOwnableContracts } from '../contracts/contracts'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; @@ -186,6 +187,18 @@ export abstract class HyperlaneAppChecker< } } + protected async checkProxy( + chain: ChainName, + name: string, + address: string, + ): Promise { + return this.checkBytecode(chain, name, address, [ + BytecodeHash.TRANSPARENT_PROXY_BYTECODE_HASH, + BytecodeHash.TRANSPARENT_PROXY_4_9_3_BYTECODE_HASH, + BytecodeHash.OPT_TRANSPARENT_PROXY_BYTECODE_HASH, + ]); + } + async ownables(chain: ChainName): Promise<{ [key: string]: Ownable }> { const contracts = this.app.getContracts(chain); return filterOwnableContracts(contracts); diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index c47ee7ca67..4714657610 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -2,6 +2,8 @@ import { Debugger, debug } from 'debug'; import { Contract, PopulatedTransaction, ethers } from 'ethers'; import { + IPostDispatchHook, + IPostDispatchHook__factory, ITransparentUpgradeableProxy, MailboxClient, Ownable, @@ -11,6 +13,7 @@ import { TimelockController__factory, TransparentUpgradeableProxy__factory, } from '@hyperlane-xyz/core'; +import SdkBuildArtifact from '@hyperlane-xyz/core/buildArtifact.json'; import { Address, ProtocolType, @@ -24,11 +27,9 @@ import { HyperlaneContractsMap, HyperlaneFactories, } from '../contracts/types'; -import { - HyperlaneIsmFactory, - moduleMatchesConfig, -} from '../ism/HyperlaneIsmFactory'; +import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; import { IsmConfig } from '../ism/types'; +import { moduleMatchesConfig } from '../ism/utils'; import { MultiProvider } from '../providers/MultiProvider'; import { MailboxClientConfig } from '../router/types'; import { ChainMap, ChainName } from '../types'; @@ -40,7 +41,9 @@ import { proxyConstructorArgs, proxyImplementation, } from './proxy'; -import { ContractVerificationInput } from './verify/types'; +import { OwnableConfig } from './types'; +import { ContractVerifier } from './verify/ContractVerifier'; +import { ContractVerificationInput, ExplorerLicenseType } from './verify/types'; import { buildVerificationInput, getContractVerificationInput, @@ -50,6 +53,7 @@ export interface DeployerOptions { logger?: Debugger; chainTimeoutMs?: number; ismFactory?: HyperlaneIsmFactory; + contractVerifier?: ContractVerifier; } export abstract class HyperlaneDeployer< @@ -67,11 +71,20 @@ export abstract class HyperlaneDeployer< constructor( protected readonly multiProvider: MultiProvider, protected readonly factories: Factories, - protected readonly options?: DeployerOptions, + protected readonly options: DeployerOptions = {}, protected readonly recoverVerificationInputs = false, ) { this.logger = options?.logger ?? debug('hyperlane:deployer'); this.chainTimeoutMs = options?.chainTimeoutMs ?? 5 * 60 * 1000; // 5 minute timeout per chain + this.options.ismFactory?.setDeployer(this); + + // if none provided, instantiate a default verifier with SDK's included build artifact + this.options.contractVerifier ??= new ContractVerifier( + multiProvider, + {}, + SdkBuildArtifact, + ExplorerLicenseType.MIT, + ); } cacheAddressesMap(addressesMap: HyperlaneAddressesMap): void { @@ -208,7 +221,7 @@ export abstract class HyperlaneDeployer< } } else { const ismFactory = - this.options?.ismFactory ?? + this.options.ismFactory ?? (() => { throw new Error('No ISM factory provided'); })(); @@ -240,27 +253,32 @@ export abstract class HyperlaneDeployer< protected async configureHook( chain: ChainName, contract: C, - targetHook: Address, + targetHook: IPostDispatchHook, getHook: (contract: C) => Promise
, setHook: (contract: C, hook: Address) => Promise, ): Promise { const configuredHook = await getHook(contract); - if (!eqAddress(targetHook, configuredHook)) { - await this.runIfOwner(chain, contract, async () => { + if (!eqAddress(targetHook.address, configuredHook)) { + const result = await this.runIfOwner(chain, contract, async () => { this.logger( - `Set hook on ${chain} to ${targetHook}, currently is ${configuredHook}`, + `Set hook on ${chain} to ${targetHook.address}, currently is ${configuredHook}`, ); await this.multiProvider.sendTransaction( chain, - setHook(contract, targetHook), + setHook(contract, targetHook.address), ); const actualHook = await getHook(contract); - if (!eqAddress(targetHook, actualHook)) { + if (!eqAddress(targetHook.address, actualHook)) { throw new Error( - `Set hook failed on ${chain}, wanted ${targetHook}, got ${actualHook}`, + `Set hook failed on ${chain}, wanted ${targetHook.address}, got ${actualHook}`, ); } + return true; }); + // if the signer is not the owner, saving the hook address in the artifacts for later use for sending test messages, etc + if (!result) { + this.addDeployedContracts(chain, { customHook: targetHook }); + } } } @@ -274,7 +292,10 @@ export abstract class HyperlaneDeployer< await this.configureHook( local, client, - config.hook, + IPostDispatchHook__factory.connect( + config.hook, + this.multiProvider.getSignerOrProvider(local), + ), (_client) => _client.hook(), (_client, _hook) => _client.populateTransaction.setHook(_hook), ); @@ -294,7 +315,7 @@ export abstract class HyperlaneDeployer< this.logger(`Mailbox client on ${local} initialized...`); } - protected async deployContractFromFactory( + public async deployContractFromFactory( chain: ChainName, factory: F, contractName: string, @@ -340,6 +361,17 @@ export abstract class HyperlaneDeployer< ); this.addVerificationArtifacts(chain, [verificationInput]); + // try verifying contract + try { + await this.options.contractVerifier?.verifyContract( + chain, + verificationInput, + ); + } catch (error) { + // log error but keep deploying, can also verify post-deployment if needed + this.logger(`Error verifying contract: ${error}`); + } + return contract; } @@ -608,16 +640,21 @@ export abstract class HyperlaneDeployer< return ret; } - protected async transferOwnershipOfContracts( + async transferOwnershipOfContracts( chain: ChainName, - owner: Address, - ownables: { [key: string]: Ownable }, + config: OwnableConfig, + ownables: Partial>, ): Promise { const receipts: ethers.ContractReceipt[] = []; - for (const contractName of Object.keys(ownables)) { - const ownable = ownables[contractName]; - const currentOwner = await ownable.owner(); - if (!eqAddress(currentOwner, owner)) { + for (const [contractName, ownable] of Object.entries( + ownables, + )) { + if (!ownable) { + continue; + } + const current = await ownable.owner(); + const owner = config.ownerOverrides?.[contractName as K] ?? config.owner; + if (!eqAddress(current, owner)) { this.logger( `Transferring ownership of ${contractName} to ${owner} on ${chain}`, ); diff --git a/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts b/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts index 37a0543a08..bf860a9efd 100644 --- a/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneProxyFactoryDeployer.ts @@ -10,14 +10,19 @@ import { proxyFactoryFactories, proxyFactoryImplementations, } from './contracts'; +import { ContractVerifier } from './verify/ContractVerifier'; export class HyperlaneProxyFactoryDeployer extends HyperlaneDeployer< {}, ProxyFactoryFactories > { - constructor(multiProvider: MultiProvider) { + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { super(multiProvider, proxyFactoryFactories, { logger: debug('hyperlane:IsmFactoryDeployer'), + contractVerifier, }); } diff --git a/typescript/sdk/src/deploy/types.ts b/typescript/sdk/src/deploy/types.ts index 8ce65d8358..3d2389858f 100644 --- a/typescript/sdk/src/deploy/types.ts +++ b/typescript/sdk/src/deploy/types.ts @@ -9,7 +9,7 @@ import { Address } from '@hyperlane-xyz/utils'; import type { ChainName } from '../types'; -export type OwnableConfig = { +export type OwnableConfig = { owner: Address; ownerOverrides?: Partial>; }; diff --git a/typescript/sdk/src/deploy/verify/ContractVerifier.ts b/typescript/sdk/src/deploy/verify/ContractVerifier.ts index 39246746fc..ea9fda3161 100644 --- a/typescript/sdk/src/deploy/verify/ContractVerifier.ts +++ b/typescript/sdk/src/deploy/verify/ContractVerifier.ts @@ -4,196 +4,241 @@ import { ethers } from 'ethers'; import { sleep, strip0x } from '@hyperlane-xyz/utils'; +import { ExplorerFamily } from '../../metadata/chainMetadataTypes'; import { MultiProvider } from '../../providers/MultiProvider'; import { ChainMap, ChainName } from '../../types'; -import { MultiGeneric } from '../../utils/MultiGeneric'; import { + BuildArtifact, CompilerOptions, ContractVerificationInput, - VerificationInput, + EXPLORER_GET_ACTIONS, + ExplorerApiActions, + ExplorerApiErrors, + FormOptions, } from './types'; -enum ExplorerApiActions { - GETSOURCECODE = 'getsourcecode', - VERIFY_IMPLEMENTATION = 'verifysourcecode', - MARK_PROXY = 'verifyproxycontract', - CHECK_STATUS = 'checkverifystatus', - CHECK_PROXY_STATUS = 'checkproxyverification', -} +export class ContractVerifier { + private logger = debug(`hyperlane:ContractVerifier`); -enum ExplorerApiErrors { - ALREADY_VERIFIED = 'Contract source code already verified', - ALREADY_VERIFIED_ALT = 'Already Verified', - VERIFICATION_PENDING = 'Pending in queue', - PROXY_FAILED = 'A corresponding implementation contract was unfortunately not detected for the proxy address.', - BYTECODE_MISMATCH = 'Fail - Unable to verify. Compiled contract deployment bytecode does NOT match the transaction deployment bytecode.', -} + private contractSourceMap: { [contractName: string]: string } = {}; -export class ContractVerifier extends MultiGeneric { - protected logger: Debugger; + protected readonly standardInputJson: string; + protected readonly compilerOptions: CompilerOptions; constructor( - verificationInputs: ChainMap, protected readonly multiProvider: MultiProvider, protected readonly apiKeys: ChainMap, - protected readonly flattenedSource: string, // flattened source code from eg `hardhat flatten` - protected readonly compilerOptions: CompilerOptions, + buildArtifact: BuildArtifact, + licenseType: CompilerOptions['licenseType'], ) { - super(verificationInputs); - this.logger = debug('hyperlane:ContractVerifier'); - } - - verify(targets = this.chains()): Promise[]> { - return Promise.allSettled( - targets.map((chain) => this.verifyChain(chain, this.get(chain))), - ); - } + // Extract the standard input json and compiler version from the build artifact + this.standardInputJson = JSON.stringify(buildArtifact.input); + const compilerversion = `v${buildArtifact.solcLongVersion}`; - async verifyChain( - chain: ChainName, - inputs: VerificationInput, - ): Promise { - this.logger(`Verifying ${chain}...`); - for (const input of inputs) { - await this.verifyContract(chain, input); + // double check compiler version matches expected format + const versionRegex = /v(\d.\d.\d+)\+commit.\w+/; + const matches = versionRegex.exec(compilerversion); + if (!matches) { + throw new Error(`Invalid compiler version ${compilerversion}`); } + + // set compiler options + // only license type is configurable, empty if not provided + this.compilerOptions = { + codeformat: 'solidity-standard-json-input', + compilerversion, + licenseType, + }; + + // process input to create mapping of contract names to source names + // this is required to construct the fully qualified contract name + const contractRegex = /contract\s+([A-Z][a-zA-Z0-9]*)/g; + Object.entries(buildArtifact.input.sources).forEach( + ([sourceName, { content }]) => { + const matches = content.matchAll(contractRegex); + for (const match of matches) { + const contractName = match[1]; + if (contractName) { + this.contractSourceMap[contractName] = sourceName; + } + } + }, + ); } private async submitForm( chain: ChainName, action: ExplorerApiActions, - options?: Record, + verificationLogger: Debugger, + options?: FormOptions, ): Promise { - const apiUrl = new URL(this.multiProvider.getExplorerApiUrl(chain)); - const isGetRequest = - action === ExplorerApiActions.CHECK_STATUS || - action === ExplorerApiActions.CHECK_PROXY_STATUS || - action === ExplorerApiActions.GETSOURCECODE; - const params = new URLSearchParams({ - apikey: this.apiKeys[chain], - module: 'contract', - action, - ...options, - }); - - let response: Response; + const { apiUrl, family } = this.multiProvider.getExplorerApi(chain); + const params = new URLSearchParams(); + params.set('module', 'contract'); + params.set('action', action); + + // no need to provide every argument for every request + for (const [key, value] of Object.entries(options ?? {})) { + params.set(key, value); + } + + // only include apikey if provided & not blockscout + if (family !== ExplorerFamily.Blockscout && this.apiKeys[chain]) { + params.set('apikey', this.apiKeys[chain]); + } + + const url = new URL(apiUrl); + const isGetRequest = EXPLORER_GET_ACTIONS.includes(action); + if (isGetRequest) { + url.search = params.toString(); + } else if (family === ExplorerFamily.Blockscout) { + // Blockscout requires module and action to be query params + url.searchParams.set('module', 'contract'); + url.searchParams.set('action', action); + } + + let response; if (isGetRequest) { - response = await fetch(`${apiUrl}?${params}`); + response = await fetch(url.toString(), { + method: 'GET', + }); } else { - response = await fetch(apiUrl, { + response = await fetch(url.toString(), { method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - }, + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: params, }); } - let result; - let responseText; - try { - responseText = await response.text(); - result = JSON.parse(responseText); - } catch (e) { - this.logger(`Failed to parse response from ${responseText}`, e); - } - if (result.message === 'NOTOK') { + const responseText = await response.text(); + const result = JSON.parse(responseText); + + if (result.message !== 'OK') { + let errorMessage; + switch (result.result) { case ExplorerApiErrors.VERIFICATION_PENDING: await sleep(5000); // wait 5 seconds - return this.submitForm(chain, action, options); + return this.submitForm(chain, action, verificationLogger, options); case ExplorerApiErrors.ALREADY_VERIFIED: case ExplorerApiErrors.ALREADY_VERIFIED_ALT: return; case ExplorerApiErrors.PROXY_FAILED: - this.logger(`Proxy verification failed for, try manually?`); - return; + errorMessage = 'Proxy verification failed, try manually?'; + break; case ExplorerApiErrors.BYTECODE_MISMATCH: - this.logger( - `Compiled bytecode does not match deployed bytecode, check constructor arguments?`, - ); - return; + errorMessage = + 'Compiled bytecode does not match deployed bytecode, check constructor arguments?'; + break; default: - this.logger( - `Verification failed for some unknown reason on ${chain}`, - result, - ); - throw new Error(`Verification failed: ${result.result}`); + errorMessage = `Verification failed. ${ + result.result ?? response.statusText + }`; + break; + } + + if (errorMessage) { + verificationLogger(errorMessage); + throw new Error(`[${chain}] ${errorMessage}`); } } + if (result.result === ExplorerApiErrors.UNKNOWN_UID) { + await sleep(1000); // wait 1 second + return this.submitForm(chain, action, verificationLogger, options); + } + + if (result.result === ExplorerApiErrors.UNABLE_TO_VERIFY) { + const errorMessage = `Verification failed. ${ + result.result ?? response.statusText + }`; + verificationLogger(errorMessage); + throw new Error(`[${chain}] ${errorMessage}`); + } + return result.result; } private async isAlreadyVerified( chain: ChainName, input: ContractVerificationInput, - ) { + verificationLogger: Debugger, + ): Promise { try { const result = await this.submitForm( chain, ExplorerApiActions.GETSOURCECODE, + verificationLogger, { - ...this.compilerOptions, address: input.address, }, ); - return result[0].SourceCode !== ''; + return !!result[0]?.SourceCode; } catch (error) { - this.logger(`Error checking if contract is already verified: ${error}`); + verificationLogger( + `Error checking if contract is already verified: ${error}`, + ); return false; } } - async verifyProxy( + private async verifyProxy( chain: ChainName, input: ContractVerificationInput, + verificationLogger: Debugger, ): Promise { - if (input.isProxy) { - try { - const proxyGuid = await this.submitForm( - chain, - ExplorerApiActions.MARK_PROXY, - { - address: input.address, - }, - ); - - const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl( - chain, - input.address, - ); - - // poll for verified proxy status - if (proxyGuid) { - await this.submitForm(chain, ExplorerApiActions.CHECK_PROXY_STATUS, { - guid: proxyGuid, - }); - this.logger( - `Successfully verified proxy ${addressUrl}#readProxyContract`, - ); - } - } catch (error) { - console.error( - `Verification of proxy at ${input.address} failed on ${chain}`, - ); - throw error; - } + if (!input.isProxy) return; + + try { + const proxyGuid = await this.submitForm( + chain, + ExplorerApiActions.MARK_PROXY, + verificationLogger, + { address: input.address }, + ); + if (!proxyGuid) return; + + await this.submitForm( + chain, + ExplorerApiActions.CHECK_PROXY_STATUS, + verificationLogger, + { + guid: proxyGuid, + }, + ); + const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl( + chain, + input.address, + ); + verificationLogger( + `Successfully verified proxy ${addressUrl}#readProxyContract`, + ); + } catch (error) { + verificationLogger( + `Verification of proxy at ${input.address} failed: ${error}`, + ); + throw error; } } - async verifyImplementation( + private async verifyImplementation( chain: ChainName, input: ContractVerificationInput, + verificationLogger: Debugger, ): Promise { - this.logger( - `Verifying ${input.name} implementation at ${input.address} on ${chain}`, - ); + verificationLogger(`Verifying implementation at ${input.address}`); + + const sourceName = this.contractSourceMap[input.name]; + if (!sourceName) { + const errorMessage = `Contract '${input.name}' not found in provided build artifact`; + verificationLogger(errorMessage); + throw new Error(`[${chain}] ${errorMessage}`); + } const data = { - sourceCode: this.flattenedSource, - contractname: input.name, + sourceCode: this.standardInputJson, + contractname: `${sourceName}:${input.name}`, contractaddress: input.address, // TYPO IS ENFORCED BY API constructorArguements: strip0x(input.constructorArguments ?? ''), @@ -203,55 +248,71 @@ export class ContractVerifier extends MultiGeneric { const guid = await this.submitForm( chain, ExplorerApiActions.VERIFY_IMPLEMENTATION, + verificationLogger, data, ); + if (!guid) return; + await this.submitForm( + chain, + ExplorerApiActions.CHECK_STATUS, + verificationLogger, + { guid }, + ); const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl( chain, input.address, ); - - // poll for verified status - if (guid) { - try { - await this.submitForm(chain, ExplorerApiActions.CHECK_STATUS, { guid }); - this.logger(`Successfully verified ${addressUrl}#code`); - } catch (error) { - console.error( - `Verifying implementation at ${input.address} failed on ${chain}`, - ); - throw error; - } - } + verificationLogger(`Successfully verified ${addressUrl}#code`); } async verifyContract( chain: ChainName, input: ContractVerificationInput, + logger = this.logger, ): Promise { - if (input.address === ethers.constants.AddressZero) { + const verificationLogger = logger.extend(`${chain}:${input.name}`); + + const metadata = this.multiProvider.tryGetChainMetadata(chain); + const rpcUrl = metadata?.rpcUrls[0].http ?? ''; + if (rpcUrl.includes('localhost') || rpcUrl.includes('127.0.0.1')) { + verificationLogger('Skipping verification for local endpoints'); + return; + } + + const explorerApi = this.multiProvider.tryGetExplorerApi(chain); + if (!explorerApi) { + verificationLogger('No explorer API set, skipping'); return; } + if (!explorerApi.family) { + verificationLogger(`No explorer family set, skipping`); + return; + } + + if (explorerApi.family === ExplorerFamily.Other) { + verificationLogger(`Unsupported explorer family, skipping`); + return; + } + + if (input.address === ethers.constants.AddressZero) return; if (Array.isArray(input.constructorArguments)) { - this.logger('Constructor arguments in legacy format, skipping'); + verificationLogger('Constructor arguments in legacy format, skipping'); return; } - if (await this.isAlreadyVerified(chain, input)) { + if (await this.isAlreadyVerified(chain, input, verificationLogger)) { const addressUrl = await this.multiProvider.tryGetExplorerAddressUrl( chain, input.address, ); - this.logger( - `Contract ${input.name} already verified on ${chain} at ${addressUrl}#code`, - ); - // There is a rate limit of 5 requests per second - await sleep(200); + verificationLogger(`Contract already verified at ${addressUrl}#code`); + await sleep(200); // There is a rate limit of 5 requests per second return; - } else { - await this.verifyImplementation(chain, input); } - await this.verifyProxy(chain, input); + + await this.verifyImplementation(chain, input, verificationLogger); + await this.verifyProxy(chain, input, verificationLogger); } } diff --git a/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts b/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts new file mode 100644 index 0000000000..22cb991abb --- /dev/null +++ b/typescript/sdk/src/deploy/verify/PostDeploymentContractVerifier.ts @@ -0,0 +1,50 @@ +import { debug } from 'debug'; + +import { ExplorerFamily } from '../../metadata/chainMetadataTypes'; +import { MultiProvider } from '../../providers/MultiProvider'; +import { ChainMap } from '../../types'; +import { MultiGeneric } from '../../utils/MultiGeneric'; + +import { ContractVerifier } from './ContractVerifier'; +import { BuildArtifact, CompilerOptions, VerificationInput } from './types'; + +export class PostDeploymentContractVerifier extends MultiGeneric { + protected logger = debug('hyperlane:PostDeploymentContractVerifier'); + protected readonly contractVerifier: ContractVerifier; + + constructor( + verificationInputs: ChainMap, + protected readonly multiProvider: MultiProvider, + apiKeys: ChainMap, + buildArtifact: BuildArtifact, + licenseType: CompilerOptions['licenseType'], + ) { + super(verificationInputs); + this.contractVerifier = new ContractVerifier( + multiProvider, + apiKeys, + buildArtifact, + licenseType, + ); + } + + verify(targets = this.chains()): Promise[]> { + return Promise.allSettled( + targets.map(async (chain) => { + // can check explorer family here to avoid doing these checks for each input in verifier + const { family } = this.multiProvider.getExplorerApi(chain); + if (family === ExplorerFamily.Other) { + this.logger( + `Skipping verification for ${chain} due to unsupported explorer family.`, + ); + return; + } + + this.logger(`Verifying ${chain}...`); + for (const input of this.get(chain)) { + await this.contractVerifier.verifyContract(chain, input, this.logger); + } + }), + ); + } +} diff --git a/typescript/sdk/src/deploy/verify/types.ts b/typescript/sdk/src/deploy/verify/types.ts index 6a1dcd2bb6..442ad5e370 100644 --- a/typescript/sdk/src/deploy/verify/types.ts +++ b/typescript/sdk/src/deploy/verify/types.ts @@ -7,9 +7,89 @@ export type ContractVerificationInput = { export type VerificationInput = ContractVerificationInput[]; +export type SolidityStandardJsonInput = { + sources: { + [sourceName: string]: { + content: string; + }; + }; +}; + +export type BuildArtifact = { + input: SolidityStandardJsonInput; + solcLongVersion: string; +}; + +// see https://etherscan.io/contract-license-types +export enum ExplorerLicenseType { + NO_LICENSE = '1', + UNLICENSED = '2', + MIT = '3', + GPL2 = '4', + GPL3 = '5', + LGPL2 = '6', + LGPL3 = '7', + BSD2 = '8', + BSD3 = '9', + MPL2 = '10', + OSL3 = '11', + APACHE2 = '12', + AGPL3 = '13', + BSL = '14', +} + export type CompilerOptions = { - codeformat: 'solidity-single-file' | 'solidity-standard-json-input'; //solidity-single-file (default) or solidity-standard-json-input (for std-input-json-format support + codeformat: 'solidity-standard-json-input'; compilerversion: string; // see https://etherscan.io/solcversions for list of support versions - optimizationUsed: '0' | '1'; //0 = No Optimization, 1 = Optimization used (applicable when codeformat=solidity-single-file) - runs: string; //set to 200 as default unless otherwise (applicable when codeformat=solidity-single-file) + licenseType?: ExplorerLicenseType; }; + +export enum ExplorerApiActions { + GETSOURCECODE = 'getsourcecode', + VERIFY_IMPLEMENTATION = 'verifysourcecode', + MARK_PROXY = 'verifyproxycontract', + CHECK_STATUS = 'checkverifystatus', + CHECK_PROXY_STATUS = 'checkproxyverification', +} + +export const EXPLORER_GET_ACTIONS = [ + ExplorerApiActions.CHECK_STATUS, + ExplorerApiActions.CHECK_PROXY_STATUS, + ExplorerApiActions.GETSOURCECODE, +]; + +export enum ExplorerApiErrors { + ALREADY_VERIFIED = 'Contract source code already verified', + ALREADY_VERIFIED_ALT = 'Already Verified', + VERIFICATION_PENDING = 'Pending in queue', + PROXY_FAILED = 'A corresponding implementation contract was unfortunately not detected for the proxy address.', + BYTECODE_MISMATCH = 'Fail - Unable to verify. Compiled contract deployment bytecode does NOT match the transaction deployment bytecode.', + UNABLE_TO_VERIFY = 'Fail - Unable to verify', + UNKNOWN_UID = 'Unknown UID', +} + +export type FormOptions = + Action extends ExplorerApiActions.GETSOURCECODE + ? { + address: string; + } + : Action extends ExplorerApiActions.VERIFY_IMPLEMENTATION + ? CompilerOptions & { + contractaddress: string; + sourceCode: string; + contractname: string; + constructorArguements?: string; // TYPO IS ENFORCED BY API + } + : Action extends ExplorerApiActions.MARK_PROXY + ? { + address: string; + } + : Action extends ExplorerApiActions.CHECK_STATUS + ? { + guid: string; + } + : Action extends ExplorerApiActions.CHECK_PROXY_STATUS + ? { + guid: string; + } + : never; diff --git a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts index d534149a98..5245a04356 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpChecker.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpChecker.ts @@ -1,15 +1,15 @@ -import { BigNumber, ethers } from 'ethers'; +import { BigNumber } from 'ethers'; -import { Address, eqAddress } from '@hyperlane-xyz/utils'; +import { eqAddress } from '@hyperlane-xyz/utils'; import { BytecodeHash } from '../consts/bytecode'; +import { chainMetadata } from '../consts/chainMetadata'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker'; import { proxyImplementation } from '../deploy/proxy'; import { ChainName } from '../types'; import { HyperlaneIgp } from './HyperlaneIgp'; import { - GasOracleContractType, IgpBeneficiaryViolation, IgpConfig, IgpGasOraclesViolation, @@ -31,11 +31,7 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< async checkDomainOwnership(chain: ChainName): Promise { const config = this.configMap[chain]; - - const ownableOverrides: Record = { - storageGasOracle: config.oracleKey, - }; - await super.checkOwnership(chain, config.owner, ownableOverrides); + await super.checkOwnership(chain, config.owner, config.ownerOverrides); } async checkBytecodes(chain: ChainName): Promise { @@ -60,14 +56,10 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< ), ); - await this.checkBytecode( + await this.checkProxy( chain, 'InterchainGasPaymaster proxy', contracts.interchainGasPaymaster.address, - [ - BytecodeHash.TRANSPARENT_PROXY_BYTECODE_HASH, - BytecodeHash.OPT_TRANSPARENT_PROXY_BYTECODE_HASH, - ], ); } @@ -98,7 +90,9 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< expectedOverhead = 0; } - const remoteId = this.multiProvider.getDomainId(remote); + const remoteId = + chainMetadata[remote]?.domainId ?? + this.multiProvider.getDomainId(remote); const existingOverhead = await defaultIsmIgp.destinationGasLimit( remoteId, 0, @@ -133,17 +127,16 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< expected: {}, }; - // In addition to all remote chains on the app, which are just Ethereum chains, - // also consider what the config says about non-Ethereum chains. - const remotes = new Set([ - ...this.app.remoteChains(local), - ...Object.keys(this.configMap[local].gasOracleType), - ]); + const remotes = new Set( + Object.keys(this.configMap[local].oracleConfig ?? {}), + ); for (const remote of remotes) { - const remoteId = this.multiProvider.getDomainId(remote); + const remoteId = + chainMetadata[remote]?.domainId ?? + this.multiProvider.getDomainId(remote); const destinationGasConfigs = await igp.destinationGasConfigs(remoteId); const actualGasOracle = destinationGasConfigs.gasOracle; - const expectedGasOracle = this.getGasOracleAddress(local, remote); + const expectedGasOracle = coreContracts.storageGasOracle.address; if (!eqAddress(actualGasOracle, expectedGasOracle)) { const remoteChain = remote as ChainName; @@ -170,22 +163,4 @@ export class HyperlaneIgpChecker extends HyperlaneAppChecker< this.addViolation(violation); } } - - getGasOracleAddress(local: ChainName, remote: ChainName): Address { - const config = this.configMap[local]; - const gasOracleType = config.gasOracleType[remote]; - if (!gasOracleType) { - this.app.logger( - `No gas oracle for local ${local} and remote ${remote}, defaulting to zero address`, - ); - return ethers.constants.AddressZero; - } - const coreContracts = this.app.getContracts(local); - switch (gasOracleType) { - case GasOracleContractType.StorageGasOracle: - return coreContracts.storageGasOracle.address; - default: - throw Error(`Unsupported gas oracle type ${gasOracleType}`); - } - } } diff --git a/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts b/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts index f7c09d36d3..fb3f416add 100644 --- a/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts +++ b/typescript/sdk/src/gas/HyperlaneIgpDeployer.ts @@ -7,21 +7,28 @@ import { } from '@hyperlane-xyz/core'; import { eqAddress } from '@hyperlane-xyz/utils'; +import { chainMetadata } from '../consts/chainMetadata'; import { HyperlaneContracts } from '../contracts/types'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; +import { ContractVerifier } from '../deploy/verify/ContractVerifier'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainName } from '../types'; import { IgpFactories, igpFactories } from './contracts'; +import { serializeDifference } from './oracle/types'; import { IgpConfig } from './types'; export class HyperlaneIgpDeployer extends HyperlaneDeployer< IgpConfig, IgpFactories > { - constructor(multiProvider: MultiProvider) { + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { super(multiProvider, igpFactories, { logger: debug('hyperlane:IgpDeployer'), + contractVerifier, }); } @@ -31,28 +38,29 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer< storageGasOracle: StorageGasOracle, config: IgpConfig, ): Promise { - const owner = config.owner; const beneficiary = config.beneficiary; const igp = await this.deployProxiedContract( chain, 'interchainGasPaymaster', proxyAdmin.address, [], - [owner, beneficiary], + [await this.multiProvider.getSignerAddress(chain), beneficiary], ); const gasParamsToSet: InterchainGasPaymaster.GasParamStruct[] = []; - const remotes = Object.keys(config.gasOracleType); - for (const remote of remotes) { - const remoteId = this.multiProvider.getDomainId(remote); - const newGasOverhead = config.overhead[remote]; + for (const [remote, newGasOverhead] of Object.entries(config.overhead)) { + const remoteId = + chainMetadata[remote]?.domainId ?? + this.multiProvider.getDomainId(remote); const currentGasConfig = await igp.destinationGasConfigs(remoteId); if ( !eqAddress(currentGasConfig.gasOracle, storageGasOracle.address) || !currentGasConfig.gasOverhead.eq(newGasOverhead) ) { - this.logger(`Setting gas params for ${remote} to ${newGasOverhead}`); + this.logger( + `Setting gas params for ${chain} -> ${remote}: gasOverhead = ${newGasOverhead} gasOracle = ${storageGasOracle.address}`, + ); gasParamsToSet.push({ remoteDomain: remoteId, config: { @@ -74,11 +82,58 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer< ), ); } + return igp; } - async deployStorageGasOracle(chain: ChainName): Promise { - return this.deployContract(chain, 'storageGasOracle', []); + async deployStorageGasOracle( + chain: ChainName, + config: IgpConfig, + ): Promise { + const gasOracle = await this.deployContract(chain, 'storageGasOracle', []); + + if (!config.oracleConfig) { + this.logger('No oracle config provided, skipping...'); + return gasOracle; + } + + this.logger(`Configuring gas oracle from ${chain}...`); + const configsToSet: Array = []; + + // For each remote, check if the gas oracle has the correct data + for (const [remote, desired] of Object.entries(config.oracleConfig)) { + // check core metadata for non EVMs and fallback to multiprovider for custom EVMs + const remoteDomain = + chainMetadata[remote]?.domainId ?? + this.multiProvider.getDomainId(remote); + + const actual = await gasOracle.remoteGasData(remoteDomain); + + if ( + !actual.gasPrice.eq(desired.gasPrice) || + !actual.tokenExchangeRate.eq(desired.tokenExchangeRate) + ) { + this.logger(`-> ${remote} ${serializeDifference(actual, desired)}`); + configsToSet.push({ + remoteDomain, + ...desired, + }); + } + } + + if (configsToSet.length > 0) { + await this.runIfOwner(chain, gasOracle, async () => + this.multiProvider.handleTx( + chain, + gasOracle.setRemoteGasDataConfigs( + configsToSet, + this.multiProvider.getTransactionOverrides(chain), + ), + ), + ); + } + + return gasOracle; } async deployContracts( @@ -89,27 +144,30 @@ export class HyperlaneIgpDeployer extends HyperlaneDeployer< // is loaded into the contract cache. const proxyAdmin = await this.deployContract(chain, 'proxyAdmin', []); - const storageGasOracle = await this.deployStorageGasOracle(chain); + const storageGasOracle = await this.deployStorageGasOracle(chain, config); const interchainGasPaymaster = await this.deployInterchainGasPaymaster( chain, proxyAdmin, storageGasOracle, config, ); - await this.transferOwnershipOfContracts(chain, config.owner, { - interchainGasPaymaster, - }); - - // Configure oracle key for StorageGasOracle separately to keep 'hot' - // for updating exchange rates regularly - await this.transferOwnershipOfContracts(chain, config.oracleKey, { - storageGasOracle, - }); - return { + const contracts = { proxyAdmin, storageGasOracle, interchainGasPaymaster, }; + + const ownerConfig = { + ...config, + ownerOverrides: { + ...config.ownerOverrides, + storageGasOracle: config.oracleKey, + }, + }; + + await this.transferOwnershipOfContracts(chain, ownerConfig, contracts); + + return contracts; } } diff --git a/typescript/sdk/src/gas/oracle/configure-gas-oracles.hardhat-test.ts b/typescript/sdk/src/gas/oracle/configure-gas-oracles.hardhat-test.ts new file mode 100644 index 0000000000..3cf0dcc1e0 --- /dev/null +++ b/typescript/sdk/src/gas/oracle/configure-gas-oracles.hardhat-test.ts @@ -0,0 +1,58 @@ +import { expect } from 'chai'; +import { ethers } from 'hardhat'; + +import { InterchainGasPaymaster } from '@hyperlane-xyz/core'; + +import { MultiProvider } from '../../providers/MultiProvider'; +import { testIgpConfig } from '../../test/testUtils'; +import { ChainMap } from '../../types'; +import { HyperlaneIgpDeployer } from '../HyperlaneIgpDeployer'; +import { IgpConfig } from '../types'; + +describe('HyperlaneIgpDeployer', () => { + const local = 'test1'; + const remote = 'test2'; + let remoteId: number; + let deployer: HyperlaneIgpDeployer; + let igp: InterchainGasPaymaster; + let multiProvider: MultiProvider; + let testConfig: ChainMap; + + before(async () => { + const [signer] = await ethers.getSigners(); + multiProvider = MultiProvider.createTestMultiProvider({ signer }); + remoteId = multiProvider.getDomainId(remote); + deployer = new HyperlaneIgpDeployer(multiProvider); + testConfig = testIgpConfig([local, remote], signer.address); + const contracts = await deployer.deploy(testConfig); + igp = contracts[local].interchainGasPaymaster; + }); + + it('should deploy storage gas oracle with config given', async () => { + // Assert + const deployedConfig = await igp.getExchangeRateAndGasPrice(remoteId); + expect({ + gasPrice: deployedConfig.gasPrice, + tokenExchangeRate: deployedConfig.tokenExchangeRate, + }).to.deep.equal(testConfig[local].oracleConfig![remote]); + }); + + it('should configure new oracle config', async () => { + testConfig[local].oracleConfig![remote] = { + tokenExchangeRate: ethers.utils.parseUnits('2', 'gwei'), + gasPrice: ethers.utils.parseUnits('3', 'gwei'), + }; + + const localContracts = await deployer.deployContracts( + local, + testConfig[local], + ); + igp = localContracts.interchainGasPaymaster; + + const modifiedConfig = await igp.getExchangeRateAndGasPrice(remoteId); + expect({ + gasPrice: modifiedConfig.gasPrice, + tokenExchangeRate: modifiedConfig.tokenExchangeRate, + }).to.deep.equal(testConfig[local].oracleConfig![remote]); + }); +}); diff --git a/typescript/sdk/src/gas/oracle/types.ts b/typescript/sdk/src/gas/oracle/types.ts new file mode 100644 index 0000000000..238ba019b5 --- /dev/null +++ b/typescript/sdk/src/gas/oracle/types.ts @@ -0,0 +1,60 @@ +import { ethers } from 'ethers'; + +import { StorageGasOracle } from '@hyperlane-xyz/core'; + +import { TOKEN_EXCHANGE_RATE_EXPONENT } from '../../consts/igp'; + +export enum GasOracleContractType { + StorageGasOracle = 'StorageGasOracle', +} + +// Gas data to configure on a single destination chain. +export type StorageGasOracleConfig = Pick< + StorageGasOracle.RemoteGasDataConfigStructOutput, + 'gasPrice' | 'tokenExchangeRate' +>; + +export const formatGasOracleConfig = ( + config: StorageGasOracleConfig, +): { + tokenExchangeRate: string; + gasPrice: string; +} => ({ + tokenExchangeRate: ethers.utils.formatUnits( + config.tokenExchangeRate, + TOKEN_EXCHANGE_RATE_EXPONENT, + ), + gasPrice: ethers.utils.formatUnits(config.gasPrice, 'gwei'), +}); + +const percentDifference = ( + actual: ethers.BigNumber, + expected: ethers.BigNumber, +): ethers.BigNumber => expected.sub(actual).mul(100).div(actual); + +const serializePercentDifference = ( + actual: ethers.BigNumber, + expected: ethers.BigNumber, +): string => { + if (actual.isZero()) { + return 'new'; + } + const diff = percentDifference(actual, expected); + return diff.isNegative() ? `${diff.toString()}%` : `+${diff.toString()}%`; +}; + +export const serializeDifference = ( + actual: StorageGasOracleConfig, + expected: StorageGasOracleConfig, +): string => { + const gasPriceDiff = serializePercentDifference( + actual.gasPrice, + expected.gasPrice, + ); + const tokenExchangeRateDiff = serializePercentDifference( + actual.tokenExchangeRate, + expected.tokenExchangeRate, + ); + const formatted = formatGasOracleConfig(expected); + return `${formatted.tokenExchangeRate} (${tokenExchangeRateDiff}), ${formatted.gasPrice} gwei (${gasPriceDiff})`; +}; diff --git a/typescript/sdk/src/gas/types.ts b/typescript/sdk/src/gas/types.ts index c4f76c05aa..38c6c49707 100644 --- a/typescript/sdk/src/gas/types.ts +++ b/typescript/sdk/src/gas/types.ts @@ -7,16 +7,16 @@ import type { CheckerViolation, OwnableConfig } from '../deploy/types'; import { ChainMap } from '../types'; import { IgpFactories } from './contracts'; - -export enum GasOracleContractType { - StorageGasOracle = 'StorageGasOracle', -} +import { GasOracleContractType, StorageGasOracleConfig } from './oracle/types'; export type IgpConfig = OwnableConfig & { beneficiary: Address; - gasOracleType: ChainMap; oracleKey: Address; overhead: ChainMap; + // TODO: require this + oracleConfig?: ChainMap; + // DEPRECATED + gasOracleType?: ChainMap; }; export enum IgpViolationType { diff --git a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts index 4c52d78fc6..0af7d4fd3c 100644 --- a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts +++ b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts @@ -12,9 +12,11 @@ import { } from '@hyperlane-xyz/core'; import { Address, addressToBytes32 } from '@hyperlane-xyz/utils'; +import { chainMetadata } from '../consts/chainMetadata'; import { HyperlaneContracts } from '../contracts/types'; import { CoreAddresses } from '../core/contracts'; import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; +import { ContractVerifier } from '../deploy/verify/ContractVerifier'; import { HyperlaneIgpDeployer } from '../gas/HyperlaneIgpDeployer'; import { IgpFactories } from '../gas/contracts'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; @@ -42,10 +44,15 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< multiProvider: MultiProvider, readonly core: ChainMap>, readonly ismFactory: HyperlaneIsmFactory, - readonly igpDeployer = new HyperlaneIgpDeployer(multiProvider), + contractVerifier?: ContractVerifier, + readonly igpDeployer = new HyperlaneIgpDeployer( + multiProvider, + contractVerifier, + ), ) { super(multiProvider, hookFactories, { logger: debug('hyperlane:HookDeployer'), + contractVerifier, }); } @@ -74,7 +81,8 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< ); hook = interchainGasPaymaster; } else if (config.type === HookType.AGGREGATION) { - return this.deployAggregation(chain, config, coreAddresses); // deploy from factory + hook = (await this.deployAggregation(chain, config, coreAddresses)) + .aggregationHook; // deploy from factory } else if (config.type === HookType.PROTOCOL_FEE) { hook = await this.deployProtocolFee(chain, config); } else if (config.type === HookType.OP_STACK) { @@ -86,7 +94,11 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< hook = await this.deployRouting(chain, config, coreAddresses); } else if (config.type === HookType.PAUSABLE) { hook = await this.deployContract(chain, config.type, []); - await this.transferOwnershipOfContracts(chain, config.owner, { hook }); + await this.transferOwnershipOfContracts( + chain, + config, + { [HookType.PAUSABLE]: hook }, + ); } else { throw new Error(`Unsupported hook config: ${config}`); } @@ -156,6 +168,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< chain, this.ismFactory.getContracts(chain).aggregationHookFactory, aggregatedHooks, + this.logger, ); hooks[HookType.AGGREGATION] = StaticAggregationHook__factory.connect( address, @@ -245,13 +258,15 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< throw new Error(`Mailbox address is required for ${config.type}`); } + const deployer = await this.multiProvider.getSigner(chain).getAddress(); + let routingHook: DomainRoutingHook | FallbackDomainRoutingHook; switch (config.type) { case HookType.ROUTING: { this.logger('Deploying DomainRoutingHook for %s', chain); routingHook = await this.deployContract(chain, HookType.ROUTING, [ mailbox, - config.owner, + deployer, ]); break; } @@ -265,7 +280,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< routingHook = await this.deployContract( chain, HookType.FALLBACK_ROUTING, - [mailbox, config.owner, fallbackHook[config.fallback.type].address], + [mailbox, deployer, fallbackHook[config.fallback.type].address], ); break; } @@ -275,7 +290,8 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< const routingConfigs: DomainRoutingHook.HookConfigStruct[] = []; for (const [dest, hookConfig] of Object.entries(config.domains)) { - const destDomain = this.multiProvider.getDomainId(dest); + const destDomain = + chainMetadata[dest]?.domainId ?? this.multiProvider.getDomainId(dest); if (typeof hookConfig === 'string') { routingConfigs.push({ destination: destDomain, @@ -294,11 +310,18 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< } } - await this.multiProvider.handleTx( - chain, - routingHook.setHooks(routingConfigs), + const overrides = this.multiProvider.getTransactionOverrides(chain); + await this.runIfOwner(chain, routingHook, async () => + this.multiProvider.handleTx( + chain, + routingHook.setHooks(routingConfigs, overrides), + ), ); + await this.transferOwnershipOfContracts(chain, config, { + [config.type]: routingHook, + }); + return routingHook; } } diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 4013fa9a9f..10997102b5 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -63,6 +63,7 @@ export { TestRecipientConfig, TestRecipientDeployer, } from './core/TestRecipientDeployer'; +export { CosmWasmCoreAdapter } from './core/adapters/CosmWasmCoreAdapter'; export { EvmCoreAdapter } from './core/adapters/EvmCoreAdapter'; export { SealevelCoreAdapter } from './core/adapters/SealevelCoreAdapter'; export { ICoreAdapter } from './core/adapters/types'; @@ -87,9 +88,12 @@ export { ViolationType, } from './deploy/types'; export { ContractVerifier } from './deploy/verify/ContractVerifier'; +export { PostDeploymentContractVerifier } from './deploy/verify/PostDeploymentContractVerifier'; export { + BuildArtifact, CompilerOptions, ContractVerificationInput, + ExplorerLicenseType, VerificationInput, } from './deploy/verify/types'; export * as verificationUtils from './deploy/verify/utils'; @@ -105,9 +109,12 @@ export { SealevelOverheadIgpDataSchema, } from './gas/adapters/serialization'; export { IgpFactories, igpFactories } from './gas/contracts'; -export { CoinGeckoTokenPriceGetter } from './gas/token-prices'; export { GasOracleContractType, + StorageGasOracleConfig, +} from './gas/oracle/types'; +export { CoinGeckoTokenPriceGetter } from './gas/token-prices'; +export { IgpBeneficiaryViolation, IgpConfig, IgpGasOraclesViolation, @@ -129,11 +136,7 @@ export { PausableHookConfig, ProtocolFeeHookConfig, } from './hook/types'; -export { - HyperlaneIsmFactory, - collectValidators, - moduleCanCertainlyVerify, -} from './ism/HyperlaneIsmFactory'; +export { HyperlaneIsmFactory } from './ism/HyperlaneIsmFactory'; export { buildAggregationIsmConfigs, buildMultisigIsmConfigs, @@ -150,6 +153,7 @@ export { PausableIsmConfig, RoutingIsmConfig, } from './ism/types'; +export { collectValidators, moduleCanCertainlyVerify } from './ism/utils'; export { ChainMetadataManager, ChainMetadataManagerOptions, @@ -179,12 +183,14 @@ export { ChainMetadata, ChainMetadataSchema, ChainMetadataSchemaObject, + ChainTechnicalStack, ExplorerFamily, ExplorerFamilyValue, RpcUrl, RpcUrlSchema, getChainIdNumber, getDomainId, + getReorgPeriod, isValidChainMetadata, } from './metadata/chainMetadataTypes'; export { ZHash } from './metadata/customZodTypes'; @@ -193,6 +199,10 @@ export { HyperlaneDeploymentArtifactsSchema, } from './metadata/deploymentArtifacts'; export { MatchingList } from './metadata/matchingList'; +export { + WarpRouteConfig, + WarpRouteConfigSchema, +} from './metadata/warpRouteConfig'; export { InterchainAccount } from './middleware/account/InterchainAccount'; export { InterchainAccountChecker } from './middleware/account/InterchainAccountChecker'; export { @@ -314,6 +324,30 @@ export { RouterViolationType, proxiedFactories, } from './router/types'; +export { IToken, TokenArgs, TokenConfigSchema } from './token/IToken'; +export { Token } from './token/Token'; +export { TokenAmount } from './token/TokenAmount'; +export { + HyperlaneTokenConnection, + IbcToHyperlaneTokenConnection, + IbcTokenConnection, + TokenConnection, + TokenConnectionConfigSchema, + TokenConnectionType, + getTokenConnectionId, + parseTokenConnectionId, +} from './token/TokenConnection'; +export { + PROTOCOL_TO_NATIVE_STANDARD, + TOKEN_COLLATERALIZED_STANDARDS, + TOKEN_COSMWASM_STANDARDS, + TOKEN_HYP_STANDARDS, + TOKEN_MULTI_CHAIN_STANDARDS, + TOKEN_NFT_STANDARDS, + TOKEN_STANDARD_TO_PROTOCOL, + TOKEN_TYPE_TO_STANDARD, + TokenStandard, +} from './token/TokenStandard'; export { CW20Metadata, CwHypCollateralAdapter, @@ -329,6 +363,7 @@ export { } from './token/adapters/CosmosTokenAdapter'; export { EvmHypCollateralAdapter, + EvmHypNativeAdapter, EvmHypSyntheticAdapter, EvmNativeTokenAdapter, EvmTokenAdapter, @@ -336,6 +371,7 @@ export { export { IHypTokenAdapter, ITokenAdapter, + InterchainGasQuote, TransferParams, TransferRemoteParams, } from './token/adapters/ITokenAdapter'; @@ -381,8 +417,8 @@ export { HypERC20Deployer, HypERC721Deployer } from './token/deploy'; export { ChainMap, ChainName, + ChainNameOrId, Connection, - NameOrDomain, TestChainNames, } from './types'; export { MultiGeneric } from './utils/MultiGeneric'; @@ -394,3 +430,12 @@ export { getSealevelAccountDataSchema, } from './utils/sealevelSerialization'; export { chainMetadataToWagmiChain, wagmiChainMetadata } from './utils/wagmi'; +export { WarpCore, WarpCoreOptions } from './warp/WarpCore'; +export { + FeeConstantConfig, + RouteBlacklist, + WarpCoreConfig, + WarpCoreConfigSchema, + WarpTxCategory, + WarpTypedTransaction, +} from './warp/types'; diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts index 0541d7d755..79c4b30a5d 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts @@ -11,10 +11,7 @@ import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDe import { MultiProvider } from '../providers/MultiProvider'; import { randomAddress, randomInt } from '../test/testUtils'; -import { - HyperlaneIsmFactory, - moduleMatchesConfig, -} from './HyperlaneIsmFactory'; +import { HyperlaneIsmFactory } from './HyperlaneIsmFactory'; import { AggregationIsmConfig, IsmConfig, @@ -23,6 +20,7 @@ import { MultisigIsmConfig, RoutingIsmConfig, } from './types'; +import { moduleMatchesConfig } from './utils'; function randomModuleType(): ModuleType { const choices = [ @@ -319,6 +317,7 @@ describe('HyperlaneIsmFactory', async () => { ), multiProvider, ); + new TestCoreDeployer(multiProvider, ismFactory); ism = await ismFactory.deploy({ destination: chain, config: exampleRoutingConfig, diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts index bcfc2a4b6e..ae0dd6d8b3 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.ts @@ -1,4 +1,4 @@ -import { debug } from 'debug'; +import debug, { Debugger } from 'debug'; import { ethers } from 'ethers'; import { @@ -12,13 +12,9 @@ import { IMultisigIsm, IMultisigIsm__factory, IRoutingIsm, - IRoutingIsm__factory, - MailboxClient__factory, - OPStackIsm, OPStackIsm__factory, PausableIsm__factory, StaticAddressSetFactory, - StaticAggregationIsm__factory, StaticThresholdAddressSetFactory, TestIsm__factory, } from '@hyperlane-xyz/core'; @@ -26,25 +22,23 @@ import { Address, Domain, eqAddress, - formatMessage, - normalizeAddress, objFilter, - objMap, warn, } from '@hyperlane-xyz/utils'; import { HyperlaneApp } from '../app/HyperlaneApp'; +import { chainMetadata } from '../consts/chainMetadata'; import { HyperlaneEnvironment, hyperlaneEnvironments, } from '../consts/environments'; import { appFromAddressesMapHelper } from '../contracts/contracts'; -import { HyperlaneAddressesMap, HyperlaneContracts } from '../contracts/types'; +import { HyperlaneAddressesMap } from '../contracts/types'; +import { HyperlaneDeployer } from '../deploy/HyperlaneDeployer'; import { ProxyFactoryFactories, proxyFactoryFactories, } from '../deploy/contracts'; -import { logger } from '../logger'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; @@ -54,13 +48,11 @@ import { DeployedIsmType, IsmConfig, IsmType, - ModuleType, MultisigIsmConfig, - OpStackIsmConfig, RoutingIsmConfig, RoutingIsmDelta, - ismTypeToModuleType, } from './types'; +import { routingModuleDelta } from './utils'; export class HyperlaneIsmFactory extends HyperlaneApp { // The shape of this object is `ChainMap
`, @@ -68,6 +60,11 @@ export class HyperlaneIsmFactory extends HyperlaneApp { // TODO: fix this in the next refactoring public deployedIsms: ChainMap = {}; + protected deployer?: HyperlaneDeployer; + setDeployer(deployer: HyperlaneDeployer): void { + this.deployer = deployer; + } + static fromEnvironment( env: Env, multiProvider: MultiProvider, @@ -113,7 +110,9 @@ export class HyperlaneIsmFactory extends HyperlaneApp { } const ismType = config.type; - this.logger( + const logger = this.logger.extend(`${destination}:${ismType}`); + + logger( `Deploying ${ismType} to ${destination} ${ origin ? `(for verifying ${origin})` : '' }`, @@ -123,7 +122,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { switch (ismType) { case IsmType.MESSAGE_ID_MULTISIG: case IsmType.MERKLE_ROOT_MULTISIG: - contract = await this.deployMultisigIsm(destination, config); + contract = await this.deployMultisigIsm(destination, config, logger); break; case IsmType.ROUTING: case IsmType.FALLBACK_ROUTING: @@ -133,6 +132,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { origin, mailbox, existingIsmAddress, + logger, }); break; case IsmType.AGGREGATION: @@ -141,22 +141,42 @@ export class HyperlaneIsmFactory extends HyperlaneApp { config, origin, mailbox, + logger, }); break; case IsmType.OP_STACK: - contract = await this.deployOpStackIsm(destination, config); + if (!this.deployer) { + throw new Error(`HyperlaneDeployer must be set to deploy ${ismType}`); + } + contract = await this.deployer?.deployContractFromFactory( + destination, + new OPStackIsm__factory(), + IsmType.OP_STACK, + [config.nativeBridge], + ); break; case IsmType.PAUSABLE: - contract = await this.multiProvider.handleDeploy( + if (!this.deployer) { + throw new Error(`HyperlaneDeployer must be set to deploy ${ismType}`); + } + contract = await this.deployer?.deployContractFromFactory( destination, new PausableIsm__factory(), + IsmType.PAUSABLE, [config.owner], ); + await this.deployer?.transferOwnershipOfContracts(destination, config, { + [IsmType.PAUSABLE]: contract, + }); break; case IsmType.TEST_ISM: - contract = await this.multiProvider.handleDeploy( + if (!this.deployer) { + throw new Error(`HyperlaneDeployer must be set to deploy ${ismType}`); + } + contract = await this.deployer?.deployContractFromFactory( destination, new TestIsm__factory(), + IsmType.TEST_ISM, [], ); break; @@ -185,6 +205,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { protected async deployMultisigIsm( destination: ChainName, config: MultisigIsmConfig, + logger: Debugger, ): Promise { const signer = this.multiProvider.getSigner(destination); const multisigIsmFactory = @@ -196,6 +217,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { destination, multisigIsmFactory, config.validators, + logger, config.threshold, ); @@ -208,6 +230,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { origin?: ChainName; mailbox?: Address; existingIsmAddress?: Address; + logger: Debugger; }): Promise { const { destination, config, mailbox, existingIsmAddress } = params; const overrides = this.multiProvider.getTransactionOverrides(destination); @@ -217,7 +240,9 @@ export class HyperlaneIsmFactory extends HyperlaneApp { config.domains = objFilter( config.domains, (domain, config): config is IsmConfig => { - const domainId = this.multiProvider.tryGetDomainId(domain); + const domainId = + chainMetadata[domain]?.domainId ?? + this.multiProvider.tryGetDomainId(domain); if (domainId === null) { warn( `Domain ${domain} doesn't have chain metadata provided, skipping ...`, @@ -226,8 +251,10 @@ export class HyperlaneIsmFactory extends HyperlaneApp { return domainId !== null; }, ); - const safeConfigDomains = Object.keys(config.domains).map((domain) => - this.multiProvider.getDomainId(domain), + const safeConfigDomains = Object.keys(config.domains).map( + (domain) => + chainMetadata[domain]?.domainId ?? + this.multiProvider.getDomainId(domain), ); const delta: RoutingIsmDelta = existingIsmAddress ? await routingModuleDelta( @@ -263,7 +290,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { // deploying all the ISMs which have to be updated for (const originDomain of delta.domainsToEnroll) { const origin = this.multiProvider.getChainName(originDomain); // already filtered to only include domains in the multiprovider - logger( + params.logger( `Reconfiguring preexisting routing ISM at for origin ${origin}...`, ); const ism = await this.deploy({ @@ -282,7 +309,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { } // unenrolling domains if needed for (const originDomain of delta.domainsToUnenroll) { - logger( + params.logger( `Unenrolling originDomain ${originDomain} from preexisting routing ISM at ${existingIsmAddress}...`, ); const tx = await routingIsm.remove(originDomain, overrides); @@ -290,7 +317,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { } // transfer ownership if needed if (delta.owner) { - logger(`Transferring ownership of routing ISM...`); + params.logger(`Transferring ownership of routing ISM...`); const tx = await routingIsm.transferOwnership(delta.owner, overrides); await this.multiProvider.handleTx(destination, tx); } @@ -314,13 +341,13 @@ export class HyperlaneIsmFactory extends HyperlaneApp { 'Mailbox address is required for deploying fallback routing ISM', ); } - logger('Deploying fallback routing ISM ...'); + params.logger('Deploying fallback routing ISM ...'); routingIsm = await this.multiProvider.handleDeploy( destination, new DefaultFallbackRoutingIsm__factory(), [mailbox], ); - logger('Initialising fallback routing ISM ...'); + params.logger('Initialising fallback routing ISM ...'); receipt = await this.multiProvider.handleTx( destination, routingIsm['initialize(address,uint32[],address[])']( @@ -371,6 +398,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { config: AggregationIsmConfig; origin?: ChainName; mailbox?: Address; + logger: Debugger; }): Promise { const { destination, config, origin, mailbox } = params; const signer = this.multiProvider.getSigner(destination); @@ -390,26 +418,17 @@ export class HyperlaneIsmFactory extends HyperlaneApp { destination, aggregationIsmFactory, addresses, + params.logger, config.threshold, ); return IAggregationIsm__factory.connect(address, signer); } - protected async deployOpStackIsm( - chain: ChainName, - config: OpStackIsmConfig, - ): Promise { - return await this.multiProvider.handleDeploy( - chain, - new OPStackIsm__factory(), - [config.nativeBridge], - ); - } - async deployStaticAddressSet( chain: ChainName, factory: StaticThresholdAddressSetFactory | StaticAddressSetFactory, values: Address[], + logger: Debugger, threshold = values.length, ): Promise
{ const sorted = [...values].sort(); @@ -420,7 +439,7 @@ export class HyperlaneIsmFactory extends HyperlaneApp { ); const code = await this.multiProvider.getProvider(chain).getCode(address); if (code === '0x') { - this.logger( + logger( `Deploying new ${threshold} of ${values.length} address set to ${chain}`, ); const overrides = this.multiProvider.getTransactionOverrides(chain); @@ -432,390 +451,10 @@ export class HyperlaneIsmFactory extends HyperlaneApp { await this.multiProvider.handleTx(chain, hash); // TODO: add proxy verification artifact? } else { - this.logger( + logger( `Recovered ${threshold} of ${values.length} address set on ${chain}`, ); } return address; } } - -// Note that this function may return false negatives, but should -// not return false positives. -// This can happen if, for example, the module has sender, recipient, or -// body specific logic, as the sample message used when querying the ISM -// sets all of these to zero. -export async function moduleCanCertainlyVerify( - destModule: Address | IsmConfig, - multiProvider: MultiProvider, - origin: ChainName, - destination: ChainName, -): Promise { - const originDomainId = multiProvider.tryGetDomainId(origin); - const destinationDomainId = multiProvider.tryGetDomainId(destination); - if (!originDomainId || !destinationDomainId) { - return false; - } - const message = formatMessage( - 0, - 0, - originDomainId, - ethers.constants.AddressZero, - destinationDomainId, - ethers.constants.AddressZero, - '0x', - ); - const provider = multiProvider.getSignerOrProvider(destination); - - if (typeof destModule === 'string') { - const module = IInterchainSecurityModule__factory.connect( - destModule, - provider, - ); - - try { - const moduleType = await module.moduleType(); - if ( - moduleType === ModuleType.MERKLE_ROOT_MULTISIG || - moduleType === ModuleType.MESSAGE_ID_MULTISIG - ) { - const multisigModule = IMultisigIsm__factory.connect( - destModule, - provider, - ); - - const [, threshold] = await multisigModule.validatorsAndThreshold( - message, - ); - return threshold > 0; - } else if (moduleType === ModuleType.ROUTING) { - const routingIsm = IRoutingIsm__factory.connect(destModule, provider); - const subModule = await routingIsm.route(message); - return moduleCanCertainlyVerify( - subModule, - multiProvider, - origin, - destination, - ); - } else if (moduleType === ModuleType.AGGREGATION) { - const aggregationIsm = IAggregationIsm__factory.connect( - destModule, - provider, - ); - const [subModules, threshold] = - await aggregationIsm.modulesAndThreshold(message); - let verified = 0; - for (const subModule of subModules) { - const canVerify = await moduleCanCertainlyVerify( - subModule, - multiProvider, - origin, - destination, - ); - if (canVerify) { - verified += 1; - } - } - return verified >= threshold; - } else { - throw new Error(`Unsupported module type: ${moduleType}`); - } - } catch (e) { - logger(`Error checking module ${destModule}: ${e}`); - return false; - } - } else { - // destModule is an IsmConfig - switch (destModule.type) { - case IsmType.MERKLE_ROOT_MULTISIG: - case IsmType.MESSAGE_ID_MULTISIG: - return destModule.threshold > 0; - case IsmType.ROUTING: { - const checking = moduleCanCertainlyVerify( - destModule.domains[destination], - multiProvider, - origin, - destination, - ); - return checking; - } - case IsmType.AGGREGATION: { - let verified = 0; - for (const subModule of destModule.modules) { - const canVerify = await moduleCanCertainlyVerify( - subModule, - multiProvider, - origin, - destination, - ); - if (canVerify) { - verified += 1; - } - } - return verified >= destModule.threshold; - } - case IsmType.OP_STACK: - return destModule.nativeBridge !== ethers.constants.AddressZero; - case IsmType.TEST_ISM: { - return true; - } - default: - throw new Error(`Unsupported module type: ${(destModule as any).type}`); - } - } -} - -export async function moduleMatchesConfig( - chain: ChainName, - moduleAddress: Address, - config: IsmConfig, - multiProvider: MultiProvider, - contracts: HyperlaneContracts, - mailbox?: Address, -): Promise { - if (typeof config === 'string') { - return eqAddress(moduleAddress, config); - } - - // If the module address is zero, it can't match any object-based config. - // The subsequent check of what moduleType it is will throw, so we fail here. - if (eqAddress(moduleAddress, ethers.constants.AddressZero)) { - return false; - } - - const provider = multiProvider.getProvider(chain); - const module = IInterchainSecurityModule__factory.connect( - moduleAddress, - provider, - ); - const actualType = await module.moduleType(); - if (actualType !== ismTypeToModuleType(config.type)) return false; - let matches = true; - switch (config.type) { - case IsmType.MERKLE_ROOT_MULTISIG: { - // A MerkleRootMultisigIsm matches if validators and threshold match the config - const expectedAddress = - await contracts.merkleRootMultisigIsmFactory.getAddress( - config.validators.sort(), - config.threshold, - ); - matches = eqAddress(expectedAddress, module.address); - break; - } - case IsmType.MESSAGE_ID_MULTISIG: { - // A MessageIdMultisigIsm matches if validators and threshold match the config - const expectedAddress = - await contracts.messageIdMultisigIsmFactory.getAddress( - config.validators.sort(), - config.threshold, - ); - matches = eqAddress(expectedAddress, module.address); - break; - } - case IsmType.FALLBACK_ROUTING: - case IsmType.ROUTING: { - // A RoutingIsm matches if: - // 1. The set of domains in the config equals those on-chain - // 2. The modules for each domain match the config - // TODO: Check (1) - const routingIsm = DomainRoutingIsm__factory.connect( - moduleAddress, - provider, - ); - // Check that the RoutingISM owner matches the config - const owner = await routingIsm.owner(); - matches &&= eqAddress(owner, config.owner); - // check if the mailbox matches the config for fallback routing - if (config.type === IsmType.FALLBACK_ROUTING) { - const client = MailboxClient__factory.connect(moduleAddress, provider); - const mailboxAddress = await client.mailbox(); - matches = - matches && - mailbox !== undefined && - eqAddress(mailboxAddress, mailbox); - } - const delta = await routingModuleDelta( - chain, - moduleAddress, - config, - multiProvider, - contracts, - mailbox, - ); - matches = - matches && - delta.domainsToEnroll.length === 0 && - delta.domainsToUnenroll.length === 0 && - !delta.mailbox && - !delta.owner; - break; - } - case IsmType.AGGREGATION: { - // An AggregationIsm matches if: - // 1. The threshold matches the config - // 2. There is a bijection between on and off-chain configured modules - const aggregationIsm = StaticAggregationIsm__factory.connect( - moduleAddress, - provider, - ); - const [subModules, threshold] = await aggregationIsm.modulesAndThreshold( - '0x', - ); - matches &&= threshold === config.threshold; - matches &&= subModules.length === config.modules.length; - - const configIndexMatched = new Map(); - for (const subModule of subModules) { - const subModuleMatchesConfig = await Promise.all( - config.modules.map((c) => - moduleMatchesConfig(chain, subModule, c, multiProvider, contracts), - ), - ); - // The submodule returned by the ISM must match exactly one - // entry in the config. - const count = subModuleMatchesConfig.filter(Boolean).length; - matches &&= count === 1; - - // That entry in the config should not have been matched already. - subModuleMatchesConfig.forEach((matched, index) => { - if (matched) { - matches &&= !configIndexMatched.has(index); - configIndexMatched.set(index, true); - } - }); - } - break; - } - case IsmType.OP_STACK: { - const opStackIsm = OPStackIsm__factory.connect(moduleAddress, provider); - const type = await opStackIsm.moduleType(); - matches &&= type === ModuleType.NULL; - break; - } - case IsmType.TEST_ISM: { - // This is just a TestISM - matches = true; - break; - } - case IsmType.PAUSABLE: { - const pausableIsm = PausableIsm__factory.connect(moduleAddress, provider); - const owner = await pausableIsm.owner(); - matches &&= eqAddress(owner, config.owner); - - if (config.paused) { - const isPaused = await pausableIsm.paused(); - matches &&= config.paused === isPaused; - } - break; - } - default: { - throw new Error('Unsupported ModuleType'); - } - } - - return matches; -} - -export async function routingModuleDelta( - destination: ChainName, - moduleAddress: Address, - config: RoutingIsmConfig, - multiProvider: MultiProvider, - contracts: HyperlaneContracts, - mailbox?: Address, -): Promise { - const provider = multiProvider.getProvider(destination); - const routingIsm = DomainRoutingIsm__factory.connect(moduleAddress, provider); - const owner = await routingIsm.owner(); - const deployedDomains = (await routingIsm.domains()).map((domain) => - domain.toNumber(), - ); - // config.domains is already filtered to only include domains in the multiprovider - const safeConfigDomains = objMap(config.domains, (domain) => - multiProvider.getDomainId(domain), - ); - - const delta: RoutingIsmDelta = { - domainsToUnenroll: [], - domainsToEnroll: [], - }; - - // if owners don't match, we need to transfer ownership - if (!eqAddress(owner, normalizeAddress(config.owner))) - delta.owner = config.owner; - if (config.type === IsmType.FALLBACK_ROUTING) { - const client = MailboxClient__factory.connect(moduleAddress, provider); - const mailboxAddress = await client.mailbox(); - if (mailbox && !eqAddress(mailboxAddress, mailbox)) delta.mailbox = mailbox; - } - // check for exclusion of domains in the config - delta.domainsToUnenroll = deployedDomains.filter( - (domain) => !Object.values(safeConfigDomains).includes(domain), - ); - // check for inclusion of domains in the config - for (const [origin, subConfig] of Object.entries(config.domains)) { - const originDomain = safeConfigDomains[origin]; - if (!deployedDomains.includes(originDomain)) { - delta.domainsToEnroll.push(originDomain); - } else { - const subModule = await routingIsm.module(originDomain); - // Recursively check that the submodule for each configured - // domain matches the submodule config. - const subModuleMatches = await moduleMatchesConfig( - destination, - subModule, - subConfig, - multiProvider, - contracts, - mailbox, - ); - if (!subModuleMatches) delta.domainsToEnroll.push(originDomain); - } - } - return delta; -} - -export function collectValidators( - origin: ChainName, - config: IsmConfig, -): Set { - // TODO: support address configurations in collectValidators - if (typeof config === 'string') { - debug('hyperlane:IsmFactory')( - 'Address config unimplemented in collectValidators', - ); - return new Set([]); - } - - let validators: string[] = []; - if ( - config.type === IsmType.MERKLE_ROOT_MULTISIG || - config.type === IsmType.MESSAGE_ID_MULTISIG - ) { - validators = config.validators; - } else if (config.type === IsmType.ROUTING) { - if (Object.keys(config.domains).includes(origin)) { - const domainValidators = collectValidators( - origin, - config.domains[origin], - ); - validators = [...domainValidators]; - } - } else if (config.type === IsmType.AGGREGATION) { - const aggregatedValidators = config.modules.map((c) => - collectValidators(origin, c), - ); - aggregatedValidators.forEach((set) => { - validators = validators.concat([...set]); - }); - } else if ( - config.type === IsmType.TEST_ISM || - config.type === IsmType.PAUSABLE - ) { - return new Set([]); - } else { - throw new Error('Unsupported ModuleType'); - } - - return new Set(validators); -} diff --git a/typescript/sdk/src/ism/utils.ts b/typescript/sdk/src/ism/utils.ts new file mode 100644 index 0000000000..1a94a08f3c --- /dev/null +++ b/typescript/sdk/src/ism/utils.ts @@ -0,0 +1,419 @@ +import debug from 'debug'; +import { ethers } from 'ethers'; + +import { + DomainRoutingIsm__factory, + IAggregationIsm__factory, + IInterchainSecurityModule__factory, + IMultisigIsm__factory, + IRoutingIsm__factory, + MailboxClient__factory, + OPStackIsm__factory, + PausableIsm__factory, + StaticAggregationIsm__factory, +} from '@hyperlane-xyz/core'; +import { + Address, + eqAddress, + formatMessage, + normalizeAddress, + objMap, +} from '@hyperlane-xyz/utils'; + +import { chainMetadata } from '../consts/chainMetadata'; +import { HyperlaneContracts } from '../contracts/types'; +import { ProxyFactoryFactories } from '../deploy/contracts'; +import { MultiProvider } from '../providers/MultiProvider'; +import { ChainName } from '../types'; + +import { + IsmConfig, + IsmType, + ModuleType, + RoutingIsmConfig, + RoutingIsmDelta, + ismTypeToModuleType, +} from './types'; + +const logger = debug('hyperlane:IsmUtils'); + +// Note that this function may return false negatives, but should +// not return false positives. +// This can happen if, for example, the module has sender, recipient, or +// body specific logic, as the sample message used when querying the ISM +// sets all of these to zero. +export async function moduleCanCertainlyVerify( + destModule: Address | IsmConfig, + multiProvider: MultiProvider, + origin: ChainName, + destination: ChainName, +): Promise { + const originDomainId = multiProvider.tryGetDomainId(origin); + const destinationDomainId = multiProvider.tryGetDomainId(destination); + if (!originDomainId || !destinationDomainId) { + return false; + } + const message = formatMessage( + 0, + 0, + originDomainId, + ethers.constants.AddressZero, + destinationDomainId, + ethers.constants.AddressZero, + '0x', + ); + const provider = multiProvider.getSignerOrProvider(destination); + + if (typeof destModule === 'string') { + const module = IInterchainSecurityModule__factory.connect( + destModule, + provider, + ); + + try { + const moduleType = await module.moduleType(); + if ( + moduleType === ModuleType.MERKLE_ROOT_MULTISIG || + moduleType === ModuleType.MESSAGE_ID_MULTISIG + ) { + const multisigModule = IMultisigIsm__factory.connect( + destModule, + provider, + ); + + const [, threshold] = await multisigModule.validatorsAndThreshold( + message, + ); + return threshold > 0; + } else if (moduleType === ModuleType.ROUTING) { + const routingIsm = IRoutingIsm__factory.connect(destModule, provider); + const subModule = await routingIsm.route(message); + return moduleCanCertainlyVerify( + subModule, + multiProvider, + origin, + destination, + ); + } else if (moduleType === ModuleType.AGGREGATION) { + const aggregationIsm = IAggregationIsm__factory.connect( + destModule, + provider, + ); + const [subModules, threshold] = + await aggregationIsm.modulesAndThreshold(message); + let verified = 0; + for (const subModule of subModules) { + const canVerify = await moduleCanCertainlyVerify( + subModule, + multiProvider, + origin, + destination, + ); + if (canVerify) { + verified += 1; + } + } + return verified >= threshold; + } else { + throw new Error(`Unsupported module type: ${moduleType}`); + } + } catch (e) { + logger(`Error checking module ${destModule}: ${e}`); + return false; + } + } else { + // destModule is an IsmConfig + switch (destModule.type) { + case IsmType.MERKLE_ROOT_MULTISIG: + case IsmType.MESSAGE_ID_MULTISIG: + return destModule.threshold > 0; + case IsmType.ROUTING: { + const checking = moduleCanCertainlyVerify( + destModule.domains[destination], + multiProvider, + origin, + destination, + ); + return checking; + } + case IsmType.AGGREGATION: { + let verified = 0; + for (const subModule of destModule.modules) { + const canVerify = await moduleCanCertainlyVerify( + subModule, + multiProvider, + origin, + destination, + ); + if (canVerify) { + verified += 1; + } + } + return verified >= destModule.threshold; + } + case IsmType.OP_STACK: + return destModule.nativeBridge !== ethers.constants.AddressZero; + case IsmType.TEST_ISM: { + return true; + } + default: + throw new Error(`Unsupported module type: ${(destModule as any).type}`); + } + } +} + +export async function moduleMatchesConfig( + chain: ChainName, + moduleAddress: Address, + config: IsmConfig, + multiProvider: MultiProvider, + contracts: HyperlaneContracts, + mailbox?: Address, +): Promise { + if (typeof config === 'string') { + return eqAddress(moduleAddress, config); + } + + // If the module address is zero, it can't match any object-based config. + // The subsequent check of what moduleType it is will throw, so we fail here. + if (eqAddress(moduleAddress, ethers.constants.AddressZero)) { + return false; + } + + const provider = multiProvider.getProvider(chain); + const module = IInterchainSecurityModule__factory.connect( + moduleAddress, + provider, + ); + const actualType = await module.moduleType(); + if (actualType !== ismTypeToModuleType(config.type)) return false; + let matches = true; + switch (config.type) { + case IsmType.MERKLE_ROOT_MULTISIG: { + // A MerkleRootMultisigIsm matches if validators and threshold match the config + const expectedAddress = + await contracts.merkleRootMultisigIsmFactory.getAddress( + config.validators.sort(), + config.threshold, + ); + matches = eqAddress(expectedAddress, module.address); + break; + } + case IsmType.MESSAGE_ID_MULTISIG: { + // A MessageIdMultisigIsm matches if validators and threshold match the config + const expectedAddress = + await contracts.messageIdMultisigIsmFactory.getAddress( + config.validators.sort(), + config.threshold, + ); + matches = eqAddress(expectedAddress, module.address); + break; + } + case IsmType.FALLBACK_ROUTING: + case IsmType.ROUTING: { + // A RoutingIsm matches if: + // 1. The set of domains in the config equals those on-chain + // 2. The modules for each domain match the config + // TODO: Check (1) + const routingIsm = DomainRoutingIsm__factory.connect( + moduleAddress, + provider, + ); + // Check that the RoutingISM owner matches the config + const owner = await routingIsm.owner(); + matches &&= eqAddress(owner, config.owner); + // check if the mailbox matches the config for fallback routing + if (config.type === IsmType.FALLBACK_ROUTING) { + const client = MailboxClient__factory.connect(moduleAddress, provider); + const mailboxAddress = await client.mailbox(); + matches = + matches && + mailbox !== undefined && + eqAddress(mailboxAddress, mailbox); + } + const delta = await routingModuleDelta( + chain, + moduleAddress, + config, + multiProvider, + contracts, + mailbox, + ); + matches = + matches && + delta.domainsToEnroll.length === 0 && + delta.domainsToUnenroll.length === 0 && + !delta.mailbox && + !delta.owner; + break; + } + case IsmType.AGGREGATION: { + // An AggregationIsm matches if: + // 1. The threshold matches the config + // 2. There is a bijection between on and off-chain configured modules + const aggregationIsm = StaticAggregationIsm__factory.connect( + moduleAddress, + provider, + ); + const [subModules, threshold] = await aggregationIsm.modulesAndThreshold( + '0x', + ); + matches &&= threshold === config.threshold; + matches &&= subModules.length === config.modules.length; + + const configIndexMatched = new Map(); + for (const subModule of subModules) { + const subModuleMatchesConfig = await Promise.all( + config.modules.map((c) => + moduleMatchesConfig(chain, subModule, c, multiProvider, contracts), + ), + ); + // The submodule returned by the ISM must match exactly one + // entry in the config. + const count = subModuleMatchesConfig.filter(Boolean).length; + matches &&= count === 1; + + // That entry in the config should not have been matched already. + subModuleMatchesConfig.forEach((matched, index) => { + if (matched) { + matches &&= !configIndexMatched.has(index); + configIndexMatched.set(index, true); + } + }); + } + break; + } + case IsmType.OP_STACK: { + const opStackIsm = OPStackIsm__factory.connect(moduleAddress, provider); + const type = await opStackIsm.moduleType(); + matches &&= type === ModuleType.NULL; + break; + } + case IsmType.TEST_ISM: { + // This is just a TestISM + matches = true; + break; + } + case IsmType.PAUSABLE: { + const pausableIsm = PausableIsm__factory.connect(moduleAddress, provider); + const owner = await pausableIsm.owner(); + matches &&= eqAddress(owner, config.owner); + + if (config.paused) { + const isPaused = await pausableIsm.paused(); + matches &&= config.paused === isPaused; + } + break; + } + default: { + throw new Error('Unsupported ModuleType'); + } + } + + return matches; +} + +export async function routingModuleDelta( + destination: ChainName, + moduleAddress: Address, + config: RoutingIsmConfig, + multiProvider: MultiProvider, + contracts: HyperlaneContracts, + mailbox?: Address, +): Promise { + const provider = multiProvider.getProvider(destination); + const routingIsm = DomainRoutingIsm__factory.connect(moduleAddress, provider); + const owner = await routingIsm.owner(); + const deployedDomains = (await routingIsm.domains()).map((domain) => + domain.toNumber(), + ); + // config.domains is already filtered to only include domains in the multiprovider + const safeConfigDomains = objMap( + config.domains, + (chainName) => + chainMetadata[chainName]?.domainId ?? + multiProvider.getDomainId(chainName), + ); + + const delta: RoutingIsmDelta = { + domainsToUnenroll: [], + domainsToEnroll: [], + }; + + // if owners don't match, we need to transfer ownership + if (!eqAddress(owner, normalizeAddress(config.owner))) + delta.owner = config.owner; + if (config.type === IsmType.FALLBACK_ROUTING) { + const client = MailboxClient__factory.connect(moduleAddress, provider); + const mailboxAddress = await client.mailbox(); + if (mailbox && !eqAddress(mailboxAddress, mailbox)) delta.mailbox = mailbox; + } + // check for exclusion of domains in the config + delta.domainsToUnenroll = deployedDomains.filter( + (domain) => !Object.values(safeConfigDomains).includes(domain), + ); + // check for inclusion of domains in the config + for (const [origin, subConfig] of Object.entries(config.domains)) { + const originDomain = safeConfigDomains[origin]; + if (!deployedDomains.includes(originDomain)) { + delta.domainsToEnroll.push(originDomain); + } else { + const subModule = await routingIsm.module(originDomain); + // Recursively check that the submodule for each configured + // domain matches the submodule config. + const subModuleMatches = await moduleMatchesConfig( + destination, + subModule, + subConfig, + multiProvider, + contracts, + mailbox, + ); + if (!subModuleMatches) delta.domainsToEnroll.push(originDomain); + } + } + return delta; +} + +export function collectValidators( + origin: ChainName, + config: IsmConfig, +): Set { + // TODO: support address configurations in collectValidators + if (typeof config === 'string') { + logger.extend(origin)('Address config unimplemented in collectValidators'); + return new Set([]); + } + + let validators: string[] = []; + if ( + config.type === IsmType.MERKLE_ROOT_MULTISIG || + config.type === IsmType.MESSAGE_ID_MULTISIG + ) { + validators = config.validators; + } else if (config.type === IsmType.ROUTING) { + if (Object.keys(config.domains).includes(origin)) { + const domainValidators = collectValidators( + origin, + config.domains[origin], + ); + validators = [...domainValidators]; + } + } else if (config.type === IsmType.AGGREGATION) { + const aggregatedValidators = config.modules.map((c) => + collectValidators(origin, c), + ); + aggregatedValidators.forEach((set) => { + validators = validators.concat([...set]); + }); + } else if ( + config.type === IsmType.TEST_ISM || + config.type === IsmType.PAUSABLE + ) { + return new Set([]); + } else { + throw new Error('Unsupported ModuleType'); + } + + return new Set(validators); +} diff --git a/typescript/sdk/src/metadata/ChainMetadataManager.ts b/typescript/sdk/src/metadata/ChainMetadataManager.ts index 97d5fbaedd..2ff6899349 100644 --- a/typescript/sdk/src/metadata/ChainMetadataManager.ts +++ b/typescript/sdk/src/metadata/ChainMetadataManager.ts @@ -3,18 +3,20 @@ import { Debugger, debug } from 'debug'; import { ProtocolType, exclude, pick } from '@hyperlane-xyz/utils'; import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata'; -import { ChainMap, ChainName } from '../types'; +import { ChainMap, ChainName, ChainNameOrId } from '../types'; import { getExplorerAddressUrl, + getExplorerApi, getExplorerApiUrl, getExplorerBaseUrl, getExplorerTxUrl, } from './blockExplorer'; import { ChainMetadata, + ExplorerFamily, getDomainId, - isValidChainMetadata, + safeParseChainMetadata, } from './chainMetadataTypes'; export interface ChainMetadataManagerOptions { @@ -55,8 +57,14 @@ export class ChainMetadataManager { * @throws if chain's name or domain/chain ID collide */ addChain(metadata: ChainMetadata): void { - if (!isValidChainMetadata(metadata)) - throw new Error(`Invalid chain metadata for ${metadata.name}`); + const parseResult = safeParseChainMetadata(metadata); + if (!parseResult.success) { + throw new Error( + `Invalid chain metadata for ${ + metadata.name + }: ${parseResult.error.format()}`, + ); + } // Ensure no two chains have overlapping names/domainIds/chainIds for (const chainMetadata of Object.values(this.metadata)) { const { name, chainId, domainId } = chainMetadata; @@ -81,7 +89,7 @@ export class ChainMetadataManager { * @throws if chain's metadata has not been set */ tryGetChainMetadata( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, ): ChainMetadata | null { // First check if it's a chain name if (this.metadata[chainNameOrId]) return this.metadata[chainNameOrId]; @@ -96,7 +104,7 @@ export class ChainMetadataManager { * Get the metadata for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getChainMetadata(chainNameOrId: ChainName | number): ChainMetadata { + getChainMetadata(chainNameOrId: ChainNameOrId): ChainMetadata { const chainMetadata = this.tryGetChainMetadata(chainNameOrId); if (!chainMetadata) { throw new Error(`No chain metadata set for ${chainNameOrId}`); @@ -104,10 +112,18 @@ export class ChainMetadataManager { return chainMetadata; } + /** + * Returns true if the given chain name, chain id, or domain id is + * include in this manager's metadata, false otherwise + */ + hasChain(chainNameOrId: ChainNameOrId): boolean { + return !!this.tryGetChainMetadata(chainNameOrId); + } + /** * Get the name for a given chain name, chain id, or domain id */ - tryGetChainName(chainNameOrId: ChainName | number): string | null { + tryGetChainName(chainNameOrId: ChainNameOrId): string | null { return this.tryGetChainMetadata(chainNameOrId)?.name ?? null; } @@ -115,7 +131,7 @@ export class ChainMetadataManager { * Get the name for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getChainName(chainNameOrId: ChainName | number): string { + getChainName(chainNameOrId: ChainNameOrId): string { return this.getChainMetadata(chainNameOrId).name; } @@ -129,7 +145,7 @@ export class ChainMetadataManager { /** * Get the id for a given chain name, chain id, or domain id */ - tryGetChainId(chainNameOrId: ChainName | number): number | string | null { + tryGetChainId(chainNameOrId: ChainNameOrId): number | string | null { return this.tryGetChainMetadata(chainNameOrId)?.chainId ?? null; } @@ -137,7 +153,7 @@ export class ChainMetadataManager { * Get the id for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getChainId(chainNameOrId: ChainName | number): number | string { + getChainId(chainNameOrId: ChainNameOrId): number | string { return this.getChainMetadata(chainNameOrId).chainId; } @@ -151,7 +167,7 @@ export class ChainMetadataManager { /** * Get the domain id for a given chain name, chain id, or domain id */ - tryGetDomainId(chainNameOrId: ChainName | number): number | null { + tryGetDomainId(chainNameOrId: ChainNameOrId): number | null { const metadata = this.tryGetChainMetadata(chainNameOrId); if (!metadata) return null; return getDomainId(metadata) ?? null; @@ -161,7 +177,7 @@ export class ChainMetadataManager { * Get the domain id for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getDomainId(chainNameOrId: ChainName | number): number { + getDomainId(chainNameOrId: ChainNameOrId): number { const domainId = this.tryGetDomainId(chainNameOrId); if (!domainId) throw new Error(`No domain id set for ${chainNameOrId}`); return domainId; @@ -170,7 +186,7 @@ export class ChainMetadataManager { /** * Get the protocol type for a given chain name, chain id, or domain id */ - tryGetProtocol(chainNameOrId: ChainName | number): ProtocolType | null { + tryGetProtocol(chainNameOrId: ChainNameOrId): ProtocolType | null { return this.tryGetChainMetadata(chainNameOrId)?.protocol ?? null; } @@ -178,7 +194,7 @@ export class ChainMetadataManager { * Get the protocol type for a given chain name, chain id, or domain id * @throws if chain's metadata or protocol has not been set */ - getProtocol(chainNameOrId: ChainName | number): ProtocolType { + getProtocol(chainNameOrId: ChainNameOrId): ProtocolType { return this.getChainMetadata(chainNameOrId).protocol; } @@ -219,7 +235,7 @@ export class ChainMetadataManager { * Get an RPC URL for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getRpcUrl(chainNameOrId: ChainName | number): string { + getRpcUrl(chainNameOrId: ChainNameOrId): string { const { rpcUrls } = this.getChainMetadata(chainNameOrId); if (!rpcUrls?.length || !rpcUrls[0].http) throw new Error(`No RPC URl configured for ${chainNameOrId}`); @@ -229,7 +245,7 @@ export class ChainMetadataManager { /** * Get a block explorer URL for a given chain name, chain id, or domain id */ - tryGetExplorerUrl(chainNameOrId: ChainName | number): string | null { + tryGetExplorerUrl(chainNameOrId: ChainNameOrId): string | null { const metadata = this.tryGetChainMetadata(chainNameOrId); if (!metadata) return null; return getExplorerBaseUrl(metadata); @@ -239,16 +255,44 @@ export class ChainMetadataManager { * Get a block explorer URL for a given chain name, chain id, or domain id * @throws if chain's metadata or block explorer data has no been set */ - getExplorerUrl(chainNameOrId: ChainName | number): string { + getExplorerUrl(chainNameOrId: ChainNameOrId): string { const url = this.tryGetExplorerUrl(chainNameOrId); if (!url) throw new Error(`No explorer url set for ${chainNameOrId}`); return url; } + /** + * Get a block explorer's API for a given chain name, chain id, or domain id + */ + tryGetExplorerApi(chainNameOrId: ChainName | number): { + apiUrl: string; + apiKey?: string; + family?: ExplorerFamily; + } | null { + const metadata = this.tryGetChainMetadata(chainNameOrId); + if (!metadata) return null; + return getExplorerApi(metadata); + } + + /** + * Get a block explorer API for a given chain name, chain id, or domain id + * @throws if chain's metadata or block explorer data has no been set + */ + getExplorerApi(chainNameOrId: ChainName | number): { + apiUrl: string; + apiKey?: string; + family?: ExplorerFamily; + } { + const explorerApi = this.tryGetExplorerApi(chainNameOrId); + if (!explorerApi) + throw new Error(`No supported explorer api set for ${chainNameOrId}`); + return explorerApi; + } + /** * Get a block explorer's API URL for a given chain name, chain id, or domain id */ - tryGetExplorerApiUrl(chainNameOrId: ChainName | number): string | null { + tryGetExplorerApiUrl(chainNameOrId: ChainNameOrId): string | null { const metadata = this.tryGetChainMetadata(chainNameOrId); if (!metadata) return null; return getExplorerApiUrl(metadata); @@ -258,7 +302,7 @@ export class ChainMetadataManager { * Get a block explorer API URL for a given chain name, chain id, or domain id * @throws if chain's metadata or block explorer data has no been set */ - getExplorerApiUrl(chainNameOrId: ChainName | number): string { + getExplorerApiUrl(chainNameOrId: ChainNameOrId): string { const url = this.tryGetExplorerApiUrl(chainNameOrId); if (!url) throw new Error(`No explorer api url set for ${chainNameOrId}`); return url; @@ -268,7 +312,7 @@ export class ChainMetadataManager { * Get a block explorer URL for given chain's tx */ tryGetExplorerTxUrl( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, response: { hash: string }, ): string | null { const metadata = this.tryGetChainMetadata(chainNameOrId); @@ -281,7 +325,7 @@ export class ChainMetadataManager { * @throws if chain's metadata or block explorer data has no been set */ getExplorerTxUrl( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, response: { hash: string }, ): string { return `${this.getExplorerUrl(chainNameOrId)}/tx/${response.hash}`; @@ -291,7 +335,7 @@ export class ChainMetadataManager { * Get a block explorer URL for given chain's address */ async tryGetExplorerAddressUrl( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, address?: string, ): Promise { const metadata = this.tryGetChainMetadata(chainNameOrId); @@ -304,7 +348,7 @@ export class ChainMetadataManager { * @throws if address or the chain's block explorer data has no been set */ async getExplorerAddressUrl( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, address?: string, ): Promise { const url = await this.tryGetExplorerAddressUrl(chainNameOrId, address); diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 5be7f21fcd..5aa89687ff 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -92,6 +92,30 @@ export type AgentSignerCosmosKey = z.infer; export type AgentSignerNode = z.infer; export type AgentSigner = z.infer; +// Additional chain metadata for Cosmos chains required by the agents. +const AgentCosmosChainMetadataSchema = z.object({ + canonicalAsset: z + .string() + .describe( + 'The name of the canonical asset for this chain, usually in "micro" form, e.g. untrn', + ), + gasPrice: z.object({ + denom: z + .string() + .describe('The coin denom, usually in "micro" form, e.g. untrn'), + amount: z + .string() + .regex(/^(\d*[.])?\d+$/) + .describe('The the gas price, in denom, to pay for each unit of gas'), + }), + contractAddressBytes: z + .number() + .int() + .positive() + .lte(32) + .describe('The number of bytes used to represent a contract address.'), +}); + export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( HyperlaneDeploymentArtifactsSchema, ) @@ -100,7 +124,7 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( .string() .optional() .describe( - 'Specify a comma seperated list of custom RPC URLs to use for this chain. If not specified, the default RPC urls will be used.', + 'Specify a comma separated list of custom RPC URLs to use for this chain. If not specified, the default RPC urls will be used.', ), rpcConsensusType: z .nativeEnum(RpcConsensusType) @@ -126,6 +150,7 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( }) .optional(), }) + .merge(AgentCosmosChainMetadataSchema.partial()) .refine((metadata) => { // Make sure that the signer is valid for the protocol @@ -138,25 +163,47 @@ export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( switch (metadata.protocol) { case ProtocolType.Ethereum: - return [ - AgentSignerKeyType.Hex, - signerType === AgentSignerKeyType.Aws, - signerType === AgentSignerKeyType.Node, - ].includes(signerType); + if ( + ![ + AgentSignerKeyType.Hex, + signerType === AgentSignerKeyType.Aws, + signerType === AgentSignerKeyType.Node, + ].includes(signerType) + ) { + return false; + } + break; case ProtocolType.Cosmos: - return [AgentSignerKeyType.Cosmos].includes(signerType); + if (![AgentSignerKeyType.Cosmos].includes(signerType)) { + return false; + } + break; case ProtocolType.Sealevel: - return [AgentSignerKeyType.Hex].includes(signerType); + if (![AgentSignerKeyType.Hex].includes(signerType)) { + return false; + } + break; case ProtocolType.Fuel: - return [AgentSignerKeyType.Hex].includes(signerType); + if (![AgentSignerKeyType.Hex].includes(signerType)) { + return false; + } + break; default: - // Just default to true if we don't know the protocol - return true; + // Just accept it if we don't know the protocol } + + // If the protocol type is Cosmos, require everything in AgentCosmosChainMetadataSchema + if (metadata.protocol === ProtocolType.Cosmos) { + if (!AgentCosmosChainMetadataSchema.safeParse(metadata).success) { + return false; + } + } + + return true; }); export type AgentChainMetadata = z.infer; @@ -342,6 +389,8 @@ export type ValidatorConfig = z.infer; export type AgentConfig = z.infer; +// Note this works well for EVM chains only, and likely needs some love +// before being useful for non-EVM chains. export function buildAgentConfig( chains: ChainName[], multiProvider: MultiProvider, diff --git a/typescript/sdk/src/metadata/blockExplorer.ts b/typescript/sdk/src/metadata/blockExplorer.ts index fbc616cc18..ccd8a0388c 100644 --- a/typescript/sdk/src/metadata/blockExplorer.ts +++ b/typescript/sdk/src/metadata/blockExplorer.ts @@ -2,7 +2,7 @@ import { ProtocolType } from '@hyperlane-xyz/utils'; import { solanaChainToClusterName } from '../consts/chainMetadata'; -import { ChainMetadata } from './chainMetadataTypes'; +import { ChainMetadata, ExplorerFamily } from './chainMetadataTypes'; export function getExplorerBaseUrl(metadata: ChainMetadata): string | null { if (!metadata?.blockExplorers?.length) return null; @@ -17,6 +17,22 @@ export function getExplorerBaseUrl(metadata: ChainMetadata): string | null { return url.toString(); } +export function getExplorerApi(metadata: ChainMetadata): { + apiUrl: string; + apiKey?: string | undefined; + family?: ExplorerFamily | undefined; +} | null { + const { protocol, blockExplorers } = metadata; + // TODO solana + cosmos support here as needed + if (protocol !== ProtocolType.Ethereum) return null; + if (!blockExplorers?.length || !blockExplorers[0].apiUrl) return null; + return { + apiUrl: blockExplorers[0].apiUrl, + apiKey: blockExplorers[0].apiKey, + family: blockExplorers[0].family, + }; +} + export function getExplorerApiUrl(metadata: ChainMetadata): string | null { const { protocol, blockExplorers } = metadata; // TODO solana + cosmos support here as needed diff --git a/typescript/sdk/src/metadata/chainMetadata.test.ts b/typescript/sdk/src/metadata/chainMetadata.test.ts index 347f3400c8..210480f879 100644 --- a/typescript/sdk/src/metadata/chainMetadata.test.ts +++ b/typescript/sdk/src/metadata/chainMetadata.test.ts @@ -9,7 +9,7 @@ import { ChainMetadata, isValidChainMetadata } from './chainMetadataTypes'; const minimalSchema: ChainMetadata = { chainId: 5, domainId: 5, - name: 'goerli', + name: 'sepolia', protocol: ProtocolType.Ethereum, rpcUrls: [{ http: 'https://foobar.com' }], }; @@ -60,6 +60,8 @@ describe('ChainMetadataSchema', () => { chainId: 'cosmos', bech32Prefix: 'cosmos', slip44: 118, + restUrls: [], + grpcUrls: [], }), ).to.eq(true); }); diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index 1ed7ecdb26..3d16dc89cf 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -2,15 +2,21 @@ * The types defined here are the source of truth for chain metadata. * ANY CHANGES HERE NEED TO BE REFLECTED IN HYPERLANE-BASE CONFIG PARSING. */ -import { z } from 'zod'; +import { SafeParseReturnType, z } from 'zod'; import { ProtocolType } from '@hyperlane-xyz/utils'; -import { ZNzUint, ZUint } from './customZodTypes'; +import { ZChainName, ZNzUint, ZUint } from './customZodTypes'; export enum ExplorerFamily { Etherscan = 'etherscan', Blockscout = 'blockscout', + Routescan = 'routescan', + Other = 'other', +} + +export enum ChainTechnicalStack { + ArbitrumNitro = 'arbitrumnitro', Other = 'other', } @@ -60,12 +66,9 @@ export type RpcUrl = z.infer; * Specified as a Zod schema */ export const ChainMetadataSchemaObject = z.object({ - name: z - .string() - .regex(/^[a-z][a-z0-9]*$/) - .describe( - 'The unique string identifier of the chain, used as the key in ChainMap dictionaries.', - ), + name: ZChainName.describe( + 'The unique string identifier of the chain, used as the key in ChainMap dictionaries.', + ), protocol: z .nativeEnum(ProtocolType) .describe( @@ -87,6 +90,12 @@ export const ChainMetadataSchemaObject = z.object({ .describe( 'A shorter human-readable name of the chain for use in user interfaces.', ), + technicalStack: z + .nativeEnum(ChainTechnicalStack) + .optional() + .describe( + 'The technical stack of the chain. See ChainTechnicalStack for valid values.', + ), logoURI: z .string() .optional() @@ -98,6 +107,7 @@ export const ChainMetadataSchemaObject = z.object({ name: z.string(), symbol: z.string(), decimals: ZUint.lt(256), + denom: z.string().optional(), }) .optional() .describe( @@ -111,6 +121,16 @@ export const ChainMetadataSchemaObject = z.object({ .array(RpcUrlSchema) .describe('For cosmos chains only, a list of Rest API URLs') .optional(), + grpcUrls: z + .array(RpcUrlSchema) + .describe('For cosmos chains only, a list of gRPC API URLs') + .optional(), + customGrpcUrls: z + .string() + .optional() + .describe( + 'Specify a comma separated list of custom GRPC URLs to use for this chain. If not specified, the default GRPC urls will be used.', + ), blockExplorers: z .array( z.object({ @@ -154,7 +174,7 @@ export const ChainMetadataSchemaObject = z.object({ .optional() .describe('Block settings for the chain/deployment.'), transactionOverrides: z - .object({}) + .record(z.any()) .optional() .describe('Properties to include when forming transaction requests.'), gasCurrencyCoinGeckoId: z @@ -216,6 +236,35 @@ export const ChainMetadataSchema = ChainMetadataSchemaObject.refine( message: 'Bech32Prefix and Slip44 required for Cosmos chains', path: ['bech32Prefix', 'slip44'], }, + ) + .refine( + (metadata) => { + if ( + metadata.protocol === ProtocolType.Cosmos && + (!metadata.restUrls || !metadata.grpcUrls) + ) + return false; + else return true; + }, + { + message: 'Rest and gRPC URLs required for Cosmos chains', + path: ['restUrls', 'grpcUrls'], + }, + ) + .refine( + (metadata) => { + if ( + metadata.protocol === ProtocolType.Cosmos && + metadata.nativeToken && + !metadata.nativeToken.denom + ) + return false; + else return true; + }, + { + message: 'Denom values are required for Cosmos native tokens', + path: ['nativeToken', 'denom'], + }, ); export type ChainMetadata = z.infer & @@ -226,6 +275,12 @@ export type BlockExplorer = Exclude< undefined >[number]; +export function safeParseChainMetadata( + c: ChainMetadata, +): SafeParseReturnType { + return ChainMetadataSchema.safeParse(c); +} + export function isValidChainMetadata(c: ChainMetadata): boolean { return ChainMetadataSchema.safeParse(c).success; } @@ -241,3 +296,9 @@ export function getChainIdNumber(chainMetadata: ChainMetadata): number { if (typeof chainMetadata.chainId === 'number') return chainMetadata.chainId; else throw new Error('ChainId is not a number, chain may be of Cosmos type'); } + +export function getReorgPeriod(chainMetadata: ChainMetadata): number { + if (chainMetadata.blocks?.reorgPeriod !== undefined) + return chainMetadata.blocks.reorgPeriod; + else throw new Error('Chain has no reorg period'); +} diff --git a/typescript/sdk/src/metadata/customZodTypes.ts b/typescript/sdk/src/metadata/customZodTypes.ts index 6f2841cd0d..bfabed4866 100644 --- a/typescript/sdk/src/metadata/customZodTypes.ts +++ b/typescript/sdk/src/metadata/customZodTypes.ts @@ -16,3 +16,5 @@ export const ZHash = z .regex( /^(0x([0-9a-fA-F]{32}|[0-9a-fA-F]{40}|[0-9a-fA-F]{64}|[0-9a-fA-F]{128}))|([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{32})$/, ); +/** Zod ChainName schema */ +export const ZChainName = z.string().regex(/^[a-z][a-z0-9]*$/); diff --git a/typescript/sdk/src/metadata/health.ts b/typescript/sdk/src/metadata/health.ts index f030894ff7..d11ed7dd62 100644 --- a/typescript/sdk/src/metadata/health.ts +++ b/typescript/sdk/src/metadata/health.ts @@ -130,7 +130,7 @@ export async function isBlockExplorerHealthy( if (!addressUrl) return false; logger(`Got address url: ${addressUrl}`); const addressReq = await fetch(addressUrl); - if (!addressReq.ok) return false; + if (!addressReq.ok && addressReq.status !== 404) return false; logger(`Explorer address page okay for ${chainMetadata.name}`); } @@ -140,7 +140,7 @@ export async function isBlockExplorerHealthy( if (!txUrl) return false; logger(`Got tx url: ${txUrl}`); const txReq = await fetch(txUrl); - if (!txReq.ok) return false; + if (!txReq.ok && txReq.status !== 404) return false; logger(`Explorer tx page okay for ${chainMetadata.name}`); } diff --git a/typescript/sdk/src/metadata/warpRouteConfig.ts b/typescript/sdk/src/metadata/warpRouteConfig.ts new file mode 100644 index 0000000000..e8028c8155 --- /dev/null +++ b/typescript/sdk/src/metadata/warpRouteConfig.ts @@ -0,0 +1,27 @@ +import { z } from 'zod'; + +import { ProtocolType } from '@hyperlane-xyz/utils'; + +import { TokenType } from '../token/config'; +import { ChainMap } from '../types'; + +const TokenConfigSchema = z.object({ + protocolType: z.nativeEnum(ProtocolType), + type: z.nativeEnum(TokenType), + hypAddress: z.string(), // HypERC20Collateral, HypERC20Synthetic, HypNativeToken address + tokenAddress: z.string().optional(), // external token address needed for collateral type eg tokenAddress.balanceOf(hypAddress) + name: z.string(), + symbol: z.string(), + decimals: z.number(), + isSpl2022: z.boolean().optional(), // Solana Program Library 2022, sealevel specific + ibcDenom: z.string().optional(), // IBC denom for cosmos native token +}); + +export const WarpRouteConfigSchema = z.object({ + description: z.string().optional(), + timeStamp: z.string().optional(), // can make it non-optional if we make it part of the warp route deployment progress + deployer: z.string().optional(), + data: z.object({ config: z.record(TokenConfigSchema) }), +}); + +export type WarpRouteConfig = ChainMap>; diff --git a/typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts b/typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts index f564342de7..329539150f 100644 --- a/typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts +++ b/typescript/sdk/src/middleware/account/InterchainAccountDeployer.ts @@ -1,6 +1,7 @@ import { ethers } from 'ethers'; import { HyperlaneContracts } from '../../contracts/types'; +import { ContractVerifier } from '../../deploy/verify/ContractVerifier'; import { MultiProvider } from '../../providers/MultiProvider'; import { ProxiedRouterDeployer } from '../../router/ProxiedRouterDeployer'; import { ProxiedRouterConfig, RouterConfig } from '../../router/types'; @@ -20,8 +21,13 @@ export class InterchainAccountDeployer extends ProxiedRouterDeployer< > { readonly routerContractName = 'interchainAccountRouter'; - constructor(multiProvider: MultiProvider) { - super(multiProvider, interchainAccountFactories); + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { + super(multiProvider, interchainAccountFactories, { + contractVerifier, + }); } async constructorArgs(_: string, config: RouterConfig): Promise<[string]> { diff --git a/typescript/sdk/src/middleware/account/accounts.hardhat-test.ts b/typescript/sdk/src/middleware/account/accounts.hardhat-test.ts index f837eb3554..19431c7a52 100644 --- a/typescript/sdk/src/middleware/account/accounts.hardhat-test.ts +++ b/typescript/sdk/src/middleware/account/accounts.hardhat-test.ts @@ -78,11 +78,12 @@ describe.skip('InterchainAccounts', async () => { ethers.constants.AddressZero, ); - await local['callRemote(uint32,address,uint256,bytes)']( + await local['callRemote(uint32,address,uint256,bytes,bytes)']( multiProvider.getDomainId(remoteChain), recipient.address, 0, data, + '', ); await coreApp.processMessages(); expect(await recipient.lastCallMessage()).to.eql(fooMessage); diff --git a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts index 6e338f1167..1662fc4403 100644 --- a/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts +++ b/typescript/sdk/src/middleware/liquidity-layer/LiquidityLayerRouterDeployer.ts @@ -11,6 +11,7 @@ import { HyperlaneContracts, HyperlaneContractsMap, } from '../../contracts/types'; +import { ContractVerifier } from '../../deploy/verify/ContractVerifier'; import { MultiProvider } from '../../providers/MultiProvider'; import { ProxiedRouterDeployer } from '../../router/ProxiedRouterDeployer'; import { RouterConfig } from '../../router/types'; @@ -57,8 +58,13 @@ export class LiquidityLayerDeployer extends ProxiedRouterDeployer< > { readonly routerContractName = 'liquidityLayerRouter'; - constructor(multiProvider: MultiProvider) { - super(multiProvider, liquidityLayerFactories); + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { + super(multiProvider, liquidityLayerFactories, { + contractVerifier, + }); } async constructorArgs( diff --git a/typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts b/typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts index 588890c33f..51af12bb9e 100644 --- a/typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts +++ b/typescript/sdk/src/middleware/query/InterchainQueryDeployer.ts @@ -1,5 +1,6 @@ import { ethers } from 'ethers'; +import { ContractVerifier } from '../../deploy/verify/ContractVerifier'; import { MultiProvider } from '../../providers/MultiProvider'; import { ProxiedRouterDeployer } from '../../router/ProxiedRouterDeployer'; import { RouterConfig } from '../../router/types'; @@ -18,8 +19,13 @@ export class InterchainQueryDeployer extends ProxiedRouterDeployer< > { readonly routerContractName = 'interchainQueryRouter'; - constructor(multiProvider: MultiProvider) { - super(multiProvider, interchainQueryFactories); + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { + super(multiProvider, interchainQueryFactories, { + contractVerifier, + }); } async constructorArgs(_: string, config: RouterConfig): Promise<[string]> { diff --git a/typescript/sdk/src/providers/MultiProtocolProvider.ts b/typescript/sdk/src/providers/MultiProtocolProvider.ts index 868c65bb6a..8b5f46ef80 100644 --- a/typescript/sdk/src/providers/MultiProtocolProvider.ts +++ b/typescript/sdk/src/providers/MultiProtocolProvider.ts @@ -1,35 +1,39 @@ import { Debugger, debug } from 'debug'; -import { ProtocolType, objFilter, objMap, pick } from '@hyperlane-xyz/utils'; +import { + Address, + HexString, + objFilter, + objMap, + pick, +} from '@hyperlane-xyz/utils'; import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata'; import { ChainMetadataManager } from '../metadata/ChainMetadataManager'; import type { ChainMetadata } from '../metadata/chainMetadataTypes'; -import type { ChainMap, ChainName } from '../types'; +import type { ChainMap, ChainName, ChainNameOrId } from '../types'; import { MultiProvider, MultiProviderOptions } from './MultiProvider'; import { CosmJsProvider, CosmJsWasmProvider, EthersV5Provider, + PROTOCOL_TO_DEFAULT_PROVIDER_TYPE, ProviderMap, ProviderType, SolanaWeb3Provider, TypedProvider, + TypedTransaction, ViemProvider, } from './ProviderType'; import { ProviderBuilderMap, defaultProviderBuilderMap, } from './providerBuilders'; - -export const PROTOCOL_DEFAULT_PROVIDER_TYPE: Partial< - Record -> = { - [ProtocolType.Ethereum]: ProviderType.EthersV5, - [ProtocolType.Sealevel]: ProviderType.SolanaWeb3, - [ProtocolType.Cosmos]: ProviderType.CosmJsWasm, -}; +import { + TransactionFeeEstimate, + estimateTransactionFee, +} from './transactionFeeEstimators'; export interface MultiProtocolProviderOptions { loggerName?: string; @@ -116,13 +120,13 @@ export class MultiProtocolProvider< } tryGetProvider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, type?: ProviderType, ): TypedProvider | null { const metadata = this.tryGetChainMetadata(chainNameOrId); if (!metadata) return null; const { protocol, name, chainId, rpcUrls } = metadata; - type = type || PROTOCOL_DEFAULT_PROVIDER_TYPE[protocol]; + type = type || PROTOCOL_TO_DEFAULT_PROVIDER_TYPE[protocol]; if (!type) return null; if (this.providers[name]?.[type]) return this.providers[name][type]!; @@ -137,7 +141,7 @@ export class MultiProtocolProvider< } getProvider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, type?: ProviderType, ): TypedProvider { const provider = this.tryGetProvider(chainNameOrId, type); @@ -147,7 +151,7 @@ export class MultiProtocolProvider< } protected getSpecificProvider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, type: ProviderType, ): T { const provider = this.getProvider(chainNameOrId, type); @@ -159,7 +163,7 @@ export class MultiProtocolProvider< } getEthersV5Provider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, ): EthersV5Provider['provider'] { return this.getSpecificProvider( chainNameOrId, @@ -167,7 +171,7 @@ export class MultiProtocolProvider< ); } - getViemProvider(chainNameOrId: ChainName | number): ViemProvider['provider'] { + getViemProvider(chainNameOrId: ChainNameOrId): ViemProvider['provider'] { return this.getSpecificProvider( chainNameOrId, ProviderType.Viem, @@ -175,7 +179,7 @@ export class MultiProtocolProvider< } getSolanaWeb3Provider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, ): SolanaWeb3Provider['provider'] { return this.getSpecificProvider( chainNameOrId, @@ -183,9 +187,7 @@ export class MultiProtocolProvider< ); } - getCosmJsProvider( - chainNameOrId: ChainName | number, - ): CosmJsProvider['provider'] { + getCosmJsProvider(chainNameOrId: ChainNameOrId): CosmJsProvider['provider'] { return this.getSpecificProvider( chainNameOrId, ProviderType.CosmJs, @@ -193,7 +195,7 @@ export class MultiProtocolProvider< } getCosmJsWasmProvider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, ): CosmJsWasmProvider['provider'] { return this.getSpecificProvider( chainNameOrId, @@ -202,7 +204,7 @@ export class MultiProtocolProvider< } setProvider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, provider: TypedProvider, ): TypedProvider { const chainName = this.getChainName(chainNameOrId); @@ -217,6 +219,28 @@ export class MultiProtocolProvider< } } + estimateTransactionFee({ + chainNameOrId, + transaction, + sender, + senderPubKey, + }: { + chainNameOrId: ChainNameOrId; + transaction: TypedTransaction; + sender: Address; + senderPubKey?: HexString; + }): Promise { + const provider = this.getProvider(chainNameOrId, transaction.type); + const chainMetadata = this.getChainMetadata(chainNameOrId); + return estimateTransactionFee({ + transaction, + provider, + chainMetadata, + sender, + senderPubKey, + }); + } + override intersect( chains: ChainName[], throwIfNotSubset = false, diff --git a/typescript/sdk/src/providers/MultiProvider.ts b/typescript/sdk/src/providers/MultiProvider.ts index 7e7d367d94..85e04c06e6 100644 --- a/typescript/sdk/src/providers/MultiProvider.ts +++ b/typescript/sdk/src/providers/MultiProvider.ts @@ -15,7 +15,7 @@ import { chainMetadata as defaultChainMetadata } from '../consts/chainMetadata'; import { CoreChainName, TestChains } from '../consts/chains'; import { ChainMetadataManager } from '../metadata/ChainMetadataManager'; import { ChainMetadata } from '../metadata/chainMetadataTypes'; -import { ChainMap, ChainName } from '../types'; +import { ChainMap, ChainName, ChainNameOrId } from '../types'; import { ProviderBuilderFn, defaultProviderBuilder } from './providerBuilders'; @@ -74,7 +74,7 @@ export class MultiProvider extends ChainMetadataManager { /** * Get an Ethers provider for a given chain name, chain id, or domain id */ - tryGetProvider(chainNameOrId: ChainName | number): Provider | null { + tryGetProvider(chainNameOrId: ChainNameOrId): Provider | null { const metadata = this.tryGetChainMetadata(chainNameOrId); if (!metadata) return null; const { name, chainId, rpcUrls } = metadata; @@ -99,7 +99,7 @@ export class MultiProvider extends ChainMetadataManager { * Get an Ethers provider for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getProvider(chainNameOrId: ChainName | number): Provider { + getProvider(chainNameOrId: ChainNameOrId): Provider { const provider = this.tryGetProvider(chainNameOrId); if (!provider) throw new Error(`No chain metadata set for ${chainNameOrId}`); @@ -110,7 +110,7 @@ export class MultiProvider extends ChainMetadataManager { * Sets an Ethers provider for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - setProvider(chainNameOrId: ChainName | number, provider: Provider): Provider { + setProvider(chainNameOrId: ChainNameOrId, provider: Provider): Provider { const chainName = this.getChainName(chainNameOrId); this.providers[chainName] = provider; const signer = this.signers[chainName]; @@ -135,7 +135,7 @@ export class MultiProvider extends ChainMetadataManager { * Get an Ethers signer for a given chain name, chain id, or domain id * If signer is not yet connected, it will be connected */ - tryGetSigner(chainNameOrId: ChainName | number): Signer | null { + tryGetSigner(chainNameOrId: ChainNameOrId): Signer | null { const chainName = this.tryGetChainName(chainNameOrId); if (!chainName) return null; const signer = this.signers[chainName]; @@ -151,7 +151,7 @@ export class MultiProvider extends ChainMetadataManager { * If signer is not yet connected, it will be connected * @throws if chain's metadata or signer has not been set */ - getSigner(chainNameOrId: ChainName | number): Signer { + getSigner(chainNameOrId: ChainNameOrId): Signer { const signer = this.tryGetSigner(chainNameOrId); if (!signer) throw new Error(`No chain signer set for ${chainNameOrId}`); return signer; @@ -161,7 +161,7 @@ export class MultiProvider extends ChainMetadataManager { * Get an Ethers signer for a given chain name, chain id, or domain id * @throws if chain's metadata or signer has not been set */ - async getSignerAddress(chainNameOrId: ChainName | number): Promise
{ + async getSignerAddress(chainNameOrId: ChainNameOrId): Promise
{ const signer = this.getSigner(chainNameOrId); const address = await signer.getAddress(); return address; @@ -171,7 +171,7 @@ export class MultiProvider extends ChainMetadataManager { * Sets an Ethers Signer for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set or shared signer has already been set */ - setSigner(chainNameOrId: ChainName | number, signer: Signer): Signer { + setSigner(chainNameOrId: ChainNameOrId, signer: Signer): Signer { if (this.useSharedSigner) { throw new Error('MultiProvider already set to use a shared signer'); } @@ -201,7 +201,7 @@ export class MultiProvider extends ChainMetadataManager { * Gets the Signer if it's been set, otherwise the provider */ tryGetSignerOrProvider( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, ): Signer | Provider | null { return ( this.tryGetSigner(chainNameOrId) || this.tryGetProvider(chainNameOrId) @@ -212,7 +212,7 @@ export class MultiProvider extends ChainMetadataManager { * Gets the Signer if it's been set, otherwise the provider * @throws if chain metadata has not been set */ - getSignerOrProvider(chainNameOrId: ChainName | number): Signer | Provider { + getSignerOrProvider(chainNameOrId: ChainNameOrId): Signer | Provider { return this.tryGetSigner(chainNameOrId) || this.getProvider(chainNameOrId); } @@ -258,7 +258,7 @@ export class MultiProvider extends ChainMetadataManager { * Get a block explorer URL for given chain's address */ override async tryGetExplorerAddressUrl( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, address?: string, ): Promise { if (address) return super.tryGetExplorerAddressUrl(chainNameOrId, address); @@ -275,7 +275,7 @@ export class MultiProvider extends ChainMetadataManager { * @throws if chain's metadata has not been set */ getTransactionOverrides( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, ): Partial { return this.getChainMetadata(chainNameOrId)?.transactionOverrides ?? {}; } @@ -285,14 +285,29 @@ export class MultiProvider extends ChainMetadataManager { * @throws if chain's metadata or signer has not been set or tx fails */ async handleDeploy( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, factory: F, params: Parameters, ): Promise>> { + // setup contract factory const overrides = this.getTransactionOverrides(chainNameOrId); const signer = this.getSigner(chainNameOrId); - const contract = await factory.connect(signer).deploy(...params, overrides); + const contractFactory = await factory.connect(signer); + + // estimate gas + const deployTx = contractFactory.getDeployTransaction(...params, overrides); + const gasEstimated = await signer.estimateGas(deployTx); + + // deploy with 10% buffer on gas limit + const contract = await contractFactory.deploy(...params, { + ...overrides, + gasLimit: gasEstimated.add(gasEstimated.div(10)), // 10% buffer + }); + + // wait for deploy tx to be confirmed await this.handleTx(chainNameOrId, contract.deployTransaction); + + // return deployed contract return contract as Awaited>; } @@ -301,7 +316,7 @@ export class MultiProvider extends ChainMetadataManager { * @throws if chain's metadata or signer has not been set or tx fails */ async handleTx( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, tx: ContractTransaction | Promise, ): Promise { const confirmations = @@ -321,7 +336,7 @@ export class MultiProvider extends ChainMetadataManager { * @throws if chain's metadata has not been set or tx fails */ async prepareTx( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, tx: PopulatedTransaction, from?: string, ): Promise { @@ -339,7 +354,7 @@ export class MultiProvider extends ChainMetadataManager { * @throws if chain's metadata has not been set or tx fails */ async estimateGas( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, tx: PopulatedTransaction, from?: string, ): Promise { @@ -360,7 +375,7 @@ export class MultiProvider extends ChainMetadataManager { * @throws if chain's metadata or signer has not been set or tx fails */ async sendTransaction( - chainNameOrId: ChainName | number, + chainNameOrId: ChainNameOrId, tx: PopulatedTransaction | Promise, ): Promise { const txReq = await this.prepareTx(chainNameOrId, await tx); diff --git a/typescript/sdk/src/providers/ProviderType.ts b/typescript/sdk/src/providers/ProviderType.ts index b59a71e1bc..5463b82bf4 100644 --- a/typescript/sdk/src/providers/ProviderType.ts +++ b/typescript/sdk/src/providers/ProviderType.ts @@ -23,6 +23,8 @@ import type { TransactionReceipt as VTransactionReceipt, } from 'viem'; +import { ProtocolType } from '@hyperlane-xyz/utils'; + export enum ProviderType { EthersV5 = 'ethers-v5', // EthersV6 = 'ethers-v6', Disabled for now to simplify build tooling @@ -30,8 +32,20 @@ export enum ProviderType { SolanaWeb3 = 'solana-web3', CosmJs = 'cosmjs', CosmJsWasm = 'cosmjs-wasm', + // TODO fuel provider types not yet defined below + Fuel = 'fuel', } +export const PROTOCOL_TO_DEFAULT_PROVIDER_TYPE: Record< + ProtocolType, + ProviderType +> = { + [ProtocolType.Ethereum]: ProviderType.EthersV5, + [ProtocolType.Sealevel]: ProviderType.SolanaWeb3, + [ProtocolType.Cosmos]: ProviderType.CosmJsWasm, + [ProtocolType.Fuel]: ProviderType.Fuel, +}; + export type ProviderMap = Partial>; /** @@ -172,7 +186,7 @@ export interface CosmJsTransaction extends TypedTransactionBase { export interface CosmJsWasmTransaction extends TypedTransactionBase { - type: ProviderType.CosmJs; + type: ProviderType.CosmJsWasm; transaction: ExecuteInstruction; } diff --git a/typescript/sdk/src/providers/SmartProvider/SmartProvider.test.ts b/typescript/sdk/src/providers/SmartProvider/SmartProvider.test.ts index 64f2ec6a72..6ce1178f6c 100644 --- a/typescript/sdk/src/providers/SmartProvider/SmartProvider.test.ts +++ b/typescript/sdk/src/providers/SmartProvider/SmartProvider.test.ts @@ -10,30 +10,30 @@ import { ChainMetadata } from '../../metadata/chainMetadataTypes'; import { ProviderMethod } from './ProviderMethods'; import { HyperlaneSmartProvider } from './SmartProvider'; -const DEFAULT_ACCOUNT = '0x9d525E28Fe5830eE92d7Aa799c4D21590567B595'; -const WETH_CONTRACT = '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6'; +const DEFAULT_ACCOUNT = '0xfaD1C94469700833717Fa8a3017278BC1cA8031C'; +const WETH_CONTRACT = '0x7b79995e5f793A07Bc00c21412e50Ecae098E7f9'; const WETH_TRANSFER_TOPIC0 = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; const WETH_CALL_DATA = '0x70a082310000000000000000000000004f7a67464b5976d7547c860109e4432d50afb38e'; const TRANSFER_TX_HASH = - '0x45a586f90ffd5d0f8e618f0f3703b14c2c9e4611af6231d6fed32c62776b6c1b'; + '0x7a975792c023733b3013ada23e1f556f5a06443765ec576e56d0b0aa3c4bdc74'; const pagination = { maxBlockRange: 1000 }; -const goerliRpcConfig1 = { ...chainMetadata.goerli.rpcUrls[0], pagination }; -const goerliRpcConfig2 = { ...chainMetadata.goerli.rpcUrls[1], pagination }; +const sepoliaRpcConfig1 = { ...chainMetadata.sepolia.rpcUrls[0], pagination }; +const sepoliaRpcConfig2 = { ...chainMetadata.sepolia.rpcUrls[1], pagination }; const justExplorersConfig: ChainMetadata = { - ...chainMetadata.goerli, + ...chainMetadata.sepolia, rpcUrls: [] as any, }; const justRpcsConfig: ChainMetadata = { - ...chainMetadata.goerli, - rpcUrls: [goerliRpcConfig1, goerliRpcConfig2], + ...chainMetadata.sepolia, + rpcUrls: [sepoliaRpcConfig1, sepoliaRpcConfig2], blockExplorers: [], }; const combinedConfig: ChainMetadata = { - ...chainMetadata.goerli, - rpcUrls: [goerliRpcConfig1], + ...chainMetadata.sepolia, + rpcUrls: [sepoliaRpcConfig1], }; const configs: [string, ChainMetadata][] = [ ['Just Explorers', justExplorersConfig], diff --git a/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts b/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts index 227dc3827a..29c1eb0511 100644 --- a/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts +++ b/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts @@ -1,4 +1,4 @@ -import debug from 'debug'; +import debug, { Debugger } from 'debug'; import { providers } from 'ethers'; import { @@ -36,7 +36,8 @@ export class HyperlaneSmartProvider extends providers.BaseProvider implements IProviderMethods { - protected readonly logger = debug('hyperlane:SmartProvider'); + protected logger: Debugger; + // TODO also support blockscout here public readonly explorerProviders: HyperlaneEtherscanProvider[]; public readonly rpcProviders: HyperlaneJsonRpcProvider[]; @@ -52,6 +53,8 @@ export class HyperlaneSmartProvider super(network); const supportedMethods = new Set(); + this.logger = debug(`hyperlane:SmartProvider:${this.network.chainId}`); + if (!rpcUrls?.length && !blockExplorers?.length) throw new Error('At least one RPC URL or block explorer is required'); @@ -263,7 +266,11 @@ export class HyperlaneSmartProvider } else if (result.status === ProviderStatus.Error) { this.throwCombinedProviderErrors( [result.error, ...providerResultErrors], - `All providers failed for method ${method}`, + `All providers failed for method ${method} and params ${JSON.stringify( + params, + null, + 2, + )}`, ); } else { throw new Error('Unexpected result from provider'); @@ -273,7 +280,11 @@ export class HyperlaneSmartProvider } else { this.throwCombinedProviderErrors( providerResultErrors, - `All providers failed for method ${method}`, + `All providers failed for method ${method} and params ${JSON.stringify( + params, + null, + 2, + )}`, ); } } diff --git a/typescript/sdk/src/providers/providerBuilders.ts b/typescript/sdk/src/providers/providerBuilders.ts index b678cf53e4..74edb30e1c 100644 --- a/typescript/sdk/src/providers/providerBuilders.ts +++ b/typescript/sdk/src/providers/providerBuilders.ts @@ -127,6 +127,7 @@ export const defaultProviderBuilderMap: ProviderBuilderMap = { [ProviderType.SolanaWeb3]: defaultSolProviderBuilder, [ProviderType.CosmJs]: defaultCosmJsProviderBuilder, [ProviderType.CosmJsWasm]: defaultCosmJsWasmProviderBuilder, + [ProtocolType.Fuel]: defaultFuelProviderBuilder, }; export const protocolToDefaultProviderBuilder: Record< diff --git a/typescript/sdk/src/providers/transactionFeeEstimators.ts b/typescript/sdk/src/providers/transactionFeeEstimators.ts new file mode 100644 index 0000000000..d3f66f58ec --- /dev/null +++ b/typescript/sdk/src/providers/transactionFeeEstimators.ts @@ -0,0 +1,288 @@ +import { encodeSecp256k1Pubkey } from '@cosmjs/amino'; +import { wasmTypes } from '@cosmjs/cosmwasm-stargate'; +import { toUtf8 } from '@cosmjs/encoding'; +import { Uint53 } from '@cosmjs/math'; +import { Registry } from '@cosmjs/proto-signing'; +import { StargateClient, defaultRegistryTypes } from '@cosmjs/stargate'; +import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'; + +import { Address, HexString, Numberish, assert } from '@hyperlane-xyz/utils'; + +import { ChainMetadata } from '../metadata/chainMetadataTypes'; + +import { + CosmJsProvider, + CosmJsTransaction, + CosmJsWasmProvider, + CosmJsWasmTransaction, + EthersV5Provider, + EthersV5Transaction, + ProviderType, + SolanaWeb3Provider, + SolanaWeb3Transaction, + TypedProvider, + TypedTransaction, + ViemProvider, + ViemTransaction, +} from './ProviderType'; + +export interface TransactionFeeEstimate { + gasUnits: number | bigint; + gasPrice: number | bigint; + fee: number | bigint; +} + +export async function estimateTransactionFeeEthersV5({ + transaction, + provider, + sender, +}: { + transaction: EthersV5Transaction; + provider: EthersV5Provider; + sender: Address; +}): Promise { + const ethersProvider = provider.provider; + const gasUnits = await ethersProvider.estimateGas({ + ...transaction.transaction, + from: sender, + }); + return estimateTransactionFeeEthersV5ForGasUnits({ + provider: ethersProvider, + gasUnits: BigInt(gasUnits.toString()), + }); +} + +// Separating out inner function to allow WarpCore to reuse logic +export async function estimateTransactionFeeEthersV5ForGasUnits({ + provider, + gasUnits, +}: { + provider: EthersV5Provider['provider']; + gasUnits: bigint; +}): Promise { + const feeData = await provider.getFeeData(); + return computeEvmTxFee( + gasUnits, + feeData.gasPrice ? BigInt(feeData.gasPrice.toString()) : undefined, + feeData.maxFeePerGas ? BigInt(feeData.maxFeePerGas.toString()) : undefined, + feeData.maxPriorityFeePerGas + ? BigInt(feeData.maxPriorityFeePerGas.toString()) + : undefined, + ); +} + +export async function estimateTransactionFeeViem({ + transaction, + provider, + sender, +}: { + transaction: ViemTransaction; + provider: ViemProvider; + sender: Address; +}): Promise { + const gasUnits = await provider.provider.estimateGas({ + ...transaction.transaction, + blockNumber: undefined, + account: sender as `0x${string}`, + }); + const feeData = await provider.provider.estimateFeesPerGas(); + return computeEvmTxFee( + gasUnits, + feeData.gasPrice, + feeData.maxFeePerGas, + feeData.maxPriorityFeePerGas, + ); +} + +function computeEvmTxFee( + gasUnits: bigint, + gasPrice?: bigint, + maxFeePerGas?: bigint, + maxPriorityFeePerGas?: bigint, +): TransactionFeeEstimate { + let estGasPrice: bigint; + if (maxFeePerGas && maxPriorityFeePerGas) { + estGasPrice = maxFeePerGas + maxPriorityFeePerGas; + } else if (gasPrice) { + estGasPrice = gasPrice; + } else { + throw new Error('Invalid fee data, neither 1559 nor legacy'); + } + return { + gasUnits, + gasPrice: estGasPrice, + fee: gasUnits * estGasPrice, + }; +} + +export async function estimateTransactionFeeSolanaWeb3({ + provider, + transaction, +}: { + transaction: SolanaWeb3Transaction; + provider: SolanaWeb3Provider; +}): Promise { + const connection = provider.provider; + const { value } = await connection.simulateTransaction( + transaction.transaction, + ); + assert(!value.err, `Solana gas estimation failed: ${value.err}`); + const gasUnits = BigInt(value.unitsConsumed || 0); + const recentFees = await connection.getRecentPrioritizationFees(); + const gasPrice = BigInt(recentFees[0].prioritizationFee); + return { + gasUnits, + gasPrice, + fee: gasUnits * gasPrice, + }; +} + +// This is based on a reverse-engineered version of the +// SigningStargateClient's simulate function. It cannot be +// used here because it requires access to the private key. +// https://github.com/cosmos/cosmjs/issues/1568 +export async function estimateTransactionFeeCosmJs({ + transaction, + provider, + estimatedGasPrice, + sender, + senderPubKey, + memo, +}: { + transaction: CosmJsTransaction; + provider: CosmJsProvider; + estimatedGasPrice: Numberish; + sender: Address; + // Unfortunately the sender pub key is required for this simulation. + // For accounts that have sent a tx, the pub key could be fetched via + // a StargateClient getAccount call. However that will fail for addresses + // that have not yet sent a tx on the queried chain. + // Related: https://github.com/cosmos/cosmjs/issues/889 + senderPubKey: HexString; + memo?: string; +}): Promise { + const stargateClient = await provider.provider; + const message = transaction.transaction; + const registry = new Registry([...defaultRegistryTypes, ...wasmTypes]); + const encodedMsg = registry.encodeAsAny(message); + const encodedPubkey = encodeSecp256k1Pubkey(Buffer.from(senderPubKey, 'hex')); + const { sequence } = await stargateClient.getSequence(sender); + const { gasInfo } = await stargateClient + // @ts-ignore force access to protected method + .forceGetQueryClient() + .tx.simulate([encodedMsg], memo, encodedPubkey, sequence); + assert(gasInfo, 'Gas estimation failed'); + const gasUnits = Uint53.fromString(gasInfo.gasUsed.toString()).toNumber(); + + const gasPrice = parseFloat(estimatedGasPrice.toString()); + + return { + gasUnits, + gasPrice, + fee: Math.floor(gasUnits * gasPrice), + }; +} + +export async function estimateTransactionFeeCosmJsWasm({ + transaction, + provider, + estimatedGasPrice, + sender, + senderPubKey, + memo, +}: { + transaction: CosmJsWasmTransaction; + provider: CosmJsWasmProvider; + estimatedGasPrice: Numberish; + sender: Address; + senderPubKey: HexString; + memo?: string; +}): Promise { + const message = { + typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract', + value: MsgExecuteContract.fromPartial({ + sender, + contract: transaction.transaction.contractAddress, + msg: toUtf8(JSON.stringify(transaction.transaction.msg)), + funds: [...(transaction.transaction.funds || [])], + }), + }; + const wasmClient = await provider.provider; + // @ts-ignore access a private field here to extract client URL + const url: string = wasmClient.tmClient.client.url; + const stargateClient = StargateClient.connect(url); + + return estimateTransactionFeeCosmJs({ + transaction: { type: ProviderType.CosmJs, transaction: message }, + provider: { type: ProviderType.CosmJs, provider: stargateClient }, + estimatedGasPrice, + sender, + senderPubKey, + memo, + }); +} + +export function estimateTransactionFee({ + transaction, + provider, + chainMetadata, + sender, + senderPubKey, +}: { + transaction: TypedTransaction; + provider: TypedProvider; + chainMetadata: ChainMetadata; + sender: Address; + senderPubKey?: HexString; +}): Promise { + if ( + transaction.type === ProviderType.EthersV5 && + provider.type === ProviderType.EthersV5 + ) { + return estimateTransactionFeeEthersV5({ transaction, provider, sender }); + } else if ( + transaction.type === ProviderType.Viem && + provider.type === ProviderType.Viem + ) { + return estimateTransactionFeeViem({ transaction, provider, sender }); + } else if ( + transaction.type === ProviderType.SolanaWeb3 && + provider.type === ProviderType.SolanaWeb3 + ) { + return estimateTransactionFeeSolanaWeb3({ transaction, provider }); + } else if ( + transaction.type === ProviderType.CosmJs && + provider.type === ProviderType.CosmJs + ) { + const { transactionOverrides } = chainMetadata; + const estimatedGasPrice = transactionOverrides?.gasPrice as Numberish; + assert(estimatedGasPrice, 'gasPrice required for CosmJS gas estimation'); + assert(senderPubKey, 'senderPubKey required for CosmJS gas estimation'); + return estimateTransactionFeeCosmJs({ + transaction, + provider, + estimatedGasPrice, + sender, + senderPubKey, + }); + } else if ( + transaction.type === ProviderType.CosmJsWasm && + provider.type === ProviderType.CosmJsWasm + ) { + const { transactionOverrides } = chainMetadata; + const estimatedGasPrice = transactionOverrides?.gasPrice as Numberish; + assert(estimatedGasPrice, 'gasPrice required for CosmJS gas estimation'); + assert(senderPubKey, 'senderPubKey required for CosmJS gas estimation'); + return estimateTransactionFeeCosmJsWasm({ + transaction, + provider, + estimatedGasPrice, + sender, + senderPubKey, + }); + } else { + throw new Error( + `Unsupported transaction type ${transaction.type} or provider type ${provider.type} for gas estimation`, + ); + } +} diff --git a/typescript/sdk/src/router/HyperlaneRouterChecker.ts b/typescript/sdk/src/router/HyperlaneRouterChecker.ts index d56e29c779..9fbe4fa551 100644 --- a/typescript/sdk/src/router/HyperlaneRouterChecker.ts +++ b/typescript/sdk/src/router/HyperlaneRouterChecker.ts @@ -7,10 +7,8 @@ import { addressToBytes32, eqAddress } from '@hyperlane-xyz/utils'; import { HyperlaneFactories } from '../contracts/types'; import { HyperlaneAppChecker } from '../deploy/HyperlaneAppChecker'; -import { - HyperlaneIsmFactory, - moduleMatchesConfig, -} from '../ism/HyperlaneIsmFactory'; +import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; +import { moduleMatchesConfig } from '../ism/utils'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; diff --git a/typescript/sdk/src/router/HyperlaneRouterDeployer.ts b/typescript/sdk/src/router/HyperlaneRouterDeployer.ts index e9d501c43c..1878ec2665 100644 --- a/typescript/sdk/src/router/HyperlaneRouterDeployer.ts +++ b/typescript/sdk/src/router/HyperlaneRouterDeployer.ts @@ -1,4 +1,4 @@ -import { Router } from '@hyperlane-xyz/core'; +import { Ownable, Router } from '@hyperlane-xyz/core'; import { Address, addressToBytes32, @@ -102,9 +102,14 @@ export abstract class HyperlaneRouterDeployer< this.logger(`Transferring ownership of ownables...`); for (const chain of Object.keys(contractsMap)) { const contracts = contractsMap[chain]; - const owner = configMap[chain].owner; - const ownables = await filterOwnableContracts(contracts); - await this.transferOwnershipOfContracts(chain, owner, ownables); + const ownables = (await filterOwnableContracts(contracts)) as Partial< + Record + >; + await this.transferOwnershipOfContracts( + chain, + configMap[chain], + ownables, + ); } } diff --git a/typescript/sdk/src/test/metadata-check.ts b/typescript/sdk/src/test/metadata-check.ts index 020f5c47f2..042dd1234b 100644 --- a/typescript/sdk/src/test/metadata-check.ts +++ b/typescript/sdk/src/test/metadata-check.ts @@ -4,84 +4,24 @@ import { ethers } from 'ethers'; import { Address, ProtocolType } from '@hyperlane-xyz/utils'; import { chainMetadata } from '../consts/chainMetadata'; -import { Chains, CoreChainName, TestChains } from '../consts/chains'; +import { CoreChainName, TestChains } from '../consts/chains'; import { isBlockExplorerHealthy, isRpcHealthy } from '../metadata/health'; import { ChainMap } from '../types'; const PROTOCOL_TO_ADDRESS: Record = { [ProtocolType.Ethereum]: ethers.constants.AddressZero, - [ProtocolType.Sealevel]: '00000000000000000000000000000000000000000000', + [ProtocolType.Sealevel]: '11111111111111111111111111111111', [ProtocolType.Cosmos]: 'cosmos100000000000000000000000000000000000000', [ProtocolType.Fuel]: '', }; -// A random tx hash for each chain, used to test explorer link -const CHAIN_TO_TX_HASH: Record = { - [Chains.alfajores]: - '0xf566f1ba4af5ac53081dc4b22fcac29fe5e9a25f5e134ca5464231ed7d2ffc81', - [Chains.arbitrum]: - '0x30093a67a823ca6b024eb5ca6f7d5cf7e967557662155e783827efcfeb29690f', - [Chains.arbitrumgoerli]: - '0xa86668384160a1b580bdbeabfce212524663143e94b68eb1e7fc48f20bbedc8c', - [Chains.avalanche]: - '0x244ae94a424906c88b2f7fc7697ce78f26fbfc74bee5040d63e1a1c6ef9eb84b', - [Chains.base]: - '0x27c0d75d1a38c0a31b0f41fd20d28a62be4ac83999abdf4f6ea607379b3f3d0d', - [Chains.basegoerli]: - '0xea6274abba0ad633d0155fc6cb5d25edb24bb7005c7b4aed33390716cf773c32', - [Chains.bsc]: - '0x18bd183cd2dc56a462b27331b8d28cddabde0c556698da29d69ee04c1b8b2c9c', - [Chains.bsctestnet]: - '0xcfa8f9c0b601913ddf0f99e03e0e2c211ef59bde7eba72eb8f7df739f913466f', - [Chains.celo]: - '0xb217245342d224c96876849bc2964cac6c648b7b054fd0b0278c5e98e540843b', - [Chains.chiado]: - '0x29d0828c8d1852097736220dd439716ec342caceb41d9edf4be9fda598c837df', - [Chains.ethereum]: - '0xf2f0373bdbdff84640b6d7f37ea999746715df499190b7a1095266066d1b7356', - [Chains.fuji]: - '0xb1b93727cea040b3164056d0b97785e8f0e4b7a749b0a56f9d1c2cf37bec0455', - [Chains.gnosis]: - '0x9f6d46b6be0adbcf6fa4517c6897a11763d4a5aa5e31e6b6b66a0463de958c25', - [Chains.goerli]: - '0xf9eeb8068f02d086fe100bc420af57384eff0fdc4f88e68e4e17e1985a7e2bf0', - [Chains.lineagoerli]: - '0xe0fad79e60d6178452bc07cf15c07cdda97deccd2b197af7790e978a8e5835ac', - [Chains.mantapacific]: - '0x045adb06cae25de2c90be0a8610f7adc226c34d0b03d4383ce3cb2157561d656', - [Chains.moonbasealpha]: - '0xe6711bc12bc1cef88f29e3bbabd9fbb050cfca086a5449f7d4da3819bdc77859', - [Chains.moonbeam]: - '0xf387fa67cf7f4a33d30c0c53900d21c4eff7867f5457a8b9f54802087a07eb96', - [Chains.mumbai]: - '0xeff94a58c83814e3c0fbaa721e95cd76f2dd00274ab547ed2e7d9a78b029c62a', - [Chains.nautilus]: '', - [Chains.neutron]: - '4663DAD97C53850A2BAE898514971BACC8EB8B3C1FE9DBA3E62F5AC86D600E73', - [Chains.optimism]: - '0x139b9beec241a1258630367a2ec0c6567bfd5ce23cfc0c189fbd26b5eb657a33', - [Chains.optimismgoerli]: - '0xd84ae2271533f83c2adea10bd1bebcb97a9bad70ccfb7d771b4159ab0cadfda3', - [Chains.polygon]: - '0x7cf70156dbf12005875f73f48e903e40914d9a69a9487f0834e2d79132ec22f3', - [Chains.polygonzkevm]: - '0xf3fd1213a7b8db63031e83de929169896cbfae33004bb7a55234a1f72cb53d5f', - [Chains.polygonzkevmtestnet]: - '0xf758cfe7f83c9556300f687b01e0f9fcb15156f70406cb54122a0531394ce496', - [Chains.proteustestnet]: '', - [Chains.scroll]: - '0x262a4c4ee74f1a81ed414ffad3a8e2046ad2521252b2091f1acb053239aab5b7', - [Chains.scrollsepolia]: - '0xe2093b1a4c6a0d9d34e6441b449e7cb4e7a785a41e5df2df9a981968888813ae', - [Chains.sepolia]: - '0xdacc9d206b55ba553afc42e2c207e355aacaf96855845b3a746f294fefd4f39d', - [Chains.solana]: - '23346vC32nGAaq4ADj8zJqzVv9DGcY6oqnEbM7g1d1Ydqh8wziEgavKXx1qNqqqHMwKq3LRqaGwMMH7wK9UhAuz4', - [Chains.solanadevnet]: - '58XxWq2AD5Hw58cJxbhLNXsbycHUmHhkUpdacZWBTTz5kFW4dstTHVcb8MKJMRxiG4eVsnmb3Qhbf3TVriuCad4n', - [Chains.test1]: '', - [Chains.test2]: '', - [Chains.test3]: '', +const PROTOCOL_TO_TX_HASH: Record = { + [ProtocolType.Ethereum]: ethers.constants.HashZero, + [ProtocolType.Sealevel]: + '1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111', + [ProtocolType.Cosmos]: + '0000000000000000000000000000000000000000000000000000000000000000', + [ProtocolType.Fuel]: '', }; // Note: run with DEBUG=hyperlane for more detailed logs @@ -136,7 +76,7 @@ async function main() { const isHealthy = await isBlockExplorerHealthy( metadata, PROTOCOL_TO_ADDRESS[metadata.protocol], - CHAIN_TO_TX_HASH[metadata.name], + PROTOCOL_TO_TX_HASH[metadata.protocol], ); if (!isHealthy) { console.error( diff --git a/typescript/sdk/src/test/testUtils.ts b/typescript/sdk/src/test/testUtils.ts index 3d2c48e56e..020017adf4 100644 --- a/typescript/sdk/src/test/testUtils.ts +++ b/typescript/sdk/src/test/testUtils.ts @@ -1,6 +1,6 @@ import { BigNumber, ethers } from 'ethers'; -import { Address, objMap } from '@hyperlane-xyz/utils'; +import { Address, exclude, objMap } from '@hyperlane-xyz/utils'; import { chainMetadata } from '../consts/chainMetadata'; import { HyperlaneContractsMap } from '../contracts/types'; @@ -13,6 +13,7 @@ import { CoinGeckoSimpleInterface, CoinGeckoSimplePriceParams, } from '../gas/token-prices'; +import { IgpConfig } from '../gas/types'; import { HookType } from '../hook/types'; import { IsmType } from '../ism/types'; import { RouterConfig } from '../router/types'; @@ -68,6 +69,34 @@ export function testCoreConfig( return Object.fromEntries(chains.map((local) => [local, chainConfig])); } +const TEST_ORACLE_CONFIG = { + gasPrice: ethers.utils.parseUnits('1', 'gwei'), + tokenExchangeRate: ethers.utils.parseUnits('1', 10), +}; + +export function testIgpConfig( + chains: ChainName[], + owner = nonZeroAddress, +): ChainMap { + return Object.fromEntries( + chains.map((local) => [ + local, + { + owner, + oracleKey: owner, + beneficiary: owner, + // TODO: these should be one map + overhead: Object.fromEntries( + exclude(local, chains).map((remote) => [remote, 60000]), + ), + oracleConfig: Object.fromEntries( + exclude(local, chains).map((remote) => [remote, TEST_ORACLE_CONFIG]), + ), + }, + ]), + ); +} + // A mock CoinGecko intended to be used by tests export class MockCoinGecko implements CoinGeckoInterface { // Prices keyed by coingecko id diff --git a/typescript/sdk/src/token/IToken.ts b/typescript/sdk/src/token/IToken.ts new file mode 100644 index 0000000000..943d3881a4 --- /dev/null +++ b/typescript/sdk/src/token/IToken.ts @@ -0,0 +1,87 @@ +import { z } from 'zod'; + +import { Address, Numberish, ProtocolType } from '@hyperlane-xyz/utils'; + +import { ZChainName, ZUint } from '../metadata/customZodTypes'; +import type { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; +import type { ChainName } from '../types'; + +import type { TokenAmount } from './TokenAmount'; +import { + type TokenConnection, + TokenConnectionConfigSchema, +} from './TokenConnection'; +import { TokenStandard } from './TokenStandard'; +import type { IHypTokenAdapter, ITokenAdapter } from './adapters/ITokenAdapter'; + +export const TokenConfigSchema = z.object({ + chainName: ZChainName.describe( + 'The name of the chain, must correspond to a chain in the multiProvider chainMetadata', + ), + standard: z + .nativeEnum(TokenStandard) + .describe('The type of token. See TokenStandard for valid values.'), + decimals: ZUint.lt(256).describe('The decimals value (e.g. 18 for Eth)'), + symbol: z.string().min(1).describe('The symbol of the token'), + name: z.string().min(1).describe('The name of the token'), + addressOrDenom: z + .string() + .min(1) + .or(z.null()) + .describe('The address or denom, or null for native tokens'), + collateralAddressOrDenom: z + .string() + .min(1) + .optional() + .describe('The address or denom of the collateralized token'), + igpTokenAddressOrDenom: z + .string() + .min(1) + .optional() + .describe('The address or denom of the token for IGP payments'), + logoURI: z.string().optional().describe('The URI of the token logo'), + connections: z + .array(TokenConnectionConfigSchema) + .optional() + .describe('The list of token connections (e.g. warp or IBC)'), +}); + +export type TokenArgs = Omit< + z.infer, + 'addressOrDenom' | 'connections' +> & { + addressOrDenom: Address | string; + connections?: Array; +}; + +export interface IToken extends TokenArgs { + protocol: ProtocolType; + + getAdapter(multiProvider: MultiProtocolProvider): ITokenAdapter; + getHypAdapter( + multiProvider: MultiProtocolProvider<{ mailbox?: Address }>, + destination?: ChainName, + ): IHypTokenAdapter; + + getBalance( + multiProvider: MultiProtocolProvider, + address: Address, + ): Promise; + + amount(amount: Numberish): TokenAmount; + + isNft(): boolean; + isNative(): boolean; + isHypToken(): boolean; + isIbcToken(): boolean; + isMultiChainToken(): boolean; + + getConnections(): TokenConnection[]; + + getConnectionForChain(chain: ChainName): TokenConnection | undefined; + addConnection(connection: TokenConnection): IToken; + removeConnection(token: IToken): IToken; + + equals(token?: IToken): boolean; + isFungibleWith(token?: IToken): boolean; +} diff --git a/typescript/sdk/src/token/Token.test.ts b/typescript/sdk/src/token/Token.test.ts new file mode 100644 index 0000000000..edaedcab96 --- /dev/null +++ b/typescript/sdk/src/token/Token.test.ts @@ -0,0 +1,176 @@ +/* eslint-disable no-console */ +import { expect } from 'chai'; +import { ethers } from 'ethers'; + +import { Address, ProtocolType } from '@hyperlane-xyz/utils'; + +import { chainMetadata } from '../consts/chainMetadata'; +import { Chains } from '../consts/chains'; +import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; + +import { TokenArgs } from './IToken'; +import { Token } from './Token'; +import { TokenStandard } from './TokenStandard'; + +// null values represent TODOs here, ideally all standards should be tested +const STANDARD_TO_TOKEN: Record = { + // EVM + [TokenStandard.ERC20]: { + chainName: Chains.ethereum, + standard: TokenStandard.ERC20, + addressOrDenom: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + decimals: 6, + symbol: 'USDC', + name: 'USDC', + }, + [TokenStandard.ERC721]: null, + [TokenStandard.EvmNative]: Token.FromChainMetadataNativeToken( + chainMetadata.optimism, + ), + [TokenStandard.EvmHypNative]: { + chainName: Chains.inevm, + standard: TokenStandard.EvmHypNative, + addressOrDenom: '0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4', + decimals: 18, + symbol: 'INJ', + name: 'Injective Coin', + }, + [TokenStandard.EvmHypCollateral]: { + chainName: Chains.bsctestnet, + standard: TokenStandard.EvmHypCollateral, + addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131', + collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930', + decimals: 18, + symbol: 'USDC', + name: 'USDC', + }, + [TokenStandard.EvmHypSynthetic]: { + chainName: Chains.inevm, + standard: TokenStandard.EvmHypSynthetic, + addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147', + decimals: 6, + symbol: 'USDC', + name: 'USDC', + }, + + // Sealevel + [TokenStandard.SealevelSpl]: { + chainName: Chains.solana, + standard: TokenStandard.SealevelSpl, + addressOrDenom: 'So11111111111111111111111111111111111111112', + decimals: 9, + symbol: 'Wrapped SOL', + name: 'SOL', + }, + [TokenStandard.SealevelSpl2022]: { + chainName: Chains.solana, + standard: TokenStandard.SealevelSpl2022, + addressOrDenom: '21zHSATJqhNkcpoNkhFzPJW9LARSmoinLEeDtdygGuWh', + decimals: 6, + symbol: 'SOLMAX', + name: 'Solana Maxi', + }, + [TokenStandard.SealevelNative]: Token.FromChainMetadataNativeToken( + chainMetadata.solana, + ), + [TokenStandard.SealevelHypNative]: null, + [TokenStandard.SealevelHypCollateral]: null, + [TokenStandard.SealevelHypSynthetic]: null, + + // Cosmos + [TokenStandard.CosmosIcs20]: null, + [TokenStandard.CosmosIcs721]: null, + [TokenStandard.CosmosNative]: Token.FromChainMetadataNativeToken( + chainMetadata.neutron, + ), + [TokenStandard.CosmosIbc]: { + chainName: Chains.neutron, + standard: TokenStandard.CosmosIbc, + addressOrDenom: + 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7', + decimals: 6, + symbol: 'TIA', + name: 'TIA', + }, + [TokenStandard.CW20]: null, + [TokenStandard.CWNative]: { + chainName: Chains.neutron, + standard: TokenStandard.CWNative, + addressOrDenom: + 'ibc/5751B8BCDA688FD0A8EC0B292EEF1CDEAB4B766B63EC632778B196D317C40C3A', + decimals: 6, + symbol: 'ASTRO', + name: 'ASTRO', + }, + [TokenStandard.CW721]: null, + [TokenStandard.CwHypNative]: { + chainName: Chains.injective, + standard: TokenStandard.CwHypNative, + addressOrDenom: 'inj1mv9tjvkaw7x8w8y9vds8pkfq46g2vcfkjehc6k', + igpTokenAddressOrDenom: 'inj', + decimals: 18, + symbol: 'INJ', + name: 'Injective Coin', + }, + [TokenStandard.CwHypCollateral]: { + chainName: Chains.neutron, + standard: TokenStandard.CwHypCollateral, + addressOrDenom: + 'neutron1jyyjd3x0jhgswgm6nnctxvzla8ypx50tew3ayxxwkrjfxhvje6kqzvzudq', + collateralAddressOrDenom: + 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7', + decimals: 6, + symbol: 'TIA.n', + name: 'TIA.n', + }, + [TokenStandard.CwHypSynthetic]: null, + + // Fuel + [TokenStandard.FuelNative]: null, +}; + +const PROTOCOL_TO_ADDRESS: Partial> = { + [ProtocolType.Ethereum]: ethers.constants.AddressZero, + [ProtocolType.Cosmos]: + 'neutron13we0myxwzlpx8l5ark8elw5gj5d59dl6cjkzmt80c5q5cv5rt54qvzkv2a', + [ProtocolType.Fuel]: '', +}; + +const STANDARD_TO_ADDRESS: Partial> = { + [TokenStandard.SealevelSpl]: 'HVSZJ2juJnMxd6yCNarTL56YmgUqzfUiwM7y7LtTXKHR', + [TokenStandard.SealevelSpl2022]: + 'EK6cs8jNnu2d9pmKTGf1Bvre9oW2xNhcCKNdLKx6t74w', + [TokenStandard.SealevelNative]: + 'EK6cs8jNnu2d9pmKTGf1Bvre9oW2xNhcCKNdLKx6t74w', + [TokenStandard.CwHypNative]: 'inj1fl48vsnmsdzcv85q5d2q4z5ajdha8yu3lj7tt0', +}; + +describe('Token', () => { + it('Handles all standards', async () => { + const multiProvider = new MultiProtocolProvider(); + for (const tokenArgs of Object.values(STANDARD_TO_TOKEN)) { + if (!tokenArgs) continue; + console.debug('Testing token standard', tokenArgs.standard); + const token = new Token(tokenArgs); + expect(token.standard).to.eql(tokenArgs.standard); + const adapter = token.getAdapter(multiProvider); + const adddress = + STANDARD_TO_ADDRESS[token.standard] ?? + PROTOCOL_TO_ADDRESS[token.protocol]; + if (!adddress) + throw new Error(`No address for standard ${tokenArgs.standard}`); + const balance = await adapter.getBalance(adddress); + expect(typeof balance).to.eql('bigint'); + } + }) + .timeout(120_000) + .retries(3); + + it('Constructs from ChainMetadata', () => { + for (const metadata of Object.values(chainMetadata)) { + if (!metadata.nativeToken) continue; + const token = Token.FromChainMetadataNativeToken(metadata); + expect(token.symbol).to.eql(metadata.nativeToken.symbol); + } + }); +}); diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts new file mode 100644 index 0000000000..6603644dcf --- /dev/null +++ b/typescript/sdk/src/token/Token.ts @@ -0,0 +1,418 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ +import { MsgTransferEncodeObject } from '@cosmjs/stargate'; + +import { + Address, + Numberish, + ProtocolType, + assert, + eqAddress, +} from '@hyperlane-xyz/utils'; + +import { ChainMetadata } from '../metadata/chainMetadataTypes'; +import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; +import { ChainName } from '../types'; + +import type { IToken, TokenArgs } from './IToken'; +import { TokenAmount } from './TokenAmount'; +import { TokenConnection, TokenConnectionType } from './TokenConnection'; +import { + PROTOCOL_TO_NATIVE_STANDARD, + TOKEN_COLLATERALIZED_STANDARDS, + TOKEN_HYP_STANDARDS, + TOKEN_MULTI_CHAIN_STANDARDS, + TOKEN_NFT_STANDARDS, + TOKEN_STANDARD_TO_PROTOCOL, + TokenStandard, +} from './TokenStandard'; +import { + CwHypCollateralAdapter, + CwHypNativeAdapter, + CwHypSyntheticAdapter, + CwNativeTokenAdapter, + CwTokenAdapter, +} from './adapters/CosmWasmTokenAdapter'; +import { + CosmIbcToWarpTokenAdapter, + CosmIbcTokenAdapter, + CosmNativeTokenAdapter, +} from './adapters/CosmosTokenAdapter'; +import { + EvmHypCollateralAdapter, + EvmHypNativeAdapter, + EvmHypSyntheticAdapter, + EvmNativeTokenAdapter, + EvmTokenAdapter, +} from './adapters/EvmTokenAdapter'; +import type { IHypTokenAdapter, ITokenAdapter } from './adapters/ITokenAdapter'; +import { + SealevelHypCollateralAdapter, + SealevelHypNativeAdapter, + SealevelHypSyntheticAdapter, + SealevelNativeTokenAdapter, + SealevelTokenAdapter, +} from './adapters/SealevelTokenAdapter'; + +// Declaring the interface in addition to class allows +// Typescript to infer the members vars from TokenArgs +export interface Token extends TokenArgs {} + +export class Token implements IToken { + public readonly protocol: ProtocolType; + + constructor(args: TokenArgs) { + Object.assign(this, args); + this.protocol = TOKEN_STANDARD_TO_PROTOCOL[this.standard]; + } + + static FromChainMetadataNativeToken(chainMetadata: ChainMetadata): Token { + const { protocol, name: chainName, nativeToken, logoURI } = chainMetadata; + assert( + nativeToken, + `ChainMetadata for ${chainMetadata.name} missing nativeToken`, + ); + + return new Token({ + chainName, + standard: PROTOCOL_TO_NATIVE_STANDARD[protocol], + addressOrDenom: nativeToken.denom ?? '', + decimals: nativeToken.decimals, + symbol: nativeToken.symbol, + name: nativeToken.name, + logoURI, + }); + } + + /** + * Returns a TokenAdapter for the token and multiProvider + * @throws If multiProvider does not contain this token's chain. + * @throws If token is an NFT (TODO NFT Adapter support) + */ + getAdapter(multiProvider: MultiProtocolProvider): ITokenAdapter { + const { standard, chainName, addressOrDenom } = this; + + assert(!this.isNft(), 'NFT adapters not yet supported'); + assert( + multiProvider.tryGetChainMetadata(chainName), + `Token chain ${chainName} not found in multiProvider`, + ); + + if (standard === TokenStandard.ERC20) { + return new EvmTokenAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.EvmNative) { + return new EvmNativeTokenAdapter(chainName, multiProvider, {}); + } else if (standard === TokenStandard.SealevelSpl) { + return new SealevelTokenAdapter( + chainName, + multiProvider, + { token: addressOrDenom }, + false, + ); + } else if (standard === TokenStandard.SealevelSpl2022) { + return new SealevelTokenAdapter( + chainName, + multiProvider, + { token: addressOrDenom }, + true, + ); + } else if (standard === TokenStandard.SealevelNative) { + return new SealevelNativeTokenAdapter(chainName, multiProvider, {}); + } else if (standard === TokenStandard.CosmosIcs20) { + throw new Error('Cosmos ICS20 token adapter not yet supported'); + } else if (standard === TokenStandard.CosmosNative) { + return new CosmNativeTokenAdapter( + chainName, + multiProvider, + {}, + { ibcDenom: addressOrDenom }, + ); + } else if (standard === TokenStandard.CW20) { + return new CwTokenAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.CWNative) { + return new CwNativeTokenAdapter( + chainName, + multiProvider, + {}, + addressOrDenom, + ); + } else if (this.isHypToken()) { + return this.getHypAdapter(multiProvider); + } else if (this.isIbcToken()) { + // Passing in a stub connection here because it's not required + // for an IBC adapter to fulfill the ITokenAdapter interface + return this.getIbcAdapter(multiProvider, { + token: this, + sourcePort: 'transfer', + sourceChannel: 'channel-0', + type: TokenConnectionType.Ibc, + }); + } else { + throw new Error(`No adapter found for token standard: ${standard}`); + } + } + + /** + * Returns a HypTokenAdapter for the token and multiProvider + * @throws If not applicable to this token's standard. + * @throws If multiProvider does not contain this token's chain. + * @throws If token is an NFT (TODO NFT Adapter support) + */ + getHypAdapter( + multiProvider: MultiProtocolProvider<{ mailbox?: Address }>, + destination?: ChainName, + ): IHypTokenAdapter { + const { + protocol, + standard, + chainName, + addressOrDenom, + collateralAddressOrDenom, + } = this; + const chainMetadata = multiProvider.tryGetChainMetadata(chainName); + const mailbox = chainMetadata?.mailbox; + + assert( + this.isMultiChainToken(), + `Token standard ${standard} not applicable to hyp adapter`, + ); + assert(!this.isNft(), 'NFT adapters not yet supported'); + assert( + chainMetadata, + `Token chain ${chainName} not found in multiProvider`, + ); + + let sealevelAddresses; + if (protocol === ProtocolType.Sealevel) { + assert(mailbox, `Mailbox required for Sealevel hyp tokens`); + assert( + collateralAddressOrDenom, + `collateralAddressOrDenom required for Sealevel hyp tokens`, + ); + sealevelAddresses = { + warpRouter: addressOrDenom, + token: collateralAddressOrDenom, + mailbox, + }; + } + if (standard === TokenStandard.EvmHypNative) { + return new EvmHypNativeAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.EvmHypCollateral) { + return new EvmHypCollateralAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.EvmHypSynthetic) { + return new EvmHypSyntheticAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.SealevelHypNative) { + return new SealevelHypNativeAdapter( + chainName, + multiProvider, + sealevelAddresses!, + false, + ); + } else if (standard === TokenStandard.SealevelHypCollateral) { + return new SealevelHypCollateralAdapter( + chainName, + multiProvider, + sealevelAddresses!, + false, + ); + } else if (standard === TokenStandard.SealevelHypSynthetic) { + return new SealevelHypSyntheticAdapter( + chainName, + multiProvider, + sealevelAddresses!, + false, + ); + } else if (standard === TokenStandard.CwHypNative) { + return new CwHypNativeAdapter(chainName, multiProvider, { + warpRouter: addressOrDenom, + }); + } else if (standard === TokenStandard.CwHypCollateral) { + assert( + collateralAddressOrDenom, + 'collateralAddressOrDenom required for CwHypCollateral', + ); + return new CwHypCollateralAdapter(chainName, multiProvider, { + warpRouter: addressOrDenom, + token: collateralAddressOrDenom, + }); + } else if (standard === TokenStandard.CwHypSynthetic) { + assert( + collateralAddressOrDenom, + 'collateralAddressOrDenom required for CwHypSyntheticAdapter', + ); + return new CwHypSyntheticAdapter(chainName, multiProvider, { + warpRouter: addressOrDenom, + token: collateralAddressOrDenom, + }); + } else if (standard === TokenStandard.CosmosIbc) { + assert(destination, 'destination required for IBC token adapters'); + const connection = this.getConnectionForChain(destination); + assert(connection, `No connection found for chain ${destination}`); + return this.getIbcAdapter(multiProvider, connection); + } else { + throw new Error(`No hyp adapter found for token standard: ${standard}`); + } + } + + protected getIbcAdapter( + multiProvider: MultiProtocolProvider, + connection: TokenConnection, + ): IHypTokenAdapter { + if (connection.type === TokenConnectionType.Ibc) { + const { sourcePort, sourceChannel } = connection; + return new CosmIbcTokenAdapter( + this.chainName, + multiProvider, + {}, + { ibcDenom: this.addressOrDenom, sourcePort, sourceChannel }, + ); + } else if (connection.type === TokenConnectionType.IbcHyperlane) { + const { + sourcePort, + sourceChannel, + intermediateChainName, + intermediateIbcDenom, + intermediateRouterAddress, + } = connection; + const destinationRouterAddress = connection.token.addressOrDenom; + return new CosmIbcToWarpTokenAdapter( + this.chainName, + multiProvider, + { + intermediateRouterAddress, + destinationRouterAddress, + }, + { + ibcDenom: this.addressOrDenom, + sourcePort, + sourceChannel, + intermediateIbcDenom, + intermediateChainName, + }, + ); + } else { + throw new Error(`Unsupported IBC connection type: ${connection.type}`); + } + } + + /** + * Convenience method to create an adapter and return an account balance + */ + async getBalance( + multiProvider: MultiProtocolProvider, + address: Address, + ): Promise { + const adapter = this.getAdapter(multiProvider); + const balance = await adapter.getBalance(address); + return new TokenAmount(balance, this); + } + + amount(amount: Numberish): TokenAmount { + return new TokenAmount(amount, this); + } + + isNft(): boolean { + return TOKEN_NFT_STANDARDS.includes(this.standard); + } + + isNative(): boolean { + return Object.values(PROTOCOL_TO_NATIVE_STANDARD).includes(this.standard); + } + + isHypToken(): boolean { + return TOKEN_HYP_STANDARDS.includes(this.standard); + } + + isIbcToken(): boolean { + return this.standard === TokenStandard.CosmosIbc; + } + + isMultiChainToken(): boolean { + return TOKEN_MULTI_CHAIN_STANDARDS.includes(this.standard); + } + + getConnections(): TokenConnection[] { + return this.connections || []; + } + + getConnectionForChain(chain: ChainName): TokenConnection | undefined { + // A token cannot have > 1 connected token for the same chain + return this.getConnections().filter((t) => t.token.chainName === chain)[0]; + } + + addConnection(connection: TokenConnection): Token { + this.connections = [...(this.connections || []), connection]; + return this; + } + + removeConnection(token: IToken): Token { + const index = this.connections?.findIndex((t) => t.token.equals(token)); + if (index && index >= 0) this.connections?.splice(index, 1); + return this; + } + + /** + * Returns true if tokens refer to the same asset + */ + equals(token?: IToken): boolean { + if (!token) return false; + return ( + this.protocol === token.protocol && + this.chainName === token.chainName && + this.standard === token.standard && + this.decimals === token.decimals && + this.addressOrDenom.toLowerCase() === + token.addressOrDenom.toLowerCase() && + this.collateralAddressOrDenom?.toLowerCase() === + token.collateralAddressOrDenom?.toLowerCase() + ); + } + + /** + * Two tokens may not be equal but may still represent the same underlying asset + * The cases for this include: + * 1) A HypCollateral contract token and its wrapped token (eg. EvmHypCollateral and ERC20) + * 2) A HypNative contract and its native currency (eg. EvmHypNative and Ether) + * 3) An IBC token and its native equivalent + * This is useful during fee estimation to determine if a TokenAmount for the transfer and the fee + * are actually fungible (represent the same asset). + * @returns true if the tokens represent the same underlying asset + */ + isFungibleWith(token?: IToken): boolean { + if (!token || token.chainName !== this.chainName) return false; + + if (this.equals(token)) return true; + + if (TOKEN_COLLATERALIZED_STANDARDS.includes(this.standard)) { + if ( + this.collateralAddressOrDenom && + eqAddress(this.collateralAddressOrDenom, token.addressOrDenom) + ) { + return true; + } + + if (!this.collateralAddressOrDenom && token.isNative()) { + return true; + } + } + + if ( + this.standard === TokenStandard.CosmosIbc && + token.standard === TokenStandard.CosmosNative && + this.addressOrDenom.toLowerCase() === token.addressOrDenom.toLowerCase() + ) { + return true; + } + + return false; + } +} diff --git a/typescript/sdk/src/token/TokenAmount.test.ts b/typescript/sdk/src/token/TokenAmount.test.ts new file mode 100644 index 0000000000..e4f154275f --- /dev/null +++ b/typescript/sdk/src/token/TokenAmount.test.ts @@ -0,0 +1,48 @@ +import { expect } from 'chai'; +import { ethers } from 'ethers'; + +import { chainMetadata } from '../consts/chainMetadata'; +import { Chains } from '../consts/chains'; + +import { Token } from './Token'; +import { TokenAmount } from './TokenAmount'; +import { TokenStandard } from './TokenStandard'; + +const token1 = new Token({ + chainName: Chains.ethereum, + standard: TokenStandard.ERC20, + addressOrDenom: ethers.constants.AddressZero, + decimals: 4, + symbol: 'FAKE', + name: 'Fake Token', +}); +const token2 = Token.FromChainMetadataNativeToken( + chainMetadata[Chains.neutron], +); + +describe('TokenAmount', () => { + let tokenAmount1: TokenAmount; + let tokenAmount2: TokenAmount; + + it('Constructs', () => { + tokenAmount1 = new TokenAmount(123456789, token1); + tokenAmount2 = new TokenAmount('1', token2); + expect(!!tokenAmount1).to.eq(true); + expect(!!tokenAmount2).to.eq(true); + }); + + it('Formats human readable string', () => { + expect(tokenAmount1.getDecimalFormattedAmount()).to.eq(12345.6789); + expect(tokenAmount2.getDecimalFormattedAmount()).to.eq(0.000001); + }); + + it('Does arithmetic', () => { + expect(tokenAmount1.plus(1).amount).to.eq(123456790n); + expect(tokenAmount2.minus(1).amount).to.eq(0n); + }); + + it('Checks equality', () => { + expect(tokenAmount1.equals(tokenAmount2)).to.be.false; + expect(tokenAmount1.equals(new TokenAmount(123456789n, token1))).to.true; + }); +}); diff --git a/typescript/sdk/src/token/TokenAmount.ts b/typescript/sdk/src/token/TokenAmount.ts new file mode 100644 index 0000000000..2f3fe63dab --- /dev/null +++ b/typescript/sdk/src/token/TokenAmount.ts @@ -0,0 +1,29 @@ +import { Numberish, fromWei } from '@hyperlane-xyz/utils'; + +import type { IToken } from './IToken'; + +export class TokenAmount { + public readonly amount: bigint; + + constructor(_amount: Numberish, public readonly token: IToken) { + this.amount = BigInt(_amount); + } + + getDecimalFormattedAmount(): number { + return Number(fromWei(this.amount.toString(), this.token.decimals)); + } + + plus(amount: Numberish): TokenAmount { + return new TokenAmount(this.amount + BigInt(amount), this.token); + } + + minus(amount: Numberish): TokenAmount { + return new TokenAmount(this.amount - BigInt(amount), this.token); + } + + equals(tokenAmount: TokenAmount): boolean { + return ( + this.amount === tokenAmount.amount && this.token.equals(tokenAmount.token) + ); + } +} diff --git a/typescript/sdk/src/token/TokenConnection.ts b/typescript/sdk/src/token/TokenConnection.ts new file mode 100644 index 0000000000..bd743e9d83 --- /dev/null +++ b/typescript/sdk/src/token/TokenConnection.ts @@ -0,0 +1,106 @@ +import { z } from 'zod'; + +import { Address, ProtocolType, assert } from '@hyperlane-xyz/utils'; + +import { ZChainName } from '../metadata/customZodTypes'; +import { ChainName } from '../types'; + +import type { IToken } from './IToken'; + +export enum TokenConnectionType { + Hyperlane = 'hyperlane', + Ibc = 'ibc', + IbcHyperlane = 'ibc-hyperlane', // a.k.a. one-click two-hop +} + +interface TokenConnectionBase { + type?: TokenConnectionType; + token: IToken; // the token that is being connected to +} + +export interface HyperlaneTokenConnection extends TokenConnectionBase { + type?: TokenConnectionType.Hyperlane; +} + +export interface IbcTokenConnection extends TokenConnectionBase { + type: TokenConnectionType.Ibc; + sourcePort: string; + sourceChannel: string; +} + +export interface IbcToHyperlaneTokenConnection extends TokenConnectionBase { + type: TokenConnectionType.IbcHyperlane; + sourcePort: string; + sourceChannel: string; + intermediateChainName: ChainName; + intermediateIbcDenom: string; + intermediateRouterAddress: Address; +} + +export type TokenConnection = + | HyperlaneTokenConnection + | IbcTokenConnection + | IbcToHyperlaneTokenConnection; + +const TokenConnectionRegex = /^(.+)|(.+)|(.+)$/; + +// Distinct from type above in that it uses a +// serialized representation of the tokens instead +// of the possibly recursive Token references +export const TokenConnectionConfigSchema = z + .object({ + type: z.literal(TokenConnectionType.Hyperlane).optional(), + token: z.string().regex(TokenConnectionRegex), + }) + .or( + z.object({ + type: z.literal(TokenConnectionType.Ibc), + token: z.string().regex(TokenConnectionRegex), + sourcePort: z.string(), + sourceChannel: z.string(), + }), + ) + .or( + z.object({ + type: z.literal(TokenConnectionType.IbcHyperlane), + token: z.string().regex(TokenConnectionRegex), + sourcePort: z.string(), + sourceChannel: z.string(), + intermediateChainName: ZChainName, + intermediateIbcDenom: z.string(), + intermediateRouterAddress: z.string(), + }), + ); + +export function getTokenConnectionId( + protocol: ProtocolType, + chainName: ChainName, + address: Address, +): string { + assert( + protocol && chainName && address, + 'Invalid token connection id params', + ); + return `${protocol}|${chainName}|${address}`; +} + +export function parseTokenConnectionId(data: string): { + protocol: ProtocolType; + chainName: ChainName; + addressOrDenom: Address; +} { + assert( + TokenConnectionRegex.test(data), + `Invalid token connection id: ${data}`, + ); + const [protocol, chainName, addressOrDenom] = data.split('|') as [ + ProtocolType, + ChainName, + Address, + ]; + assert( + Object.values(ProtocolType).includes(protocol), + `Invalid protocol: ${protocol}`, + ); + return { protocol, chainName, addressOrDenom }; +} diff --git a/typescript/sdk/src/token/TokenStandard.ts b/typescript/sdk/src/token/TokenStandard.ts new file mode 100644 index 0000000000..d7ecf0d40f --- /dev/null +++ b/typescript/sdk/src/token/TokenStandard.ts @@ -0,0 +1,149 @@ +import { ProtocolType, objMap } from '@hyperlane-xyz/utils'; + +import { + PROTOCOL_TO_DEFAULT_PROVIDER_TYPE, + ProviderType, +} from '../providers/ProviderType'; + +import { TokenType } from './config'; + +export enum TokenStandard { + // EVM + ERC20 = 'ERC20', + ERC721 = 'ERC721', + EvmNative = 'EvmNative', + EvmHypNative = 'EvmHypNative', + EvmHypCollateral = 'EvmHypCollateral', + EvmHypSynthetic = 'EvmHypSynthetic', + + // Sealevel (Solana) + SealevelSpl = 'SealevelSpl', + SealevelSpl2022 = 'SealevelSpl2022', + SealevelNative = 'SealevelNative', + SealevelHypNative = 'SealevelHypNative', + SealevelHypCollateral = 'SealevelHypCollateral', + SealevelHypSynthetic = 'SealevelHypSynthetic', + + // Cosmos + CosmosIcs20 = 'CosmosIcs20', + CosmosIcs721 = 'CosmosIcs721', + CosmosNative = 'CosmosNative', + CosmosIbc = 'CosmosIbc', + + // CosmWasm + CW20 = 'CW20', + CWNative = 'CWNative', + CW721 = 'CW721', + CwHypNative = 'CwHypNative', + CwHypCollateral = 'CwHypCollateral', + CwHypSynthetic = 'CwHypSynthetic', + + // Fuel (TODO) + FuelNative = 'FuelNative', +} + +// Allows for omission of protocol field in token args +export const TOKEN_STANDARD_TO_PROTOCOL: Record = { + // EVM + ERC20: ProtocolType.Ethereum, + ERC721: ProtocolType.Ethereum, + EvmNative: ProtocolType.Ethereum, + EvmHypNative: ProtocolType.Ethereum, + EvmHypCollateral: ProtocolType.Ethereum, + EvmHypSynthetic: ProtocolType.Ethereum, + + // Sealevel (Solana) + SealevelSpl: ProtocolType.Sealevel, + SealevelSpl2022: ProtocolType.Sealevel, + SealevelNative: ProtocolType.Sealevel, + SealevelHypNative: ProtocolType.Sealevel, + SealevelHypCollateral: ProtocolType.Sealevel, + SealevelHypSynthetic: ProtocolType.Sealevel, + + // Cosmos + CosmosIcs20: ProtocolType.Cosmos, + CosmosIcs721: ProtocolType.Cosmos, + CosmosNative: ProtocolType.Cosmos, + CosmosIbc: ProtocolType.Cosmos, + + // CosmWasm + CW20: ProtocolType.Cosmos, + CWNative: ProtocolType.Cosmos, + CW721: ProtocolType.Cosmos, + CwHypNative: ProtocolType.Cosmos, + CwHypCollateral: ProtocolType.Cosmos, + CwHypSynthetic: ProtocolType.Cosmos, + + // Fuel (TODO) + FuelNative: ProtocolType.Fuel, +}; + +export const TOKEN_STANDARD_TO_PROVIDER_TYPE: Record< + TokenStandard, + ProviderType +> = objMap(TOKEN_STANDARD_TO_PROTOCOL, (k, v) => { + if (k.startsWith('Cosmos')) return ProviderType.CosmJs; + return PROTOCOL_TO_DEFAULT_PROVIDER_TYPE[v]; +}); + +export const TOKEN_NFT_STANDARDS = [ + TokenStandard.ERC721, + TokenStandard.CosmosIcs721, + TokenStandard.CW721, + // TODO solana here +]; + +export const TOKEN_COLLATERALIZED_STANDARDS = [ + TokenStandard.EvmHypCollateral, + TokenStandard.EvmHypNative, + TokenStandard.SealevelHypCollateral, + TokenStandard.SealevelHypNative, + TokenStandard.CwHypCollateral, + TokenStandard.CwHypNative, +]; + +export const TOKEN_HYP_STANDARDS = [ + TokenStandard.EvmHypNative, + TokenStandard.EvmHypCollateral, + TokenStandard.EvmHypSynthetic, + TokenStandard.SealevelHypNative, + TokenStandard.SealevelHypCollateral, + TokenStandard.SealevelHypSynthetic, + TokenStandard.CwHypNative, + TokenStandard.CwHypCollateral, + TokenStandard.CwHypSynthetic, +]; + +export const TOKEN_MULTI_CHAIN_STANDARDS = [ + ...TOKEN_HYP_STANDARDS, + TokenStandard.CosmosIbc, +]; + +// Useful for differentiating from norma Cosmos standards +// (e.g. for determining the appropriate cosmos client) +export const TOKEN_COSMWASM_STANDARDS = [ + TokenStandard.CW20, + TokenStandard.CWNative, + TokenStandard.CW721, + TokenStandard.CwHypNative, + TokenStandard.CwHypCollateral, + TokenStandard.CwHypSynthetic, +]; + +export const TOKEN_TYPE_TO_STANDARD: Record = { + [TokenType.native]: TokenStandard.EvmHypNative, + [TokenType.collateral]: TokenStandard.EvmHypCollateral, + [TokenType.collateralUri]: TokenStandard.EvmHypCollateral, + [TokenType.fastCollateral]: TokenStandard.EvmHypCollateral, + [TokenType.synthetic]: TokenStandard.EvmHypSynthetic, + [TokenType.syntheticUri]: TokenStandard.EvmHypSynthetic, + [TokenType.fastSynthetic]: TokenStandard.EvmHypSynthetic, +}; + +export const PROTOCOL_TO_NATIVE_STANDARD: Record = + { + [ProtocolType.Ethereum]: TokenStandard.EvmNative, + [ProtocolType.Cosmos]: TokenStandard.CosmosNative, + [ProtocolType.Sealevel]: TokenStandard.SealevelNative, + [ProtocolType.Fuel]: TokenStandard.FuelNative, + }; diff --git a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts index fcbc0f6c4d..24af2a15d8 100644 --- a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts @@ -1,11 +1,11 @@ import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate'; import { Coin } from '@cosmjs/stargate'; -import BigNumber from 'bignumber.js'; import { Address, Domain, addressToBytes32, + assert, strip0x, } from '@hyperlane-xyz/utils'; @@ -16,6 +16,7 @@ import { QueryMsg as Cw20Query, TokenInfoResponse, } from '../../cw-types/Cw20Base.types'; +import { QuoteDispatchResponse } from '../../cw-types/Mailbox.types'; import { DomainsResponse, InterchainSecurityModuleResponse, @@ -34,6 +35,7 @@ import { ERC20Metadata } from '../config'; import { IHypTokenAdapter, ITokenAdapter, + InterchainGasQuote, TransferParams, TransferRemoteParams, } from './ITokenAdapter'; @@ -41,27 +43,31 @@ import { // Interacts with IBC denom tokens in CosmWasm export class CwNativeTokenAdapter extends BaseCosmWasmAdapter - implements ITokenAdapter + implements ITokenAdapter { constructor( public readonly chainName: string, public readonly multiProvider: MultiProtocolProvider, public readonly addresses: Record, - public readonly ibcDenom: string = 'untrn', + public readonly denom: string, ) { super(chainName, multiProvider, addresses); } - async getBalance(address: Address): Promise { + async getBalance(address: Address): Promise { const provider = await this.getProvider(); - const balance = await provider.getBalance(address, this.ibcDenom); - return balance.amount; + const balance = await provider.getBalance(address, this.denom); + return BigInt(balance.amount); } async getMetadata(): Promise { throw new Error('Metadata not available to native tokens'); } + async isApproveRequired(): Promise { + return false; + } + async populateApproveTx( _params: TransferParams, ): Promise { @@ -79,7 +85,7 @@ export class CwNativeTokenAdapter funds: [ { amount: weiAmountOrId.toString(), - denom: this.ibcDenom, + denom: this.denom, }, ], }; @@ -92,13 +98,12 @@ type CW20Response = TokenInfoResponse | BalanceResponse; // Interacts with CW20/721 contracts export class CwTokenAdapter extends BaseCosmWasmAdapter - implements ITokenAdapter + implements ITokenAdapter { constructor( public readonly chainName: string, public readonly multiProvider: MultiProtocolProvider, public readonly addresses: { token: Address }, - public readonly denom = 'untrn', ) { super(chainName, multiProvider, addresses); } @@ -120,10 +125,10 @@ export class CwTokenAdapter }; } - async getBalance(address: Address): Promise { + async getBalance(address: Address): Promise { const provider = await this.getProvider(); const balance = await provider.getBalance(address, this.addresses.token); - return balance.amount; + return BigInt(balance.amount); } async getMetadata(): Promise { @@ -136,6 +141,10 @@ export class CwTokenAdapter }; } + async isApproveRequired(): Promise { + return false; + } + async populateApproveTx({ weiAmountOrId, recipient, @@ -171,17 +180,17 @@ type TokenRouterResponse = | DomainsResponse | OwnerResponse | RouteResponseForHexBinary - | RoutesResponseForHexBinary; + | RoutesResponseForHexBinary + | QuoteDispatchResponse; export class CwHypSyntheticAdapter extends CwTokenAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { constructor( public readonly chainName: ChainName, public readonly multiProvider: MultiProtocolProvider, public readonly addresses: { token: Address; warpRouter: Address }, - public readonly gasDenom = 'untrn', ) { super(chainName, multiProvider, addresses); } @@ -205,7 +214,7 @@ export class CwHypSyntheticAdapter }; } - async tokenType(): Promise { + async getTokenType(): Promise { const resp = await this.queryRouter({ token_default: { token_type: {}, @@ -214,11 +223,11 @@ export class CwHypSyntheticAdapter return resp.type; } - async interchainSecurityModule(): Promise
{ + async getInterchainSecurityModule(): Promise
{ throw new Error('Router does not support ISM config yet.'); } - async owner(): Promise
{ + async getOwner(): Promise
{ const resp = await this.queryRouter({ ownable: { get_owner: {}, @@ -265,19 +274,35 @@ export class CwHypSyntheticAdapter })); } - quoteGasPayment(_destination: number): Promise { - throw new Error('Method not implemented.'); + async quoteTransferRemoteGas( + _destination: Domain, + ): Promise { + // TODO this may require separate queries to get the hook and/or mailbox + // before making a query for the QuoteDispatchResponse + // Punting on this given that only static quotes are used for now + // const resp = await this.queryRouter({ + // router: { + // TODO: {}, + // }, + // }); + // return { + // amount: BigInt(resp.gas_amount?.amount || 0), + // addressOrDenom: resp.gas_amount?.denom, + // }; + throw new Error('CW adpater quoteTransferRemoteGas method not implemented'); } - populateTransferRemoteTx({ + async populateTransferRemoteTx({ destination, recipient, weiAmountOrId, - txValue, - }: TransferRemoteParams): ExecuteInstruction { - if (!txValue) { - throw new Error('txValue is required for native tokens'); - } + interchainGas, + }: TransferRemoteParams): Promise { + if (!interchainGas) + interchainGas = await this.quoteTransferRemoteGas(destination); + const { addressOrDenom: igpDenom, amount: igpAmount } = interchainGas; + assert(igpDenom, 'Interchain gas denom required for Cosmos'); + return this.prepareRouter( { transfer_remote: { @@ -288,8 +313,8 @@ export class CwHypSyntheticAdapter }, [ { - amount: txValue.toString(), - denom: this.gasDenom, + amount: igpAmount.toString(), + denom: igpDenom, }, ], ); @@ -298,7 +323,7 @@ export class CwHypSyntheticAdapter export class CwHypNativeAdapter extends CwNativeTokenAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { private readonly cw20adapter: CwHypSyntheticAdapter; @@ -306,30 +331,27 @@ export class CwHypNativeAdapter public readonly chainName: ChainName, public readonly multiProvider: MultiProtocolProvider, public readonly addresses: { warpRouter: Address }, - public readonly gasDenom = 'untrn', ) { - super(chainName, multiProvider, addresses, gasDenom); - this.cw20adapter = new CwHypSyntheticAdapter( - chainName, - multiProvider, - { token: '', warpRouter: addresses.warpRouter }, - gasDenom, - ); + super(chainName, multiProvider, addresses, ''); + this.cw20adapter = new CwHypSyntheticAdapter(chainName, multiProvider, { + token: '', + warpRouter: addresses.warpRouter, + }); } - async getBalance(address: string): Promise { + async getBalance(address: string): Promise { const provider = await this.getProvider(); - const denom = await this.denom(); + const denom = await this.getDenom(); const balance = await provider.getBalance(address, denom); - return balance.amount; + return BigInt(balance.amount); } - async interchainSecurityModule(): Promise
{ - return this.cw20adapter.interchainSecurityModule(); + async getInterchainSecurityModule(): Promise
{ + return this.cw20adapter.getInterchainSecurityModule(); } - async owner(): Promise
{ - return this.cw20adapter.owner(); + async getOwner(): Promise
{ + return this.cw20adapter.getOwner(); } async getDomains(): Promise { @@ -344,12 +366,12 @@ export class CwHypNativeAdapter return this.cw20adapter.getAllRouters(); } - quoteGasPayment(destination: number): Promise { - return this.cw20adapter.quoteGasPayment(destination); + quoteTransferRemoteGas(destination: Domain): Promise { + return this.cw20adapter.quoteTransferRemoteGas(destination); } - async denom(): Promise { - const tokenType = await this.cw20adapter.tokenType(); + async getDenom(): Promise { + const tokenType = await this.cw20adapter.getTokenType(); if ('native' in tokenType) { if ('fungible' in tokenType.native) { return tokenType.native.fungible.denom; @@ -363,18 +385,20 @@ export class CwHypNativeAdapter destination, recipient, weiAmountOrId, - txValue, + interchainGas, }: TransferRemoteParams): Promise { - if (!txValue) { - throw new Error('txValue is required for native tokens'); - } - const collateralDenom = await this.denom(); + const collateralDenom = await this.getDenom(); + + if (!interchainGas) + interchainGas = await this.quoteTransferRemoteGas(destination); + const { addressOrDenom: igpDenom, amount: igpAmount } = interchainGas; + assert(igpDenom, 'Interchain gas denom required for Cosmos'); const funds: Coin[] = - collateralDenom === this.gasDenom + collateralDenom === igpDenom ? [ { - amount: new BigNumber(weiAmountOrId).plus(txValue).toFixed(0), + amount: (BigInt(weiAmountOrId) + igpAmount).toString(), denom: collateralDenom, }, ] @@ -384,8 +408,8 @@ export class CwHypNativeAdapter denom: collateralDenom, }, { - amount: txValue.toString(), - denom: this.gasDenom, + amount: igpAmount.toString(), + denom: igpDenom, }, ]; @@ -404,14 +428,13 @@ export class CwHypNativeAdapter export class CwHypCollateralAdapter extends CwHypNativeAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { constructor( public readonly chainName: ChainName, public readonly multiProvider: MultiProtocolProvider, public readonly addresses: { warpRouter: Address; token: Address }, - public readonly gasDenom = 'untrn', ) { - super(chainName, multiProvider, addresses, gasDenom); + super(chainName, multiProvider, addresses); } } diff --git a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts index ab482fdbfa..fb17ec4912 100644 --- a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts @@ -1,7 +1,7 @@ import { MsgTransferEncodeObject } from '@cosmjs/stargate'; import Long from 'long'; -import { Address, Domain } from '@hyperlane-xyz/utils'; +import { Address, Domain, assert } from '@hyperlane-xyz/utils'; import { BaseCosmosAdapter } from '../../app/MultiProtocolApp'; import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; @@ -12,6 +12,7 @@ import { CwHypCollateralAdapter } from './CosmWasmTokenAdapter'; import { IHypTokenAdapter, ITokenAdapter, + InterchainGasQuote, TransferParams, TransferRemoteParams, } from './ITokenAdapter'; @@ -21,7 +22,7 @@ const COSMOS_IBC_TRANSFER_TIMEOUT = 600_000; // 10 minutes // Interacts with native tokens on a Cosmos chain (e.g TIA on Celestia) export class CosmNativeTokenAdapter extends BaseCosmosAdapter - implements ITokenAdapter + implements ITokenAdapter { constructor( public readonly chainName: ChainName, @@ -36,17 +37,23 @@ export class CosmNativeTokenAdapter super(chainName, multiProvider, addresses); } - async getBalance(address: string): Promise { + async getBalance(address: string): Promise { const provider = await this.getProvider(); const coin = await provider.getBalance(address, this.properties.ibcDenom); - return coin.amount; + return BigInt(coin.amount); } getMetadata(): Promise { throw new Error('Metadata not available to native tokens'); } - populateApproveTx(_transferParams: TransferParams): unknown { + async isApproveRequired(): Promise { + return false; + } + + populateApproveTx( + _transferParams: TransferParams, + ): Promise { throw new Error('Approve not required for native tokens'); } @@ -62,7 +69,7 @@ export class CosmNativeTokenAdapter // methods don't apply to IBC transfers the way they do for Warp transfers export class CosmIbcTokenAdapter extends CosmNativeTokenAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { constructor( public readonly chainName: ChainName, @@ -97,8 +104,11 @@ export class CosmIbcTokenAdapter > { throw new Error('Method not applicable to IBC adapters'); } - quoteGasPayment(_destination: Domain): Promise { - throw new Error('Method not applicable to IBC adapters'); + async quoteTransferRemoteGas( + _destination: Domain, + ): Promise { + // TODO implement IBC interchain transfer gas estimation here + return { amount: 0n, addressOrDenom: this.properties.ibcDenom }; } async populateTransferRemoteTx( @@ -134,7 +144,7 @@ export class CosmIbcTokenAdapter // A.k.a. 'One-Click' cosmos to evm transfers export class CosmIbcToWarpTokenAdapter extends CosmIbcTokenAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { constructor( public readonly chainName: ChainName, @@ -144,13 +154,20 @@ export class CosmIbcToWarpTokenAdapter destinationRouterAddress: Address; }, public readonly properties: CosmIbcTokenAdapter['properties'] & { - derivedIbcDenom: string; + intermediateIbcDenom: string; intermediateChainName: ChainName; }, ) { super(chainName, multiProvider, addresses, properties); } + async quoteTransferRemoteGas( + _destination: Domain, + ): Promise { + // TODO implement IBC interchain transfer gas estimation here + return { amount: 0n, addressOrDenom: this.properties.intermediateIbcDenom }; + } + async populateTransferRemoteTx( transferParams: TransferRemoteParams, ): Promise { @@ -158,12 +175,24 @@ export class CosmIbcToWarpTokenAdapter this.properties.intermediateChainName, this.multiProvider, { - token: this.properties.derivedIbcDenom, + token: this.properties.intermediateIbcDenom, warpRouter: this.addresses.intermediateRouterAddress, }, - this.properties.derivedIbcDenom, ); - const transfer = await cwAdapter.populateTransferRemoteTx(transferParams); + assert( + transferParams.interchainGas?.addressOrDenom === this.properties.ibcDenom, + 'Only same-denom interchain gas is supported for IBC to Warp transfers', + ); + // This transformation is necessary to ensure the CW adapter recognizes the gas + // denom is the same as this adapter's denom (e.g. utia & igp/77...) + const intermediateInterchainGas = { + addressOrDenom: this.properties.intermediateIbcDenom, + amount: transferParams.interchainGas?.amount || 0n, + }; + const transfer = await cwAdapter.populateTransferRemoteTx({ + ...transferParams, + interchainGas: intermediateInterchainGas, + }); const cwMemo = { wasm: { contract: transfer.contractAddress, diff --git a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts index 338c484378..34a62a7b1f 100644 --- a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts @@ -4,12 +4,14 @@ import { ERC20, ERC20__factory, HypERC20, + HypERC20Collateral, HypERC20Collateral__factory, HypERC20__factory, } from '@hyperlane-xyz/core'; import { Address, Domain, + Numberish, addressToByteHexString, addressToBytes32, bytes32ToAddress, @@ -24,18 +26,23 @@ import { MinimalTokenMetadata } from '../config'; import { IHypTokenAdapter, ITokenAdapter, + InterchainGasQuote, TransferParams, TransferRemoteParams, } from './ITokenAdapter'; +// An estimate of the gas amount for a typical EVM token router transferRemote transaction +// Computed by estimating on a few different chains, taking the max, and then adding ~50% padding +export const EVM_TRANSFER_REMOTE_GAS_ESTIMATE = 450_000n; + // Interacts with native currencies export class EvmNativeTokenAdapter extends BaseEvmAdapter - implements ITokenAdapter + implements ITokenAdapter { - async getBalance(address: Address): Promise { + async getBalance(address: Address): Promise { const balance = await this.getProvider().getBalance(address); - return balance.toString(); + return BigInt(balance.toString()); } async getMetadata(): Promise { @@ -43,6 +50,14 @@ export class EvmNativeTokenAdapter throw new Error('Metadata not available to native tokens'); } + async isApproveRequired( + _owner: Address, + _spender: Address, + _weiAmountOrId: Numberish, + ): Promise { + return false; + } + async populateApproveTx( _params: TransferParams, ): Promise { @@ -53,7 +68,7 @@ export class EvmNativeTokenAdapter weiAmountOrId, recipient, }: TransferParams): Promise { - const value = BigNumber.from(weiAmountOrId); + const value = BigNumber.from(weiAmountOrId.toString()); return { value, to: recipient }; } } @@ -61,7 +76,7 @@ export class EvmNativeTokenAdapter // Interacts with ERC20/721 contracts export class EvmTokenAdapter extends EvmNativeTokenAdapter - implements ITokenAdapter + implements ITokenAdapter { public readonly contract: T; @@ -78,9 +93,9 @@ export class EvmTokenAdapter ); } - override async getBalance(address: Address): Promise { + override async getBalance(address: Address): Promise { const balance = await this.contract.balanceOf(address); - return balance.toString(); + return BigInt(balance.toString()); } override async getMetadata(isNft?: boolean): Promise { @@ -92,25 +107,40 @@ export class EvmTokenAdapter return { decimals, symbol, name }; } + override async isApproveRequired( + owner: Address, + spender: Address, + weiAmountOrId: Numberish, + ): Promise { + const allowance = await this.contract.allowance(owner, spender); + return allowance.lt(weiAmountOrId); + } + override populateApproveTx({ weiAmountOrId, recipient, }: TransferParams): Promise { - return this.contract.populateTransaction.approve(recipient, weiAmountOrId); + return this.contract.populateTransaction.approve( + recipient, + weiAmountOrId.toString(), + ); } override populateTransferTx({ weiAmountOrId, recipient, }: TransferParams): Promise { - return this.contract.populateTransaction.transfer(recipient, weiAmountOrId); + return this.contract.populateTransaction.transfer( + recipient, + weiAmountOrId.toString(), + ); } } // Interacts with Hyp Synthetic token contracts (aka 'HypTokens') -export class EvmHypSyntheticAdapter - extends EvmTokenAdapter - implements IHypTokenAdapter +export class EvmHypSyntheticAdapter + extends EvmTokenAdapter + implements IHypTokenAdapter { constructor( public readonly chainName: ChainName, @@ -121,6 +151,14 @@ export class EvmHypSyntheticAdapter super(chainName, multiProvider, addresses, contractFactory); } + override async isApproveRequired( + _owner: Address, + _spender: Address, + _weiAmountOrId: Numberish, + ): Promise { + return false; + } + getDomains(): Promise { return this.contract.domains(); } @@ -147,64 +185,134 @@ export class EvmHypSyntheticAdapter return domains.map((d, i) => ({ domain: d, address: routers[i] })); } - async quoteGasPayment(destination: Domain): Promise { + async quoteTransferRemoteGas( + destination: Domain, + ): Promise { const gasPayment = await this.contract.quoteGasPayment(destination); - return gasPayment.toString(); + // If EVM hyp contracts eventually support alternative IGP tokens, + // this would need to determine the correct token address + return { amount: BigInt(gasPayment.toString()) }; } - populateTransferRemoteTx({ + async populateTransferRemoteTx({ weiAmountOrId, destination, recipient, - txValue, + interchainGas, }: TransferRemoteParams): Promise { + if (!interchainGas) + interchainGas = await this.quoteTransferRemoteGas(destination); + const recipBytes32 = addressToBytes32(addressToByteHexString(recipient)); return this.contract.populateTransaction.transferRemote( destination, recipBytes32, weiAmountOrId, - { - // Note, typically the value is the gas payment as quoted by IGP - value: txValue, - }, + { value: interchainGas.amount.toString() }, ); } } -// Interacts with HypCollateral and HypNative contracts +// Interacts with HypCollateral contracts export class EvmHypCollateralAdapter extends EvmHypSyntheticAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { + public readonly collateralContract: HypERC20Collateral; + protected wrappedTokenAddress?: Address; + constructor( public readonly chainName: ChainName, public readonly multiProvider: MultiProtocolProvider, public readonly addresses: { token: Address }, - public readonly contractFactory: any = HypERC20Collateral__factory, ) { - super(chainName, multiProvider, addresses, contractFactory); + super(chainName, multiProvider, addresses); + this.collateralContract = HypERC20Collateral__factory.connect( + addresses.token, + this.getProvider(), + ); + } + + protected async getWrappedTokenAddress(): Promise
{ + if (!this.wrappedTokenAddress) { + this.wrappedTokenAddress = await this.collateralContract.wrappedToken(); + } + return this.wrappedTokenAddress; + } + + protected async getWrappedTokenAdapter(): Promise< + ITokenAdapter + > { + return new EvmTokenAdapter(this.chainName, this.multiProvider, { + token: await this.getWrappedTokenAddress(), + }); + } + + override getMetadata(isNft?: boolean): Promise { + return this.getWrappedTokenAdapter().then((t) => t.getMetadata(isNft)); } - override getMetadata(): Promise { - // TODO pass through metadata from wrapped token or chainMetadata config - throw new Error( - 'Metadata not available for HypCollateral/HypNative contract.', + override isApproveRequired( + owner: Address, + spender: Address, + weiAmountOrId: Numberish, + ): Promise { + return this.getWrappedTokenAdapter().then((t) => + t.isApproveRequired(owner, spender, weiAmountOrId), ); } override populateApproveTx( - _params: TransferParams, + params: TransferParams, ): Promise { - throw new Error( - 'Approve not applicable to HypCollateral/HypNative contract.', + return this.getWrappedTokenAdapter().then((t) => + t.populateApproveTx(params), ); } override populateTransferTx( - _params: TransferParams, + params: TransferParams, ): Promise { - throw new Error( - 'Local transfer not supported for HypCollateral/HypNative contract.', + return this.getWrappedTokenAdapter().then((t) => + t.populateTransferTx(params), + ); + } +} + +// Interacts HypNative contracts +export class EvmHypNativeAdapter + extends EvmHypCollateralAdapter + implements IHypTokenAdapter +{ + override async isApproveRequired(): Promise { + return false; + } + + override async populateTransferRemoteTx({ + weiAmountOrId, + destination, + recipient, + interchainGas, + }: TransferRemoteParams): Promise { + if (!interchainGas) + interchainGas = await this.quoteTransferRemoteGas(destination); + + let txValue: bigint | undefined = undefined; + const { addressOrDenom: igpAddressOrDenom, amount: igpAmount } = + interchainGas; + // If the igp token is native Eth + if (!igpAddressOrDenom) { + txValue = igpAmount + BigInt(weiAmountOrId); + } else { + txValue = igpAmount; + } + + const recipBytes32 = addressToBytes32(addressToByteHexString(recipient)); + return this.contract.populateTransaction.transferRemote( + destination, + recipBytes32, + weiAmountOrId, + { value: txValue?.toString() }, ); } } diff --git a/typescript/sdk/src/token/adapters/ITokenAdapter.ts b/typescript/sdk/src/token/adapters/ITokenAdapter.ts index b41c42ebae..4d2f64b4a9 100644 --- a/typescript/sdk/src/token/adapters/ITokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/ITokenAdapter.ts @@ -1,37 +1,42 @@ -import { Address, Domain } from '@hyperlane-xyz/utils'; +import { Address, Domain, Numberish } from '@hyperlane-xyz/utils'; import { MinimalTokenMetadata } from '../config'; export interface TransferParams { - weiAmountOrId: string | number; + weiAmountOrId: Numberish; recipient: Address; - - // Solana-specific params - // Included here optionally to keep Adapter types simple - fromTokenAccount?: Address; + // Required for Cosmos + Solana fromAccountOwner?: Address; + // Required for Solana + fromTokenAccount?: Address; } export interface TransferRemoteParams extends TransferParams { destination: Domain; - txValue?: string; + interchainGas?: InterchainGasQuote; +} + +export interface InterchainGasQuote { + addressOrDenom?: string; // undefined values represent default native tokens + amount: bigint; } -export interface ITokenAdapter { - getBalance(address: Address): Promise; +export interface ITokenAdapter { + getBalance(address: Address): Promise; getMetadata(isNft?: boolean): Promise; - populateApproveTx(TransferParams: TransferParams): unknown | Promise; - populateTransferTx( - TransferParams: TransferParams, - ): unknown | Promise; + isApproveRequired( + owner: Address, + spender: Address, + weiAmountOrId: Numberish, + ): Promise; + populateApproveTx(params: TransferParams): Promise; + populateTransferTx(params: TransferParams): Promise; } -export interface IHypTokenAdapter extends ITokenAdapter { +export interface IHypTokenAdapter extends ITokenAdapter { getDomains(): Promise; getRouterAddress(domain: Domain): Promise; getAllRouters(): Promise>; - quoteGasPayment(destination: Domain): Promise; - populateTransferRemoteTx( - TransferParams: TransferRemoteParams, - ): unknown | Promise; + quoteTransferRemoteGas(destination: Domain): Promise; + populateTransferRemoteTx(p: TransferRemoteParams): Promise; } diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index 2d93c4d2f9..50b965f857 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -13,7 +13,6 @@ import { Transaction, TransactionInstruction, } from '@solana/web3.js'; -import BigNumber from 'bignumber.js'; import { deserializeUnchecked, serialize } from 'borsh'; import { @@ -39,6 +38,7 @@ import { MinimalTokenMetadata } from '../config'; import { IHypTokenAdapter, ITokenAdapter, + InterchainGasQuote, TransferParams, TransferRemoteParams, } from './ITokenAdapter'; @@ -54,33 +54,37 @@ import { // Interacts with native currencies export class SealevelNativeTokenAdapter extends BaseSealevelAdapter - implements ITokenAdapter + implements ITokenAdapter { - async getBalance(address: Address): Promise { + async getBalance(address: Address): Promise { const balance = await this.getProvider().getBalance(new PublicKey(address)); - return balance.toString(); + return BigInt(balance.toString()); } async getMetadata(): Promise { throw new Error('Metadata not available to native tokens'); } - populateApproveTx(_params: TransferParams): Transaction { + async isApproveRequired(): Promise { + return false; + } + + async populateApproveTx(): Promise { throw new Error('Approve not required for native tokens'); } - populateTransferTx({ + async populateTransferTx({ weiAmountOrId, recipient, fromAccountOwner, - }: TransferParams): Transaction { + }: TransferParams): Promise { if (!fromAccountOwner) throw new Error('fromAccountOwner required for Sealevel'); return new Transaction().add( SystemProgram.transfer({ fromPubkey: new PublicKey(fromAccountOwner), toPubkey: new PublicKey(recipient), - lamports: new BigNumber(weiAmountOrId).toNumber(), + lamports: BigInt(weiAmountOrId), }), ); } @@ -89,7 +93,7 @@ export class SealevelNativeTokenAdapter // Interacts with SPL token programs export class SealevelTokenAdapter extends BaseSealevelAdapter - implements ITokenAdapter + implements ITokenAdapter { public readonly tokenProgramPubKey: PublicKey; @@ -103,12 +107,12 @@ export class SealevelTokenAdapter this.tokenProgramPubKey = new PublicKey(addresses.token); } - async getBalance(owner: Address): Promise { + async getBalance(owner: Address): Promise { const tokenPubKey = this.deriveAssociatedTokenAccount(new PublicKey(owner)); const response = await this.getProvider().getTokenAccountBalance( tokenPubKey, ); - return response.value.amount; + return BigInt(response.value.amount); } async getMetadata(_isNft?: boolean): Promise { @@ -116,16 +120,20 @@ export class SealevelTokenAdapter return { decimals: 9, symbol: 'SPL', name: 'SPL Token' }; } + async isApproveRequired(): Promise { + return false; + } + populateApproveTx(_params: TransferParams): Promise { throw new Error('Approve not required for sealevel tokens'); } - populateTransferTx({ + async populateTransferTx({ weiAmountOrId, recipient, fromAccountOwner, fromTokenAccount, - }: TransferParams): Transaction { + }: TransferParams): Promise { if (!fromTokenAccount) throw new Error('fromTokenAccount required for Sealevel'); if (!fromAccountOwner) @@ -135,7 +143,7 @@ export class SealevelTokenAdapter new PublicKey(fromTokenAccount), new PublicKey(recipient), new PublicKey(fromAccountOwner), - new BigNumber(weiAmountOrId).toNumber(), + BigInt(weiAmountOrId), ), ); } @@ -164,7 +172,7 @@ const TRANSFER_REMOTE_COMPUTE_LIMIT = 1_000_000; export abstract class SealevelHypTokenAdapter extends SealevelTokenAdapter - implements IHypTokenAdapter + implements IHypTokenAdapter { public readonly warpProgramPubKey: PublicKey; protected cachedTokenAccountData: SealevelHyperlaneTokenData | undefined; @@ -235,9 +243,11 @@ export abstract class SealevelHypTokenAdapter })); } - async quoteGasPayment(_destination: Domain): Promise { + async quoteTransferRemoteGas( + _destination: Domain, + ): Promise { // TODO Solana support - return '0'; + return { amount: 0n }; } async populateTransferRemoteTx({ @@ -264,7 +274,7 @@ export abstract class SealevelHypTokenAdapter data: new SealevelTransferRemoteInstruction({ destination_domain: destination, recipient: addressToBytes(recipient), - amount_or_id: new BigNumber(weiAmountOrId).toNumber(), + amount_or_id: BigInt(weiAmountOrId), }), }); const serializedData = serialize(SealevelTransferRemoteSchema, value); @@ -492,7 +502,7 @@ export class SealevelHypNativeAdapter extends SealevelHypTokenAdapter { ); } - override async getBalance(owner: Address): Promise { + override async getBalance(owner: Address): Promise { return this.wrappedNative.getBalance(owner); } @@ -525,7 +535,7 @@ export class SealevelHypNativeAdapter extends SealevelHypTokenAdapter { // Interacts with Hyp Collateral token programs export class SealevelHypCollateralAdapter extends SealevelHypTokenAdapter { - async getBalance(owner: Address): Promise { + async getBalance(owner: Address): Promise { // Special case where the owner is the warp route program ID. // This is because collateral warp routes don't hold escrowed collateral // tokens in their associated token account - instead, they hold them in @@ -535,7 +545,7 @@ export class SealevelHypCollateralAdapter extends SealevelHypTokenAdapter { const response = await this.getProvider().getTokenAccountBalance( collateralAccount, ); - return response.value.amount; + return BigInt(response.value.amount); } return super.getBalance(owner); @@ -593,12 +603,12 @@ export class SealevelHypSyntheticAdapter extends SealevelHypTokenAdapter { ]; } - override async getBalance(owner: Address): Promise { + override async getBalance(owner: Address): Promise { const tokenPubKey = this.deriveAssociatedTokenAccount(new PublicKey(owner)); const response = await this.getProvider().getTokenAccountBalance( tokenPubKey, ); - return response.value.amount; + return BigInt(response.value.amount); } deriveMintAuthorityAccount(): PublicKey { diff --git a/typescript/sdk/src/token/deploy.ts b/typescript/sdk/src/token/deploy.ts index 23ac7db353..a158d03c01 100644 --- a/typescript/sdk/src/token/deploy.ts +++ b/typescript/sdk/src/token/deploy.ts @@ -24,6 +24,7 @@ import { import { objMap } from '@hyperlane-xyz/utils'; import { HyperlaneContracts } from '../contracts/types'; +import { ContractVerifier } from '../deploy/verify/ContractVerifier'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory'; import { MultiProvider } from '../providers/MultiProvider'; import { GasRouterDeployer } from '../router/GasRouterDeployer'; @@ -56,10 +57,15 @@ export class HypERC20Deployer extends GasRouterDeployer< ERC20RouterConfig, HypERC20Factories > { - constructor(multiProvider: MultiProvider, ismFactory?: HyperlaneIsmFactory) { + constructor( + multiProvider: MultiProvider, + ismFactory?: HyperlaneIsmFactory, + contractVerifier?: ContractVerifier, + ) { super(multiProvider, {} as HypERC20Factories, { logger: debug('hyperlane:HypERC20Deployer'), ismFactory, + contractVerifier, }); // factories not used in deploy } @@ -183,10 +189,17 @@ export class HypERC20Deployer extends GasRouterDeployer< config.mailbox, ]); - await this.multiProvider.handleTx( - chain, - router.initialize(config.totalSupply, config.name, config.symbol), - ); + try { + await this.multiProvider.handleTx( + chain, + router.initialize(config.totalSupply, config.name, config.symbol), + ); + } catch (e: any) { + if (!e.message.includes('already initialized')) { + throw e; + } + this.logger(`${name} already initialized`); + } return router; } @@ -273,9 +286,13 @@ export class HypERC721Deployer extends GasRouterDeployer< ERC721RouterConfig, HypERC721Factories > { - constructor(multiProvider: MultiProvider) { + constructor( + multiProvider: MultiProvider, + contractVerifier?: ContractVerifier, + ) { super(multiProvider, {} as HypERC721Factories, { logger: debug('hyperlane:HypERC721Deployer'), + contractVerifier, }); // factories not used in deploy } diff --git a/typescript/sdk/src/types.ts b/typescript/sdk/src/types.ts index 3b10b97103..8eabb6a74b 100644 --- a/typescript/sdk/src/types.ts +++ b/typescript/sdk/src/types.ts @@ -1,5 +1,7 @@ import type { ethers } from 'ethers'; +import type { ChainId, Domain } from '@hyperlane-xyz/utils'; + // An alias for string to clarify type is a chain name export type ChainName = string; // A map of chain names to a value type @@ -7,6 +9,6 @@ export type ChainMap = Record; // The names of test chains, should be kept up to date if new are added export type TestChainNames = 'test1' | 'test2' | 'test3'; -export type NameOrDomain = ChainName | number; +export type ChainNameOrId = ChainName | ChainId | Domain; export type Connection = ethers.providers.Provider | ethers.Signer; diff --git a/typescript/sdk/src/warp/WarpCore.test.ts b/typescript/sdk/src/warp/WarpCore.test.ts new file mode 100644 index 0000000000..f944187d72 --- /dev/null +++ b/typescript/sdk/src/warp/WarpCore.test.ts @@ -0,0 +1,286 @@ +import { expect } from 'chai'; +import { ethers } from 'ethers'; +import fs from 'fs'; +import path from 'path'; +import sinon from 'sinon'; +import { parse as yamlParse } from 'yaml'; + +import { chainMetadata } from '../consts/chainMetadata'; +import { Chains } from '../consts/chains'; +import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; +import { ProviderType } from '../providers/ProviderType'; +import { Token } from '../token/Token'; +import { TokenStandard } from '../token/TokenStandard'; +import { InterchainGasQuote } from '../token/adapters/ITokenAdapter'; +import { ChainName } from '../types'; + +import { WarpCore } from './WarpCore'; +import { WarpTxCategory } from './types'; + +const MOCK_LOCAL_QUOTE = { gasUnits: 2_000n, gasPrice: 100, fee: 200_000n }; +const MOCK_INTERCHAIN_QUOTE = { amount: 20_000n }; +const TRANSFER_AMOUNT = BigInt('1000000000000000000'); // 1 units @ 18 decimals +const BIG_TRANSFER_AMOUNT = BigInt('100000000000000000000'); // 100 units @ 18 decimals +const MOCK_BALANCE = BigInt('10000000000000000000'); // 10 units @ 18 decimals + +describe('WarpCore', () => { + const multiProvider = new MultiProtocolProvider(); + let warpCore: WarpCore; + let evmHypNative: Token; + let evmHypSynthetic: Token; + let sealevelHypSynthetic: Token; + let cwHypCollateral: Token; + let cw20: Token; + let cosmosIbc: Token; + + // Stub MultiProvider fee estimation to avoid real network calls + sinon + .stub(multiProvider, 'estimateTransactionFee') + .returns(Promise.resolve(MOCK_LOCAL_QUOTE)); + + it('Constructs', () => { + const fromArgs = new WarpCore(multiProvider, [ + Token.FromChainMetadataNativeToken(chainMetadata[Chains.ethereum]), + ]); + const exampleConfig = yamlParse( + fs.readFileSync( + path.join(__dirname, './example-warp-core-config.yaml'), + 'utf-8', + ), + ); + const fromConfig = WarpCore.FromConfig(multiProvider, exampleConfig); + expect(fromArgs).to.be.instanceOf(WarpCore); + expect(fromConfig).to.be.instanceOf(WarpCore); + expect(fromConfig.tokens.length).to.equal(exampleConfig.tokens.length); + + warpCore = fromConfig; + [ + evmHypNative, + evmHypSynthetic, + sealevelHypSynthetic, + cwHypCollateral, + cw20, + cosmosIbc, + ] = warpCore.tokens; + }); + + it('Finds tokens', () => { + expect( + warpCore.findToken(Chains.ethereum, evmHypNative.addressOrDenom), + ).to.be.instanceOf(Token); + expect( + warpCore.findToken(Chains.ethereum, sealevelHypSynthetic.addressOrDenom), + ).to.be.null; + expect( + warpCore.findToken(Chains.neutron, cw20.addressOrDenom), + ).to.be.instanceOf(Token); + }); + + it('Gets transfer gas quote', async () => { + const stubs = warpCore.tokens.map((t) => + sinon.stub(t, 'getHypAdapter').returns({ + quoteTransferRemoteGas: () => Promise.resolve(MOCK_INTERCHAIN_QUOTE), + isApproveRequired: () => Promise.resolve(false), + populateTransferRemoteTx: () => Promise.resolve({}), + } as any), + ); + + const testQuote = async ( + token: Token, + destination: ChainName, + standard: TokenStandard, + interchainQuote: InterchainGasQuote = MOCK_INTERCHAIN_QUOTE, + ) => { + const result = await warpCore.estimateTransferRemoteFees({ + originToken: token, + destination, + sender: ethers.constants.AddressZero, + }); + expect( + result.localQuote.token.standard, + `token local standard check for ${token.chainName} to ${destination}`, + ).equals(standard); + expect( + result.localQuote.amount, + `token local amount check for ${token.chainName} to ${destination}`, + ).to.equal(MOCK_LOCAL_QUOTE.fee); + expect( + result.interchainQuote.token.standard, + `token interchain standard check for ${token.chainName} to ${destination}`, + ).equals(standard); + expect( + result.interchainQuote.amount, + `token interchain amount check for ${token.chainName} to ${destination}`, + ).to.equal(interchainQuote.amount); + }; + + await testQuote(evmHypNative, Chains.arbitrum, TokenStandard.EvmNative); + await testQuote(evmHypNative, Chains.neutron, TokenStandard.EvmNative); + await testQuote(evmHypNative, Chains.solana, TokenStandard.EvmNative); + await testQuote(evmHypSynthetic, Chains.ethereum, TokenStandard.EvmNative); + await testQuote( + sealevelHypSynthetic, + Chains.ethereum, + TokenStandard.SealevelNative, + ); + await testQuote(cosmosIbc, Chains.arbitrum, TokenStandard.CosmosNative); + // Note, this route uses an igp quote const config + await testQuote( + cwHypCollateral, + Chains.arbitrum, + TokenStandard.CosmosNative, + { + amount: 1n, + addressOrDenom: 'untrn', + }, + ); + + stubs.forEach((s) => s.restore()); + }); + + it('Checks for destination collateral', async () => { + const stubs = warpCore.tokens.map((t) => + sinon.stub(t, 'getHypAdapter').returns({ + getBalance: () => Promise.resolve(MOCK_BALANCE), + } as any), + ); + + const testCollateral = async ( + token: Token, + destination: ChainName, + expectedBigResult = true, + ) => { + const smallResult = await warpCore.isDestinationCollateralSufficient({ + originTokenAmount: token.amount(TRANSFER_AMOUNT), + destination, + }); + expect( + smallResult, + `small collateral check for ${token.chainName} to ${destination}`, + ).to.be.true; + const bigResult = await warpCore.isDestinationCollateralSufficient({ + originTokenAmount: token.amount(BIG_TRANSFER_AMOUNT), + destination, + }); + expect( + bigResult, + `big collateral check for ${token.chainName} to ${destination}`, + ).to.equal(expectedBigResult); + }; + + await testCollateral(evmHypNative, Chains.arbitrum); + await testCollateral(evmHypNative, Chains.neutron, false); + await testCollateral(evmHypNative, Chains.solana); + await testCollateral(cwHypCollateral, Chains.arbitrum); + + stubs.forEach((s) => s.restore()); + }); + + it('Validates transfers', async () => { + const balanceStubs = warpCore.tokens.map((t) => + sinon + .stub(t, 'getBalance') + .returns(Promise.resolve({ amount: MOCK_BALANCE } as any)), + ); + const quoteStubs = warpCore.tokens.map((t) => + sinon.stub(t, 'getHypAdapter').returns({ + quoteTransferRemoteGas: () => Promise.resolve(MOCK_INTERCHAIN_QUOTE), + isApproveRequired: () => Promise.resolve(false), + populateTransferRemoteTx: () => Promise.resolve({}), + } as any), + ); + + const validResult = await warpCore.validateTransfer({ + originTokenAmount: evmHypNative.amount(TRANSFER_AMOUNT), + destination: Chains.arbitrum, + recipient: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + }); + expect(validResult).to.be.null; + + const invalidChain = await warpCore.validateTransfer({ + originTokenAmount: evmHypNative.amount(TRANSFER_AMOUNT), + destination: 'fakechain', + recipient: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + }); + expect(Object.keys(invalidChain || {})[0]).to.equal('destination'); + + const invalidRecipient = await warpCore.validateTransfer({ + originTokenAmount: evmHypNative.amount(TRANSFER_AMOUNT), + destination: Chains.neutron, + recipient: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + }); + expect(Object.keys(invalidRecipient || {})[0]).to.equal('recipient'); + + const invalidAmount = await warpCore.validateTransfer({ + originTokenAmount: evmHypNative.amount(-10), + destination: Chains.arbitrum, + recipient: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + }); + expect(Object.keys(invalidAmount || {})[0]).to.equal('amount'); + + const insufficientBalance = await warpCore.validateTransfer({ + originTokenAmount: evmHypNative.amount(BIG_TRANSFER_AMOUNT), + destination: Chains.arbitrum, + recipient: ethers.constants.AddressZero, + sender: ethers.constants.AddressZero, + }); + expect(Object.keys(insufficientBalance || {})[0]).to.equal('amount'); + + balanceStubs.forEach((s) => s.restore()); + quoteStubs.forEach((s) => s.restore()); + }); + + it('Gets transfer remote txs', async () => { + const coreStub = sinon + .stub(warpCore, 'isApproveRequired') + .returns(Promise.resolve(false)); + + const adapterStubs = warpCore.tokens.map((t) => + sinon.stub(t, 'getHypAdapter').returns({ + quoteTransferRemoteGas: () => Promise.resolve(MOCK_INTERCHAIN_QUOTE), + populateTransferRemoteTx: () => Promise.resolve({}), + } as any), + ); + + const testGetTxs = async ( + token: Token, + destination: ChainName, + providerType = ProviderType.EthersV5, + ) => { + const result = await warpCore.getTransferRemoteTxs({ + originTokenAmount: token.amount(TRANSFER_AMOUNT), + destination, + sender: ethers.constants.AddressZero, + recipient: ethers.constants.AddressZero, + }); + expect(result.length).to.equal(1); + expect( + result[0], + `transfer tx for ${token.chainName} to ${destination}`, + ).to.eql({ + category: WarpTxCategory.Transfer, + transaction: {}, + type: providerType, + }); + }; + + await testGetTxs(evmHypNative, Chains.arbitrum); + await testGetTxs(evmHypNative, Chains.neutron); + await testGetTxs(evmHypNative, Chains.solana); + await testGetTxs(evmHypSynthetic, Chains.ethereum); + await testGetTxs( + sealevelHypSynthetic, + Chains.ethereum, + ProviderType.SolanaWeb3, + ); + await testGetTxs(cwHypCollateral, Chains.arbitrum, ProviderType.CosmJsWasm); + await testGetTxs(cosmosIbc, Chains.arbitrum, ProviderType.CosmJs); + + coreStub.restore(); + adapterStubs.forEach((s) => s.restore()); + }); +}); diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts new file mode 100644 index 0000000000..84e95f24f4 --- /dev/null +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -0,0 +1,680 @@ +import debug, { Debugger } from 'debug'; + +import { + Address, + HexString, + ProtocolType, + assert, + convertDecimals, + convertToProtocolAddress, + isValidAddress, +} from '@hyperlane-xyz/utils'; + +import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; +import { + TransactionFeeEstimate, + estimateTransactionFeeEthersV5ForGasUnits, +} from '../providers/transactionFeeEstimators'; +import { IToken } from '../token/IToken'; +import { Token } from '../token/Token'; +import { TokenAmount } from '../token/TokenAmount'; +import { parseTokenConnectionId } from '../token/TokenConnection'; +import { + TOKEN_COLLATERALIZED_STANDARDS, + TOKEN_STANDARD_TO_PROVIDER_TYPE, +} from '../token/TokenStandard'; +import { EVM_TRANSFER_REMOTE_GAS_ESTIMATE } from '../token/adapters/EvmTokenAdapter'; +import { ChainName, ChainNameOrId } from '../types'; + +import { + FeeConstantConfig, + RouteBlacklist, + WarpCoreConfigSchema, + WarpCoreFeeEstimate, + WarpTxCategory, + WarpTypedTransaction, +} from './types'; + +export interface WarpCoreOptions { + loggerName?: string; + localFeeConstants?: FeeConstantConfig; + interchainFeeConstants?: FeeConstantConfig; + routeBlacklist?: RouteBlacklist; +} + +export class WarpCore { + public readonly multiProvider: MultiProtocolProvider<{ mailbox?: Address }>; + public readonly tokens: Token[]; + public readonly localFeeConstants: FeeConstantConfig; + public readonly interchainFeeConstants: FeeConstantConfig; + public readonly routeBlacklist: RouteBlacklist; + public readonly logger: Debugger; + + constructor( + multiProvider: MultiProtocolProvider<{ mailbox?: Address }>, + tokens: Token[], + options?: WarpCoreOptions, + ) { + this.multiProvider = multiProvider; + this.tokens = tokens; + this.localFeeConstants = options?.localFeeConstants || []; + this.interchainFeeConstants = options?.interchainFeeConstants || []; + this.routeBlacklist = options?.routeBlacklist || []; + this.logger = debug(options?.loggerName || 'hyperlane:WarpCore'); + } + + /** + * Takes the serialized representation of a warp config and returns a WarpCore instance + * @param multiProvider the MultiProtocolProvider containing chain metadata + * @param config the config object of type WarpCoreConfig + */ + static FromConfig( + multiProvider: MultiProtocolProvider<{ mailbox?: Address }>, + config: unknown, + ): WarpCore { + // Validate and parse config data + const parsedConfig = WarpCoreConfigSchema.parse(config); + // Instantiate all tokens + const tokens = parsedConfig.tokens.map( + (t) => + new Token({ + ...t, + addressOrDenom: t.addressOrDenom || '', + connections: undefined, + }), + ); + // Connect tokens together + parsedConfig.tokens.forEach((config, i) => { + for (const connection of config.connections || []) { + const token1 = tokens[i]; + const { chainName, addressOrDenom } = parseTokenConnectionId( + connection.token, + ); + const token2 = tokens.find( + (t) => + t.chainName === chainName && t.addressOrDenom === addressOrDenom, + ); + assert( + token2, + `Connected token not found: ${chainName} ${addressOrDenom}`, + ); + token1.addConnection({ + ...connection, + token: token2, + }); + } + }); + // Create new Warp + return new WarpCore(multiProvider, tokens, parsedConfig.options); + } + + /** + * Queries the token router for an interchain gas quote (i.e. IGP fee) + */ + async getInterchainTransferFee({ + originToken, + destination, + }: { + originToken: IToken; + destination: ChainNameOrId; + }): Promise { + this.logger(`Fetching interchain transfer quote to ${destination}`); + const { chainName: originName } = originToken; + const destinationName = this.multiProvider.getChainName(destination); + + let gasAmount: bigint; + let gasAddressOrDenom: string | undefined; + // Check constant quotes first + const defaultQuote = this.interchainFeeConstants.find( + (q) => q.origin === originName && q.destination === destinationName, + ); + if (defaultQuote) { + gasAmount = BigInt(defaultQuote.amount.toString()); + gasAddressOrDenom = defaultQuote.addressOrDenom; + } else { + // Otherwise, compute IGP quote via the adapter + const hypAdapter = originToken.getHypAdapter( + this.multiProvider, + destinationName, + ); + const destinationDomainId = this.multiProvider.getDomainId(destination); + const quote = await hypAdapter.quoteTransferRemoteGas( + destinationDomainId, + ); + gasAmount = BigInt(quote.amount); + gasAddressOrDenom = quote.addressOrDenom; + } + + let igpToken: Token; + if (!gasAddressOrDenom) { + // An empty/undefined addressOrDenom indicates the native token + igpToken = Token.FromChainMetadataNativeToken( + this.multiProvider.getChainMetadata(originName), + ); + } else { + const searchResult = this.findToken(originName, gasAddressOrDenom); + assert(searchResult, `Fee token ${gasAddressOrDenom} is unknown`); + igpToken = searchResult; + } + + this.logger( + `Quoted interchain transfer fee: ${gasAmount} ${igpToken.symbol}`, + ); + return new TokenAmount(gasAmount, igpToken); + } + + /** + * Simulates a transfer to estimate 'local' gas fees on the origin chain + */ + async getLocalTransferFee({ + originToken, + destination, + sender, + senderPubKey, + interchainFee, + }: { + originToken: IToken; + destination: ChainNameOrId; + sender: Address; + senderPubKey?: HexString; + interchainFee?: TokenAmount; + }): Promise { + const originMetadata = this.multiProvider.getChainMetadata( + originToken.chainName, + ); + const destinationMetadata = + this.multiProvider.getChainMetadata(destination); + + // Check constant quotes first + const defaultQuote = this.localFeeConstants.find( + (q) => + q.origin === originMetadata.name && + q.destination === destinationMetadata.name, + ); + if (defaultQuote) { + return { gasUnits: 0, gasPrice: 0, fee: Number(defaultQuote.amount) }; + } + + // Form transactions to estimate local gas with + const recipient = convertToProtocolAddress( + sender, + destinationMetadata.protocol, + destinationMetadata.bech32Prefix, + ); + const txs = await this.getTransferRemoteTxs({ + originTokenAmount: originToken.amount(1), + destination, + sender, + recipient, + interchainFee, + }); + + // Typically the transfers require a single transaction + if (txs.length === 1) { + return this.multiProvider.estimateTransactionFee({ + chainNameOrId: originMetadata.name, + transaction: txs[0], + sender, + senderPubKey, + }); + } + // On ethereum, sometimes 2 txs are required (one approve, one transferRemote) + else if ( + txs.length === 2 && + originToken.protocol === ProtocolType.Ethereum + ) { + const provider = this.multiProvider.getEthersV5Provider( + originMetadata.name, + ); + // We use a hard-coded const as an estimate for the transferRemote because we + // cannot reliably simulate the tx when an approval tx is required first + return estimateTransactionFeeEthersV5ForGasUnits({ + provider, + gasUnits: EVM_TRANSFER_REMOTE_GAS_ESTIMATE, + }); + } else { + throw new Error('Cannot estimate local gas for multiple transactions'); + } + } + + /** + * Gets a list of populated transactions required to transfer a token to a remote chain + * Typically just 1 transaction but sometimes more, like when an approval is required first + */ + async getTransferRemoteTxs({ + originTokenAmount, + destination, + sender, + recipient, + interchainFee, + }: { + originTokenAmount: TokenAmount; + destination: ChainNameOrId; + sender: Address; + recipient: Address; + interchainFee?: TokenAmount; + }): Promise> { + const transactions: Array = []; + + const { token, amount } = originTokenAmount; + const destinationName = this.multiProvider.getChainName(destination); + const destinationDomainId = this.multiProvider.getDomainId(destination); + const providerType = TOKEN_STANDARD_TO_PROVIDER_TYPE[token.standard]; + const hypAdapter = token.getHypAdapter(this.multiProvider, destinationName); + + if (await this.isApproveRequired({ originTokenAmount, owner: sender })) { + this.logger(`Approval required for transfer of ${token.symbol}`); + const approveTxReq = await hypAdapter.populateApproveTx({ + weiAmountOrId: amount.toString(), + recipient: token.addressOrDenom, + }); + this.logger(`Approval tx for ${token.symbol} populated`); + + const approveTx = { + category: WarpTxCategory.Approval, + type: providerType, + transaction: approveTxReq, + } as WarpTypedTransaction; + transactions.push(approveTx); + } + + if (!interchainFee) { + interchainFee = await this.getInterchainTransferFee({ + originToken: token, + destination, + }); + } + + const transferTxReq = await hypAdapter.populateTransferRemoteTx({ + weiAmountOrId: amount.toString(), + destination: destinationDomainId, + fromAccountOwner: sender, + recipient, + interchainGas: { + amount: interchainFee.amount, + addressOrDenom: interchainFee.token.addressOrDenom, + }, + }); + this.logger(`Remote transfer tx for ${token.symbol} populated`); + + const transferTx = { + category: WarpTxCategory.Transfer, + type: providerType, + transaction: transferTxReq, + } as WarpTypedTransaction; + transactions.push(transferTx); + + return transactions; + } + + /** + * Fetch local and interchain fee estimates for a remote transfer + */ + async estimateTransferRemoteFees({ + originToken, + destination, + sender, + senderPubKey, + }: { + originToken: IToken; + destination: ChainNameOrId; + sender: Address; + senderPubKey?: HexString; + }): Promise { + this.logger('Fetching remote transfer fee estimates'); + + // First get interchain gas quote (aka IGP quote) + // Start with this because it's used in the local fee estimation + const interchainQuote = await this.getInterchainTransferFee({ + originToken, + destination, + }); + + const originMetadata = this.multiProvider.getChainMetadata( + originToken.chainName, + ); + // If there's no native token, we can't represent local gas + if (!originMetadata.nativeToken) + throw new Error(`No native token found for ${originMetadata.name}`); + + // Next, get the local gas quote + const localFee = await this.getLocalTransferFee({ + originToken, + destination, + sender, + senderPubKey, + interchainFee: interchainQuote, + }); + + // Get the local gas token. This assumes the chain's native token will pay for local gas + // This will need to be smarter if more complex scenarios on Cosmos are supported + const localGasToken = Token.FromChainMetadataNativeToken(originMetadata); + const localQuote = localGasToken.amount(localFee.fee); + + return { + interchainQuote, + localQuote, + }; + } + + /** + * Computes the max transferrable amount of the from the given + * token balance, accounting for local and interchain gas fees + */ + async getMaxTransferAmount({ + balance, + destination, + sender, + senderPubKey, + feeEstimate, + }: { + balance: TokenAmount; + destination: ChainNameOrId; + sender: Address; + senderPubKey?: HexString; + feeEstimate?: WarpCoreFeeEstimate; + }): Promise { + const originToken = balance.token; + + if (!feeEstimate) { + feeEstimate = await this.estimateTransferRemoteFees({ + originToken, + destination, + sender, + senderPubKey, + }); + } + const { localQuote, interchainQuote } = feeEstimate; + + let maxAmount = balance; + if (originToken.isFungibleWith(localQuote.token)) { + maxAmount = maxAmount.minus(localQuote.amount); + } + + if (originToken.isFungibleWith(interchainQuote.token)) { + maxAmount = maxAmount.minus(interchainQuote.amount); + } + + if (maxAmount.amount > 0) return maxAmount; + else return originToken.amount(0); + } + + /** + * Checks if destination chain's collateral is sufficient to cover the transfer + */ + async isDestinationCollateralSufficient({ + originTokenAmount, + destination, + }: { + originTokenAmount: TokenAmount; + destination: ChainNameOrId; + }): Promise { + const { token: originToken, amount } = originTokenAmount; + const destinationName = this.multiProvider.getChainName(destination); + this.logger( + `Checking collateral for ${originToken.symbol} to ${destination}`, + ); + + const destinationToken = + originToken.getConnectionForChain(destinationName)?.token; + assert(destinationToken, `No connection found for ${destinationName}`); + + if (!TOKEN_COLLATERALIZED_STANDARDS.includes(destinationToken.standard)) { + this.logger(`${destinationToken.symbol} is not collateralized, skipping`); + return true; + } + + const adapter = destinationToken.getAdapter(this.multiProvider); + const destinationBalance = await adapter.getBalance( + destinationToken.addressOrDenom, + ); + const destinationBalanceInOriginDecimals = convertDecimals( + destinationToken.decimals, + originToken.decimals, + destinationBalance.toString(), + ); + + const isSufficient = BigInt(destinationBalanceInOriginDecimals) >= amount; + this.logger( + `${originTokenAmount.token.symbol} to ${destination} has ${ + isSufficient ? 'sufficient' : 'INSUFFICIENT' + } collateral`, + ); + return isSufficient; + } + + /** + * Checks if a token transfer requires an approval tx first + */ + async isApproveRequired({ + originTokenAmount, + owner, + }: { + originTokenAmount: TokenAmount; + owner: Address; + }): Promise { + const { token, amount } = originTokenAmount; + const adapter = token.getAdapter(this.multiProvider); + const isRequired = await adapter.isApproveRequired( + owner, + token.addressOrDenom, + amount, + ); + this.logger( + `Approval is${isRequired ? '' : ' not'} required for transfer of ${ + token.symbol + }`, + ); + return isRequired; + } + + /** + * Ensure the remote token transfer would be valid for the given chains, amount, sender, and recipient + */ + async validateTransfer({ + originTokenAmount, + destination, + recipient, + sender, + senderPubKey, + }: { + originTokenAmount: TokenAmount; + destination: ChainNameOrId; + recipient: Address; + sender: Address; + senderPubKey?: HexString; + }): Promise | null> { + const chainError = this.validateChains( + originTokenAmount.token.chainName, + destination, + ); + if (chainError) return chainError; + + const recipientError = this.validateRecipient(recipient, destination); + if (recipientError) return recipientError; + + const amountError = this.validateAmount(originTokenAmount); + if (amountError) return amountError; + + const balancesError = await this.validateTokenBalances( + originTokenAmount, + destination, + sender, + senderPubKey, + ); + if (balancesError) return balancesError; + + return null; + } + + /** + * Ensure the origin and destination chains are valid and known by this WarpCore + */ + protected validateChains( + origin: ChainNameOrId, + destination: ChainNameOrId, + ): Record | null { + if (!origin) return { origin: 'Origin chain required' }; + if (!destination) return { destination: 'Destination chain required' }; + const originMetadata = this.multiProvider.tryGetChainMetadata(origin); + const destinationMetadata = + this.multiProvider.tryGetChainMetadata(destination); + if (!originMetadata) return { origin: 'Origin chain metadata missing' }; + if (!destinationMetadata) + return { destination: 'Destination chain metadata missing' }; + if ( + this.routeBlacklist.some( + (bl) => + bl.origin === originMetadata.name && + bl.destination === destinationMetadata.name, + ) + ) { + return { destination: 'Route is not currently allowed' }; + } + return null; + } + + /** + * Ensure recipient address is valid for the destination chain + */ + protected validateRecipient( + recipient: Address, + destination: ChainNameOrId, + ): Record | null { + const destinationMetadata = + this.multiProvider.getChainMetadata(destination); + const { protocol, bech32Prefix } = destinationMetadata; + // Ensure recip address is valid for the destination chain's protocol + if (!isValidAddress(recipient, protocol)) + return { recipient: 'Invalid recipient' }; + // Also ensure the address denom is correct if the dest protocol is Cosmos + if (protocol === ProtocolType.Cosmos) { + if (!bech32Prefix) { + this.logger(`No bech32 prefix found for chain ${destination}`); + return { destination: 'Invalid chain data' }; + } else if (!recipient.startsWith(bech32Prefix)) { + this.logger(`Recipient prefix should be ${bech32Prefix}`); + return { recipient: `Invalid recipient prefix` }; + } + } + return null; + } + + /** + * Ensure token amount is valid + */ + protected validateAmount( + originTokenAmount: TokenAmount, + ): Record | null { + if (!originTokenAmount.amount || originTokenAmount.amount < 0n) { + const isNft = originTokenAmount.token.isNft(); + return { amount: isNft ? 'Invalid Token Id' : 'Invalid amount' }; + } + return null; + } + + /** + * Ensure the sender has sufficient balances for transfer and interchain gas + */ + protected async validateTokenBalances( + originTokenAmount: TokenAmount, + destination: ChainNameOrId, + sender: Address, + senderPubKey?: HexString, + ): Promise | null> { + const { token, amount } = originTokenAmount; + const { amount: senderBalance } = await token.getBalance( + this.multiProvider, + sender, + ); + const senderBalanceAmount = originTokenAmount.token.amount(senderBalance); + + // First check basic token balance + if (amount > senderBalance) return { amount: 'Insufficient balance' }; + + // Next, ensure balances can cover the COMBINED amount and fees + const feeEstimate = await this.estimateTransferRemoteFees({ + originToken: token, + destination, + sender, + senderPubKey, + }); + const maxTransfer = await this.getMaxTransferAmount({ + balance: senderBalanceAmount, + destination, + sender, + senderPubKey, + feeEstimate, + }); + if (amount > maxTransfer.amount) { + return { amount: 'Insufficient balance for gas and transfer' }; + } + + // Finally, ensure there's sufficient balance for the IGP fee, which may + // be a different token than the transfer token + const igpQuote = feeEstimate.interchainQuote; + const igpTokenBalance = await igpQuote.token.getBalance( + this.multiProvider, + sender, + ); + if (igpTokenBalance.amount < igpQuote.amount) { + return { amount: `Insufficient ${igpQuote.token.symbol} for gas` }; + } + + return null; + } + + /** + * Search through token list to find token with matching chain and address + */ + findToken( + chainName: ChainName, + addressOrDenom?: Address | string, + ): Token | null { + if (!addressOrDenom) return null; + + const results = this.tokens.filter( + (token) => + token.chainName === chainName && + token.addressOrDenom.toLowerCase() === addressOrDenom.toLowerCase(), + ); + + if (results.length === 1) return results[0]; + + if (results.length > 1) + throw new Error(`Ambiguous token search results for ${addressOrDenom}`); + + // If the token is not found, check to see if it matches the denom of chain's native token + // This is a convenience so WarpConfigs don't need to include definitions for native tokens + const chainMetadata = this.multiProvider.getChainMetadata(chainName); + if (chainMetadata.nativeToken?.denom === addressOrDenom) { + return Token.FromChainMetadataNativeToken(chainMetadata); + } + + return null; + } + + /** + * Get the list of chains referenced by the tokens in this WarpCore + */ + getTokenChains(): ChainName[] { + return [...new Set(this.tokens.map((t) => t.chainName)).values()]; + } + + /** + * Get the subset of tokens whose chain matches the given chainName + */ + getTokensForChain(chainName: ChainName): Token[] { + return this.tokens.filter((t) => t.chainName === chainName); + } + + /** + * Get the subset of tokens whose chain matches the given chainName + * and which are connected to a token on the given destination chain + */ + getTokensForRoute(origin: ChainName, destination: ChainName): Token[] { + return this.tokens.filter( + (t) => t.chainName === origin && t.getConnectionForChain(destination), + ); + } +} diff --git a/typescript/sdk/src/warp/example-warp-core-config.yaml b/typescript/sdk/src/warp/example-warp-core-config.yaml new file mode 100644 index 0000000000..c074b4caa6 --- /dev/null +++ b/typescript/sdk/src/warp/example-warp-core-config.yaml @@ -0,0 +1,73 @@ +# An example Warp Core config +# Contains the token + route data needed to create a Warp Core +--- +tokens: + # Eth Mainnet HypNative token + - chainName: ethereum + standard: EvmHypNative + decimals: 18 + symbol: ETH + name: Ether + addressOrDenom: '0x1234567890123456789012345678901234567890' + connections: + - { token: ethereum|arbitrum|0x9876543210987654321098765432109876543210 } + - { token: cosmos|neutron|neutron1abcdefghijklmnopqrstuvwxyz1234567890ab } + - { token: sealevel|solana|s0LaBcEeFgHiJkLmNoPqRsTuVwXyZ456789012345678 } + # Arbitrum HypSynthetic token + - chainName: arbitrum + standard: EvmHypSynthetic + decimals: 18 + symbol: ETH + name: Ether + addressOrDenom: '0x9876543210987654321098765432109876543210' + connections: + - { token: ethereum|ethereum|0x1234567890123456789012345678901234567890 } + - { token: cosmos|neutron|neutron1abcdefghijklmnopqrstuvwxyz1234567890ab } + # Solana HypSynthetic + - chainName: solana + standard: SealevelHypSynthetic + decimals: 9 + symbol: ETH.sol + name: Ether on Solana + addressOrDenom: s0LaBcEeFgHiJkLmNoPqRsTuVwXyZ456789012345678 + connections: + - { token: ethereum|ethereum|0x1234567890123456789012345678901234567890 } + # Cosmos Neutron HypCollateral token + - chainName: neutron + standard: CwHypCollateral + decimals: 18 + symbol: ETH.in + name: Ether on Neutron + addressOrDenom: neutron1abcdefghijklmnopqrstuvwxyz1234567890ab + collateralAddressOrDenom: neutron1c0ll4t3r4lc0ll4t3r4lc0ll4t3r4lc0ll4t3r + connections: + - { token: ethereum|ethereum|0x1234567890123456789012345678901234567890 } + - { token: ethereum|arbitrum|0x9876543210987654321098765432109876543210 } + # Cosmos Neutron Collateralized token + - chainName: neutron + standard: CW20 + decimals: 18 + symbol: ETH.in + name: Ether on Neutron + addressOrDenom: neutron1c0ll4t3r4lc0ll4t3r4lc0ll4t3r4lc0ll4t3r + # Cosmos Injective token with IBC two-hop + - chainName: injective + standard: CosmosIbc + decimals: 18 + symbol: INJ + name: Injective + addressOrDenom: inj + connections: + - token: ethereum|arbitrum|0x9876543210987654321098765432109876543210 + type: ibc + sourcePort: transfer + sourceChannel: channel-1 + intermediateChainName: neutron + intermediateIbcDenom: untrn + intermediateRouterAddress: neutron1abcdefghijklmnopqrstuvwxyz1234567890ab +options: + interchainFeeConstants: + - origin: neutron + destination: arbitrum + amount: 1 + addressOrDenom: untrn diff --git a/typescript/sdk/src/warp/types.ts b/typescript/sdk/src/warp/types.ts new file mode 100644 index 0000000000..a8eab762cf --- /dev/null +++ b/typescript/sdk/src/warp/types.ts @@ -0,0 +1,64 @@ +import { z } from 'zod'; + +import { ZChainName } from '../metadata/customZodTypes'; +import type { TypedTransaction } from '../providers/ProviderType'; +import { TokenConfigSchema } from '../token/IToken'; +import type { TokenAmount } from '../token/TokenAmount'; +import type { ChainName } from '../types'; + +/** + * Configuration used for instantiating a WarpCore + * Contains the relevant tokens and their connections + */ +const FeeConstantConfigSchema = z.array( + z.object({ + origin: ZChainName, + destination: ZChainName, + amount: z.union([z.string(), z.number(), z.bigint()]), + addressOrDenom: z.string().optional(), + }), +); + +export const WarpCoreConfigSchema = z.object({ + tokens: z.array(TokenConfigSchema), + options: z + .object({ + localFeeConstants: FeeConstantConfigSchema.optional(), + interchainFeeConstants: FeeConstantConfigSchema.optional(), + routeBlacklist: z + .array( + z.object({ + origin: ZChainName, + destination: ZChainName, + }), + ) + .optional(), + }) + .optional(), +}); + +// List of constant values for local or interchain fees +export type FeeConstantConfig = z.infer; + +// List of chain pairs to blacklist for warp routes +export type RouteBlacklist = Array<{ + origin: ChainName; + destination: ChainName; +}>; + +// Transaction types for warp core remote transfers +export enum WarpTxCategory { + Approval = 'approval', + Transfer = 'transfer', +} + +export type WarpTypedTransaction = TypedTransaction & { + category: WarpTxCategory; +}; + +export type WarpCoreConfig = z.infer; + +export interface WarpCoreFeeEstimate { + interchainQuote: TokenAmount; + localQuote: TokenAmount; +} diff --git a/typescript/sdk/tsconfig.json b/typescript/sdk/tsconfig.json index 9bf7368a74..72d5a04621 100644 --- a/typescript/sdk/tsconfig.json +++ b/typescript/sdk/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../tsconfig.json", "compilerOptions": { "outDir": "./dist/", - "rootDir": "./src/" + "rootDir": "./src/", + "resolveJsonModule": true }, - "include": ["./src/**/*.ts", "./src/*.d.ts"], + "include": ["./src/**/*.ts", "./src/*.d.ts"] } diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 324822dd6d..1609e95789 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,25 @@ # @hyperlane-xyz/utils +## 3.8.0 + +### Minor Changes + +- 9681df08d: Add `WarpCore`, `Token`, and `TokenAmount` classes for interacting with Warp Route instances. + + _Breaking change_: The params to the `IHypTokenAdapter` `populateTransferRemoteTx` method have changed. `txValue` has been replaced with `interchainGas`. + +## 3.7.0 + +## 3.6.2 + +## 3.6.1 + +### Patch Changes + +- 3c298d064: Add isAddress function to check if string matches EVM, Cosmos, or Solana address formats +- df24eec8b: Fix for address utils falsy fallbacks +- 78e50e7da: addressToBytes32 changed to work for all protocol types + ## 3.6.0 ## 3.5.1 diff --git a/typescript/utils/index.ts b/typescript/utils/index.ts index cc859e192c..015268a3e9 100644 --- a/typescript/utils/index.ts +++ b/typescript/utils/index.ts @@ -110,12 +110,14 @@ export { AddressBytes32, CallData, ChainCaip2Id, + ChainId, Checkpoint, Domain, HexString, InterchainSecurityModuleType, MerkleProof, MessageStatus, + Numberish, ParsedLegacyMultisigIsmMetadata, ParsedMessage, ProtocolSmallestUnit, diff --git a/typescript/utils/package.json b/typescript/utils/package.json index fd356157bb..56ad3ed850 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -1,7 +1,7 @@ { "name": "@hyperlane-xyz/utils", "description": "General utilities and types for the Hyperlane network", - "version": "3.6.0", + "version": "3.8.0", "dependencies": { "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index a5a1a556a8..47852c4525 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -2,11 +2,14 @@ import { fromBech32, normalizeBech32, toBech32 } from '@cosmjs/encoding'; import { PublicKey } from '@solana/web3.js'; import { utils as ethersUtils } from 'ethers'; +import { isNullish } from './typeof'; import { Address, HexString, ProtocolType } from './types'; const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/; const SEALEVEL_ADDRESS_REGEX = /^[a-zA-Z0-9]{36,44}$/; +const HEX_BYTES32_REGEX = /^0x[a-fA-F0-9]{64}$/; + // https://github.com/cosmos/cosmos-sdk/blob/84c33215658131d87daf3c629e909e12ed9370fa/types/coin.go#L601C17-L601C44 const COSMOS_DENOM_PATTERN = `[a-zA-Z][a-zA-Z0-9]{2,127}`; // https://en.bitcoin.it/wiki/BIP_0173 @@ -65,7 +68,7 @@ function routeAddressUtil( ) { protocol ||= getAddressProtocolType(param); if (protocol && fns[protocol]) return fns[protocol]!(param); - else if (fallback) return fallback; + else if (!isNullish(fallback)) return fallback; else throw new Error(`Unsupported protocol ${protocol}`); } @@ -233,8 +236,7 @@ export function capitalizeAddress(address: Address) { else return address.toUpperCase(); } -// For EVM addresses only, kept for backwards compatibility and convenience -export function addressToBytes32(address: Address): string { +export function addressToBytes32Evm(address: Address): string { return ethersUtils .hexZeroPad(ethersUtils.hexStripZeros(address), 32) .toLowerCase(); @@ -246,7 +248,7 @@ export function bytes32ToAddress(bytes32: HexString): Address { } export function addressToBytesEvm(address: Address): Uint8Array { - const addrBytes32 = addressToBytes32(address); + const addrBytes32 = addressToBytes32Evm(address); return Buffer.from(strip0x(addrBytes32), 'hex'); } @@ -258,7 +260,10 @@ export function addressToBytesCosmos(address: Address): Uint8Array { return fromBech32(address).data; } -export function addressToBytes(address: Address, protocol?: ProtocolType) { +export function addressToBytes( + address: Address, + protocol?: ProtocolType, +): Uint8Array { return routeAddressUtil( { [ProtocolType.Ethereum]: addressToBytesEvm, @@ -280,6 +285,29 @@ export function addressToByteHexString( ); } +export function addressToBytes32( + address: Address, + protocol?: ProtocolType, +): string { + // If the address is already bytes32, just return, avoiding a regression + // where an already bytes32 address cannot be categorized as a protocol address. + if (HEX_BYTES32_REGEX.test(ensure0x(address))) return ensure0x(address); + + const bytes = addressToBytes(address, protocol); + return bytesToBytes32(bytes); +} + +export function bytesToBytes32(bytes: Uint8Array): string { + if (bytes.length > 32) { + throw new Error('bytes must be 32 bytes or less'); + } + // This 0x-prefixes the hex string + return ethersUtils.hexZeroPad( + ensure0x(Buffer.from(bytes).toString('hex')), + 32, + ); +} + export function bytesToAddressEvm(bytes: Uint8Array): Address { return bytes32ToAddress(Buffer.from(bytes).toString('hex')); } diff --git a/typescript/utils/src/typeof.ts b/typescript/utils/src/typeof.ts index eb08c4f15a..a204981dbb 100644 --- a/typescript/utils/src/typeof.ts +++ b/typescript/utils/src/typeof.ts @@ -1,6 +1,7 @@ -export function isNullish(val: T) { - if (val === null || val === undefined) return true; - else return false; +export function isNullish( + val: T, +): val is T extends null | undefined ? T : never { + return val === null || val === undefined; } export function isNumeric(value: string | number) { diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index b29c91fd28..a28e97acdc 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -17,11 +17,13 @@ export const ProtocolSmallestUnit = { /********* BASIC TYPES *********/ export type Domain = number; +export type ChainId = string | number; export type Address = string; export type AddressBytes32 = string; export type ChainCaip2Id = `${string}:${string}`; // e.g. ethereum:1 or sealevel:1399811149 export type TokenCaip19Id = `${string}:${string}/${string}:${string}`; // e.g. ethereum:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f export type HexString = string; +export type Numberish = number | string | bigint; // copied from node_modules/@ethersproject/bytes/src.ts/index.ts export type SignatureLike = diff --git a/typescript/utils/src/validation.ts b/typescript/utils/src/validation.ts index d5e85c43f3..1b3dd22899 100644 --- a/typescript/utils/src/validation.ts +++ b/typescript/utils/src/validation.ts @@ -1,4 +1,7 @@ -export function assert(predicate: any, errorMessage?: string) { +export function assert( + predicate: T, + errorMessage?: string, +): asserts predicate is NonNullable { if (!predicate) { throw new Error(errorMessage ?? 'Error'); } diff --git a/vectors/message.json b/vectors/message.json index 6a21a198c9..16644e9360 100644 --- a/vectors/message.json +++ b/vectors/message.json @@ -1 +1 @@ -[{"body":[18,52],"destination":2000,"id":"0x545b9ae16e93875efda786a09f3b78221d7f568f46a445fe4cd4a1e38096c576","nonce":0,"origin":1000,"recipient":"0x0000000000000000000000002222222222222222222222222222222222222222","sender":"0x0000000000000000000000001111111111111111111111111111111111111111","version":0}] \ No newline at end of file +[{"body":[18,52],"destination":2000,"id":"0xf8a66f8aadee751d842616fee0ed14a3ad6da1e13564920364ee0ad35a02703f","nonce":0,"origin":1000,"recipient":"0x0000000000000000000000002222222222222222222222222222222222222222","sender":"0x0000000000000000000000001111111111111111111111111111111111111111","version":3}] \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 15bd3bdd65..4c242d4937 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,6 +12,16 @@ __metadata: languageName: node linkType: hard +"@ampproject/remapping@npm:^2.2.0": + version: 2.2.1 + resolution: "@ampproject/remapping@npm:2.2.1" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.0" + "@jridgewell/trace-mapping": "npm:^0.3.9" + checksum: e15fecbf3b54c988c8b4fdea8ef514ab482537e8a080b2978cc4b47ccca7140577ca7b65ad3322dcce65bc73ee6e5b90cbfe0bbd8c766dad04d5c62ec9634c42 + languageName: node + linkType: hard + "@arbitrum/sdk@npm:^3.0.0": version: 3.0.0 resolution: "@arbitrum/sdk@npm:3.0.0" @@ -2428,6 +2438,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" + dependencies: + "@babel/highlight": "npm:^7.23.4" + chalk: "npm:^2.4.2" + checksum: 44e58529c9d93083288dc9e649c553c5ba997475a7b0758cc3ddc4d77b8a7d985dbe78cc39c9bbc61f26d50af6da1ddf0a3427eae8cc222a9370619b671ed8f5 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.22.13": version: 7.22.13 resolution: "@babel/code-frame@npm:7.22.13" @@ -2438,6 +2458,36 @@ __metadata: languageName: node linkType: hard +"@babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 088f14f646ecbddd5ef89f120a60a1b3389a50a9705d44603dca77662707d0175a5e0e0da3943c3298f1907a4ab871468656fbbf74bb7842cd8b0686b2c19736 + languageName: node + linkType: hard + +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3": + version: 7.23.9 + resolution: "@babel/core@npm:7.23.9" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-compilation-targets": "npm:^7.23.6" + "@babel/helper-module-transforms": "npm:^7.23.3" + "@babel/helpers": "npm:^7.23.9" + "@babel/parser": "npm:^7.23.9" + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 268cdbb86bef1b8ea5b1300f2f325e56a1740a5051360cb228ffeaa0f80282b6674f3a2b4d6466adb0691183759b88d4c37b4a4f77232c84a49ed771c84cdc27 + languageName: node + linkType: hard + "@babel/generator@npm:7.17.7": version: 7.17.7 resolution: "@babel/generator@npm:7.17.7" @@ -2461,6 +2511,31 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.23.6, @babel/generator@npm:^7.7.2": + version: 7.23.6 + resolution: "@babel/generator@npm:7.23.6" + dependencies: + "@babel/types": "npm:^7.23.6" + "@jridgewell/gen-mapping": "npm:^0.3.2" + "@jridgewell/trace-mapping": "npm:^0.3.17" + jsesc: "npm:^2.5.1" + checksum: 864090d5122c0aa3074471fd7b79d8a880c1468480cbd28925020a3dcc7eb6e98bedcdb38983df299c12b44b166e30915b8085a7bc126e68fa7e2aadc7bd1ac5 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" + dependencies: + "@babel/compat-data": "npm:^7.23.5" + "@babel/helper-validator-option": "npm:^7.23.5" + browserslist: "npm:^4.22.2" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 05595cd73087ddcd81b82d2f3297aac0c0422858dfdded43d304786cf680ec33e846e2317e6992d2c964ee61d93945cbf1fa8ec80b55aee5bfb159227fb02cb9 + languageName: node + linkType: hard + "@babel/helper-environment-visitor@npm:^7.22.20": version: 7.22.20 resolution: "@babel/helper-environment-visitor@npm:7.22.20" @@ -2487,6 +2562,46 @@ __metadata: languageName: node linkType: hard +"@babel/helper-module-imports@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" + dependencies: + "@babel/types": "npm:^7.22.15" + checksum: 5ecf9345a73b80c28677cfbe674b9f567bb0d079e37dcba9055e36cb337db24ae71992a58e1affa9d14a60d3c69907d30fe1f80aea105184501750a58d15c81c + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-module-imports": "npm:^7.22.15" + "@babel/helper-simple-access": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/helper-validator-identifier": "npm:^7.22.20" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 583fa580f8e50e6f45c4f46aa76a8e49c2528deb84e25f634d66461b9a0e2420e13979b0a607b67aef67eaf8db8668eb9edc038b4514b16e3879fe09e8fd294b + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.8.0": + version: 7.22.5 + resolution: "@babel/helper-plugin-utils@npm:7.22.5" + checksum: ab220db218089a2aadd0582f5833fd17fa300245999f5f8784b10f5a75267c4e808592284a29438a0da365e702f05acb369f99e1c915c02f9f9210ec60eab8ea + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-simple-access@npm:7.22.5" + dependencies: + "@babel/types": "npm:^7.22.5" + checksum: 7d5430eecf880937c27d1aed14245003bd1c7383ae07d652b3932f450f60bfcf8f2c1270c593ab063add185108d26198c69d1aca0e6fb7c6fdada4bcf72ab5b7 + languageName: node + linkType: hard + "@babel/helper-split-export-declaration@npm:^7.22.6": version: 7.22.6 resolution: "@babel/helper-split-export-declaration@npm:7.22.6" @@ -2503,6 +2618,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: c352082474a2ee1d2b812bd116a56b2e8b38065df9678a32a535f151ec6f58e54633cc778778374f10544b930703cca6ddf998803888a636afa27e2658068a9c + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.16.7": version: 7.16.7 resolution: "@babel/helper-validator-identifier@npm:7.16.7" @@ -2524,6 +2646,24 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/helpers@npm:7.23.9" + dependencies: + "@babel/template": "npm:^7.23.9" + "@babel/traverse": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + checksum: dd56daac8bbd7ed174bb00fd185926fd449e591d9a00edaceb7ac6edbdd7a8db57e2cb365b4fafda382201752789ced2f7ae010f667eab0f198a4571cda4d2c5 + languageName: node + linkType: hard + "@babel/highlight@npm:^7.16.7": version: 7.17.12 resolution: "@babel/highlight@npm:7.17.12" @@ -2546,6 +2686,26 @@ __metadata: languageName: node linkType: hard +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.22.20" + chalk: "npm:^2.4.2" + js-tokens: "npm:^4.0.0" + checksum: 62fef9b5bcea7131df4626d009029b1ae85332042f4648a4ce6e740c3fd23112603c740c45575caec62f260c96b11054d3be5987f4981a5479793579c3aac71f + languageName: node + linkType: hard + +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/parser@npm:7.23.9" + bin: + parser: ./bin/babel-parser.js + checksum: 727a7a807100f6a26df859e2f009c4ddbd0d3363287b45daa50bd082ccd0d431d0c4d0e610a91f806e04a1918726cd0f5a0592c9b902a815337feed12e1cafd9 + languageName: node + linkType: hard + "@babel/parser@npm:^7.22.15": version: 7.22.16 resolution: "@babel/parser@npm:7.22.16" @@ -2573,6 +2733,169 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-syntax-async-generators@npm:^7.8.4": + version: 7.8.4 + resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 7ed1c1d9b9e5b64ef028ea5e755c0be2d4e5e4e3d6cf7df757b9a8c4cfa4193d268176d0f1f7fbecdda6fe722885c7fda681f480f3741d8a2d26854736f05367 + languageName: node + linkType: hard + +"@babel/plugin-syntax-bigint@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-bigint@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 3a10849d83e47aec50f367a9e56a6b22d662ddce643334b087f9828f4c3dd73bdc5909aaeabe123fed78515767f9ca43498a0e621c438d1cd2802d7fae3c9648 + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-properties@npm:^7.8.3": + version: 7.12.13 + resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.12.13" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 24f34b196d6342f28d4bad303612d7ff566ab0a013ce89e775d98d6f832969462e7235f3e7eaf17678a533d4be0ba45d3ae34ab4e5a9dcbda5d98d49e5efa2fc + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-meta@npm:^7.8.3": + version: 7.10.4 + resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 166ac1125d10b9c0c430e4156249a13858c0366d38844883d75d27389621ebe651115cb2ceb6dc011534d5055719fa1727b59f39e1ab3ca97820eef3dcab5b9b + languageName: node + linkType: hard + +"@babel/plugin-syntax-json-strings@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bf5aea1f3188c9a507e16efe030efb996853ca3cadd6512c51db7233cc58f3ac89ff8c6bdfb01d30843b161cfe7d321e1bf28da82f7ab8d7e6bc5464666f354a + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.7.2": + version: 7.23.3 + resolution: "@babel/plugin-syntax-jsx@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 89037694314a74e7f0e7a9c8d3793af5bf6b23d80950c29b360db1c66859d67f60711ea437e70ad6b5b4b29affe17eababda841b6c01107c2b638e0493bafb4e + languageName: node + linkType: hard + +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.8.3": + version: 7.10.4 + resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: aff33577037e34e515911255cdbb1fd39efee33658aa00b8a5fd3a4b903585112d037cce1cc9e4632f0487dc554486106b79ccd5ea63a2e00df4363f6d4ff886 + languageName: node + linkType: hard + +"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 87aca4918916020d1fedba54c0e232de408df2644a425d153be368313fdde40d96088feed6c4e5ab72aac89be5d07fef2ddf329a15109c5eb65df006bf2580d1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-numeric-separator@npm:^7.8.3": + version: 7.10.4 + resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 01ec5547bd0497f76cc903ff4d6b02abc8c05f301c88d2622b6d834e33a5651aa7c7a3d80d8d57656a4588f7276eba357f6b7e006482f5b564b7a6488de493a1 + languageName: node + linkType: hard + +"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: fddcf581a57f77e80eb6b981b10658421bc321ba5f0a5b754118c6a92a5448f12a0c336f77b8abf734841e102e5126d69110a306eadb03ca3e1547cab31f5cbf + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 910d90e72bc90ea1ce698e89c1027fed8845212d5ab588e35ef91f13b93143845f94e2539d831dc8d8ededc14ec02f04f7bd6a8179edd43a326c784e7ed7f0b9 + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: eef94d53a1453361553c1f98b68d17782861a04a392840341bc91780838dd4e695209c783631cf0de14c635758beafb6a3a65399846ffa4386bff90639347f30 + languageName: node + linkType: hard + +"@babel/plugin-syntax-top-level-await@npm:^7.8.3": + version: 7.14.5 + resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: bbd1a56b095be7820029b209677b194db9b1d26691fe999856462e66b25b281f031f3dfd91b1619e9dcf95bebe336211833b854d0fb8780d618e35667c2d0d7e + languageName: node + linkType: hard + +"@babel/plugin-syntax-typescript@npm:^7.7.2": + version: 7.23.3 + resolution: "@babel/plugin-syntax-typescript@npm:7.23.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.22.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: abfad3a19290d258b028e285a1f34c9b8a0cbe46ef79eafed4ed7ffce11b5d0720b5e536c82f91cbd8442cde35a3dd8e861fa70366d87ff06fdc0d4756e30876 + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.12.5": + version: 7.23.9 + resolution: "@babel/runtime@npm:7.23.9" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 9a520fe1bf72249f7dd60ff726434251858de15cccfca7aa831bd19d0d3fb17702e116ead82724659b8da3844977e5e13de2bae01eb8a798f2823a669f122be6 + languageName: node + linkType: hard + "@babel/runtime@npm:^7.17.2": version: 7.22.5 resolution: "@babel/runtime@npm:7.22.5" @@ -2620,6 +2943,17 @@ __metadata: languageName: node linkType: hard +"@babel/template@npm:^7.23.9, @babel/template@npm:^7.3.3": + version: 7.23.9 + resolution: "@babel/template@npm:7.23.9" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + checksum: 1b011ba9354dc2e646561d54b6862e0df51760e6179faadd79be05825b0b6da04911e4e192df943f1766748da3037fd8493615b38707f7cadb0cf0c96601c170 + languageName: node + linkType: hard + "@babel/traverse@npm:7.23.2": version: 7.23.2 resolution: "@babel/traverse@npm:7.23.2" @@ -2638,6 +2972,24 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.23.9": + version: 7.23.9 + resolution: "@babel/traverse@npm:7.23.9" + dependencies: + "@babel/code-frame": "npm:^7.23.5" + "@babel/generator": "npm:^7.23.6" + "@babel/helper-environment-visitor": "npm:^7.22.20" + "@babel/helper-function-name": "npm:^7.23.0" + "@babel/helper-hoist-variables": "npm:^7.22.5" + "@babel/helper-split-export-declaration": "npm:^7.22.6" + "@babel/parser": "npm:^7.23.9" + "@babel/types": "npm:^7.23.9" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: e2bb845f7f229feb7c338f7e150f5f1abc5395dcd3a6a47f63a25242ec3ec6b165f04a6df7d4849468547faee34eb3cf52487eb0bd867a7d3c42fec2a648266f + languageName: node + linkType: hard + "@babel/types@npm:7.17.0": version: 7.17.0 resolution: "@babel/types@npm:7.17.0" @@ -2648,6 +3000,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.23.6, @babel/types@npm:^7.23.9, @babel/types@npm:^7.3.3": + version: 7.23.9 + resolution: "@babel/types@npm:7.23.9" + dependencies: + "@babel/helper-string-parser": "npm:^7.23.4" + "@babel/helper-validator-identifier": "npm:^7.22.20" + to-fast-properties: "npm:^2.0.0" + checksum: bed9634e5fd0f9dc63c84cfa83316c4cb617192db9fedfea464fca743affe93736d7bf2ebf418ee8358751a9d388e303af87a0c050cb5d87d5870c1b0154f6cb + languageName: node + linkType: hard + "@babel/types@npm:^7.17.0, @babel/types@npm:^7.8.3": version: 7.18.4 resolution: "@babel/types@npm:7.18.4" @@ -2691,6 +3054,24 @@ __metadata: languageName: node linkType: hard +"@bcoe/v8-coverage@npm:^0.2.3": + version: 0.2.3 + resolution: "@bcoe/v8-coverage@npm:0.2.3" + checksum: 1a1f0e356a3bb30b5f1ced6f79c413e6ebacf130421f15fac5fcd8be5ddf98aedb4404d7f5624e3285b700e041f9ef938321f3ca4d359d5b716f96afa120d88d + languageName: node + linkType: hard + +"@chainlink/ccip-read-server@npm:^0.2.1": + version: 0.2.1 + resolution: "@chainlink/ccip-read-server@npm:0.2.1" + dependencies: + cors: "npm:^2.8.5" + ethers: "npm:^5.3.1" + express: "npm:^4.17.1" + checksum: a0a45c7e7f7c612e96caa8bfd1b9cf6b45db569eb7afe5c69f7f09060fcfbd88517d7e32cfe25561b3b0f6b6063d7ee789f562132c6951bb6d09abb080a4cc96 + languageName: node + linkType: hard + "@chainsafe/as-sha256@npm:^0.3.1": version: 0.3.1 resolution: "@chainsafe/as-sha256@npm:0.3.1" @@ -2973,6 +3354,23 @@ __metadata: languageName: node linkType: hard +"@coinbase/wallet-sdk@npm:^3.6.6": + version: 3.9.1 + resolution: "@coinbase/wallet-sdk@npm:3.9.1" + dependencies: + bn.js: "npm:^5.2.1" + buffer: "npm:^6.0.3" + clsx: "npm:^1.2.1" + eth-block-tracker: "npm:^7.1.0" + eth-json-rpc-filters: "npm:^6.0.0" + eventemitter3: "npm:^5.0.1" + keccak: "npm:^3.0.3" + preact: "npm:^10.16.0" + sha.js: "npm:^2.4.11" + checksum: afa2b01ba69edb96c5d8d0b34e68eb9ab1ef99c20f0a6db81c0b42f6f234c4dec538b978e6dc69d9dd37539e6d7290068e3aae960029afee78995bd515bc8077 + languageName: node + linkType: hard + "@confio/ics23@npm:^0.6.8": version: 0.6.8 resolution: "@confio/ics23@npm:0.6.8" @@ -3149,6 +3547,13 @@ __metadata: languageName: node linkType: hard +"@emotion/hash@npm:^0.8.0": + version: 0.8.0 + resolution: "@emotion/hash@npm:0.8.0" + checksum: 4b35d88a97e67275c1d990c96d3b0450451d089d1508619488fc0acb882cb1ac91e93246d471346ebd1b5402215941ef4162efe5b51534859b39d8b3a0e3ffaa + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -3397,6 +3802,16 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/common@npm:^3.2.0": + version: 3.2.0 + resolution: "@ethereumjs/common@npm:3.2.0" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + crc-32: "npm:^1.2.0" + checksum: b3f612406b6bcefaf9117ceb42eff58d311e2b50205e3d55b4c793d803de517efbc84075e058dc0e2ec27a2bff11dfc279dda1fa2b249ed6ab3973be045898f4 + languageName: node + linkType: hard + "@ethereumjs/ethash@npm:^1.1.0": version: 1.1.0 resolution: "@ethereumjs/ethash@npm:1.1.0" @@ -3410,6 +3825,15 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/rlp@npm:^4.0.1": + version: 4.0.1 + resolution: "@ethereumjs/rlp@npm:4.0.1" + bin: + rlp: bin/rlp + checksum: bfdffd634ce72f3b17e3d085d071f2fe7ce9680aebdf10713d74b30afd80ef882d17f19ff7175fcb049431a56e800bd3558d3b028bd0d82341927edb303ab450 + languageName: node + linkType: hard + "@ethereumjs/tx@npm:3.3.2": version: 3.3.2 resolution: "@ethereumjs/tx@npm:3.3.2" @@ -3440,6 +3864,29 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/tx@npm:^4.1.2, @ethereumjs/tx@npm:^4.2.0": + version: 4.2.0 + resolution: "@ethereumjs/tx@npm:4.2.0" + dependencies: + "@ethereumjs/common": "npm:^3.2.0" + "@ethereumjs/rlp": "npm:^4.0.1" + "@ethereumjs/util": "npm:^8.1.0" + ethereum-cryptography: "npm:^2.0.0" + checksum: cbd2ffc3ef76ca5416d58f2f694858d9fcac946e6a107fef44cf3f308a7c9fcc996a6847868609354d72d5b356faee68408e9d5601c4c4f7dad8e18cb2c24a95 + languageName: node + linkType: hard + +"@ethereumjs/util@npm:^8.1.0": + version: 8.1.0 + resolution: "@ethereumjs/util@npm:8.1.0" + dependencies: + "@ethereumjs/rlp": "npm:^4.0.1" + ethereum-cryptography: "npm:^2.0.0" + micro-ftch: "npm:^0.3.1" + checksum: cc35338932e49b15e54ca6e548b32a1f48eed7d7e1d34ee743e4d3600dd616668bd50f70139e86c5c35f55aac35fba3b6cc4e6f679cf650aeba66bf93016200c + languageName: node + linkType: hard + "@ethereumjs/vm@npm:5.6.0": version: 5.6.0 resolution: "@ethereumjs/vm@npm:5.6.0" @@ -4206,6 +4653,19 @@ __metadata: languageName: node linkType: hard +"@headlessui/react@npm:^1.7.17": + version: 1.7.18 + resolution: "@headlessui/react@npm:1.7.18" + dependencies: + "@tanstack/react-virtual": "npm:^3.0.0-beta.60" + client-only: "npm:^0.0.1" + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + checksum: 9615f7b709842f2e56b3ef1a8f1a77faed7982e86af52f057a5c2a9950d073672073a6c09829f0c26ed7287e298f5e2345f3898dbc2cc13318af10598ceb4bbc + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.10": version: 0.11.10 resolution: "@humanwhocodes/config-array@npm:0.11.10" @@ -4231,12 +4691,31 @@ __metadata: languageName: node linkType: hard +"@hyperlane-xyz/ccip-server@workspace:typescript/ccip-server": + version: 0.0.0-use.local + resolution: "@hyperlane-xyz/ccip-server@workspace:typescript/ccip-server" + dependencies: + "@chainlink/ccip-read-server": "npm:^0.2.1" + "@jest/globals": "npm:^29.7.0" + "@types/node": "npm:^16.9.1" + dotenv-flow: "npm:^4.1.0" + ethers: "npm:5.7.2" + hyperlane-explorer: "https://github.com/hyperlane-xyz/hyperlane-explorer.git" + jest: "npm:^29.7.0" + nodemon: "npm:^3.0.3" + prettier: "npm:^2.8.8" + ts-jest: "npm:^29.1.2" + ts-node: "npm:^10.8.0" + typescript: "npm:5.1.6" + languageName: unknown + linkType: soft + "@hyperlane-xyz/cli@workspace:typescript/cli": version: 0.0.0-use.local resolution: "@hyperlane-xyz/cli@workspace:typescript/cli" dependencies: - "@hyperlane-xyz/sdk": "npm:3.6.0" - "@hyperlane-xyz/utils": "npm:3.6.0" + "@hyperlane-xyz/sdk": "npm:3.8.0" + "@hyperlane-xyz/utils": "npm:3.8.0" "@inquirer/prompts": "npm:^3.0.0" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -4249,6 +4728,7 @@ __metadata: eslint: "npm:^8.43.0" eslint-config-prettier: "npm:^8.8.0" ethers: "npm:^5.7.2" + latest-version: "npm:^8.0.0" mocha: "npm:^10.2.0" prettier: "npm:^2.8.8" terminal-link: "npm:^3.0.0" @@ -4261,12 +4741,28 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:3.6.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:3.7.0": + version: 3.7.0 + resolution: "@hyperlane-xyz/core@npm:3.7.0" + dependencies: + "@eth-optimism/contracts": "npm:^0.6.0" + "@hyperlane-xyz/utils": "npm:3.7.0" + "@openzeppelin/contracts": "npm:^4.9.3" + "@openzeppelin/contracts-upgradeable": "npm:^v4.9.3" + peerDependencies: + "@ethersproject/abi": "*" + "@ethersproject/providers": "*" + "@types/sinon-chai": "*" + checksum: efa01d943dd5b67830bb7244291c8ba9849472e804dff589463de76d3c03e56bc8d62454b575a6621aa1b8b53cc0d1d3b752a83d34f4b328ecd85e1ff23230d5 + languageName: node + linkType: hard + +"@hyperlane-xyz/core@npm:3.8.0, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@eth-optimism/contracts": "npm:^0.6.0" - "@hyperlane-xyz/utils": "npm:3.6.0" + "@hyperlane-xyz/utils": "npm:3.8.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.1" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts": "npm:^4.9.3" @@ -4293,12 +4789,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@npm:3.6.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:3.8.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: - "@hyperlane-xyz/core": "npm:3.6.0" - "@hyperlane-xyz/sdk": "npm:3.6.0" + "@hyperlane-xyz/core": "npm:3.8.0" + "@hyperlane-xyz/sdk": "npm:3.8.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.1" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -4343,9 +4839,9 @@ __metadata: "@ethersproject/experimental": "npm:^5.7.0" "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" - "@hyperlane-xyz/helloworld": "npm:3.6.0" - "@hyperlane-xyz/sdk": "npm:3.6.0" - "@hyperlane-xyz/utils": "npm:3.6.0" + "@hyperlane-xyz/helloworld": "npm:3.8.0" + "@hyperlane-xyz/sdk": "npm:3.8.0" + "@hyperlane-xyz/utils": "npm:3.8.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.1" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -4353,6 +4849,7 @@ __metadata: "@safe-global/protocol-kit": "npm:^1.2.0" "@solana/web3.js": "npm:^1.78.0" "@types/chai": "npm:^4.2.21" + "@types/json-stable-stringify": "npm:^1.0.36" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^16.9.1" "@types/prompts": "npm:^2.0.14" @@ -4365,6 +4862,7 @@ __metadata: ethereum-waffle: "npm:^4.0.10" ethers: "npm:^5.7.2" hardhat: "npm:^2.19.0" + json-stable-stringify: "npm:^1.1.1" mocha: "npm:^10.2.0" prettier: "npm:^2.8.8" prom-client: "npm:^14.0.1" @@ -4393,35 +4891,63 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/sdk@npm:3.6.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": - version: 0.0.0-use.local - resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" +"@hyperlane-xyz/sdk@npm:3.7.0": + version: 3.7.0 + resolution: "@hyperlane-xyz/sdk@npm:3.7.0" dependencies: "@cosmjs/cosmwasm-stargate": "npm:^0.31.3" "@cosmjs/stargate": "npm:^0.31.3" - "@hyperlane-xyz/core": "npm:3.6.0" - "@hyperlane-xyz/utils": "npm:3.6.0" - "@nomiclabs/hardhat-ethers": "npm:^2.2.1" - "@nomiclabs/hardhat-waffle": "npm:^2.0.6" + "@hyperlane-xyz/core": "npm:3.7.0" + "@hyperlane-xyz/utils": "npm:3.7.0" "@solana/spl-token": "npm:^0.3.8" "@solana/web3.js": "npm:^1.78.0" "@types/coingecko-api": "npm:^1.0.10" "@types/debug": "npm:^4.1.7" - "@types/mocha": "npm:^10.0.1" - "@types/node": "npm:^16.9.1" - "@types/sinon": "npm:^17.0.1" - "@types/sinon-chai": "npm:^3.2.12" - "@types/ws": "npm:^8.5.5" "@wagmi/chains": "npm:^1.8.0" bignumber.js: "npm:^9.1.1" - chai: "npm:^4.3.6" coingecko-api: "npm:^1.0.10" cosmjs-types: "npm:^0.9.0" cross-fetch: "npm:^3.1.5" debug: "npm:^4.3.4" - dotenv: "npm:^10.0.0" - eslint: "npm:^8.43.0" - ethereum-waffle: "npm:^4.0.10" + ethers: "npm:^5.7.2" + viem: "npm:^1.20.0" + zod: "npm:^3.21.2" + peerDependencies: + "@ethersproject/abi": "*" + "@ethersproject/providers": "*" + checksum: b124a42f34502c4dad4127723d345158f592056d7e60e17d87c84bf81664ead20232ffaff66e6c21968dfd5693ba5122910fbcaa6b7db5b05fdd5d2051592835 + languageName: node + linkType: hard + +"@hyperlane-xyz/sdk@npm:3.8.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": + version: 0.0.0-use.local + resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" + dependencies: + "@cosmjs/cosmwasm-stargate": "npm:^0.31.3" + "@cosmjs/stargate": "npm:^0.31.3" + "@hyperlane-xyz/core": "npm:3.8.0" + "@hyperlane-xyz/utils": "npm:3.8.0" + "@nomiclabs/hardhat-ethers": "npm:^2.2.1" + "@nomiclabs/hardhat-waffle": "npm:^2.0.6" + "@solana/spl-token": "npm:^0.3.8" + "@solana/web3.js": "npm:^1.78.0" + "@types/coingecko-api": "npm:^1.0.10" + "@types/debug": "npm:^4.1.7" + "@types/mocha": "npm:^10.0.1" + "@types/node": "npm:^16.9.1" + "@types/sinon": "npm:^17.0.1" + "@types/sinon-chai": "npm:^3.2.12" + "@types/ws": "npm:^8.5.5" + "@wagmi/chains": "npm:^1.8.0" + bignumber.js: "npm:^9.1.1" + chai: "npm:^4.3.6" + coingecko-api: "npm:^1.0.10" + cosmjs-types: "npm:^0.9.0" + cross-fetch: "npm:^3.1.5" + debug: "npm:^4.3.4" + dotenv: "npm:^10.0.0" + eslint: "npm:^8.43.0" + ethereum-waffle: "npm:^4.0.10" ethers: "npm:^5.7.2" hardhat: "npm:^2.19.0" mocha: "npm:^10.2.0" @@ -4430,6 +4956,7 @@ __metadata: ts-node: "npm:^10.8.0" typescript: "npm:5.1.6" viem: "npm:^1.20.0" + yaml: "npm:^2.3.1" zod: "npm:^3.21.2" peerDependencies: "@ethersproject/abi": "*" @@ -4437,7 +4964,19 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@npm:3.6.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:3.7.0": + version: 3.7.0 + resolution: "@hyperlane-xyz/utils@npm:3.7.0" + dependencies: + "@cosmjs/encoding": "npm:^0.31.3" + "@solana/web3.js": "npm:^1.78.0" + bignumber.js: "npm:^9.1.1" + ethers: "npm:^5.7.2" + checksum: c76f36913c572702b9dfe22fd868db6fed01c0da9485319e33e8d00a6b8a1bfdcecb5f61c8a3fd8ccbef0b36809e8055db62d75d0c6759d5e079ee330586bcd1 + languageName: node + linkType: hard + +"@hyperlane-xyz/utils@npm:3.8.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: @@ -4453,6 +4992,17 @@ __metadata: languageName: unknown linkType: soft +"@hyperlane-xyz/widgets@npm:3.7.0": + version: 3.7.0 + resolution: "@hyperlane-xyz/widgets@npm:3.7.0" + peerDependencies: + "@hyperlane-xyz/sdk": ^3.1 + react: ^18 + react-dom: ^18 + checksum: 5ddf7a7a5f599f1b340bbe4e981e0cb10dd8930cc616c566abb58210658acd2826386afd18e8789f8bb62fc19d14b69db87433adcc97c38ebda2c322cc7865a2 + languageName: node + linkType: hard + "@inquirer/checkbox@npm:^1.3.5": version: 1.3.5 resolution: "@inquirer/checkbox@npm:1.3.5" @@ -4593,7 +5143,264 @@ __metadata: languageName: node linkType: hard -"@jridgewell/gen-mapping@npm:^0.3.2": +"@ioredis/commands@npm:^1.1.1": + version: 1.2.0 + resolution: "@ioredis/commands@npm:1.2.0" + checksum: a8253c9539b7e5463d4a98e6aa5b1b863fb4a4978191ba9dc42ec2c0fb5179d8d1fe4a29096d5954f91ba9600d1bdc6c1d18b044eab36f645f267fd37d7c0906 + languageName: node + linkType: hard + +"@istanbuljs/load-nyc-config@npm:^1.0.0": + version: 1.1.0 + resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" + dependencies: + camelcase: "npm:^5.3.1" + find-up: "npm:^4.1.0" + get-package-type: "npm:^0.1.0" + js-yaml: "npm:^3.13.1" + resolve-from: "npm:^5.0.0" + checksum: b000a5acd8d4fe6e34e25c399c8bdbb5d3a202b4e10416e17bfc25e12bab90bb56d33db6089ae30569b52686f4b35ff28ef26e88e21e69821d2b85884bd055b8 + languageName: node + linkType: hard + +"@istanbuljs/schema@npm:^0.1.2": + version: 0.1.3 + resolution: "@istanbuljs/schema@npm:0.1.3" + checksum: a9b1e49acdf5efc2f5b2359f2df7f90c5c725f2656f16099e8b2cd3a000619ecca9fc48cf693ba789cf0fd989f6e0df6a22bc05574be4223ecdbb7997d04384b + languageName: node + linkType: hard + +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + slash: "npm:^3.0.0" + checksum: 4a80c750e8a31f344233cb9951dee9b77bf6b89377cb131f8b3cde07ff218f504370133a5963f6a786af4d2ce7f85642db206ff7a15f99fe58df4c38ac04899e + languageName: node + linkType: hard + +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/reporters": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-changed-files: "npm:^29.7.0" + jest-config: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-resolve-dependencies: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-ansi: "npm:^6.0.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: ab6ac2e562d083faac7d8152ec1cc4eccc80f62e9579b69ed40aedf7211a6b2d57024a6cd53c4e35fd051c39a236e86257d1d99ebdb122291969a0a04563b51e + languageName: node + linkType: hard + +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" + dependencies: + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + checksum: 90b5844a9a9d8097f2cf107b1b5e57007c552f64315da8c1f51217eeb0a9664889d3f145cdf8acf23a84f4d8309a6675e27d5b059659a004db0ea9546d1c81a8 + languageName: node + linkType: hard + +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" + dependencies: + jest-get-type: "npm:^29.6.3" + checksum: ef8d379778ef574a17bde2801a6f4469f8022a46a5f9e385191dc73bb1fc318996beaed4513fbd7055c2847227a1bed2469977821866534593a6e52a281499ee + languageName: node + linkType: hard + +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" + dependencies: + expect: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + checksum: fea6c3317a8da5c840429d90bfe49d928e89c9e89fceee2149b93a11b7e9c73d2f6e4d7cdf647163da938fc4e2169e4490be6bae64952902bc7a701033fd4880 + languageName: node + linkType: hard + +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@sinonjs/fake-timers": "npm:^10.0.2" + "@types/node": "npm:*" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 9b394e04ffc46f91725ecfdff34c4e043eb7a16e1d78964094c9db3fde0b1c8803e45943a980e8c740d0a3d45661906de1416ca5891a538b0660481a3a828c27 + languageName: node + linkType: hard + +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + jest-mock: "npm:^29.7.0" + checksum: 97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 + languageName: node + linkType: hard + +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" + dependencies: + "@bcoe/v8-coverage": "npm:^0.2.3" + "@jest/console": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + collect-v8-coverage: "npm:^1.0.0" + exit: "npm:^0.1.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + istanbul-lib-coverage: "npm:^3.0.0" + istanbul-lib-instrument: "npm:^6.0.0" + istanbul-lib-report: "npm:^3.0.0" + istanbul-lib-source-maps: "npm:^4.0.0" + istanbul-reports: "npm:^3.1.3" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + slash: "npm:^3.0.0" + string-length: "npm:^4.0.1" + strip-ansi: "npm:^6.0.0" + v8-to-istanbul: "npm:^9.0.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + checksum: a17d1644b26dea14445cedd45567f4ba7834f980be2ef74447204e14238f121b50d8b858fde648083d2cd8f305f81ba434ba49e37a5f4237a6f2a61180cc73dc + languageName: node + linkType: hard + +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": "npm:^0.27.8" + checksum: 910040425f0fc93cd13e68c750b7885590b8839066dfa0cd78e7def07bbb708ad869381f725945d66f2284de5663bbecf63e8fdd856e2ae6e261ba30b1687e93 + languageName: node + linkType: hard + +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.18" + callsites: "npm:^3.0.0" + graceful-fs: "npm:^4.2.9" + checksum: bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb + languageName: node + linkType: hard + +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + collect-v8-coverage: "npm:^1.0.0" + checksum: c073ab7dfe3c562bff2b8fee6cc724ccc20aa96bcd8ab48ccb2aa309b4c0c1923a9e703cea386bd6ae9b71133e92810475bb9c7c22328fc63f797ad3324ed189 + languageName: node + linkType: hard + +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" + dependencies: + "@jest/test-result": "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + slash: "npm:^3.0.0" + checksum: 4420c26a0baa7035c5419b0892ff8ffe9a41b1583ec54a10db3037cd46a7e29dd3d7202f8aa9d376e9e53be5f8b1bc0d16e1de6880a6d319b033b01dc4c8f639 + languageName: node + linkType: hard + +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@jest/types": "npm:^29.6.3" + "@jridgewell/trace-mapping": "npm:^0.3.18" + babel-plugin-istanbul: "npm:^6.1.1" + chalk: "npm:^4.0.0" + convert-source-map: "npm:^2.0.0" + fast-json-stable-stringify: "npm:^2.1.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + pirates: "npm:^4.0.4" + slash: "npm:^3.0.0" + write-file-atomic: "npm:^4.0.2" + checksum: 30f42293545ab037d5799c81d3e12515790bb58513d37f788ce32d53326d0d72ebf5b40f989e6896739aa50a5f77be44686e510966370d58511d5ad2637c68c1 + languageName: node + linkType: hard + +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" + dependencies: + "@jest/schemas": "npm:^29.6.3" + "@types/istanbul-lib-coverage": "npm:^2.0.0" + "@types/istanbul-reports": "npm:^3.0.0" + "@types/node": "npm:*" + "@types/yargs": "npm:^17.0.8" + chalk: "npm:^4.0.0" + checksum: f74bf512fd09bbe2433a2ad460b04668b7075235eea9a0c77d6a42222c10a79b9747dc2b2a623f140ed40d6865a2ed8f538f3cbb75169120ea863f29a7ed76cd + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.0, @jridgewell/gen-mapping@npm:^0.3.2": version: 0.3.3 resolution: "@jridgewell/gen-mapping@npm:0.3.3" dependencies: @@ -4618,6 +5425,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 97106439d750a409c22c8bff822d648f6a71f3aa9bc8e5129efdc36343cd3096ddc4eeb1c62d2fe48e9bdd4db37b05d4646a17114ecebd3bbcacfa2de51c3c1d + languageName: node + linkType: hard + "@jridgewell/set-array@npm:^1.0.1": version: 1.1.2 resolution: "@jridgewell/set-array@npm:1.1.2" @@ -4639,6 +5453,13 @@ __metadata: languageName: node linkType: hard +"@jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: 89960ac087781b961ad918978975bcdf2051cd1741880469783c42de64239703eab9db5230d776d8e6a09d73bb5e4cb964e07d93ee6e2e7aea5a7d726e865c09 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:0.3.9": version: 0.3.9 resolution: "@jridgewell/trace-mapping@npm:0.3.9" @@ -4649,6 +5470,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.18": + version: 0.3.22 + resolution: "@jridgewell/trace-mapping@npm:0.3.22" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 48d3e3db00dbecb211613649a1849876ba5544a3f41cf5e6b99ea1130272d6cf18591b5b67389bce20f1c871b4ede5900c3b6446a7aab6d0a3b2fe806a834db7 + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.17": version: 0.3.18 resolution: "@jridgewell/trace-mapping@npm:0.3.18" @@ -4669,6 +5500,13 @@ __metadata: languageName: node linkType: hard +"@ledgerhq/connect-kit-loader@npm:^1.0.1": + version: 1.1.8 + resolution: "@ledgerhq/connect-kit-loader@npm:1.1.8" + checksum: 8352e0932e9c4a7179eb0d162b0e7c92437de9216a2efa04bd274450edf205fdba3fe971ff41593c1ac851cfb3a7a474900b7e467da87f982c85f0c5edba45b5 + languageName: node + linkType: hard + "@ledgerhq/cryptoassets@npm:^5.27.2": version: 5.53.0 resolution: "@ledgerhq/cryptoassets@npm:5.53.0" @@ -4780,6 +5618,22 @@ __metadata: languageName: node linkType: hard +"@lit-labs/ssr-dom-shim@npm:^1.0.0, @lit-labs/ssr-dom-shim@npm:^1.1.0": + version: 1.2.0 + resolution: "@lit-labs/ssr-dom-shim@npm:1.2.0" + checksum: 33679defe08538ac6fb612854e7d32b4ea1e787cceba2c3373d26fd56baa9833881887da7bade3930a176ba518dc00bb42ce95d82ddb6af6b05b8fbe1fc3169f + languageName: node + linkType: hard + +"@lit/reactive-element@npm:^1.3.0, @lit/reactive-element@npm:^1.6.0": + version: 1.6.3 + resolution: "@lit/reactive-element@npm:1.6.3" + dependencies: + "@lit-labs/ssr-dom-shim": "npm:^1.0.0" + checksum: 664c899bb0b144590dc4faf83b358b1504810eac107778c3aeb384affc65a7ef4eda754944bcc34a57237db03dff145332406345ac24da19ca37cf4b3cb343d3 + languageName: node + linkType: hard + "@manypkg/find-root@npm:^1.1.0": version: 1.1.0 resolution: "@manypkg/find-root@npm:1.1.0" @@ -4806,6 +5660,17 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-provider@npm:^1.0.0": + version: 1.0.1 + resolution: "@metamask/eth-json-rpc-provider@npm:1.0.1" + dependencies: + "@metamask/json-rpc-engine": "npm:^7.0.0" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^5.0.1" + checksum: 4ed1a96afc32eb46f585ff54e16cb2aee2e7027dcf6a142d875b9c6248f15c9a00dd1df43035f2e64efbf01a96954040699d9d97e3b483c958f5b1d6c0fa6f50 + languageName: node + linkType: hard + "@metamask/eth-sig-util@npm:^4.0.0": version: 4.0.1 resolution: "@metamask/eth-sig-util@npm:4.0.1" @@ -4819,102 +5684,339 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": - version: 1.2.0 - resolution: "@noble/curves@npm:1.2.0" +"@metamask/jazzicon@https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6": + version: 2.1.0 + resolution: "@metamask/jazzicon@https://github.com/jmrossy/jazzicon.git#commit=7a8df28974b4e81129bfbe3cab76308b889032a6" dependencies: - "@noble/hashes": "npm:1.3.2" - checksum: 94e02e9571a9fd42a3263362451849d2f54405cb3ce9fa7c45bc6b9b36dcd7d1d20e2e1e14cfded24937a13d82f1e60eefc4d7a14982ce0bc219a9fc0f51d1f9 + mersenne-twister: "npm:^1.1.0" + checksum: 5e56251b375eade58294334783fb37a15e8fd48d792f6dc93f7247b8897541324f9cf2d3f1d9b1cffdac1d932a8bc48a89dee7cdbd6e4a312ca2ff85df90131b languageName: node linkType: hard -"@noble/curves@npm:^1.0.0": - version: 1.1.0 - resolution: "@noble/curves@npm:1.1.0" +"@metamask/json-rpc-engine@npm:^7.0.0": + version: 7.3.2 + resolution: "@metamask/json-rpc-engine@npm:7.3.2" dependencies: - "@noble/hashes": "npm:1.3.1" - checksum: 7028e3f19a4a2a601f9159e5423f51ae86ab231bed79a6e40649b063e1ed7f55f5da0475f1377bd2c5a8e5fc485af9ce0549ad89da6b983d6af48e5d0a2041ca + "@metamask/rpc-errors": "npm:^6.1.0" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^8.3.0" + checksum: d90e5fdf88461aa90af41ba0304729200afa8226ab8b73db348704a1f545e416c49281a1dfd58591dde769e1ab263080b26d5a0ab1be8362398639dc2d6354de languageName: node linkType: hard -"@noble/hashes@npm:1.0.0, @noble/hashes@npm:~1.0.0": - version: 1.0.0 - resolution: "@noble/hashes@npm:1.0.0" - checksum: cbe27966ed82daa0d6d984b168928719f139c8d96567cbc8b055b59a9fbd99ed9674d03e881cc93753b4f1cc94ffd466f116a68c0f25a8818bf220851b8e3bee +"@metamask/rpc-errors@npm:^6.1.0": + version: 6.2.0 + resolution: "@metamask/rpc-errors@npm:6.2.0" + dependencies: + "@metamask/utils": "npm:^8.3.0" + fast-safe-stringify: "npm:^2.0.6" + checksum: 25c05005f461a7db99d7ad6d2942cbdeb49337f47ce86823b8c3b8785d865584f19ca17abcb70c811fbc2bd394227f82fb7f0c5085b3b68e5d65bbe2f1c1dd9b languageName: node linkType: hard -"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:~1.3.0": - version: 1.3.1 - resolution: "@noble/hashes@npm:1.3.1" - checksum: 39474bab7e7813dbbfd8750476f48046d3004984e161fcd4333e40ca823f07b069010b35a20246e5b4ac20858e29913172a4d69720fd1e93620f7bedb70f9b72 +"@metamask/safe-event-emitter@npm:^2.0.0": + version: 2.0.0 + resolution: "@metamask/safe-event-emitter@npm:2.0.0" + checksum: 3e4f00c64aa1ddf9b9ae5c2337fb8cee359b6c481ded0ec21ef70610960c51cdcc4a9b569de334dcd7cb1fe445cafd298360907c1e211e244c5990b55246f350 languageName: node linkType: hard -"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.3.1": - version: 1.3.2 - resolution: "@noble/hashes@npm:1.3.2" - checksum: 685f59d2d44d88e738114b71011d343a9f7dce9dfb0a121f1489132f9247baa60bc985e5ec6f3213d114fbd1e1168e7294644e46cbd0ce2eba37994f28eeb51b +"@metamask/safe-event-emitter@npm:^3.0.0": + version: 3.0.0 + resolution: "@metamask/safe-event-emitter@npm:3.0.0" + checksum: 8dc58a76f9f75bf2405931465fc311c68043d851e6b8ebe9f82ae339073a08a83430dba9338f8e3adc4bfc8067607125074bcafa32baee3a5157f42343dc89e5 languageName: node linkType: hard -"@noble/hashes@npm:~1.3.2": - version: 1.3.3 - resolution: "@noble/hashes@npm:1.3.3" - checksum: 1025ddde4d24630e95c0818e63d2d54ee131b980fe113312d17ed7468bc18f54486ac86c907685759f8a7e13c2f9b9e83ec7b67d1cc20836f36b5e4a65bb102d +"@metamask/utils@npm:^5.0.1": + version: 5.0.2 + resolution: "@metamask/utils@npm:5.0.2" + dependencies: + "@ethereumjs/tx": "npm:^4.1.2" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + semver: "npm:^7.3.8" + superstruct: "npm:^1.0.3" + checksum: c0d3ee4c3144b557936ab01c1a64950c0f99782bd0cf5596c0fabe8fd224dba48ed3483c0ea954791fe2ee81064a445adb489df50c776bbbeb67b5b96e930115 languageName: node linkType: hard -"@noble/secp256k1@npm:1.5.5, @noble/secp256k1@npm:~1.5.2": - version: 1.5.5 - resolution: "@noble/secp256k1@npm:1.5.5" - checksum: 7d8478ca71bff32f48d197c1dd4fbbb4cebf9c29b46b40dc89a49c680e73bbddd9e3ce8362817d730748d219406161909caf1a01155fda0c947955a3a726c8e3 +"@metamask/utils@npm:^8.3.0": + version: 8.3.0 + resolution: "@metamask/utils@npm:8.3.0" + dependencies: + "@ethereumjs/tx": "npm:^4.2.0" + "@noble/hashes": "npm:^1.3.1" + "@scure/base": "npm:^1.1.3" + "@types/debug": "npm:^4.1.7" + debug: "npm:^4.3.4" + pony-cause: "npm:^2.1.10" + semver: "npm:^7.5.4" + superstruct: "npm:^1.0.3" + checksum: 728a4f6b3ab14223a487e8974a21b1917e470ff2c131afc0b8a6a6823839d6cf7454243ddb0ff695ceebede62feaf628f4d32b4b529bb5c044c6c95576a142ef languageName: node linkType: hard -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" +"@motionone/animation@npm:^10.15.1, @motionone/animation@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/animation@npm:10.17.0" dependencies: - "@nodelib/fs.stat": "npm:2.0.5" - run-parallel: "npm:^1.1.9" - checksum: 6ab2a9b8a1d67b067922c36f259e3b3dfd6b97b219c540877a4944549a4d49ea5ceba5663905ab5289682f1f3c15ff441d02f0447f620a42e1cb5e1937174d4b + "@motionone/easing": "npm:^10.17.0" + "@motionone/types": "npm:^10.17.0" + "@motionone/utils": "npm:^10.17.0" + tslib: "npm:^2.3.1" + checksum: 85ac8a36f33b7510cec239b12d90eec38a8f191158e2686c95c7ba237b17cac0e14b1533748fb27e10c18b8f4f4ea9798bc0a9286cf854852ab957d290a09ba9 languageName: node linkType: hard -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 +"@motionone/dom@npm:^10.16.2, @motionone/dom@npm:^10.16.4": + version: 10.17.0 + resolution: "@motionone/dom@npm:10.17.0" + dependencies: + "@motionone/animation": "npm:^10.17.0" + "@motionone/generators": "npm:^10.17.0" + "@motionone/types": "npm:^10.17.0" + "@motionone/utils": "npm:^10.17.0" + hey-listen: "npm:^1.0.8" + tslib: "npm:^2.3.1" + checksum: 7a9c5f01eacc084b95ac59c5f96de9c153b713d60cc99bc66b4c7619326f6b04d9acc14445ce0f3d9c7f65c8834a9543c59d3c90f7399de916aaaacbf38f4fb9 languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" +"@motionone/easing@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/easing@npm:10.17.0" dependencies: - "@nodelib/fs.scandir": "npm:2.1.5" - fastq: "npm:^1.6.0" - checksum: 40033e33e96e97d77fba5a238e4bba4487b8284678906a9f616b5579ddaf868a18874c0054a75402c9fbaaa033a25ceae093af58c9c30278e35c23c9479e79b0 + "@motionone/utils": "npm:^10.17.0" + tslib: "npm:^2.3.1" + checksum: 69f0fc4999a209801b128586cbb328937d9db1c091bed26762d30d035ecc5c01b0cbdce610c6550f609c0be78c1ad03c808e6c61f15fc52621f614449ce10a86 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-block@npm:5.0.2": - version: 5.0.2 - resolution: "@nomicfoundation/ethereumjs-block@npm:5.0.2" +"@motionone/generators@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/generators@npm:10.17.0" dependencies: - "@nomicfoundation/ethereumjs-common": "npm:4.0.2" - "@nomicfoundation/ethereumjs-rlp": "npm:5.0.2" - "@nomicfoundation/ethereumjs-trie": "npm:6.0.2" - "@nomicfoundation/ethereumjs-tx": "npm:5.0.2" - "@nomicfoundation/ethereumjs-util": "npm:9.0.2" - ethereum-cryptography: "npm:0.1.3" - ethers: "npm:^5.7.1" - checksum: e3d7c24aa10306ae26389ce464f71e36fe8d331706e942a626594afdd097451d3bda210238b1da843e582b347f4349115b53432076b0f9b37ba36e31818b12cb + "@motionone/types": "npm:^10.17.0" + "@motionone/utils": "npm:^10.17.0" + tslib: "npm:^2.3.1" + checksum: 06bd6c16cdb3c9fbb3a3fca05d6941d5e756b6ce151e2e9cc4f49c3b021fb54a5b970b01e3ddae9d77175e58b66cacb00927ee829f545fafd0bbdbdc838933aa languageName: node linkType: hard -"@nomicfoundation/ethereumjs-blockchain@npm:7.0.2": +"@motionone/svelte@npm:^10.16.2": + version: 10.16.4 + resolution: "@motionone/svelte@npm:10.16.4" + dependencies: + "@motionone/dom": "npm:^10.16.4" + tslib: "npm:^2.3.1" + checksum: 5ad532d4d9bb16a9f311487e6409fa7e1a66ec12f82e3c36434ab6dfe3cedc61b35dae6314cee4fba8dca463b8a259cafb83801a932b7ad5f4a6e45baaa581f4 + languageName: node + linkType: hard + +"@motionone/types@npm:^10.15.1, @motionone/types@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/types@npm:10.17.0" + checksum: 9449991493f6e7be59261e4fc1a3d4a5b842da8962084d742905f964b4d3aad5fd6c37bd95d5ab51f65fda7b0c389a332c5f7c7eccd6be54eb765ee2fc6e7070 + languageName: node + linkType: hard + +"@motionone/utils@npm:^10.15.1, @motionone/utils@npm:^10.17.0": + version: 10.17.0 + resolution: "@motionone/utils@npm:10.17.0" + dependencies: + "@motionone/types": "npm:^10.17.0" + hey-listen: "npm:^1.0.8" + tslib: "npm:^2.3.1" + checksum: 030359d37a6edebf29e0477050e638340f3756fc993a75b877e923b31ed4f3092a61f9d2323494f4b561ada1afc5ea774fb34022e7afbe2ec449c215585ab392 + languageName: node + linkType: hard + +"@motionone/vue@npm:^10.16.2": + version: 10.16.4 + resolution: "@motionone/vue@npm:10.16.4" + dependencies: + "@motionone/dom": "npm:^10.16.4" + tslib: "npm:^2.3.1" + checksum: 2400d31bbf5c3e02bc68f4b88d96d9c0672ba646bca0b6566e555cd7e8f14849a645f558f574e658fd90574a0b548b61712ae5edcee055c60288fd9382d711ea + languageName: node + linkType: hard + +"@next/env@npm:13.5.6": + version: 13.5.6 + resolution: "@next/env@npm:13.5.6" + checksum: c81bd6052db366407da701e4e431becbc80ef36a88bec7883b0266cdfeb45a7da959d37c38e1a816006cd2da287e5ff5b928bdb71025e3d4aa59e07dea3edd59 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-darwin-arm64@npm:13.5.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-darwin-x64@npm:13.5.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-arm64-gnu@npm:13.5.6" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-arm64-musl@npm:13.5.6" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-x64-gnu@npm:13.5.6" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-linux-x64-musl@npm:13.5.6" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-win32-arm64-msvc@npm:13.5.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-ia32-msvc@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-win32-ia32-msvc@npm:13.5.6" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:13.5.6": + version: 13.5.6 + resolution: "@next/swc-win32-x64-msvc@npm:13.5.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@noble/curves@npm:1.2.0, @noble/curves@npm:~1.2.0": + version: 1.2.0 + resolution: "@noble/curves@npm:1.2.0" + dependencies: + "@noble/hashes": "npm:1.3.2" + checksum: 94e02e9571a9fd42a3263362451849d2f54405cb3ce9fa7c45bc6b9b36dcd7d1d20e2e1e14cfded24937a13d82f1e60eefc4d7a14982ce0bc219a9fc0f51d1f9 + languageName: node + linkType: hard + +"@noble/curves@npm:1.3.0, @noble/curves@npm:~1.3.0": + version: 1.3.0 + resolution: "@noble/curves@npm:1.3.0" + dependencies: + "@noble/hashes": "npm:1.3.3" + checksum: f3cbdd1af00179e30146eac5539e6df290228fb857a7a8ba36d1a772cbe59288a2ca83d06f175d3446ef00db3a80d7fd8b8347f7de9c2d4d5bf3865d8bb78252 + languageName: node + linkType: hard + +"@noble/curves@npm:^1.0.0": + version: 1.1.0 + resolution: "@noble/curves@npm:1.1.0" + dependencies: + "@noble/hashes": "npm:1.3.1" + checksum: 7028e3f19a4a2a601f9159e5423f51ae86ab231bed79a6e40649b063e1ed7f55f5da0475f1377bd2c5a8e5fc485af9ce0549ad89da6b983d6af48e5d0a2041ca + languageName: node + linkType: hard + +"@noble/hashes@npm:1.0.0, @noble/hashes@npm:~1.0.0": + version: 1.0.0 + resolution: "@noble/hashes@npm:1.0.0" + checksum: cbe27966ed82daa0d6d984b168928719f139c8d96567cbc8b055b59a9fbd99ed9674d03e881cc93753b4f1cc94ffd466f116a68c0f25a8818bf220851b8e3bee + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.1, @noble/hashes@npm:^1.3.0, @noble/hashes@npm:~1.3.0": + version: 1.3.1 + resolution: "@noble/hashes@npm:1.3.1" + checksum: 39474bab7e7813dbbfd8750476f48046d3004984e161fcd4333e40ca823f07b069010b35a20246e5b4ac20858e29913172a4d69720fd1e93620f7bedb70f9b72 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.3.1": + version: 1.3.2 + resolution: "@noble/hashes@npm:1.3.2" + checksum: 685f59d2d44d88e738114b71011d343a9f7dce9dfb0a121f1489132f9247baa60bc985e5ec6f3213d114fbd1e1168e7294644e46cbd0ce2eba37994f28eeb51b + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:~1.3.2": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 1025ddde4d24630e95c0818e63d2d54ee131b980fe113312d17ed7468bc18f54486ac86c907685759f8a7e13c2f9b9e83ec7b67d1cc20836f36b5e4a65bb102d + languageName: node + linkType: hard + +"@noble/secp256k1@npm:1.5.5, @noble/secp256k1@npm:~1.5.2": + version: 1.5.5 + resolution: "@noble/secp256k1@npm:1.5.5" + checksum: 7d8478ca71bff32f48d197c1dd4fbbb4cebf9c29b46b40dc89a49c680e73bbddd9e3ce8362817d730748d219406161909caf1a01155fda0c947955a3a726c8e3 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 6ab2a9b8a1d67b067922c36f259e3b3dfd6b97b219c540877a4944549a4d49ea5ceba5663905ab5289682f1f3c15ff441d02f0447f620a42e1cb5e1937174d4b + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 012480b5ca9d97bff9261571dbbec7bbc6033f69cc92908bc1ecfad0792361a5a1994bc48674b9ef76419d056a03efadfce5a6cf6dbc0a36559571a7a483f6f0 + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 40033e33e96e97d77fba5a238e4bba4487b8284678906a9f616b5579ddaf868a18874c0054a75402c9fbaaa033a25ceae093af58c9c30278e35c23c9479e79b0 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-block@npm:5.0.2": + version: 5.0.2 + resolution: "@nomicfoundation/ethereumjs-block@npm:5.0.2" + dependencies: + "@nomicfoundation/ethereumjs-common": "npm:4.0.2" + "@nomicfoundation/ethereumjs-rlp": "npm:5.0.2" + "@nomicfoundation/ethereumjs-trie": "npm:6.0.2" + "@nomicfoundation/ethereumjs-tx": "npm:5.0.2" + "@nomicfoundation/ethereumjs-util": "npm:9.0.2" + ethereum-cryptography: "npm:0.1.3" + ethers: "npm:^5.7.1" + checksum: e3d7c24aa10306ae26389ce464f71e36fe8d331706e942a626594afdd097451d3bda210238b1da843e582b347f4349115b53432076b0f9b37ba36e31818b12cb + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-blockchain@npm:7.0.2": version: 7.0.2 resolution: "@nomicfoundation/ethereumjs-blockchain@npm:7.0.2" dependencies: @@ -5243,6 +6345,151 @@ __metadata: languageName: node linkType: hard +"@parcel/watcher-android-arm64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-android-arm64@npm:2.4.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-darwin-arm64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-darwin-arm64@npm:2.4.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-darwin-x64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-darwin-x64@npm:2.4.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher-freebsd-x64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-freebsd-x64@npm:2.4.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm-glibc@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-arm-glibc@npm:2.4.0" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm64-glibc@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-arm64-glibc@npm:2.4.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-arm64-musl@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-arm64-musl@npm:2.4.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-linux-x64-glibc@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-x64-glibc@npm:2.4.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@parcel/watcher-linux-x64-musl@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-linux-x64-musl@npm:2.4.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@parcel/watcher-wasm@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-wasm@npm:2.4.0" + dependencies: + is-glob: "npm:^4.0.3" + micromatch: "npm:^4.0.5" + napi-wasm: "npm:^1.1.0" + checksum: bb6943b4c4b894d54b42de73fc901d0ad8496e2d761def88cd245eb8e1cbf12bd708755375b616c28c5cdc67209c6f811835d0d073dd8cd78c29c82e54e82840 + languageName: node + linkType: hard + +"@parcel/watcher-win32-arm64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-win32-arm64@npm:2.4.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@parcel/watcher-win32-ia32@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-win32-ia32@npm:2.4.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@parcel/watcher-win32-x64@npm:2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher-win32-x64@npm:2.4.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@parcel/watcher@npm:^2.4.0": + version: 2.4.0 + resolution: "@parcel/watcher@npm:2.4.0" + dependencies: + "@parcel/watcher-android-arm64": "npm:2.4.0" + "@parcel/watcher-darwin-arm64": "npm:2.4.0" + "@parcel/watcher-darwin-x64": "npm:2.4.0" + "@parcel/watcher-freebsd-x64": "npm:2.4.0" + "@parcel/watcher-linux-arm-glibc": "npm:2.4.0" + "@parcel/watcher-linux-arm64-glibc": "npm:2.4.0" + "@parcel/watcher-linux-arm64-musl": "npm:2.4.0" + "@parcel/watcher-linux-x64-glibc": "npm:2.4.0" + "@parcel/watcher-linux-x64-musl": "npm:2.4.0" + "@parcel/watcher-win32-arm64": "npm:2.4.0" + "@parcel/watcher-win32-ia32": "npm:2.4.0" + "@parcel/watcher-win32-x64": "npm:2.4.0" + detect-libc: "npm:^1.0.3" + is-glob: "npm:^4.0.3" + micromatch: "npm:^4.0.5" + node-addon-api: "npm:^7.0.0" + node-gyp: "npm:latest" + dependenciesMeta: + "@parcel/watcher-android-arm64": + optional: true + "@parcel/watcher-darwin-arm64": + optional: true + "@parcel/watcher-darwin-x64": + optional: true + "@parcel/watcher-freebsd-x64": + optional: true + "@parcel/watcher-linux-arm-glibc": + optional: true + "@parcel/watcher-linux-arm64-glibc": + optional: true + "@parcel/watcher-linux-arm64-musl": + optional: true + "@parcel/watcher-linux-x64-glibc": + optional: true + "@parcel/watcher-linux-x64-musl": + optional: true + "@parcel/watcher-win32-arm64": + optional: true + "@parcel/watcher-win32-ia32": + optional: true + "@parcel/watcher-win32-x64": + optional: true + checksum: 2839cf275ea38b47a2c9c6d74ff6fc312613b58e63b072ece75307d718712cecda5ca7f5e88eee3619bdd3cad3bcb1c4048a50573afe76956eb48a94d3949760 + languageName: node + linkType: hard + "@pnpm/config.env-replace@npm:^1.1.0": version: 1.1.0 resolution: "@pnpm/config.env-replace@npm:1.1.0" @@ -5343,6 +6590,25 @@ __metadata: languageName: node linkType: hard +"@rainbow-me/rainbowkit@npm:0.12.16": + version: 0.12.16 + resolution: "@rainbow-me/rainbowkit@npm:0.12.16" + dependencies: + "@vanilla-extract/css": "npm:1.9.1" + "@vanilla-extract/dynamic": "npm:2.0.2" + "@vanilla-extract/sprinkles": "npm:1.5.0" + clsx: "npm:1.1.1" + qrcode: "npm:1.5.0" + react-remove-scroll: "npm:2.5.4" + peerDependencies: + ethers: ">=5.6.8" + react: ">=17" + react-dom: ">=17" + wagmi: ">=0.12.18 <1.0.0" + checksum: b55af69f295c857f33b01e0d0460912ef1b66b76746b698f49d3d350a59c2f501b58cda0aa2338b53f2d8d8fe9a2b3f7aa57b930a09cb88b504d75148a04e948 + languageName: node + linkType: hard + "@resolver-engine/core@npm:^0.3.3": version: 0.3.3 resolution: "@resolver-engine/core@npm:0.3.3" @@ -5416,15 +6682,45 @@ __metadata: languageName: node linkType: hard -"@safe-global/safe-core-sdk-types@npm:^2.2.0": - version: 2.2.0 - resolution: "@safe-global/safe-core-sdk-types@npm:2.2.0" +"@safe-global/safe-apps-provider@npm:^0.15.2": + version: 0.15.2 + resolution: "@safe-global/safe-apps-provider@npm:0.15.2" dependencies: - "@ethersproject/bignumber": "npm:^5.7.0" - "@ethersproject/contracts": "npm:^5.7.0" - "@safe-global/safe-deployments": "npm:^1.26.0" - web3-core: "npm:^1.8.1" - web3-utils: "npm:^1.8.1" + "@safe-global/safe-apps-sdk": "npm:7.9.0" + events: "npm:^3.3.0" + checksum: 9e4c8a3fd58e6b563452c173d569f0a249a8e122e89d95dbb06a515629e627a5e304ab16bc91bf1c198d2d710426990e1e294f619bbeb48bb931804421dca5c7 + languageName: node + linkType: hard + +"@safe-global/safe-apps-sdk@npm:7.9.0": + version: 7.9.0 + resolution: "@safe-global/safe-apps-sdk@npm:7.9.0" + dependencies: + "@safe-global/safe-gateway-typescript-sdk": "npm:^3.5.3" + ethers: "npm:^5.7.2" + checksum: d350dc1de984ac57ce07b4d5fd3f63b867959c2f0ac57ead91499cf1e57f32c39e33cf5e3740a3449aa06a95b717c9e5798b39e55086123e4cf529f6b7194ba7 + languageName: node + linkType: hard + +"@safe-global/safe-apps-sdk@npm:^7.9.0": + version: 7.11.0 + resolution: "@safe-global/safe-apps-sdk@npm:7.11.0" + dependencies: + "@safe-global/safe-gateway-typescript-sdk": "npm:^3.5.3" + ethers: "npm:^5.7.2" + checksum: 698df52d088496db994d4df065e3624289550821f938e68f21a2f4a732032bbb7c1ab78746c1e8eca3769e9dc8ea07db9b3e5248cc91e145d31f69cfd44f8cb4 + languageName: node + linkType: hard + +"@safe-global/safe-core-sdk-types@npm:^2.2.0": + version: 2.2.0 + resolution: "@safe-global/safe-core-sdk-types@npm:2.2.0" + dependencies: + "@ethersproject/bignumber": "npm:^5.7.0" + "@ethersproject/contracts": "npm:^5.7.0" + "@safe-global/safe-deployments": "npm:^1.26.0" + web3-core: "npm:^1.8.1" + web3-utils: "npm:^1.8.1" checksum: 8887b911e0748e2ef2b88150befa33e6d58ee59acf70abc5beb49a3f6e16b702d0e007fe82db4bcecf3ed5d77984adad340e7930a91b3cc0477fa60637ba27bc languageName: node linkType: hard @@ -5438,6 +6734,20 @@ __metadata: languageName: node linkType: hard +"@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3": + version: 3.17.0 + resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.17.0" + checksum: 8fe4ba9b2811205fad6b4cf1d8ae4cf694cdf9228812a5bc1b08625ec4003efa7c2f7daae45e884bff52b805c6cd67669d82be0a00913a30bb0100198da6d183 + languageName: node + linkType: hard + +"@scure/base@npm:^1.1.3, @scure/base@npm:~1.1.4": + version: 1.1.5 + resolution: "@scure/base@npm:1.1.5" + checksum: 543fa9991c6378b6a0d5ab7f1e27b30bb9c1e860d3ac81119b4213cfdf0ad7b61be004e06506e89de7ce0cec9391c17f5c082bb34c3b617a2ee6a04129f52481 + languageName: node + linkType: hard + "@scure/base@npm:~1.0.0": version: 1.0.0 resolution: "@scure/base@npm:1.0.0" @@ -5481,6 +6791,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.3": + version: 1.3.3 + resolution: "@scure/bip32@npm:1.3.3" + dependencies: + "@noble/curves": "npm:~1.3.0" + "@noble/hashes": "npm:~1.3.2" + "@scure/base": "npm:~1.1.4" + checksum: 4b8b75567866ff7d6b3ba154538add02d2951e9433e8dd7f0014331ac500cda5a88fe3d39b408fcc36e86b633682013f172b967af022c2e4e4ab07336801d688 + languageName: node + linkType: hard + "@scure/bip39@npm:1.0.0": version: 1.0.0 resolution: "@scure/bip39@npm:1.0.0" @@ -5501,6 +6822,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.2.2": + version: 1.2.2 + resolution: "@scure/bip39@npm:1.2.2" + dependencies: + "@noble/hashes": "npm:~1.3.2" + "@scure/base": "npm:~1.1.4" + checksum: f71aceda10a7937bf3779fd2b4c4156c95ec9813269470ddca464cb8ab610d2451b173037f4b1e6dac45414e406e7adc7b5814c51279f4474d5d38140bbee542 + languageName: node + linkType: hard + "@sentry/core@npm:5.30.0": version: 5.30.0 resolution: "@sentry/core@npm:5.30.0" @@ -5583,6 +6914,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 297f95ff77c82c54de8c9907f186076e715ff2621c5222ba50b8d40a170661c0c5242c763cba2a4791f0f91cb1d8ffa53ea1d7294570cf8cd4694c0e383e484d + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.6.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" @@ -5606,6 +6944,15 @@ __metadata: languageName: node linkType: hard +"@sinonjs/commons@npm:^3.0.0": + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" + dependencies: + type-detect: "npm:4.0.8" + checksum: a0af217ba7044426c78df52c23cedede6daf377586f3ac58857c565769358ab1f44ebf95ba04bbe38814fba6e316ca6f02870a009328294fc2c555d0f85a7117 + languageName: node + linkType: hard + "@sinonjs/fake-timers@npm:>=5, @sinonjs/fake-timers@npm:^9.1.2": version: 9.1.2 resolution: "@sinonjs/fake-timers@npm:9.1.2" @@ -5615,6 +6962,15 @@ __metadata: languageName: node linkType: hard +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.3.0 + resolution: "@sinonjs/fake-timers@npm:10.3.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.0" + checksum: 78155c7bd866a85df85e22028e046b8d46cf3e840f72260954f5e3ed5bd97d66c595524305a6841ffb3f681a08f6e5cef572a2cce5442a8a232dc29fb409b83e + languageName: node + linkType: hard + "@sinonjs/samsam@npm:^6.1.1": version: 6.1.1 resolution: "@sinonjs/samsam@npm:6.1.1" @@ -5731,6 +7087,185 @@ __metadata: languageName: node linkType: hard +"@stablelib/aead@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/aead@npm:1.0.1" + checksum: 1a6f68d138f105d17dd65349751515bd252ab0498c77255b8555478d28415600dde493f909eb718245047a993f838dfae546071e1687566ffb7b8c3e10c918d9 + languageName: node + linkType: hard + +"@stablelib/binary@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/binary@npm:1.0.1" + dependencies: + "@stablelib/int": "npm:^1.0.1" + checksum: c5ed769e2b5d607a5cdb72d325fcf98db437627862fade839daad934bd9ccf02a6f6e34f9de8cb3b18d72fce2ba6cc019a5d22398187d7d69d2607165f27f8bf + languageName: node + linkType: hard + +"@stablelib/bytes@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/bytes@npm:1.0.1" + checksum: 23d4d632a8a15ca91be1dc56da92eefed695d9b66068d1ab27a5655d0233dc2ac0b8668f875af542ca4ed526893c65dd53e777c72c8056f3648115aac98823ee + languageName: node + linkType: hard + +"@stablelib/chacha20poly1305@npm:1.0.1": + version: 1.0.1 + resolution: "@stablelib/chacha20poly1305@npm:1.0.1" + dependencies: + "@stablelib/aead": "npm:^1.0.1" + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/chacha": "npm:^1.0.1" + "@stablelib/constant-time": "npm:^1.0.1" + "@stablelib/poly1305": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 2a4df136b078b7c09acb3c6fe029613d4c9f70a0ce8bec65551a4a5016930a4f9091d3b83ed1cfc9c2e7bd6ec7f5ee93a7dc729b784b3900dcb97f3c7f5da84a + languageName: node + linkType: hard + +"@stablelib/chacha@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/chacha@npm:1.0.1" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 38cd8095d94eda29a9bb8a742b1c945dba7f9ec91fc07ab351c826680d03976641ac6366c3d004a00a72d746fcd838215fe1263ef4b0660c453c5de18a0a4295 + languageName: node + linkType: hard + +"@stablelib/constant-time@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/constant-time@npm:1.0.1" + checksum: dba4f4bf508de2ff15f7f0cbd875e70391aa3ba3698290fe1ed2feb151c243ba08a90fc6fb390ec2230e30fcc622318c591a7c0e35dcb8150afb50c797eac3d7 + languageName: node + linkType: hard + +"@stablelib/ed25519@npm:^1.0.2": + version: 1.0.3 + resolution: "@stablelib/ed25519@npm:1.0.3" + dependencies: + "@stablelib/random": "npm:^1.0.2" + "@stablelib/sha512": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 52e861e4fbd9d3d0a1a370d9ad96de8e2e15f133249bbbc32da66b8993e843db598054a3af17a746beb3fd5043b7529613a5dda7f2e79de6613eb3ebe5ffe3dd + languageName: node + linkType: hard + +"@stablelib/hash@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/hash@npm:1.0.1" + checksum: 3ff1f12d1a4082aaf4b6cdf40c2010aabe5c4209d3b40b97b5bbb0d9abc0ee94abdc545e57de0614afaea807ca0212ac870e247ec8f66cdce91ec39ce82948cf + languageName: node + linkType: hard + +"@stablelib/hkdf@npm:1.0.1": + version: 1.0.1 + resolution: "@stablelib/hkdf@npm:1.0.1" + dependencies: + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/hmac": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 9d45e303715a1835c8612b78e6c1b9d2b7463699b484241d8681fb5c17e0f2bbde5ce211c882134b64616a402e09177baeba80426995ff227b3654a155ab225d + languageName: node + linkType: hard + +"@stablelib/hmac@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/hmac@npm:1.0.1" + dependencies: + "@stablelib/constant-time": "npm:^1.0.1" + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: d3ac9e2fea2b4972a5d874ee9d96c94f8c8207452e2d243a2668b1325a7b20bd9a1541df32387789a0e9bfef82c3fe021a785f46eb3442c782443863faf75205 + languageName: node + linkType: hard + +"@stablelib/int@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/int@npm:1.0.1" + checksum: 65bfbf50a382eea70c68e05366bf379cfceff8fbc076f1c267ef2f2411d7aed64fd140c415cb6c29f19a3910d3b8b7805d4b32ad5721a5007a8e744a808c7ae3 + languageName: node + linkType: hard + +"@stablelib/keyagreement@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/keyagreement@npm:1.0.1" + dependencies: + "@stablelib/bytes": "npm:^1.0.1" + checksum: 3c8ec904dd50f72f3162f5447a0fa8f1d9ca6e24cd272d3dbe84971267f3b47f9bd5dc4e4eeedf3fbac2fe01f2d9277053e57c8e60db8c5544bfb35c62d290dd + languageName: node + linkType: hard + +"@stablelib/poly1305@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/poly1305@npm:1.0.1" + dependencies: + "@stablelib/constant-time": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: b01d4b532a42e5260f7f263e3a670924849c7ba51569abd8ece8279a448e625cbe4049bff1d50ad0d3a9d5f268c1b52fc611808640a6e684550edd7589a0a581 + languageName: node + linkType: hard + +"@stablelib/random@npm:^1.0.1, @stablelib/random@npm:^1.0.2": + version: 1.0.2 + resolution: "@stablelib/random@npm:1.0.2" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: f5ace0a588dc4c21f01cb85837892d4c872e994ae77a58a8eb7dd61aa0b26fb1e9b46b0445e71af57d963ef7d9f5965c64258fc0d04df7b2947bc48f2d3560c5 + languageName: node + linkType: hard + +"@stablelib/sha256@npm:1.0.1": + version: 1.0.1 + resolution: "@stablelib/sha256@npm:1.0.1" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 4d55f6c676e2cc0dd2a32be0cfa96837f3e15ae48dc50a340e56db2b201f1341a9ecabb429a3a44a5bf31adee0a8151467a8e7cc15346c561c914faad415d4d4 + languageName: node + linkType: hard + +"@stablelib/sha512@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/sha512@npm:1.0.1" + dependencies: + "@stablelib/binary": "npm:^1.0.1" + "@stablelib/hash": "npm:^1.0.1" + "@stablelib/wipe": "npm:^1.0.1" + checksum: 35d188cd62f20d27e1d61ea07984022e9a78815a023c8f7c747d92456a60823f0683138591e87158a47cd72e73cf24ecf97f8936aa6fba8b3bef6fcb138e723d + languageName: node + linkType: hard + +"@stablelib/wipe@npm:^1.0.1": + version: 1.0.1 + resolution: "@stablelib/wipe@npm:1.0.1" + checksum: 287802eb146810a46ba72af70b82022caf83a8aeebde23605f5ee0decf64fe2b97a60c856e43b6617b5801287c30cfa863cfb0469e7fcde6f02d143cf0c6cbf4 + languageName: node + linkType: hard + +"@stablelib/x25519@npm:^1.0.3": + version: 1.0.3 + resolution: "@stablelib/x25519@npm:1.0.3" + dependencies: + "@stablelib/keyagreement": "npm:^1.0.1" + "@stablelib/random": "npm:^1.0.2" + "@stablelib/wipe": "npm:^1.0.1" + checksum: fb5469e390ee2515d926633e3e179038894ac4f5e8c8cd2c2fc912022e34a051112eab0fe80c4dbc6e59129679844182562a036abff89444e5c4a05dd42ed329 + languageName: node + linkType: hard + +"@swc/helpers@npm:0.5.2": + version: 0.5.2 + resolution: "@swc/helpers@npm:0.5.2" + dependencies: + tslib: "npm:^2.4.0" + checksum: 3a3b179b3369acd26c5da89a0e779c756ae5231eb18a5507524c7abf955f488d34d86649f5b8417a0e19879688470d06319f5cfca2273d6d6b2046950e0d79af + languageName: node + linkType: hard + "@szmarczak/http-timer@npm:^5.0.1": version: 5.0.1 resolution: "@szmarczak/http-timer@npm:5.0.1" @@ -5740,6 +7275,80 @@ __metadata: languageName: node linkType: hard +"@tanstack/query-core@npm:4.36.1": + version: 4.36.1 + resolution: "@tanstack/query-core@npm:4.36.1" + checksum: 7c648872cd491bcab2aa4c18e0b7ca130c072f05c277a5876977fa3bfa87634bbfde46e9d249236587d78c39866889a02e4e202b478dc6074ff96093732ae56d + languageName: node + linkType: hard + +"@tanstack/query-persist-client-core@npm:4.36.1": + version: 4.36.1 + resolution: "@tanstack/query-persist-client-core@npm:4.36.1" + dependencies: + "@tanstack/query-core": "npm:4.36.1" + checksum: b511da36e5648f2680ba168db6ddc3f2e8fadda98431643ff21323726eb45bbf9334dd9e8a37687279526b464a0c8f1762332470f32e2bd8a0e72511878371cf + languageName: node + linkType: hard + +"@tanstack/query-sync-storage-persister@npm:^4.27.1": + version: 4.36.1 + resolution: "@tanstack/query-sync-storage-persister@npm:4.36.1" + dependencies: + "@tanstack/query-persist-client-core": "npm:4.36.1" + checksum: 27a31696ecf7f7ed64294a5212a873fc2852973982edcedf36d1fa53f1cb8b20654a79509f819d35f64861cdcf5d0d7e8782136a4f9aabcc8160cc577c1a4c03 + languageName: node + linkType: hard + +"@tanstack/react-query-persist-client@npm:^4.28.0": + version: 4.36.1 + resolution: "@tanstack/react-query-persist-client@npm:4.36.1" + dependencies: + "@tanstack/query-persist-client-core": "npm:4.36.1" + peerDependencies: + "@tanstack/react-query": ^4.36.1 + checksum: d6720fba52d98401d593be2f2d0a6e429394581082164c36629937abd788c8d80838749d36dce2b20558efd9cbbfa3131bf0212a187f0bef63811ea160d7b390 + languageName: node + linkType: hard + +"@tanstack/react-query@npm:^4.24.10, @tanstack/react-query@npm:^4.28.0": + version: 4.36.1 + resolution: "@tanstack/react-query@npm:4.36.1" + dependencies: + "@tanstack/query-core": "npm:4.36.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: "*" + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: 764b860c3ac8d254fc6b07e01054a0f58058644d59626c724b213293fbf1e31c198cbb26e4c32c0d16dcaec0353c0ae19147d9c667675b31f8cea1d64f1ff4ac + languageName: node + linkType: hard + +"@tanstack/react-virtual@npm:^3.0.0-beta.60": + version: 3.1.2 + resolution: "@tanstack/react-virtual@npm:3.1.2" + dependencies: + "@tanstack/virtual-core": "npm:3.1.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: dd7a241b4ea047bdf0fedc102ee73e9908cfe49145e48b2bbfa24279eb6ee2861465cb4f779a77182ffb4809ed8ffb584415f0670046ad2ccf2c43c03c59859a + languageName: node + linkType: hard + +"@tanstack/virtual-core@npm:3.1.2": + version: 3.1.2 + resolution: "@tanstack/virtual-core@npm:3.1.2" + checksum: 0e5c251050362eb0803b658210196e364172b9874900ea6bd1621ac58cb7b5d5add90a0e86a5b7a8f696921f9ff955effb58e65c02931056ce73adbc170af5cb + languageName: node + linkType: hard + "@tootallnate/once@npm:2": version: 2.0.0 resolution: "@tootallnate/once@npm:2.0.0" @@ -5855,6 +7464,47 @@ __metadata: languageName: node linkType: hard +"@types/babel__core@npm:^7.1.14": + version: 7.20.5 + resolution: "@types/babel__core@npm:7.20.5" + dependencies: + "@babel/parser": "npm:^7.20.7" + "@babel/types": "npm:^7.20.7" + "@types/babel__generator": "npm:*" + "@types/babel__template": "npm:*" + "@types/babel__traverse": "npm:*" + checksum: c32838d280b5ab59d62557f9e331d3831f8e547ee10b4f85cb78753d97d521270cebfc73ce501e9fb27fe71884d1ba75e18658692c2f4117543f0fc4e3e118b3 + languageName: node + linkType: hard + +"@types/babel__generator@npm:*": + version: 7.6.8 + resolution: "@types/babel__generator@npm:7.6.8" + dependencies: + "@babel/types": "npm:^7.0.0" + checksum: b53c215e9074c69d212402990b0ca8fa57595d09e10d94bda3130aa22b55d796e50449199867879e4ea0ee968f3a2099e009cfb21a726a53324483abbf25cd30 + languageName: node + linkType: hard + +"@types/babel__template@npm:*": + version: 7.4.4 + resolution: "@types/babel__template@npm:7.4.4" + dependencies: + "@babel/parser": "npm:^7.1.0" + "@babel/types": "npm:^7.0.0" + checksum: d7a02d2a9b67e822694d8e6a7ddb8f2b71a1d6962dfd266554d2513eefbb205b33ca71a0d163b1caea3981ccf849211f9964d8bd0727124d18ace45aa6c9ae29 + languageName: node + linkType: hard + +"@types/babel__traverse@npm:*, @types/babel__traverse@npm:^7.0.6": + version: 7.20.5 + resolution: "@types/babel__traverse@npm:7.20.5" + dependencies: + "@babel/types": "npm:^7.20.7" + checksum: f0352d537448e1e37f27e6bb8c962d7893720a92fde9d8601a68a93dbc14e15c088b4c0c8f71021d0966d09fba802ef3de11fdb6766c33993f8cf24f1277c6a9 + languageName: node + linkType: hard + "@types/bn.js@npm:^4.11.3": version: 4.11.6 resolution: "@types/bn.js@npm:4.11.6" @@ -5954,6 +7604,25 @@ __metadata: languageName: node linkType: hard +"@types/graceful-fs@npm:^4.1.3": + version: 4.1.9 + resolution: "@types/graceful-fs@npm:4.1.9" + dependencies: + "@types/node": "npm:*" + checksum: 79d746a8f053954bba36bd3d94a90c78de995d126289d656fb3271dd9f1229d33f678da04d10bce6be440494a5a73438e2e363e92802d16b8315b051036c5256 + languageName: node + linkType: hard + +"@types/hoist-non-react-statics@npm:^3.3.1": + version: 3.3.5 + resolution: "@types/hoist-non-react-statics@npm:3.3.5" + dependencies: + "@types/react": "npm:*" + hoist-non-react-statics: "npm:^3.3.0" + checksum: b645b062a20cce6ab1245ada8274051d8e2e0b2ee5c6bd58215281d0ec6dae2f26631af4e2e7c8abe238cdcee73fcaededc429eef569e70908f82d0cc0ea31d7 + languageName: node + linkType: hard + "@types/http-cache-semantics@npm:*": version: 4.0.1 resolution: "@types/http-cache-semantics@npm:4.0.1" @@ -5977,6 +7646,31 @@ __metadata: languageName: node linkType: hard +"@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": + version: 2.0.6 + resolution: "@types/istanbul-lib-coverage@npm:2.0.6" + checksum: 3feac423fd3e5449485afac999dcfcb3d44a37c830af898b689fadc65d26526460bedb889db278e0d4d815a670331796494d073a10ee6e3a6526301fe7415778 + languageName: node + linkType: hard + +"@types/istanbul-lib-report@npm:*": + version: 3.0.3 + resolution: "@types/istanbul-lib-report@npm:3.0.3" + dependencies: + "@types/istanbul-lib-coverage": "npm:*" + checksum: b91e9b60f865ff08cb35667a427b70f6c2c63e88105eadd29a112582942af47ed99c60610180aa8dcc22382fa405033f141c119c69b95db78c4c709fbadfeeb4 + languageName: node + linkType: hard + +"@types/istanbul-reports@npm:^3.0.0": + version: 3.0.4 + resolution: "@types/istanbul-reports@npm:3.0.4" + dependencies: + "@types/istanbul-lib-report": "npm:*" + checksum: 93eb18835770b3431f68ae9ac1ca91741ab85f7606f310a34b3586b5a34450ec038c3eed7ab19266635499594de52ff73723a54a72a75b9f7d6a956f01edee95 + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.9": version: 7.0.11 resolution: "@types/json-schema@npm:7.0.11" @@ -5984,6 +7678,13 @@ __metadata: languageName: node linkType: hard +"@types/json-stable-stringify@npm:^1.0.36": + version: 1.0.36 + resolution: "@types/json-stable-stringify@npm:1.0.36" + checksum: 765b07589e11a3896c3d06bb9e3a9be681e7edd95adf27370df0647a91bd2bfcfaf0e091fd4a13729343b388973f73f7e789d6cc62ab988240518a2d27c4a4e2 + languageName: node + linkType: hard + "@types/keyv@npm:^3.1.1, @types/keyv@npm:^3.1.4": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" @@ -6176,6 +7877,13 @@ __metadata: languageName: node linkType: hard +"@types/prop-types@npm:*": + version: 15.7.11 + resolution: "@types/prop-types@npm:15.7.11" + checksum: 7519ff11d06fbf6b275029fe03fff9ec377b4cb6e864cac34d87d7146c7f5a7560fd164bdc1d2dbe00b60c43713631251af1fd3d34d46c69cd354602bc0c7c54 + languageName: node + linkType: hard + "@types/qs@npm:^6.2.31": version: 6.9.7 resolution: "@types/qs@npm:6.9.7" @@ -6183,6 +7891,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:*": + version: 18.2.57 + resolution: "@types/react@npm:18.2.57" + dependencies: + "@types/prop-types": "npm:*" + "@types/scheduler": "npm:*" + csstype: "npm:^3.0.2" + checksum: beee45a8ee48862fb5101f6ebdd89ccc20c5a6df29dcd2315560bc3b57ea3af8d09a8e9bb1c58063a70f9010e0d2c7bd300819438e2ca62810285c3d7275ab5a + languageName: node + linkType: hard + "@types/readable-stream@npm:^2.3.13": version: 2.3.15 resolution: "@types/readable-stream@npm:2.3.15" @@ -6211,6 +7930,13 @@ __metadata: languageName: node linkType: hard +"@types/scheduler@npm:*": + version: 0.16.8 + resolution: "@types/scheduler@npm:0.16.8" + checksum: 6c091b096daa490093bf30dd7947cd28e5b2cd612ec93448432b33f724b162587fed9309a0acc104d97b69b1d49a0f3fc755a62282054d62975d53d7fd13472d + languageName: node + linkType: hard + "@types/secp256k1@npm:^4.0.1": version: 4.0.3 resolution: "@types/secp256k1@npm:4.0.3" @@ -6276,6 +8002,20 @@ __metadata: languageName: node linkType: hard +"@types/stack-utils@npm:^2.0.0": + version: 2.0.3 + resolution: "@types/stack-utils@npm:2.0.3" + checksum: 72576cc1522090fe497337c2b99d9838e320659ac57fa5560fcbdcbafcf5d0216c6b3a0a8a4ee4fdb3b1f5e3420aa4f6223ab57b82fef3578bec3206425c6cf5 + languageName: node + linkType: hard + +"@types/trusted-types@npm:^2.0.2": + version: 2.0.7 + resolution: "@types/trusted-types@npm:2.0.7" + checksum: 8e4202766a65877efcf5d5a41b7dd458480b36195e580a3b1085ad21e948bc417d55d6f8af1fd2a7ad008015d4117d5fdfe432731157da3c68678487174e4ba3 + languageName: node + linkType: hard + "@types/wrap-ansi@npm:^3.0.0": version: 3.0.0 resolution: "@types/wrap-ansi@npm:3.0.0" @@ -6317,6 +8057,15 @@ __metadata: languageName: node linkType: hard +"@types/yargs@npm:^17.0.8": + version: 17.0.32 + resolution: "@types/yargs@npm:17.0.32" + dependencies: + "@types/yargs-parser": "npm:*" + checksum: 1e2b2673847011ce43607df690d392f137d95a2d6ea85aa319403eadda2ef4277365efd4982354d8843f2611ef3846c88599660aaeb537fa9ccddae83c2a89de + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^5.62.0": version: 5.62.0 resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" @@ -6438,21 +8187,560 @@ __metadata: languageName: node linkType: hard -"@wagmi/chains@npm:^1.8.0": - version: 1.8.0 - resolution: "@wagmi/chains@npm:1.8.0" +"@urql/core@npm:^3.2.0": + version: 3.2.2 + resolution: "@urql/core@npm:3.2.2" + dependencies: + wonka: "npm:^6.1.2" peerDependencies: - typescript: ">=5.0.4" - peerDependenciesMeta: - typescript: - optional: true - checksum: 8248419554a90c0d514acfc46f3a6f2090a282ff546b2488705e81fcdfaf197590e67a1fc62539383b4dd22ccafe9f16018cadad27acee098dc9d87b82f173e4 + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + checksum: 2ad58a282897d6c7ee07d09b5e78c12e86c8820e41390c3b2663262cb86e028557a77138d07476f58daea60e6950ff91063e7ed5827844d3d542d0a256688697 languageName: node linkType: hard -"JSONStream@npm:^1.3.5": - version: 1.3.5 - resolution: "JSONStream@npm:1.3.5" +"@vanilla-extract/css@npm:1.9.1": + version: 1.9.1 + resolution: "@vanilla-extract/css@npm:1.9.1" + dependencies: + "@emotion/hash": "npm:^0.8.0" + "@vanilla-extract/private": "npm:^1.0.3" + ahocorasick: "npm:1.0.2" + chalk: "npm:^4.1.1" + css-what: "npm:^5.0.1" + cssesc: "npm:^3.0.0" + csstype: "npm:^3.0.7" + deep-object-diff: "npm:^1.1.0" + deepmerge: "npm:^4.2.2" + media-query-parser: "npm:^2.0.2" + outdent: "npm:^0.8.0" + checksum: 0f3ff175356bdc7dd420bd68c997bedfb539ea2d0a056aca37f0825a518951edcb05a489969d083a97072a6ccc6a89bc64b89d8b26d9cc67774488855c44e1d3 + languageName: node + linkType: hard + +"@vanilla-extract/dynamic@npm:2.0.2": + version: 2.0.2 + resolution: "@vanilla-extract/dynamic@npm:2.0.2" + dependencies: + "@vanilla-extract/private": "npm:^1.0.3" + checksum: 26a73d2273f9da91ccbe3ffdcac92d3bab00921ade71c1bfd1936333d82aa3a8fc32c3c4ef962196d25a79fbdff1ccf060c9dd0fe0d73ca2f14f08fbc16a976c + languageName: node + linkType: hard + +"@vanilla-extract/private@npm:^1.0.3": + version: 1.0.3 + resolution: "@vanilla-extract/private@npm:1.0.3" + checksum: 5f27238d711fc190146869cb76258328d8d8c09bf4fca9df65168ce13704a5c78750824eb469fa961a2ab1cfefca43c37607d755b8a4aa937c8dd7df478036df + languageName: node + linkType: hard + +"@vanilla-extract/sprinkles@npm:1.5.0": + version: 1.5.0 + resolution: "@vanilla-extract/sprinkles@npm:1.5.0" + peerDependencies: + "@vanilla-extract/css": ^1.0.0 + checksum: 20045dc160ba7daa9772219dd4e73d76a6a9d77ede13f0f995a236a87e5f99b31ca409c35c49cab71cb2c568c2660b019bd4d7bbde26a523bd906b7f09146426 + languageName: node + linkType: hard + +"@wagmi/chains@npm:0.2.22": + version: 0.2.22 + resolution: "@wagmi/chains@npm:0.2.22" + peerDependencies: + typescript: ">=4.9.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 018f798831988579501311b934959dc93e101328794de6048e9fd6a6496260907642039e119d9266458b6884a99a3582d2a4011d823e57980d0145bebc7743d0 + languageName: node + linkType: hard + +"@wagmi/chains@npm:^1.8.0": + version: 1.8.0 + resolution: "@wagmi/chains@npm:1.8.0" + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 8248419554a90c0d514acfc46f3a6f2090a282ff546b2488705e81fcdfaf197590e67a1fc62539383b4dd22ccafe9f16018cadad27acee098dc9d87b82f173e4 + languageName: node + linkType: hard + +"@wagmi/connectors@npm:0.3.22": + version: 0.3.22 + resolution: "@wagmi/connectors@npm:0.3.22" + dependencies: + "@coinbase/wallet-sdk": "npm:^3.6.6" + "@ledgerhq/connect-kit-loader": "npm:^1.0.1" + "@safe-global/safe-apps-provider": "npm:^0.15.2" + "@safe-global/safe-apps-sdk": "npm:^7.9.0" + "@walletconnect/ethereum-provider": "npm:2.8.4" + "@walletconnect/legacy-provider": "npm:^2.0.0" + "@walletconnect/modal": "npm:^2.5.4" + abitype: "npm:^0.3.0" + eventemitter3: "npm:^4.0.7" + peerDependencies: + "@wagmi/core": ">=0.9.x" + ethers: ">=5.5.1 <6" + typescript: ">=4.9.4" + peerDependenciesMeta: + "@wagmi/core": + optional: true + typescript: + optional: true + checksum: 1f527a2ec46ff735ee73395cb2f2fa6271324be48aa926c9dea9c5dd779ea4e00dd0f5e6dc35e9cc8adf95e00c00d0597dda61ba9537061f6fedd31c0a23350e + languageName: node + linkType: hard + +"@wagmi/core@npm:0.10.16": + version: 0.10.16 + resolution: "@wagmi/core@npm:0.10.16" + dependencies: + "@wagmi/chains": "npm:0.2.22" + "@wagmi/connectors": "npm:0.3.22" + abitype: "npm:^0.3.0" + eventemitter3: "npm:^4.0.7" + zustand: "npm:^4.3.1" + peerDependencies: + ethers: ">=5.5.1 <6" + typescript: ">=4.9.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: cd1c1c53985bac26fcecd00f3bb6cf6de260e797662518721a80c02902ce20b52e5d435681df6dbc8302665d06cfa84a5043e51fb814e822242575227768b0a9 + languageName: node + linkType: hard + +"@walletconnect/core@npm:2.8.4": + version: 2.8.4 + resolution: "@walletconnect/core@npm:2.8.4" + dependencies: + "@walletconnect/heartbeat": "npm:1.2.1" + "@walletconnect/jsonrpc-provider": "npm:1.0.13" + "@walletconnect/jsonrpc-types": "npm:1.0.3" + "@walletconnect/jsonrpc-utils": "npm:1.0.8" + "@walletconnect/jsonrpc-ws-connection": "npm:^1.0.11" + "@walletconnect/keyvaluestorage": "npm:^1.0.2" + "@walletconnect/logger": "npm:^2.0.1" + "@walletconnect/relay-api": "npm:^1.0.9" + "@walletconnect/relay-auth": "npm:^1.0.4" + "@walletconnect/safe-json": "npm:^1.0.2" + "@walletconnect/time": "npm:^1.0.2" + "@walletconnect/types": "npm:2.8.4" + "@walletconnect/utils": "npm:2.8.4" + events: "npm:^3.3.0" + lodash.isequal: "npm:4.5.0" + uint8arrays: "npm:^3.1.0" + checksum: 4b8089b526f83578ba5e004417e005d79ad33b7a08079e177b6e389e6c03588581a95f1b5c92819a6cc3330f2a8543b404a2a5a6524cb2d47405d1cc5b95f825 + languageName: node + linkType: hard + +"@walletconnect/crypto@npm:^1.0.3": + version: 1.0.3 + resolution: "@walletconnect/crypto@npm:1.0.3" + dependencies: + "@walletconnect/encoding": "npm:^1.0.2" + "@walletconnect/environment": "npm:^1.0.1" + "@walletconnect/randombytes": "npm:^1.0.3" + aes-js: "npm:^3.1.2" + hash.js: "npm:^1.1.7" + tslib: "npm:1.14.1" + checksum: 6749da7f6b1e03a8aa2aa750bff0827ff59c70be6d58f7170e287d18507744fee507cba0f2a67b7ec3e50a4843420dc1f58a01f73735f90a4e75e47c7d39d5ab + languageName: node + linkType: hard + +"@walletconnect/encoding@npm:^1.0.2": + version: 1.0.2 + resolution: "@walletconnect/encoding@npm:1.0.2" + dependencies: + is-typedarray: "npm:1.0.0" + tslib: "npm:1.14.1" + typedarray-to-buffer: "npm:3.1.5" + checksum: 046a7725864aba319a284fe5e8cebb45bb9d3cb81f15dd6a82f12c4a01aafaadf6b8b0239172948eacbbee87bfde4f47c22148a0f760f15f0161a39534e41e41 + languageName: node + linkType: hard + +"@walletconnect/environment@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/environment@npm:1.0.1" + dependencies: + tslib: "npm:1.14.1" + checksum: f6a1e3456e50cc7cfa58d99fd513ecac75573d0b8bcbbedcb1d7ec04ca9108df16b471afd40761b2a5cb4f66d8e33b7ba25f02c62c8365d68b1bd1ef52c1813e + languageName: node + linkType: hard + +"@walletconnect/ethereum-provider@npm:2.8.4": + version: 2.8.4 + resolution: "@walletconnect/ethereum-provider@npm:2.8.4" + dependencies: + "@walletconnect/jsonrpc-http-connection": "npm:^1.0.7" + "@walletconnect/jsonrpc-provider": "npm:^1.0.13" + "@walletconnect/jsonrpc-types": "npm:^1.0.3" + "@walletconnect/jsonrpc-utils": "npm:^1.0.8" + "@walletconnect/sign-client": "npm:2.8.4" + "@walletconnect/types": "npm:2.8.4" + "@walletconnect/universal-provider": "npm:2.8.4" + "@walletconnect/utils": "npm:2.8.4" + events: "npm:^3.3.0" + peerDependencies: + "@walletconnect/modal": ">=2" + peerDependenciesMeta: + "@walletconnect/modal": + optional: true + checksum: 5aee30c31c16016d5240f215bbae03b276b7cb783371d22d03ae2b7359cbcbcf9d9bce78b70dc035268d7a220c5e032e556477b16fb840c4e8406e6b04b1316b + languageName: node + linkType: hard + +"@walletconnect/events@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/events@npm:1.0.1" + dependencies: + keyvaluestorage-interface: "npm:^1.0.0" + tslib: "npm:1.14.1" + checksum: b5a105e9ac4d7d0a500085afd77b71e71a8ab78fd38b033e4ce91f8626fd8c254b1ba49a59c8c0ed8a00a7e8b93995163f414eda73c58694f8f830e453a902b6 + languageName: node + linkType: hard + +"@walletconnect/heartbeat@npm:1.2.1": + version: 1.2.1 + resolution: "@walletconnect/heartbeat@npm:1.2.1" + dependencies: + "@walletconnect/events": "npm:^1.0.1" + "@walletconnect/time": "npm:^1.0.2" + tslib: "npm:1.14.1" + checksum: a68d7efe4e69c9749dd7c3a9e351dd22adccbb925447dd7f2b2978a4cd730695cc0b4e717a08bad0d0c60e0177b77618a53f3bfb4347659f3ccfe72d412c27fb + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-http-connection@npm:^1.0.4, @walletconnect/jsonrpc-http-connection@npm:^1.0.7": + version: 1.0.7 + resolution: "@walletconnect/jsonrpc-http-connection@npm:1.0.7" + dependencies: + "@walletconnect/jsonrpc-utils": "npm:^1.0.6" + "@walletconnect/safe-json": "npm:^1.0.1" + cross-fetch: "npm:^3.1.4" + tslib: "npm:1.14.1" + checksum: 2d915df34e37592bdc69712244fd4e19da68eab42a8c576dd94cbca66ccdf30d4bf223c093042c0c5b9c8acb0e0af5cd682e8d9916098bd6cdea9593b9474971 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-provider@npm:1.0.13, @walletconnect/jsonrpc-provider@npm:^1.0.13, @walletconnect/jsonrpc-provider@npm:^1.0.6": + version: 1.0.13 + resolution: "@walletconnect/jsonrpc-provider@npm:1.0.13" + dependencies: + "@walletconnect/jsonrpc-utils": "npm:^1.0.8" + "@walletconnect/safe-json": "npm:^1.0.2" + tslib: "npm:1.14.1" + checksum: 27c7dfa898896ffd7250aecaf92b889663abe64ea605dae1b638743a9f1609f0e27b2bca761b3bbc2ed722bde1b012d901bba4de4067424905bfce514cc5e909 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-types@npm:1.0.3, @walletconnect/jsonrpc-types@npm:^1.0.2, @walletconnect/jsonrpc-types@npm:^1.0.3": + version: 1.0.3 + resolution: "@walletconnect/jsonrpc-types@npm:1.0.3" + dependencies: + keyvaluestorage-interface: "npm:^1.0.0" + tslib: "npm:1.14.1" + checksum: 7b1209c2e6ff476e45b0d828bd4d7773873c4cff41e5ed235ff8014b4e8ff09ec704817347702fe3b8ca1c1b7920abfd0af94e0cdf582a92d8a0192d8c42dce8 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-utils@npm:1.0.8, @walletconnect/jsonrpc-utils@npm:^1.0.4, @walletconnect/jsonrpc-utils@npm:^1.0.6, @walletconnect/jsonrpc-utils@npm:^1.0.7, @walletconnect/jsonrpc-utils@npm:^1.0.8": + version: 1.0.8 + resolution: "@walletconnect/jsonrpc-utils@npm:1.0.8" + dependencies: + "@walletconnect/environment": "npm:^1.0.1" + "@walletconnect/jsonrpc-types": "npm:^1.0.3" + tslib: "npm:1.14.1" + checksum: 4687b4582a5c33883d94e87ca8bb22d129a2a47b6e1d9e2c3210b74f02d9677723b3bf2283d2f0fa69866b0a66a80cdfada9a2f1c204d485fbd10d2baed1f0a6 + languageName: node + linkType: hard + +"@walletconnect/jsonrpc-ws-connection@npm:^1.0.11": + version: 1.0.14 + resolution: "@walletconnect/jsonrpc-ws-connection@npm:1.0.14" + dependencies: + "@walletconnect/jsonrpc-utils": "npm:^1.0.6" + "@walletconnect/safe-json": "npm:^1.0.2" + events: "npm:^3.3.0" + ws: "npm:^7.5.1" + checksum: 2ad66217b62fb57a43c8edd33c27da0c9ba09cfec79f4d43e5d30bcb8224a48c1d1f0d6273be0371f2c7e33d8138a6fe03afa499b429ab7829d719677cd48f4d + languageName: node + linkType: hard + +"@walletconnect/keyvaluestorage@npm:^1.0.2": + version: 1.1.1 + resolution: "@walletconnect/keyvaluestorage@npm:1.1.1" + dependencies: + "@walletconnect/safe-json": "npm:^1.0.1" + idb-keyval: "npm:^6.2.1" + unstorage: "npm:^1.9.0" + peerDependencies: + "@react-native-async-storage/async-storage": 1.x + peerDependenciesMeta: + "@react-native-async-storage/async-storage": + optional: true + checksum: fd9c275b3249d8e9f722866703b5c040eb35d0670c92a297428ffb700ac36c6b9978242beac5d2cfe97eb522ae01307cacd9c79ecf95640878804fce0f13c5e7 + languageName: node + linkType: hard + +"@walletconnect/legacy-client@npm:^2.0.0": + version: 2.0.0 + resolution: "@walletconnect/legacy-client@npm:2.0.0" + dependencies: + "@walletconnect/crypto": "npm:^1.0.3" + "@walletconnect/encoding": "npm:^1.0.2" + "@walletconnect/jsonrpc-utils": "npm:^1.0.4" + "@walletconnect/legacy-types": "npm:^2.0.0" + "@walletconnect/legacy-utils": "npm:^2.0.0" + "@walletconnect/safe-json": "npm:^1.0.1" + "@walletconnect/window-getters": "npm:^1.0.1" + "@walletconnect/window-metadata": "npm:^1.0.1" + detect-browser: "npm:^5.3.0" + query-string: "npm:^6.13.5" + checksum: ae70c9f8a251a4f2eee97a4c6cd24ed64f30fbd38cfc9b4ed9e329e3817a2439bdc2b460515677511c551c2cbbaf23aafff512eb427b77ee61843eb4754430eb + languageName: node + linkType: hard + +"@walletconnect/legacy-modal@npm:^2.0.0": + version: 2.0.0 + resolution: "@walletconnect/legacy-modal@npm:2.0.0" + dependencies: + "@walletconnect/legacy-types": "npm:^2.0.0" + "@walletconnect/legacy-utils": "npm:^2.0.0" + copy-to-clipboard: "npm:^3.3.3" + preact: "npm:^10.12.0" + qrcode: "npm:^1.5.1" + checksum: 3b9c741b781c2bff9c104134f1a17585c8c879ee8c1c2218a06f7a5f5f385a9b0f039d57df55bdc28f9f7d45b8a02e221ce7df025c9182001dad33f5efca18b5 + languageName: node + linkType: hard + +"@walletconnect/legacy-provider@npm:^2.0.0": + version: 2.0.0 + resolution: "@walletconnect/legacy-provider@npm:2.0.0" + dependencies: + "@walletconnect/jsonrpc-http-connection": "npm:^1.0.4" + "@walletconnect/jsonrpc-provider": "npm:^1.0.6" + "@walletconnect/legacy-client": "npm:^2.0.0" + "@walletconnect/legacy-modal": "npm:^2.0.0" + "@walletconnect/legacy-types": "npm:^2.0.0" + "@walletconnect/legacy-utils": "npm:^2.0.0" + checksum: 49b18d2ef7652d71a66ace75957e49b8a38e80cd5af43b8786276b8179cf1281d7158716f0605b6c15189e0c48736bd3779ec23fd46ebbb83d7b770f85d53eab + languageName: node + linkType: hard + +"@walletconnect/legacy-types@npm:^2.0.0": + version: 2.0.0 + resolution: "@walletconnect/legacy-types@npm:2.0.0" + dependencies: + "@walletconnect/jsonrpc-types": "npm:^1.0.2" + checksum: 6d89021d1735a4a3f182aee78421bd8e783fdb1c51c93059f7b4727d66072afdc889b07be8e791919e7c1f52b93735f57b72fc1bfd5b890e17d9037fbb06fec7 + languageName: node + linkType: hard + +"@walletconnect/legacy-utils@npm:^2.0.0": + version: 2.0.0 + resolution: "@walletconnect/legacy-utils@npm:2.0.0" + dependencies: + "@walletconnect/encoding": "npm:^1.0.2" + "@walletconnect/jsonrpc-utils": "npm:^1.0.4" + "@walletconnect/legacy-types": "npm:^2.0.0" + "@walletconnect/safe-json": "npm:^1.0.1" + "@walletconnect/window-getters": "npm:^1.0.1" + "@walletconnect/window-metadata": "npm:^1.0.1" + detect-browser: "npm:^5.3.0" + query-string: "npm:^6.13.5" + checksum: 6dd7738b0b7c11eb8f06f639a37527759440453f350d616e7116e89dbec03f381a462102be2c2175ed02b886f1b420a80a144b623f1a63cf9e02cebe82bcdefe + languageName: node + linkType: hard + +"@walletconnect/logger@npm:^2.0.1": + version: 2.0.1 + resolution: "@walletconnect/logger@npm:2.0.1" + dependencies: + pino: "npm:7.11.0" + tslib: "npm:1.14.1" + checksum: 93ad8fd59a07a512ffb0f250dba83b15ea0b4ba7c5d676241c98238b78910e1c26d86a270b85a8c2809833bfd9e87325c37f55c88255102ad199d73da537bf42 + languageName: node + linkType: hard + +"@walletconnect/modal-core@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal-core@npm:2.6.2" + dependencies: + valtio: "npm:1.11.2" + checksum: 671184da341eebb6b7a3ad7c334851113683d71e6118f7203a377e493b61eb94bc0571484e497e577b9f4d7221a8a7034ad4b52af722c89fa4105627bed638ba + languageName: node + linkType: hard + +"@walletconnect/modal-ui@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal-ui@npm:2.6.2" + dependencies: + "@walletconnect/modal-core": "npm:2.6.2" + lit: "npm:2.8.0" + motion: "npm:10.16.2" + qrcode: "npm:1.5.3" + checksum: 5460ad7f4591c016b723b3f707ac0020e185b60744cf7132b4b4f48d71c87c1c55826f6e11005860f96bd11e0ed3f88da7cda4c0a1c35a0e5b7d6e53bc14cf15 + languageName: node + linkType: hard + +"@walletconnect/modal@npm:^2.5.4": + version: 2.6.2 + resolution: "@walletconnect/modal@npm:2.6.2" + dependencies: + "@walletconnect/modal-core": "npm:2.6.2" + "@walletconnect/modal-ui": "npm:2.6.2" + checksum: f8f132c89d1d7f44f2fa564c8d5122163610be4afb0cadc9576c77083471297c37ff62aae3a25492c0ddb480240a2a6ffefe3eba1fd48f1664160c6bac01466d + languageName: node + linkType: hard + +"@walletconnect/randombytes@npm:^1.0.3": + version: 1.0.3 + resolution: "@walletconnect/randombytes@npm:1.0.3" + dependencies: + "@walletconnect/encoding": "npm:^1.0.2" + "@walletconnect/environment": "npm:^1.0.1" + randombytes: "npm:^2.1.0" + tslib: "npm:1.14.1" + checksum: 3069e58d3735af15895cade98665a50339275cbf98b20e638ae9266556183b01b8cb286739de6adfd733a86c51fd6322aff034c05dc464d7581f35c33eacb25b + languageName: node + linkType: hard + +"@walletconnect/relay-api@npm:^1.0.9": + version: 1.0.9 + resolution: "@walletconnect/relay-api@npm:1.0.9" + dependencies: + "@walletconnect/jsonrpc-types": "npm:^1.0.2" + tslib: "npm:1.14.1" + checksum: 037781d51427fbaf866848a3f0a0260bd97cfb077c4ebe6db3024b56895ba977633ca8b3e0e37b48673ba04f1abf6e40e9ef46da10ff0a3e1cf5722f0c5ec32a + languageName: node + linkType: hard + +"@walletconnect/relay-auth@npm:^1.0.4": + version: 1.0.4 + resolution: "@walletconnect/relay-auth@npm:1.0.4" + dependencies: + "@stablelib/ed25519": "npm:^1.0.2" + "@stablelib/random": "npm:^1.0.1" + "@walletconnect/safe-json": "npm:^1.0.1" + "@walletconnect/time": "npm:^1.0.2" + tslib: "npm:1.14.1" + uint8arrays: "npm:^3.0.0" + checksum: d9128b2a25f38ebf2f49f8c184dad5c997ad6343513bddd7941459c2f2757e6acfbcdd36dc9c12d0491f55723d5e2c5c0ee2e9cf381b3247274b920e95d4db0e + languageName: node + linkType: hard + +"@walletconnect/safe-json@npm:^1.0.1, @walletconnect/safe-json@npm:^1.0.2": + version: 1.0.2 + resolution: "@walletconnect/safe-json@npm:1.0.2" + dependencies: + tslib: "npm:1.14.1" + checksum: b9d031dab3916d20fa5241d7ad2be425368ae489995ba3ba18d6ad88e81ad3ed093b8e867b8a4fc44759099896aeb5afee5635858cb80c4819ebc7ebb71ed5a6 + languageName: node + linkType: hard + +"@walletconnect/sign-client@npm:2.8.4": + version: 2.8.4 + resolution: "@walletconnect/sign-client@npm:2.8.4" + dependencies: + "@walletconnect/core": "npm:2.8.4" + "@walletconnect/events": "npm:^1.0.1" + "@walletconnect/heartbeat": "npm:1.2.1" + "@walletconnect/jsonrpc-utils": "npm:1.0.8" + "@walletconnect/logger": "npm:^2.0.1" + "@walletconnect/time": "npm:^1.0.2" + "@walletconnect/types": "npm:2.8.4" + "@walletconnect/utils": "npm:2.8.4" + events: "npm:^3.3.0" + checksum: a4c91d45925b7786df3f9a3028bf7adb34aaa55349707d6524e72c6b7f7370fe72b18cb019d92b574eaf86799a736f7ac1670f95a697ba52fcb4ac0a1b43968a + languageName: node + linkType: hard + +"@walletconnect/time@npm:^1.0.2": + version: 1.0.2 + resolution: "@walletconnect/time@npm:1.0.2" + dependencies: + tslib: "npm:1.14.1" + checksum: ea84d0850e63306837f98a228e08a59f6945da38ba5553b1f158abeaa8ec4dc8a0025a0f0cfc843ddf05ce2947da95c02ac1e8cedce7092bbe1c2d46ca816dd9 + languageName: node + linkType: hard + +"@walletconnect/types@npm:2.8.4": + version: 2.8.4 + resolution: "@walletconnect/types@npm:2.8.4" + dependencies: + "@walletconnect/events": "npm:^1.0.1" + "@walletconnect/heartbeat": "npm:1.2.1" + "@walletconnect/jsonrpc-types": "npm:1.0.3" + "@walletconnect/keyvaluestorage": "npm:^1.0.2" + "@walletconnect/logger": "npm:^2.0.1" + events: "npm:^3.3.0" + checksum: ac6dde3acaa324fadf593a7c6d8325428434d21c8b2efa64632245d9a0d1e8e6f19325abc28dfc31cdfe88122da49b727bf6fe5e5f217c0b92b272ef65f39873 + languageName: node + linkType: hard + +"@walletconnect/universal-provider@npm:2.8.4": + version: 2.8.4 + resolution: "@walletconnect/universal-provider@npm:2.8.4" + dependencies: + "@walletconnect/jsonrpc-http-connection": "npm:^1.0.7" + "@walletconnect/jsonrpc-provider": "npm:1.0.13" + "@walletconnect/jsonrpc-types": "npm:^1.0.2" + "@walletconnect/jsonrpc-utils": "npm:^1.0.7" + "@walletconnect/logger": "npm:^2.0.1" + "@walletconnect/sign-client": "npm:2.8.4" + "@walletconnect/types": "npm:2.8.4" + "@walletconnect/utils": "npm:2.8.4" + events: "npm:^3.3.0" + checksum: 5c72ca7fff90e96de5a939dfafaddbba74cddc1da1b76f6d04ce36691b3b8a72e107e90b2acaea53684397bd89da6f27219413da7388b6c8a29141004957288c + languageName: node + linkType: hard + +"@walletconnect/utils@npm:2.8.4": + version: 2.8.4 + resolution: "@walletconnect/utils@npm:2.8.4" + dependencies: + "@stablelib/chacha20poly1305": "npm:1.0.1" + "@stablelib/hkdf": "npm:1.0.1" + "@stablelib/random": "npm:^1.0.2" + "@stablelib/sha256": "npm:1.0.1" + "@stablelib/x25519": "npm:^1.0.3" + "@walletconnect/relay-api": "npm:^1.0.9" + "@walletconnect/safe-json": "npm:^1.0.2" + "@walletconnect/time": "npm:^1.0.2" + "@walletconnect/types": "npm:2.8.4" + "@walletconnect/window-getters": "npm:^1.0.1" + "@walletconnect/window-metadata": "npm:^1.0.1" + detect-browser: "npm:5.3.0" + query-string: "npm:7.1.3" + uint8arrays: "npm:^3.1.0" + checksum: c6947246eea36ce316e2d48922b2c0fe1238a3e83171b9c237bd300e0bc36119c6372c352ad015927f64d860e54b318b0d924972672da52b67c87732af2a6ac0 + languageName: node + linkType: hard + +"@walletconnect/window-getters@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/window-getters@npm:1.0.1" + dependencies: + tslib: "npm:1.14.1" + checksum: 8d3fcb134fbbe903ba4a63f1fa5a7849fd443874bf45488260afc2fe3b1cbe211f86da1d76ee844be7c0e8618ae67402f94c213432fd80b04715eaf72e2e00e3 + languageName: node + linkType: hard + +"@walletconnect/window-metadata@npm:^1.0.1": + version: 1.0.1 + resolution: "@walletconnect/window-metadata@npm:1.0.1" + dependencies: + "@walletconnect/window-getters": "npm:^1.0.1" + tslib: "npm:1.14.1" + checksum: cf322e0860c4448cefcd81f34bc6d49d1a235a81e74a6146baefb74e47cf6c3c8050b65e534a3dc13f8d2aed3fc59732ccf48d5a01b5b23e08e1847fcffa950c + languageName: node + linkType: hard + +"JSONStream@npm:^1.3.5": + version: 1.3.5 + resolution: "JSONStream@npm:1.3.5" dependencies: jsonparse: "npm:^1.2.0" through: "npm:>=2.2.7 <3" @@ -6491,6 +8779,19 @@ __metadata: languageName: node linkType: hard +"abitype@npm:^0.3.0": + version: 0.3.0 + resolution: "abitype@npm:0.3.0" + peerDependencies: + typescript: ">=4.9.4" + zod: ">=3.19.1" + peerDependenciesMeta: + zod: + optional: true + checksum: 5da1f1fa953b77fbe13586419ef5d2908e585a53907dab658b1e87dd7744ec636d990eb09902c9bf216d5976d0ea1b45644de61434971ae94410ae40483b59dd + languageName: node + linkType: hard + "abortcontroller-polyfill@npm:^1.7.3": version: 1.7.3 resolution: "abortcontroller-polyfill@npm:1.7.3" @@ -6579,6 +8880,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.11.3": + version: 8.11.3 + resolution: "acorn@npm:8.11.3" + bin: + acorn: bin/acorn + checksum: b688e7e3c64d9bfb17b596e1b35e4da9d50553713b3b3630cf5690f2b023a84eac90c56851e6912b483fe60e8b4ea28b254c07e92f17ef83d72d78745a8352dd + languageName: node + linkType: hard + "acorn@npm:^8.4.1": version: 8.7.1 resolution: "acorn@npm:8.7.1" @@ -6618,6 +8928,13 @@ __metadata: languageName: node linkType: hard +"aes-js@npm:^3.1.2": + version: 3.1.2 + resolution: "aes-js@npm:3.1.2" + checksum: b65916767034a51375a3ac5aad62af452d89a386c1ae7b607bb9145d0bb8b8823bf2f3eba85bdfa52d61c65d5aed90ba90f677b8c826bfa1a8b7ae2fa3b54d91 + languageName: node + linkType: hard + "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -6657,6 +8974,13 @@ __metadata: languageName: node linkType: hard +"ahocorasick@npm:1.0.2": + version: 1.0.2 + resolution: "ahocorasick@npm:1.0.2" + checksum: b2da9f3a7e6faae9975ffdb15a0d7a6c6590f8cf902fe8dc08ba972b59b61a25276923d7776fbd1844685a15600c24353dee3ee5a1cb27244fd64f1522b2c04a + languageName: node + linkType: hard + "ajv@npm:^6.10.0, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -6709,7 +9033,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -6780,6 +9104,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: d7f4e97ce0623aea6bc0d90dcd28881ee04cba06c570b97fd3391bd7a268eedfd9d5e2dd4fdcbdd82b8105df5faf6f24aaedc08eaf3da898e702db5948f63469 + languageName: node + linkType: hard + "ansi-styles@npm:^6.0.0": version: 6.1.0 resolution: "ansi-styles@npm:6.1.0" @@ -6803,6 +9134,16 @@ __metadata: languageName: node linkType: hard +"anymatch@npm:^3.0.3, anymatch@npm:^3.1.3": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 3e044fd6d1d26545f235a9fe4d7a534e2029d8e59fa7fd9f2a6eb21230f6b5380ea1eaf55136e60cbf8e613544b3b766e7a6fa2102e2a3a117505466e3025dc2 + languageName: node + linkType: hard + "anymatch@npm:~3.1.1, anymatch@npm:~3.1.2": version: 3.1.2 resolution: "anymatch@npm:3.1.2" @@ -7045,6 +9386,15 @@ __metadata: languageName: node linkType: hard +"async-mutex@npm:^0.2.6": + version: 0.2.6 + resolution: "async-mutex@npm:0.2.6" + dependencies: + tslib: "npm:^2.0.0" + checksum: 3cf676fc48b4686abf534cc02d4784bab3f35d7836a0a7476c96e57c3f6607dd3d94cc0989b29d33ce5ae5cde8be8e1a96f3e769ba3b0e1ba4a244f873aa5623 + languageName: node + linkType: hard + "async@npm:^2.6.4": version: 2.6.4 resolution: "async@npm:2.6.4" @@ -7068,6 +9418,13 @@ __metadata: languageName: node linkType: hard +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: 3ab6d2cf46b31394b4607e935ec5c1c3c4f60f3e30f0913d35ea74b51b3585e84f590d09e58067f11762eec71c87d25314ce859030983dc0e4397eed21daa12e + languageName: node + linkType: hard + "available-typed-arrays@npm:^1.0.5": version: 1.0.5 resolution: "available-typed-arrays@npm:1.0.5" @@ -7131,6 +9488,82 @@ __metadata: languageName: node linkType: hard +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" + dependencies: + "@jest/transform": "npm:^29.7.0" + "@types/babel__core": "npm:^7.1.14" + babel-plugin-istanbul: "npm:^6.1.1" + babel-preset-jest: "npm:^29.6.3" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + slash: "npm:^3.0.0" + peerDependencies: + "@babel/core": ^7.8.0 + checksum: 8a0953bd813b3a8926008f7351611055548869e9a53dd36d6e7e96679001f71e65fd7dbfe253265c3ba6a4e630dc7c845cf3e78b17d758ef1880313ce8fba258 + languageName: node + linkType: hard + +"babel-plugin-istanbul@npm:^6.1.1": + version: 6.1.1 + resolution: "babel-plugin-istanbul@npm:6.1.1" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.0.0" + "@istanbuljs/load-nyc-config": "npm:^1.0.0" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-instrument: "npm:^5.0.4" + test-exclude: "npm:^6.0.0" + checksum: ffd436bb2a77bbe1942a33245d770506ab2262d9c1b3c1f1da7f0592f78ee7445a95bc2efafe619dd9c1b6ee52c10033d6c7d29ddefe6f5383568e60f31dfe8d + languageName: node + linkType: hard + +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" + dependencies: + "@babel/template": "npm:^7.3.3" + "@babel/types": "npm:^7.3.3" + "@types/babel__core": "npm:^7.1.14" + "@types/babel__traverse": "npm:^7.0.6" + checksum: 9bfa86ec4170bd805ab8ca5001ae50d8afcb30554d236ba4a7ffc156c1a92452e220e4acbd98daefc12bf0216fccd092d0a2efed49e7e384ec59e0597a926d65 + languageName: node + linkType: hard + +"babel-preset-current-node-syntax@npm:^1.0.0": + version: 1.0.1 + resolution: "babel-preset-current-node-syntax@npm:1.0.1" + dependencies: + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + "@babel/plugin-syntax-bigint": "npm:^7.8.3" + "@babel/plugin-syntax-class-properties": "npm:^7.8.3" + "@babel/plugin-syntax-import-meta": "npm:^7.8.3" + "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/plugin-syntax-numeric-separator": "npm:^7.8.3" + "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" + "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/plugin-syntax-top-level-await": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 94561959cb12bfa80867c9eeeace7c3d48d61707d33e55b4c3fdbe82fc745913eb2dbfafca62aef297421b38aadcb58550e5943f50fbcebbeefd70ce2bed4b74 + languageName: node + linkType: hard + +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" + dependencies: + babel-plugin-jest-hoist: "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -7219,6 +9652,13 @@ __metadata: languageName: node linkType: hard +"bignumber.js@npm:^9.1.2": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: d89b8800a987225d2c00dcbf8a69dc08e92aa0880157c851c287b307d31ceb2fc2acb0c62c3e3a3d42b6c5fcae9b004035f13eb4386e56d529d7edac18d5c9d8 + languageName: node + linkType: hard + "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -7320,6 +9760,26 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.20.1": + version: 1.20.1 + resolution: "body-parser@npm:1.20.1" + dependencies: + bytes: "npm:3.1.2" + content-type: "npm:~1.0.4" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + on-finished: "npm:2.4.1" + qs: "npm:6.11.0" + raw-body: "npm:2.5.1" + type-is: "npm:~1.6.18" + unpipe: "npm:1.0.0" + checksum: 5f8d128022a2fb8b6e7990d30878a0182f300b70e46b3f9d358a9433ad6275f0de46add6d63206da3637c01c3b38b6111a7480f7e7ac2e9f7b989f6133fe5510 + languageName: node + linkType: hard + "borsh@npm:^0.7.0": version: 0.7.0 resolution: "borsh@npm:0.7.0" @@ -7415,6 +9875,29 @@ __metadata: languageName: node linkType: hard +"browserslist@npm:^4.22.2": + version: 4.23.0 + resolution: "browserslist@npm:4.23.0" + dependencies: + caniuse-lite: "npm:^1.0.30001587" + electron-to-chromium: "npm:^1.4.668" + node-releases: "npm:^2.0.14" + update-browserslist-db: "npm:^1.0.13" + bin: + browserslist: cli.js + checksum: 496c3862df74565dd942b4ae65f502c575cbeba1fa4a3894dad7aa3b16130dc3033bc502d8848147f7b625154a284708253d9598bcdbef5a1e34cf11dc7bad8e + languageName: node + linkType: hard + +"bs-logger@npm:0.x": + version: 0.2.6 + resolution: "bs-logger@npm:0.2.6" + dependencies: + fast-json-stable-stringify: "npm:2.x" + checksum: e6d3ff82698bb3f20ce64fb85355c5716a3cf267f3977abe93bf9c32a2e46186b253f48a028ae5b96ab42bacd2c826766d9ae8cf6892f9b944656be9113cf212 + languageName: node + linkType: hard + "bs58@npm:^4.0.0, bs58@npm:^4.0.1": version: 4.0.1 resolution: "bs58@npm:4.0.1" @@ -7435,6 +9918,15 @@ __metadata: languageName: node linkType: hard +"bser@npm:2.1.1": + version: 2.1.1 + resolution: "bser@npm:2.1.1" + dependencies: + node-int64: "npm:^0.4.0" + checksum: edba1b65bae682450be4117b695997972bd9a3c4dfee029cab5bcb72ae5393a79a8f909b8bc77957eb0deec1c7168670f18f4d5c556f46cdd3bca5f3b3a8d020 + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -7519,7 +10011,7 @@ __metadata: languageName: node linkType: hard -"busboy@npm:^1.6.0": +"busboy@npm:1.6.0, busboy@npm:^1.6.0": version: 1.6.0 resolution: "busboy@npm:1.6.0" dependencies: @@ -7651,13 +10143,27 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.0.0": +"camelcase@npm:^6.0.0, camelcase@npm:^6.2.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d languageName: node linkType: hard +"caniuse-lite@npm:^1.0.30001406": + version: 1.0.30001589 + resolution: "caniuse-lite@npm:1.0.30001589" + checksum: 5e1d2eb7c32d48c52204227bc1377f0f4c758ef889c53b9b479e28470e7f82eb1db5853e7754be9600ee662ae32a1d58e8bef0fde6edab06322ddbabfd9d212f + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001587": + version: 1.0.30001587 + resolution: "caniuse-lite@npm:1.0.30001587" + checksum: 960e26927ad876971021186337df1df2d37d7ed4fc7907098c060f56ae8de737d471791e51387ca55bea07f56b0a76553a90125f88a2f958ca1f4f715013cf71 + languageName: node + linkType: hard + "case@npm:^1.6.3": version: 1.6.3 resolution: "case@npm:1.6.3" @@ -7730,7 +10236,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -7747,6 +10253,13 @@ __metadata: languageName: node linkType: hard +"char-regex@npm:^1.0.2": + version: 1.0.2 + resolution: "char-regex@npm:1.0.2" + checksum: 1ec5c2906adb9f84e7f6732a40baef05d7c85401b82ffcbc44b85fbd0f7a2b0c2a96f2eb9cf55cae3235dc12d4023003b88f09bcae8be9ae894f52ed746f4d48 + languageName: node + linkType: hard + "chardet@npm:^0.7.0": version: 0.7.0 resolution: "chardet@npm:0.7.0" @@ -7815,6 +10328,25 @@ __metadata: languageName: node linkType: hard +"chokidar@npm:^3.5.2, chokidar@npm:^3.5.3": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: c327fb07704443f8d15f7b4a7ce93b2f0bc0e6cea07ec28a7570aa22cd51fcf0379df589403976ea956c369f25aa82d84561947e227cd925902e1751371658df + languageName: node + linkType: hard + "chownr@npm:^1.1.1, chownr@npm:^1.1.4": version: 1.1.4 resolution: "chownr@npm:1.1.4" @@ -7866,6 +10398,22 @@ __metadata: languageName: node linkType: hard +"citty@npm:^0.1.5": + version: 0.1.6 + resolution: "citty@npm:0.1.6" + dependencies: + consola: "npm:^3.2.3" + checksum: 3208947e73abb699a12578ee2bfee254bf8dd1ce0d5698e8a298411cabf16bd3620d63433aef5bd88cdb2b9da71aef18adefa3b4ffd18273bb62dd1d28c344f5 + languageName: node + linkType: hard + +"cjs-module-lexer@npm:^1.0.0": + version: 1.2.3 + resolution: "cjs-module-lexer@npm:1.2.3" + checksum: f96a5118b0a012627a2b1c13bd2fcb92509778422aaa825c5da72300d6dcadfb47134dd2e9d97dfa31acd674891dd91642742772d19a09a8adc3e56bd2f5928c + languageName: node + linkType: hard + "class-is@npm:^1.1.0": version: 1.1.0 resolution: "class-is@npm:1.1.0" @@ -7951,6 +10499,24 @@ __metadata: languageName: node linkType: hard +"client-only@npm:0.0.1, client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 + languageName: node + linkType: hard + +"clipboardy@npm:^4.0.0": + version: 4.0.0 + resolution: "clipboardy@npm:4.0.0" + dependencies: + execa: "npm:^8.0.1" + is-wsl: "npm:^3.1.0" + is64bit: "npm:^2.0.0" + checksum: ec4ebe7e5c81d9c9cb994637e7b0e068c1c8fc272167ecd5519f967427271ec66e0e64da7268a2630b860eff42933aeabe25ba5e42bb80dbf1fae6362df059ed + languageName: node + linkType: hard + "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -8011,6 +10577,34 @@ __metadata: languageName: node linkType: hard +"clsx@npm:1.1.1": + version: 1.1.1 + resolution: "clsx@npm:1.1.1" + checksum: ff052650329773b9b245177305fc4c4dc3129f7b2be84af4f58dc5defa99538c61d4207be7419405a5f8f3d92007c954f4daba5a7b74e563d5de71c28c830063 + languageName: node + linkType: hard + +"clsx@npm:^1.1.1, clsx@npm:^1.2.1": + version: 1.2.1 + resolution: "clsx@npm:1.2.1" + checksum: 5ded6f61f15f1fa0350e691ccec43a28b12fb8e64c8e94715f2a937bc3722d4c3ed41d6e945c971fc4dcc2a7213a43323beaf2e1c28654af63ba70c9968a8643 + languageName: node + linkType: hard + +"cluster-key-slot@npm:^1.1.0": + version: 1.1.2 + resolution: "cluster-key-slot@npm:1.1.2" + checksum: 516ed8b5e1a14d9c3a9c96c72ef6de2d70dfcdbaa0ec3a90bc7b9216c5457e39c09a5775750c272369070308542e671146120153062ab5f2f481bed5de2c925f + languageName: node + linkType: hard + +"co@npm:^4.6.0": + version: 4.6.0 + resolution: "co@npm:4.6.0" + checksum: a5d9f37091c70398a269e625cedff5622f200ed0aa0cff22ee7b55ed74a123834b58711776eb0f1dc58eb6ebbc1185aa7567b57bd5979a948c6e4f85073e2c05 + languageName: node + linkType: hard + "code-point-at@npm:^1.0.0": version: 1.1.0 resolution: "code-point-at@npm:1.1.0" @@ -8025,6 +10619,13 @@ __metadata: languageName: node linkType: hard +"collect-v8-coverage@npm:^1.0.0": + version: 1.0.2 + resolution: "collect-v8-coverage@npm:1.0.2" + checksum: 30ea7d5c9ee51f2fdba4901d4186c5b7114a088ef98fd53eda3979da77eed96758a2cae81cc6d97e239aaea6065868cf908b24980663f7b7e96aa291b3e12fa4 + languageName: node + linkType: hard + "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -8184,6 +10785,13 @@ __metadata: languageName: node linkType: hard +"consola@npm:^3.2.3": + version: 3.2.3 + resolution: "consola@npm:3.2.3" + checksum: 02972dcb048c337357a3628438e5976b8e45bcec22fdcfbe9cd17622992953c4d695d5152f141464a02deac769b1d23028e8ac87f56483838df7a6bbf8e0f5a2 + languageName: node + linkType: hard + "console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0, console-control-strings@npm:~1.1.0": version: 1.1.0 resolution: "console-control-strings@npm:1.1.0" @@ -8218,6 +10826,20 @@ __metadata: languageName: node linkType: hard +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: c987be3ec061348cdb3c2bfb924bec86dea1eacad10550a85ca23edb0fe3556c3a61c7399114f3331ccb3499d7fd0285ab24566e5745929412983494c3926e15 + languageName: node + linkType: hard + +"cookie-es@npm:^1.0.0": + version: 1.0.0 + resolution: "cookie-es@npm:1.0.0" + checksum: 7654e65c3a0b6b6e5d695aa05da72e5e77235a0a8bc3ac94afb3be250db82bea721aa18fb879d6ebc9627ea39c3efc8211ef76bf24bc534e600ac575929f2f1b + languageName: node + linkType: hard + "cookie-signature@npm:1.0.6": version: 1.0.6 resolution: "cookie-signature@npm:1.0.6" @@ -8239,6 +10861,15 @@ __metadata: languageName: node linkType: hard +"copy-to-clipboard@npm:^3.3.3": + version: 3.3.3 + resolution: "copy-to-clipboard@npm:3.3.3" + dependencies: + toggle-selection: "npm:^1.0.6" + checksum: e0a325e39b7615108e6c1c8ac110ae7b829cdc4ee3278b1df6a0e4228c490442cc86444cd643e2da344fbc424b3aab8909e2fec82f8bc75e7e5b190b7c24eecf + languageName: node + linkType: hard + "core-js-pure@npm:^3.0.1": version: 3.22.8 resolution: "core-js-pure@npm:3.22.8" @@ -8260,7 +10891,7 @@ __metadata: languageName: node linkType: hard -"cors@npm:^2.8.1": +"cors@npm:^2.8.1, cors@npm:^2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" dependencies: @@ -8340,6 +10971,23 @@ __metadata: languageName: node linkType: hard +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + exit: "npm:^0.1.2" + graceful-fs: "npm:^4.2.9" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + prompts: "npm:^2.0.1" + bin: + create-jest: bin/create-jest.js + checksum: 847b4764451672b4174be4d5c6d7d63442ec3aa5f3de52af924e4d996d87d7801c18e125504f25232fc75840f6625b3ac85860fac6ce799b5efae7bdcaf4a2b7 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -8378,6 +11026,13 @@ __metadata: languageName: node linkType: hard +"crossws@npm:^0.1.0": + version: 0.1.1 + resolution: "crossws@npm:0.1.1" + checksum: 2029638e059f4bb62c10fe420e24d7fff97bcf9f93e380e0d90f117cf055deb7f07dae16df65a0aaeeb8ecd7b71fedd735deca7dde56351ea7b377915c37b68b + languageName: node + linkType: hard + "crypt@npm:>= 0.0.1": version: 0.0.2 resolution: "crypt@npm:0.0.2" @@ -8392,6 +11047,29 @@ __metadata: languageName: node linkType: hard +"css-what@npm:^5.0.1": + version: 5.1.0 + resolution: "css-what@npm:5.1.0" + checksum: 3b1f0abdf104a2e887be45c5b710b063d3fa7468d1d1eea071fbd6e5b3e2e7d4c0cb001edec07ea5a360c06425f351e0356539155b70ea461382c9c7bcaba4d7 + languageName: node + linkType: hard + +"cssesc@npm:^3.0.0": + version: 3.0.0 + resolution: "cssesc@npm:3.0.0" + bin: + cssesc: bin/cssesc + checksum: 0e161912c1306861d8f46e1883be1cbc8b1b2879f0f509287c0db71796e4ddfb97ac96bdfca38f77f452e2c10554e1bb5678c99b07a5cf947a12778f73e47e12 + languageName: node + linkType: hard + +"csstype@npm:^3.0.2, csstype@npm:^3.0.7": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: f593cce41ff5ade23f44e77521e3a1bcc2c64107041e1bf6c3c32adc5187d0d60983292fda326154d20b01079e24931aa5b08e4467cc488b60bb1e7f6d478ade + languageName: node + linkType: hard + "csv-generate@npm:^3.4.3": version: 3.4.3 resolution: "csv-generate@npm:3.4.3" @@ -8469,7 +11147,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -8521,6 +11199,13 @@ __metadata: languageName: node linkType: hard +"decode-uri-component@npm:^0.2.2": + version: 0.2.2 + resolution: "decode-uri-component@npm:0.2.2" + checksum: 17a0e5fa400bf9ea84432226e252aa7b5e72793e16bf80b907c99b46a799aeacc139ec20ea57121e50c7bd875a1a4365928f884e92abf02e21a5a13790a0f33e + languageName: node + linkType: hard + "decompress-response@npm:^3.2.0, decompress-response@npm:^3.3.0": version: 3.3.0 resolution: "decompress-response@npm:3.3.0" @@ -8548,6 +11233,18 @@ __metadata: languageName: node linkType: hard +"dedent@npm:^1.0.0": + version: 1.5.1 + resolution: "dedent@npm:1.5.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: fc00a8bc3dfb7c413a778dc40ee8151b6c6ff35159d641f36ecd839c1df5c6e0ec5f4992e658c82624a1a62aaecaffc23b9c965ceb0bbf4d698bfc16469ac27d + languageName: node + linkType: hard + "deep-eql@npm:^3.0.1": version: 3.0.1 resolution: "deep-eql@npm:3.0.1" @@ -8580,6 +11277,27 @@ __metadata: languageName: node linkType: hard +"deep-object-diff@npm:^1.1.0": + version: 1.1.9 + resolution: "deep-object-diff@npm:1.1.9" + checksum: b9771cc1ca08a34e408309eaab967bd2ab697684abdfa1262f4283ced8230a9ace966322f356364ff71a785c6e9cc356b7596582e900da5726e6b87d4b2a1463 + languageName: node + linkType: hard + +"deepmerge@npm:^2.1.1": + version: 2.2.1 + resolution: "deepmerge@npm:2.2.1" + checksum: a3da411cd3d471a8ae86ff7fd5e19abb648377b3f8c42a9e4c822406c2960a391cb829e4cca53819b73715e68f56b06f53c643ca7bba21cab569fecc9a723de1 + languageName: node + linkType: hard + +"deepmerge@npm:^4.2.2": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 058d9e1b0ff1a154468bf3837aea436abcfea1ba1d165ddaaf48ca93765fdd01a30d33c36173da8fbbed951dd0a267602bc782fe288b0fc4b7e1e7091afc4529 + languageName: node + linkType: hard + "defaults@npm:^1.0.3": version: 1.0.4 resolution: "defaults@npm:1.0.4" @@ -8638,6 +11356,13 @@ __metadata: languageName: node linkType: hard +"defu@npm:^6.1.3, defu@npm:^6.1.4": + version: 6.1.4 + resolution: "defu@npm:6.1.4" + checksum: aeffdb47300f45b4fdef1c5bd3880ac18ea7a1fd5b8a8faf8df29350ff03bf16dd34f9800205cab513d476e4c0a3783aa0cff0a433aff0ac84a67ddc4c8a2d64 + languageName: node + linkType: hard + "delay@npm:^5.0.0": version: 5.0.0 resolution: "delay@npm:5.0.0" @@ -8659,6 +11384,13 @@ __metadata: languageName: node linkType: hard +"denque@npm:^2.1.0": + version: 2.1.0 + resolution: "denque@npm:2.1.0" + checksum: 8ea05321576624b90acfc1ee9208b8d1d04b425cf7573b9b4fa40a2c3ed4d4b0af5190567858f532f677ed2003d4d2b73c8130b34e3c7b8d5e88cdcfbfaa1fe7 + languageName: node + linkType: hard + "depd@npm:2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -8673,6 +11405,13 @@ __metadata: languageName: node linkType: hard +"destr@npm:^2.0.1, destr@npm:^2.0.2": + version: 2.0.3 + resolution: "destr@npm:2.0.3" + checksum: dbb756baa876810ec0ca4bcb702d86cc3b480ed14f36bf5747718ed211f96bca5520b63a4109eb181ad940ee2a645677d9a63d4a0ed11a7510619dae97317201 + languageName: node + linkType: hard + "destroy@npm:1.2.0": version: 1.2.0 resolution: "destroy@npm:1.2.0" @@ -8680,6 +11419,13 @@ __metadata: languageName: node linkType: hard +"detect-browser@npm:5.3.0, detect-browser@npm:^5.3.0": + version: 5.3.0 + resolution: "detect-browser@npm:5.3.0" + checksum: 4a8551e1f5170633c9aa976f16c57f81f1044d071b2eb853c572bd817bf9cd0cc90c9c520d950edb5accd31b1b0c8ddb7a96e82040b0b5579f9f09c77446a117 + languageName: node + linkType: hard + "detect-indent@npm:^6.0.0": version: 6.1.0 resolution: "detect-indent@npm:6.1.0" @@ -8696,6 +11442,20 @@ __metadata: languageName: node linkType: hard +"detect-newline@npm:^3.0.0": + version: 3.1.0 + resolution: "detect-newline@npm:3.1.0" + checksum: ae6cd429c41ad01b164c59ea36f264a2c479598e61cba7c99da24175a7ab80ddf066420f2bec9a1c57a6bead411b4655ff15ad7d281c000a89791f48cbe939e7 + languageName: node + linkType: hard + +"detect-node-es@npm:^1.1.0": + version: 1.1.0 + resolution: "detect-node-es@npm:1.1.0" + checksum: e46307d7264644975b71c104b9f028ed1d3d34b83a15b8a22373640ce5ea630e5640b1078b8ea15f202b54641da71e4aa7597093bd4b91f113db520a26a37449 + languageName: node + linkType: hard + "detect-port@npm:^1.3.0": version: 1.3.0 resolution: "detect-port@npm:1.3.0" @@ -8709,6 +11469,13 @@ __metadata: languageName: node linkType: hard +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 179daf9d2f9af5c57ad66d97cb902a538bcf8ed64963fa7aa0c329b3de3665ce2eb6ffdc2f69f29d445fa4af2517e5e55e5b6e00c00a9ae4f43645f97f7078cb + languageName: node + linkType: hard + "diff@npm:3.5.0": version: 3.5.0 resolution: "diff@npm:3.5.0" @@ -8746,6 +11513,13 @@ __metadata: languageName: node linkType: hard +"dijkstrajs@npm:^1.0.1": + version: 1.0.3 + resolution: "dijkstrajs@npm:1.0.3" + checksum: 0d8429699a6d5897ed371de494ef3c7072e8052b42abbd978e686a9b8689e70af005fa3e93e93263ee3653673ff5f89c36db830a57ae7c2e088cb9c496307507 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -8771,6 +11545,15 @@ __metadata: languageName: node linkType: hard +"dotenv-flow@npm:^4.1.0": + version: 4.1.0 + resolution: "dotenv-flow@npm:4.1.0" + dependencies: + dotenv: "npm:^16.0.0" + checksum: 75c36fefbc25e8c4f2cd6cff0a161444c7f2e992ea24662c63887aa57950007355df5bf153a6acc7e20c05ff3e5b783150e255ce543464a748de32bb9c6e222c + languageName: node + linkType: hard + "dotenv@npm:^10.0.0": version: 10.0.0 resolution: "dotenv@npm:10.0.0" @@ -8778,6 +11561,13 @@ __metadata: languageName: node linkType: hard +"dotenv@npm:^16.0.0": + version: 16.4.4 + resolution: "dotenv@npm:16.4.4" + checksum: ddf43ede209d5f54c9da688e93326162eb0fc367ad1869909568cb15571ab8c87a0fab4e1d4af9860be0571c707a8b4aefa060a4f1b0bb14298a81e0ccf0017d + languageName: node + linkType: hard + "duplexer3@npm:^0.1.4": version: 0.1.4 resolution: "duplexer3@npm:0.1.4" @@ -8785,6 +11575,18 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^4.1.2": + version: 4.1.2 + resolution: "duplexify@npm:4.1.2" + dependencies: + end-of-stream: "npm:^1.4.1" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + stream-shift: "npm:^1.0.0" + checksum: eeb4f362defa4da0b2474d853bc4edfa446faeb1bde76819a68035632c118de91f6a58e6fe05c84f6e6de2548f8323ec8473aa9fe37332c99e4d77539747193e + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -8809,6 +11611,13 @@ __metadata: languageName: node linkType: hard +"electron-to-chromium@npm:^1.4.668": + version: 1.4.671 + resolution: "electron-to-chromium@npm:1.4.671" + checksum: 2b3d85feb86800020bb249ace65dec428452a2e035145718c08023de788873ca3d35915845824cbe6773ee40dc35ecd596b45fc9e2c16de58ecdda6f551cba90 + languageName: node + linkType: hard + "elliptic@npm:6.5.4, elliptic@npm:^6.4.0, elliptic@npm:^6.5.2, elliptic@npm:^6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -8831,6 +11640,13 @@ __metadata: languageName: node linkType: hard +"emittery@npm:^0.13.1": + version: 0.13.1 + resolution: "emittery@npm:0.13.1" + checksum: fbe214171d878b924eedf1757badf58a5dce071cd1fa7f620fa841a0901a80d6da47ff05929d53163105e621ce11a71b9d8acb1148ffe1745e045145f6e69521 + languageName: node + linkType: hard + "emoji-regex@npm:^7.0.1": version: 7.0.3 resolution: "emoji-regex@npm:7.0.3" @@ -8852,6 +11668,13 @@ __metadata: languageName: node linkType: hard +"encode-utf8@npm:^1.0.3": + version: 1.0.3 + resolution: "encode-utf8@npm:1.0.3" + checksum: 0204c37cda21bf19bb8f87f7ec6c89a23d43488c2ef1e5cfa40b64ee9568e63e15dc323fa7f50a491e2c6d33843a6b409f6de09afbf6cf371cb8da596cc64b44 + languageName: node + linkType: hard + "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -9131,6 +11954,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^2.0.0": + version: 2.0.0 + resolution: "escape-string-regexp@npm:2.0.0" + checksum: 9f8a2d5743677c16e85c810e3024d54f0c8dea6424fad3c79ef6666e81dd0846f7437f5e729dfcdac8981bc9e5294c39b4580814d114076b8d36318f46ae4395 + languageName: node + linkType: hard + "escodegen@npm:1.8.x": version: 1.8.1 resolution: "escodegen@npm:1.8.1" @@ -9328,6 +12158,19 @@ __metadata: languageName: node linkType: hard +"eth-block-tracker@npm:^7.1.0": + version: 7.1.0 + resolution: "eth-block-tracker@npm:7.1.0" + dependencies: + "@metamask/eth-json-rpc-provider": "npm:^1.0.0" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^5.0.1" + json-rpc-random-id: "npm:^1.0.1" + pify: "npm:^3.0.0" + checksum: b001ecb126e949a9ff19950596d5180b2f1bc5504e3dec0c01b3417e8ad190f4a53dfc61be901b72ab6dd558d1d711b73eca560bc8a605d0348eef9f501defab + languageName: node + linkType: hard + "eth-ens-namehash@npm:2.0.8": version: 2.0.8 resolution: "eth-ens-namehash@npm:2.0.8" @@ -9366,6 +12209,19 @@ __metadata: languageName: node linkType: hard +"eth-json-rpc-filters@npm:^6.0.0": + version: 6.0.1 + resolution: "eth-json-rpc-filters@npm:6.0.1" + dependencies: + "@metamask/safe-event-emitter": "npm:^3.0.0" + async-mutex: "npm:^0.2.6" + eth-query: "npm:^2.1.2" + json-rpc-engine: "npm:^6.1.0" + pify: "npm:^5.0.0" + checksum: d1fa8bb21da07c2f5d37c1e6053d499b272b4f49542077efc6b05eebe49affa9df7221c8c2439c4e33caa3f4ccb35240a6105abc83b83375dae03c0de53113a7 + languageName: node + linkType: hard + "eth-lib@npm:0.2.8": version: 0.2.8 resolution: "eth-lib@npm:0.2.8" @@ -9391,6 +12247,25 @@ __metadata: languageName: node linkType: hard +"eth-query@npm:^2.1.2": + version: 2.1.2 + resolution: "eth-query@npm:2.1.2" + dependencies: + json-rpc-random-id: "npm:^1.0.0" + xtend: "npm:^4.0.1" + checksum: af4f3575b8315f8156a83a24e850881053748aca97e4aee12dd6645ab56f0985c7000a5c45ccf315702f3e532f0c6464e03f4aba294c658dee89f5e5d1b86702 + languageName: node + linkType: hard + +"eth-rpc-errors@npm:^4.0.2": + version: 4.0.3 + resolution: "eth-rpc-errors@npm:4.0.3" + dependencies: + fast-safe-stringify: "npm:^2.0.6" + checksum: 47ce14170eabaee51ab1cc7e643bb3ef96ee6b15c6404806aedcd51750e00ae0b1a12c37785b180679b8d452b6dd44a0240bb018d01fa73efc85fcfa808b35a7 + languageName: node + linkType: hard + "ethereum-bloom-filters@npm:^1.0.6": version: 1.0.10 resolution: "ethereum-bloom-filters@npm:1.0.10" @@ -9435,6 +12310,18 @@ __metadata: languageName: node linkType: hard +"ethereum-cryptography@npm:^2.0.0": + version: 2.1.3 + resolution: "ethereum-cryptography@npm:2.1.3" + dependencies: + "@noble/curves": "npm:1.3.0" + "@noble/hashes": "npm:1.3.3" + "@scure/bip32": "npm:1.3.3" + "@scure/bip39": "npm:1.2.2" + checksum: cc5aa9a4368dc1dd7680ba921957c098ced7b3d7dbb1666334013ab2f8d4cd25a785ad84e66fd9f5c5a9b6de337930ea24ff8c722938f36a9c00cec597ca16b5 + languageName: node + linkType: hard + "ethereum-waffle@npm:^4.0.10": version: 4.0.10 resolution: "ethereum-waffle@npm:4.0.10" @@ -9504,24 +12391,7 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^4.0.40": - version: 4.0.49 - resolution: "ethers@npm:4.0.49" - dependencies: - aes-js: "npm:3.0.0" - bn.js: "npm:^4.11.9" - elliptic: "npm:6.5.4" - hash.js: "npm:1.1.3" - js-sha3: "npm:0.5.7" - scrypt-js: "npm:2.0.4" - setimmediate: "npm:1.0.4" - uuid: "npm:2.0.1" - xmlhttprequest: "npm:1.8.0" - checksum: a4cec0254f940a0fb118317d23676faa46eb5540fc0a3b9177b8aef71318f509ed19b8264f102b1a2a32d0256274ecc526fd926bd22a4a4ac25cd8e0e6560f12 - languageName: node - linkType: hard - -"ethers@npm:^5.1.0, ethers@npm:^5.7.0, ethers@npm:^5.7.1, ethers@npm:^5.7.2": +"ethers@npm:5.7.2, ethers@npm:^5.1.0, ethers@npm:^5.3.1, ethers@npm:^5.7.0, ethers@npm:^5.7.1, ethers@npm:^5.7.2": version: 5.7.2 resolution: "ethers@npm:5.7.2" dependencies: @@ -9559,6 +12429,23 @@ __metadata: languageName: node linkType: hard +"ethers@npm:^4.0.40": + version: 4.0.49 + resolution: "ethers@npm:4.0.49" + dependencies: + aes-js: "npm:3.0.0" + bn.js: "npm:^4.11.9" + elliptic: "npm:6.5.4" + hash.js: "npm:1.1.3" + js-sha3: "npm:0.5.7" + scrypt-js: "npm:2.0.4" + setimmediate: "npm:1.0.4" + uuid: "npm:2.0.1" + xmlhttprequest: "npm:1.8.0" + checksum: a4cec0254f940a0fb118317d23676faa46eb5540fc0a3b9177b8aef71318f509ed19b8264f102b1a2a32d0256274ecc526fd926bd22a4a4ac25cd8e0e6560f12 + languageName: node + linkType: hard + "ethjs-unit@npm:0.1.6": version: 0.1.6 resolution: "ethjs-unit@npm:0.1.6" @@ -9593,6 +12480,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: ac6423ec31124629c84c7077eed1e6987f6d66c31cf43c6fcbf6c87791d56317ce808d9ead483652436df171b526fc7220eccdc9f3225df334e81582c3cf7dd5 + languageName: node + linkType: hard + "events@npm:^3.2.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" @@ -9611,7 +12505,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^5.1.1": +"execa@npm:^5.0.0, execa@npm:^5.1.1": version: 5.1.1 resolution: "execa@npm:5.1.1" dependencies: @@ -9628,6 +12522,30 @@ __metadata: languageName: node linkType: hard +"execa@npm:^8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: d2ab5fe1e2bb92b9788864d0713f1fce9a07c4594e272c0c97bc18c90569897ab262e4ea58d27a694d288227a2e24f16f5e2575b44224ad9983b799dc7f1098d + languageName: node + linkType: hard + +"exit@npm:^0.1.2": + version: 0.1.2 + resolution: "exit@npm:0.1.2" + checksum: 387555050c5b3c10e7a9e8df5f43194e95d7737c74532c409910e585d5554eaff34960c166643f5e23d042196529daad059c292dcf1fb61b8ca878d3677f4b87 + languageName: node + linkType: hard + "expand-template@npm:^2.0.3": version: 2.0.3 resolution: "expand-template@npm:2.0.3" @@ -9635,6 +12553,19 @@ __metadata: languageName: node linkType: hard +"expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" + dependencies: + "@jest/expect-utils": "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 63f97bc51f56a491950fb525f9ad94f1916e8a014947f8d8445d3847a665b5471b768522d659f5e865db20b6c2033d2ac10f35fcbd881a4d26407a4f6f18451a + languageName: node + linkType: hard + "express@npm:^4.14.0": version: 4.18.1 resolution: "express@npm:4.18.1" @@ -9674,6 +12605,45 @@ __metadata: languageName: node linkType: hard +"express@npm:^4.17.1": + version: 4.18.2 + resolution: "express@npm:4.18.2" + dependencies: + accepts: "npm:~1.3.8" + array-flatten: "npm:1.1.1" + body-parser: "npm:1.20.1" + content-disposition: "npm:0.5.4" + content-type: "npm:~1.0.4" + cookie: "npm:0.5.0" + cookie-signature: "npm:1.0.6" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + finalhandler: "npm:1.2.0" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + merge-descriptors: "npm:1.0.1" + methods: "npm:~1.1.2" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + path-to-regexp: "npm:0.1.7" + proxy-addr: "npm:~2.0.7" + qs: "npm:6.11.0" + range-parser: "npm:~1.2.1" + safe-buffer: "npm:5.2.1" + send: "npm:0.18.0" + serve-static: "npm:1.15.0" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + type-is: "npm:~1.6.18" + utils-merge: "npm:1.0.1" + vary: "npm:~1.1.2" + checksum: 869ae89ed6ff4bed7b373079dc58e5dddcf2915a2669b36037ff78c99d675ae930e5fe052b35c24f56557d28a023bb1cbe3e2f2fb87eaab96a1cedd7e597809d + languageName: node + linkType: hard + "ext@npm:^1.1.2": version: 1.6.0 resolution: "ext@npm:1.6.0" @@ -9763,7 +12733,7 @@ __metadata: languageName: node linkType: hard -"fast-json-stable-stringify@npm:^2.0.0": +"fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" checksum: 2c20055c1fa43c922428f16ca8bb29f2807de63e5c851f665f7ac9790176c01c3b40335257736b299764a8d383388dabc73c8083b8e1bc3d99f0a941444ec60e @@ -9777,6 +12747,20 @@ __metadata: languageName: node linkType: hard +"fast-redact@npm:^3.0.0": + version: 3.3.0 + resolution: "fast-redact@npm:3.3.0" + checksum: a69c5cb52396eafc4f466f46864406cbd4a6ead6782caf74750ce817794829048baaa933ad98543e744dd54ffb4cddff71f3e75e465a86e3d887894e281ec154 + languageName: node + linkType: hard + +"fast-safe-stringify@npm:^2.0.6": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: dc1f063c2c6ac9533aee14d406441f86783a8984b2ca09b19c2fe281f9ff59d315298bc7bc22fd1f83d26fe19ef2f20e2ddb68e96b15040292e555c5ced0c1e4 + languageName: node + linkType: hard + "fast-stable-stringify@npm:^1.0.0": version: 1.0.0 resolution: "fast-stable-stringify@npm:1.0.0" @@ -9802,6 +12786,15 @@ __metadata: languageName: node linkType: hard +"fb-watchman@npm:^2.0.0": + version: 2.0.2 + resolution: "fb-watchman@npm:2.0.2" + dependencies: + bser: "npm:2.1.1" + checksum: 4f95d336fb805786759e383fd7fff342ceb7680f53efcc0ef82f502eb479ce35b98e8b207b6dfdfeea0eba845862107dc73813775fc6b56b3098c6e90a2dad77 + languageName: node + linkType: hard + "figures@npm:^3.2.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -9836,6 +12829,13 @@ __metadata: languageName: node linkType: hard +"filter-obj@npm:^1.1.0": + version: 1.1.0 + resolution: "filter-obj@npm:1.1.0" + checksum: 9d681939eec2b4b129cb4f307b7e93d954a0657421d4e5357d86093b26d3f4f570909ed43717dcfd62428b3cf8cddd9841b35f9d40d12ac62cfabaa677942593 + languageName: node + linkType: hard + "finalhandler@npm:1.2.0": version: 1.2.0 resolution: "finalhandler@npm:1.2.0" @@ -10017,6 +13017,24 @@ __metadata: languageName: node linkType: hard +"formik@npm:^2.2.9": + version: 2.4.5 + resolution: "formik@npm:2.4.5" + dependencies: + "@types/hoist-non-react-statics": "npm:^3.3.1" + deepmerge: "npm:^2.1.1" + hoist-non-react-statics: "npm:^3.3.0" + lodash: "npm:^4.17.21" + lodash-es: "npm:^4.17.21" + react-fast-compare: "npm:^2.0.1" + tiny-warning: "npm:^1.0.2" + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 223fb3e6b0a7803221c030364a015b9adb01b61f7aed7c64e28ef8341a3e7c94c7a70aef7ed9f65d03ac44e4e19972c1247fb0e39538e4e084833fd1fa3b11c4 + languageName: node + linkType: hard + "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -10142,6 +13160,16 @@ __metadata: languageName: node linkType: hard +"fsevents@npm:^2.3.2": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 4c1ade961ded57cdbfbb5cac5106ec17bc8bccd62e16343c569a0ceeca83b9dfef87550b4dc5cbb89642da412b20c5071f304c8c464b80415446e8e155a038c0 + conditions: os=darwin + languageName: node + linkType: hard + "fsevents@npm:~2.1.1": version: 2.1.3 resolution: "fsevents@npm:2.1.3" @@ -10162,6 +13190,15 @@ __metadata: languageName: node linkType: hard +"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + "fsevents@patch:fsevents@npm%3A~2.1.1#optional!builtin": version: 2.1.3 resolution: "fsevents@patch:fsevents@npm%3A2.1.3#optional!builtin::version=2.1.3&hash=31d12a" @@ -10290,6 +13327,13 @@ __metadata: languageName: node linkType: hard +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 17d8333460204fbf1f9160d067e1e77f908a5447febb49424b8ab043026049835c9ef3974445c57dbd39161f4d2b04356d7de12b2eecaa27a7a7ea7d871cbedd + languageName: node + linkType: hard + "get-caller-file@npm:^2.0.1, get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -10334,6 +13378,27 @@ __metadata: languageName: node linkType: hard +"get-nonce@npm:^1.0.0": + version: 1.0.1 + resolution: "get-nonce@npm:1.0.1" + checksum: ad5104871d114a694ecc506a2d406e2331beccb961fe1e110dc25556b38bcdbf399a823a8a375976cd8889668156a9561e12ebe3fa6a4c6ba169c8466c2ff868 + languageName: node + linkType: hard + +"get-package-type@npm:^0.1.0": + version: 0.1.0 + resolution: "get-package-type@npm:0.1.0" + checksum: bba0811116d11e56d702682ddef7c73ba3481f114590e705fc549f4d868972263896af313c57a25c076e3c0d567e11d919a64ba1b30c879be985fc9d44f96148 + languageName: node + linkType: hard + +"get-port-please@npm:^3.1.2": + version: 3.1.2 + resolution: "get-port-please@npm:3.1.2" + checksum: ec8b8da9f816edde114b76742ec29695730094904bb0e94309081e4adf3f797b483b9d648abcf5e0511c4e21a7bf68334672b9575f8b23bccf93bf97eb517f0e + languageName: node + linkType: hard + "get-port@npm:^3.1.0": version: 3.2.0 resolution: "get-port@npm:3.2.0" @@ -10364,6 +13429,13 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: dde5511e2e65a48e9af80fea64aff11b4921b14b6e874c6f8294c50975095af08f41bfb0b680c887f28b566dd6ec2cb2f960f9d36a323359be324ce98b766e9e + languageName: node + linkType: hard + "get-symbol-description@npm:^1.0.0": version: 1.0.0 resolution: "get-symbol-description@npm:1.0.0" @@ -10420,6 +13492,13 @@ __metadata: languageName: node linkType: hard +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: 9009529195a955c40d7b9690794aeff5ba665cc38f1519e111c58bb54366fd0c106bde80acf97ba4e533208eb53422c83b136611a54c5fefb1edd8dc267cb62e + languageName: node + linkType: hard + "glob@npm:7.1.3": version: 7.1.3 resolution: "glob@npm:7.1.3" @@ -10649,6 +13728,25 @@ __metadata: languageName: node linkType: hard +"got@npm:^13.0.0": + version: 13.0.0 + resolution: "got@npm:13.0.0" + dependencies: + "@sindresorhus/is": "npm:^5.2.0" + "@szmarczak/http-timer": "npm:^5.0.1" + cacheable-lookup: "npm:^7.0.0" + cacheable-request: "npm:^10.2.8" + decompress-response: "npm:^6.0.0" + form-data-encoder: "npm:^2.1.2" + get-stream: "npm:^6.0.1" + http2-wrapper: "npm:^2.1.10" + lowercase-keys: "npm:^3.0.0" + p-cancelable: "npm:^3.0.0" + responselike: "npm:^3.0.0" + checksum: 35ac9fe37daca3d0a4f90305d8e64626268ef5a42584f5bcb42eea3cb9bbeb691cf9041d5ea72133a7295d1291684789a3148ff89a95f3d3ce3d0ebb6fb2f680 + languageName: node + linkType: hard + "got@npm:^7.1.0": version: 7.1.0 resolution: "got@npm:7.1.0" @@ -10678,7 +13776,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.5": +"graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: bf152d0ed1dc159239db1ba1f74fdbc40cb02f626770dcd5815c427ce0688c2635a06ed69af364396da4636d0408fcf7d4afdf7881724c3307e46aff30ca49e2 @@ -10699,6 +13797,13 @@ __metadata: languageName: node linkType: hard +"graphql@npm:^16.6.0": + version: 16.8.1 + resolution: "graphql@npm:16.8.1" + checksum: 7a09d3ec5f75061afe2bd2421a2d53cf37273d2ecaad8f34febea1f1ac205dfec2834aec3419fa0a10fcc9fb345863b2f893562fb07ea825da2ae82f6392893c + languageName: node + linkType: hard + "growl@npm:1.10.5": version: 1.10.5 resolution: "growl@npm:1.10.5" @@ -10706,6 +13811,23 @@ __metadata: languageName: node linkType: hard +"h3@npm:^1.10.1, h3@npm:^1.8.2": + version: 1.10.2 + resolution: "h3@npm:1.10.2" + dependencies: + cookie-es: "npm:^1.0.0" + defu: "npm:^6.1.4" + destr: "npm:^2.0.2" + iron-webcrypto: "npm:^1.0.0" + ohash: "npm:^1.1.3" + radix3: "npm:^1.1.0" + ufo: "npm:^1.3.2" + uncrypto: "npm:^0.1.3" + unenv: "npm:^1.9.0" + checksum: 7dbf129fe2d7eb9d4f306a3fb79e381842500476de49a124cfbc5cf94aff95cffa8b6e5e88b523b252abe3f20688de05fff837e8a4197eb3acec27b664427be2 + languageName: node + linkType: hard + "handlebars@npm:^4.0.1": version: 4.7.7 resolution: "handlebars@npm:4.7.7" @@ -10975,6 +14097,13 @@ __metadata: languageName: node linkType: hard +"hey-listen@npm:^1.0.8": + version: 1.0.8 + resolution: "hey-listen@npm:1.0.8" + checksum: 744b5f4c18c7cfb82b22bd22e1d300a9ac4eafe05a22e58fb87e48addfca8be00604d9aa006434ea02f9530990eb4b393ddb28659e2ab7f833ce873e32eb809c + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -10986,6 +14115,15 @@ __metadata: languageName: node linkType: hard +"hoist-non-react-statics@npm:^3.3.0": + version: 3.3.2 + resolution: "hoist-non-react-statics@npm:3.3.2" + dependencies: + react-is: "npm:^16.7.0" + checksum: 1acbe85f33e5a39f90c822ad4d28b24daeb60f71c545279431dc98c312cd28a54f8d64788e477fe21dc502b0e3cf58589ebe5c1ad22af27245370391c2d24ea6 + languageName: node + linkType: hard + "hosted-git-info@npm:^2.1.4, hosted-git-info@npm:^2.6.0": version: 2.8.9 resolution: "hosted-git-info@npm:2.8.9" @@ -10993,6 +14131,13 @@ __metadata: languageName: node linkType: hard +"html-escaper@npm:^2.0.0": + version: 2.0.2 + resolution: "html-escaper@npm:2.0.2" + checksum: 034d74029dcca544a34fb6135e98d427acd73019796ffc17383eaa3ec2fe1c0471dcbbc8f8ed39e46e86d43ccd753a160631615e4048285e313569609b66d5b7 + languageName: node + linkType: hard + "http-basic@npm:^8.1.1": version: 8.1.3 resolution: "http-basic@npm:8.1.3" @@ -11059,6 +14204,13 @@ __metadata: languageName: node linkType: hard +"http-shutdown@npm:^1.2.2": + version: 1.2.2 + resolution: "http-shutdown@npm:1.2.2" + checksum: 1c99b575b1a7ebd749950e7f59410348723638808336063321d89588b7f7b548d61c8e3566af0f1f4f961d941c758677d062d2289bc63356ead143da4d8f3daf + languageName: node + linkType: hard + "http-signature@npm:~1.2.0": version: 1.2.0 resolution: "http-signature@npm:1.2.0" @@ -11104,6 +14256,13 @@ __metadata: languageName: node linkType: hard +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 30f8870d831cdcd2d6ec0486a7d35d49384996742052cee792854273fa9dd9e7d5db06bb7985d4953e337e10714e994e0302e90dc6848069171b05ec836d65b0 + languageName: node + linkType: hard + "humanize-ms@npm:^1.2.1": version: 1.2.1 resolution: "humanize-ms@npm:1.2.1" @@ -11122,6 +14281,35 @@ __metadata: languageName: node linkType: hard +"hyperlane-explorer@https://github.com/hyperlane-xyz/hyperlane-explorer.git": + version: 3.7.0 + resolution: "hyperlane-explorer@https://github.com/hyperlane-xyz/hyperlane-explorer.git#commit=72243333c4883b0dd3480af66a939f224ed64cd7" + dependencies: + "@headlessui/react": "npm:^1.7.17" + "@hyperlane-xyz/sdk": "npm:3.7.0" + "@hyperlane-xyz/utils": "npm:3.7.0" + "@hyperlane-xyz/widgets": "npm:3.7.0" + "@metamask/jazzicon": "https://github.com/jmrossy/jazzicon#7a8df28974b4e81129bfbe3cab76308b889032a6" + "@rainbow-me/rainbowkit": "npm:0.12.16" + "@tanstack/react-query": "npm:^4.24.10" + bignumber.js: "npm:^9.1.2" + buffer: "npm:^6.0.3" + ethers: "npm:^5.7.2" + formik: "npm:^2.2.9" + graphql: "npm:^16.6.0" + next: "npm:^13.4.19" + nextjs-cors: "npm:^2.1.2" + react: "npm:^18.2.0" + react-dom: "npm:^18.2.0" + react-toastify: "npm:^9.1.1" + urql: "npm:^3.0.3" + wagmi: "npm:0.12.18" + zod: "npm:^3.21.2" + zustand: "npm:4.3.8" + checksum: 8caec09863f13297b7af12fde0c7df373eb0665e6302914ffdd9df939e6fd24a5aff563ada554014ff58e967f8710df4fa08d81414a93ee5266d569d9cb7b89e + languageName: node + linkType: hard + "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -11140,6 +14328,13 @@ __metadata: languageName: node linkType: hard +"idb-keyval@npm:^6.2.1": + version: 6.2.1 + resolution: "idb-keyval@npm:6.2.1" + checksum: 9a1416ff5e2ceff3832f5645518f438833a5ff6ee316fe3ec111d580db120425991d64d8098a847be7541bbbb7cc941984b4d0d62d541c39f7a0f415594837c2 + languageName: node + linkType: hard + "idna-uts46-hx@npm:^2.3.1": version: 2.3.1 resolution: "idna-uts46-hx@npm:2.3.1" @@ -11156,6 +14351,13 @@ __metadata: languageName: node linkType: hard +"ignore-by-default@npm:^1.0.1": + version: 1.0.1 + resolution: "ignore-by-default@npm:1.0.1" + checksum: 441509147b3615e0365e407a3c18e189f78c07af08564176c680be1fabc94b6c789cad1342ad887175d4ecd5225de86f73d376cec8e06b42fd9b429505ffcf8a + languageName: node + linkType: hard + "ignore@npm:^5.1.1, ignore@npm:^5.2.0": version: 5.2.0 resolution: "ignore@npm:5.2.0" @@ -11201,6 +14403,18 @@ __metadata: languageName: node linkType: hard +"import-local@npm:^3.0.2": + version: 3.1.0 + resolution: "import-local@npm:3.1.0" + dependencies: + pkg-dir: "npm:^4.2.0" + resolve-cwd: "npm:^3.0.0" + bin: + import-local-fixture: fixtures/cli.js + checksum: bfcdb63b5e3c0e245e347f3107564035b128a414c4da1172a20dc67db2504e05ede4ac2eee1252359f78b0bfd7b19ef180aec427c2fce6493ae782d73a04cddd + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -11275,7 +14489,7 @@ __metadata: languageName: node linkType: hard -"invariant@npm:2": +"invariant@npm:2, invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" dependencies: @@ -11288,8 +14502,25 @@ __metadata: version: 1.10.4 resolution: "io-ts@npm:1.10.4" dependencies: - fp-ts: "npm:^1.0.0" - checksum: d68cb0928b37485cf631c923628dd189784d3dbbcb2d681d86f5c64b9b0321aa33bd2ff271381ac54a279aec5935ff7a743264c858b5172e83b6a9f0cbafc7d1 + fp-ts: "npm:^1.0.0" + checksum: d68cb0928b37485cf631c923628dd189784d3dbbcb2d681d86f5c64b9b0321aa33bd2ff271381ac54a279aec5935ff7a743264c858b5172e83b6a9f0cbafc7d1 + languageName: node + linkType: hard + +"ioredis@npm:^5.3.2": + version: 5.3.2 + resolution: "ioredis@npm:5.3.2" + dependencies: + "@ioredis/commands": "npm:^1.1.1" + cluster-key-slot: "npm:^1.1.0" + debug: "npm:^4.3.4" + denque: "npm:^2.1.0" + lodash.defaults: "npm:^4.2.0" + lodash.isarguments: "npm:^3.1.0" + redis-errors: "npm:^1.2.0" + redis-parser: "npm:^3.0.0" + standard-as-callback: "npm:^2.1.0" + checksum: 0140f055ef81d28e16ca8400b99dabb9ce82009f54afd83cba952c7d0c5d736841e43247765b8ee1af1f02843531c5b8df240af18bd3d7e2ca3d60b36e76213f languageName: node linkType: hard @@ -11307,6 +14538,13 @@ __metadata: languageName: node linkType: hard +"iron-webcrypto@npm:^1.0.0": + version: 1.0.0 + resolution: "iron-webcrypto@npm:1.0.0" + checksum: 1af9fc319c21d44023e08b7019b4c5d0b58f32c6fccab6e4885522b3efa2f6c17491f9caccba74d816f04b4af3148f5bd91a9b506b6d84c2db6ac0a678fbd88a + languageName: node + linkType: hard + "is-arguments@npm:^1.0.4": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" @@ -11422,6 +14660,15 @@ __metadata: languageName: node linkType: hard +"is-docker@npm:^3.0.0": + version: 3.0.0 + resolution: "is-docker@npm:3.0.0" + bin: + is-docker: cli.js + checksum: b698118f04feb7eaf3338922bd79cba064ea54a1c3db6ec8c0c8d8ee7613e7e5854d802d3ef646812a8a3ace81182a085dfa0a71cc68b06f3fa794b9783b3c90 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -11466,6 +14713,13 @@ __metadata: languageName: node linkType: hard +"is-generator-fn@npm:^2.0.0": + version: 2.1.0 + resolution: "is-generator-fn@npm:2.1.0" + checksum: a6ad5492cf9d1746f73b6744e0c43c0020510b59d56ddcb78a91cbc173f09b5e6beff53d75c9c5a29feb618bfef2bf458e025ecf3a57ad2268e2fb2569f56215 + languageName: node + linkType: hard + "is-generator-function@npm:^1.0.7": version: 1.0.10 resolution: "is-generator-function@npm:1.0.10" @@ -11491,6 +14745,17 @@ __metadata: languageName: node linkType: hard +"is-inside-container@npm:^1.0.0": + version: 1.0.0 + resolution: "is-inside-container@npm:1.0.0" + dependencies: + is-docker: "npm:^3.0.0" + bin: + is-inside-container: cli.js + checksum: c50b75a2ab66ab3e8b92b3bc534e1ea72ca25766832c0623ac22d134116a98bcf012197d1caabe1d1c4bd5f84363d4aa5c36bb4b585fbcaf57be172cd10a1a03 + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -11589,6 +14854,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: 172093fe99119ffd07611ab6d1bcccfe8bc4aa80d864b15f43e63e54b7abc71e779acd69afdb854c4e2a67fdc16ae710e370eda40088d1cfc956a50ed82d8f16 + languageName: node + linkType: hard + "is-string@npm:^1.0.5, is-string@npm:^1.0.7": version: 1.0.7 resolution: "is-string@npm:1.0.7" @@ -11638,7 +14910,7 @@ __metadata: languageName: node linkType: hard -"is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": +"is-typedarray@npm:1.0.0, is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": version: 1.0.0 resolution: "is-typedarray@npm:1.0.0" checksum: 4b433bfb0f9026f079f4eb3fbaa4ed2de17c9995c3a0b5c800bec40799b4b2a8b4e051b1ada77749deb9ded4ae52fe2096973f3a93ff83df1a5a7184a669478c @@ -11675,6 +14947,24 @@ __metadata: languageName: node linkType: hard +"is-wsl@npm:^3.1.0": + version: 3.1.0 + resolution: "is-wsl@npm:3.1.0" + dependencies: + is-inside-container: "npm:^1.0.0" + checksum: f9734c81f2f9cf9877c5db8356bfe1ff61680f1f4c1011e91278a9c0564b395ae796addb4bf33956871041476ec82c3e5260ed57b22ac91794d4ae70a1d2f0a9 + languageName: node + linkType: hard + +"is64bit@npm:^2.0.0": + version: 2.0.0 + resolution: "is64bit@npm:2.0.0" + dependencies: + system-architecture: "npm:^0.1.0" + checksum: 94dafd5f29bfb96c542e89ef8c33e811159ca7d07a2890ab83026fa87706612af4101308d9392e9ee68e046e8604a6b59a8f41091f8556f6235efbcfd9c5574c + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -11712,58 +15002,571 @@ __metadata: languageName: node linkType: hard -"isows@npm:1.0.3": - version: 1.0.3 - resolution: "isows@npm:1.0.3" - peerDependencies: - ws: "*" - checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 +"isows@npm:1.0.3": + version: 1.0.3 + resolution: "isows@npm:1.0.3" + peerDependencies: + ws: "*" + checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 + languageName: node + linkType: hard + +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 22d9c181015226d4534a227539256897bbbcb7edd1066ca4fc4d3a06dbd976325dfdd16b3983c7d236a89f256805c1a685a772e0364e98873d3819b064ad35a1 + languageName: node + linkType: hard + +"istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": + version: 3.2.2 + resolution: "istanbul-lib-coverage@npm:3.2.2" + checksum: 40bbdd1e937dfd8c830fa286d0f665e81b7a78bdabcd4565f6d5667c99828bda3db7fb7ac6b96a3e2e8a2461ddbc5452d9f8bc7d00cb00075fa6a3e99f5b6a81 + languageName: node + linkType: hard + +"istanbul-lib-instrument@npm:^5.0.4": + version: 5.2.1 + resolution: "istanbul-lib-instrument@npm:5.2.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^6.3.0" + checksum: bbc4496c2f304d799f8ec22202ab38c010ac265c441947f075c0f7d46bd440b45c00e46017cf9053453d42182d768b1d6ed0e70a142c95ab00df9843aa5ab80e + languageName: node + linkType: hard + +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": "npm:^7.12.3" + "@babel/parser": "npm:^7.14.7" + "@istanbuljs/schema": "npm:^0.1.2" + istanbul-lib-coverage: "npm:^3.2.0" + semver: "npm:^7.5.4" + checksum: 95fd8c66e586840989cb3c7819c6da66c4742a6fedbf16b51a5c7f1898941ad07b79ddff020f479d3a1d76743ecdbf255d93c35221875687477d4b118026e7e7 + languageName: node + linkType: hard + +"istanbul-lib-report@npm:^3.0.0": + version: 3.0.1 + resolution: "istanbul-lib-report@npm:3.0.1" + dependencies: + istanbul-lib-coverage: "npm:^3.0.0" + make-dir: "npm:^4.0.0" + supports-color: "npm:^7.1.0" + checksum: 86a83421ca1cf2109a9f6d193c06c31ef04a45e72a74579b11060b1e7bb9b6337a4e6f04abfb8857e2d569c271273c65e855ee429376a0d7c91ad91db42accd1 + languageName: node + linkType: hard + +"istanbul-lib-source-maps@npm:^4.0.0": + version: 4.0.1 + resolution: "istanbul-lib-source-maps@npm:4.0.1" + dependencies: + debug: "npm:^4.1.1" + istanbul-lib-coverage: "npm:^3.0.0" + source-map: "npm:^0.6.1" + checksum: 5526983462799aced011d776af166e350191b816821ea7bcf71cab3e5272657b062c47dc30697a22a43656e3ced78893a42de677f9ccf276a28c913190953b82 + languageName: node + linkType: hard + +"istanbul-reports@npm:^3.1.3": + version: 3.1.6 + resolution: "istanbul-reports@npm:3.1.6" + dependencies: + html-escaper: "npm:^2.0.0" + istanbul-lib-report: "npm:^3.0.0" + checksum: 135c178e509b21af5c446a6951fc01c331331bb0fdb1ed1dd7f68a8c875603c2e2ee5c82801db5feb868e5cc35e9babe2d972d322afc50f6de6cce6431b9b2ff + languageName: node + linkType: hard + +"isurl@npm:^1.0.0-alpha5": + version: 1.0.0 + resolution: "isurl@npm:1.0.0" + dependencies: + has-to-string-tag-x: "npm:^1.2.0" + is-object: "npm:^1.0.1" + checksum: 28a96e019269d57015fa5869f19dda5a3ed1f7b21e3e0c4ff695419bd0541547db352aa32ee4a3659e811a177b0e37a5bc1a036731e71939dd16b59808ab92bd + languageName: node + linkType: hard + +"javascript-natural-sort@npm:0.7.1": + version: 0.7.1 + resolution: "javascript-natural-sort@npm:0.7.1" + checksum: 7bf6eab67871865d347f09a95aa770f9206c1ab0226bcda6fdd9edec340bf41111a7f82abac30556aa16a21cfa3b2b1ca4a362c8b73dd5ce15220e5d31f49d79 + languageName: node + linkType: hard + +"jayson@npm:^4.1.0": + version: 4.1.0 + resolution: "jayson@npm:4.1.0" + dependencies: + "@types/connect": "npm:^3.4.33" + "@types/node": "npm:^12.12.54" + "@types/ws": "npm:^7.4.4" + JSONStream: "npm:^1.3.5" + commander: "npm:^2.20.3" + delay: "npm:^5.0.0" + es6-promisify: "npm:^5.0.0" + eyes: "npm:^0.1.8" + isomorphic-ws: "npm:^4.0.1" + json-stringify-safe: "npm:^5.0.1" + uuid: "npm:^8.3.2" + ws: "npm:^7.4.5" + bin: + jayson: bin/jayson.js + checksum: d76b3f220e14388007958b8f79e793009d6bc572b6e5ea65848a0f027b324d1950d836468986d7e38ddfb30b660e8b048b459c8bc8456e9b38dbbebc60a563b4 + languageName: node + linkType: hard + +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" + dependencies: + execa: "npm:^5.0.0" + jest-util: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + checksum: 3d93742e56b1a73a145d55b66e96711fbf87ef89b96c2fab7cfdfba8ec06612591a982111ca2b712bb853dbc16831ec8b43585a2a96b83862d6767de59cbf83d + languageName: node + linkType: hard + +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/expect": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + co: "npm:^4.6.0" + dedent: "npm:^1.0.0" + is-generator-fn: "npm:^2.0.0" + jest-each: "npm:^29.7.0" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + pretty-format: "npm:^29.7.0" + pure-rand: "npm:^6.0.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.3" + checksum: 716a8e3f40572fd0213bcfc1da90274bf30d856e5133af58089a6ce45089b63f4d679bd44e6be9d320e8390483ebc3ae9921981993986d21639d9019b523123d + languageName: node + linkType: hard + +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" + dependencies: + "@jest/core": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + create-jest: "npm:^29.7.0" + exit: "npm:^0.1.2" + import-local: "npm:^3.0.2" + jest-config: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + yargs: "npm:^17.3.1" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 6cc62b34d002c034203065a31e5e9a19e7c76d9e8ef447a6f70f759c0714cb212c6245f75e270ba458620f9c7b26063cd8cf6cd1f7e3afd659a7cc08add17307 + languageName: node + linkType: hard + +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@jest/test-sequencer": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-jest: "npm:^29.7.0" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + deepmerge: "npm:^4.2.2" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-circus: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-runner: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + parse-json: "npm:^5.2.0" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-json-comments: "npm:^3.1.1" + peerDependencies: + "@types/node": "*" + ts-node: ">=9.0.0" + peerDependenciesMeta: + "@types/node": + optional: true + ts-node: + optional: true + checksum: 6bdf570e9592e7d7dd5124fc0e21f5fe92bd15033513632431b211797e3ab57eaa312f83cc6481b3094b72324e369e876f163579d60016677c117ec4853cf02b + languageName: node + linkType: hard + +"jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + diff-sequences: "npm:^29.6.3" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 6f3a7eb9cd9de5ea9e5aa94aed535631fa6f80221832952839b3cb59dd419b91c20b73887deb0b62230d06d02d6b6cf34ebb810b88d904bb4fe1e2e4f0905c98 + languageName: node + linkType: hard + +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" + dependencies: + detect-newline: "npm:^3.0.0" + checksum: 8d48818055bc96c9e4ec2e217a5a375623c0d0bfae8d22c26e011074940c202aa2534a3362294c81d981046885c05d304376afba9f2874143025981148f3e96d + languageName: node + linkType: hard + +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + pretty-format: "npm:^29.7.0" + checksum: bd1a077654bdaa013b590deb5f7e7ade68f2e3289180a8c8f53bc8a49f3b40740c0ec2d3a3c1aee906f682775be2bebbac37491d80b634d15276b0aa0f2e3fda + languageName: node + linkType: hard + +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-mock: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + checksum: 9cf7045adf2307cc93aed2f8488942e39388bff47ec1df149a997c6f714bfc66b2056768973770d3f8b1bf47396c19aa564877eb10ec978b952c6018ed1bd637 + languageName: node + linkType: hard + +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 + languageName: node + linkType: hard + +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/graceful-fs": "npm:^4.1.3" + "@types/node": "npm:*" + anymatch: "npm:^3.0.3" + fb-watchman: "npm:^2.0.0" + fsevents: "npm:^2.3.2" + graceful-fs: "npm:^4.2.9" + jest-regex-util: "npm:^29.6.3" + jest-util: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + micromatch: "npm:^4.0.4" + walker: "npm:^1.0.8" + dependenciesMeta: + fsevents: + optional: true + checksum: 8531b42003581cb18a69a2774e68c456fb5a5c3280b1b9b77475af9e346b6a457250f9d756bfeeae2fe6cbc9ef28434c205edab9390ee970a919baddfa08bb85 + languageName: node + linkType: hard + +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" + dependencies: + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 + languageName: node + linkType: hard + +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 981904a494299cf1e3baed352f8a3bd8b50a8c13a662c509b6a53c31461f94ea3bfeffa9d5efcfeb248e384e318c87de7e3baa6af0f79674e987482aa189af40 + languageName: node + linkType: hard + +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" + dependencies: + "@babel/code-frame": "npm:^7.12.13" + "@jest/types": "npm:^29.6.3" + "@types/stack-utils": "npm:^2.0.0" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + micromatch: "npm:^4.0.4" + pretty-format: "npm:^29.7.0" + slash: "npm:^3.0.0" + stack-utils: "npm:^2.0.3" + checksum: 31d53c6ed22095d86bab9d14c0fa70c4a92c749ea6ceece82cf30c22c9c0e26407acdfbdb0231435dc85a98d6d65ca0d9cbcd25cd1abb377fe945e843fb770b9 + languageName: node + linkType: hard + +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + jest-util: "npm:^29.7.0" + checksum: ae51d1b4f898724be5e0e52b2268a68fcd876d9b20633c864a6dd6b1994cbc48d62402b0f40f3a1b669b30ebd648821f086c26c08ffde192ced951ff4670d51c + languageName: node + linkType: hard + +"jest-pnp-resolver@npm:^1.2.2": + version: 1.2.3 + resolution: "jest-pnp-resolver@npm:1.2.3" + peerDependencies: + jest-resolve: "*" + peerDependenciesMeta: + jest-resolve: + optional: true + checksum: db1a8ab2cb97ca19c01b1cfa9a9c8c69a143fde833c14df1fab0766f411b1148ff0df878adea09007ac6a2085ec116ba9a996a6ad104b1e58c20adbf88eed9b2 + languageName: node + linkType: hard + +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a + languageName: node + linkType: hard + +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" + dependencies: + jest-regex-util: "npm:^29.6.3" + jest-snapshot: "npm:^29.7.0" + checksum: 1e206f94a660d81e977bcfb1baae6450cb4a81c92e06fad376cc5ea16b8e8c6ea78c383f39e95591a9eb7f925b6a1021086c38941aa7c1b8a6a813c2f6e93675 + languageName: node + linkType: hard + +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-pnp-resolver: "npm:^1.2.2" + jest-util: "npm:^29.7.0" + jest-validate: "npm:^29.7.0" + resolve: "npm:^1.20.0" + resolve.exports: "npm:^2.0.0" + slash: "npm:^3.0.0" + checksum: faa466fd9bc69ea6c37a545a7c6e808e073c66f46ab7d3d8a6ef084f8708f201b85d5fe1799789578b8b47fa1de47b9ee47b414d1863bc117a49e032ba77b7c7 + languageName: node + linkType: hard + +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" + dependencies: + "@jest/console": "npm:^29.7.0" + "@jest/environment": "npm:^29.7.0" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + emittery: "npm:^0.13.1" + graceful-fs: "npm:^4.2.9" + jest-docblock: "npm:^29.7.0" + jest-environment-node: "npm:^29.7.0" + jest-haste-map: "npm:^29.7.0" + jest-leak-detector: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-resolve: "npm:^29.7.0" + jest-runtime: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + jest-watcher: "npm:^29.7.0" + jest-worker: "npm:^29.7.0" + p-limit: "npm:^3.1.0" + source-map-support: "npm:0.5.13" + checksum: 9d8748a494bd90f5c82acea99be9e99f21358263ce6feae44d3f1b0cd90991b5df5d18d607e73c07be95861ee86d1cbab2a3fc6ca4b21805f07ac29d47c1da1e + languageName: node + linkType: hard + +"jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" + dependencies: + "@jest/environment": "npm:^29.7.0" + "@jest/fake-timers": "npm:^29.7.0" + "@jest/globals": "npm:^29.7.0" + "@jest/source-map": "npm:^29.6.3" + "@jest/test-result": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + cjs-module-lexer: "npm:^1.0.0" + collect-v8-coverage: "npm:^1.0.0" + glob: "npm:^7.1.3" + graceful-fs: "npm:^4.2.9" + jest-haste-map: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-mock: "npm:^29.7.0" + jest-regex-util: "npm:^29.6.3" + jest-resolve: "npm:^29.7.0" + jest-snapshot: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + slash: "npm:^3.0.0" + strip-bom: "npm:^4.0.0" + checksum: 59eb58eb7e150e0834a2d0c0d94f2a0b963ae7182cfa6c63f2b49b9c6ef794e5193ef1634e01db41420c36a94cefc512cdd67a055cd3e6fa2f41eaf0f82f5a20 + languageName: node + linkType: hard + +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" + dependencies: + "@babel/core": "npm:^7.11.6" + "@babel/generator": "npm:^7.7.2" + "@babel/plugin-syntax-jsx": "npm:^7.7.2" + "@babel/plugin-syntax-typescript": "npm:^7.7.2" + "@babel/types": "npm:^7.3.3" + "@jest/expect-utils": "npm:^29.7.0" + "@jest/transform": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + babel-preset-current-node-syntax: "npm:^1.0.0" + chalk: "npm:^4.0.0" + expect: "npm:^29.7.0" + graceful-fs: "npm:^4.2.9" + jest-diff: "npm:^29.7.0" + jest-get-type: "npm:^29.6.3" + jest-matcher-utils: "npm:^29.7.0" + jest-message-util: "npm:^29.7.0" + jest-util: "npm:^29.7.0" + natural-compare: "npm:^1.4.0" + pretty-format: "npm:^29.7.0" + semver: "npm:^7.5.3" + checksum: cb19a3948256de5f922d52f251821f99657339969bf86843bd26cf3332eae94883e8260e3d2fba46129a27c3971c1aa522490e460e16c7fad516e82d10bbf9f8 + languageName: node + linkType: hard + +"jest-util@npm:^29.0.0, jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + ci-info: "npm:^3.2.0" + graceful-fs: "npm:^4.2.9" + picomatch: "npm:^2.2.3" + checksum: 30d58af6967e7d42bd903ccc098f3b4d3859ed46238fbc88d4add6a3f10bea00c226b93660285f058bc7a65f6f9529cf4eb80f8d4707f79f9e3a23686b4ab8f3 + languageName: node + linkType: hard + +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" + dependencies: + "@jest/types": "npm:^29.6.3" + camelcase: "npm:^6.2.0" + chalk: "npm:^4.0.0" + jest-get-type: "npm:^29.6.3" + leven: "npm:^3.1.0" + pretty-format: "npm:^29.7.0" + checksum: 8ee1163666d8eaa16d90a989edba2b4a3c8ab0ffaa95ad91b08ca42b015bfb70e164b247a5b17f9de32d096987cada63ed8491ab82761bfb9a28bc34b27ae161 languageName: node linkType: hard -"isstream@npm:~0.1.2": - version: 0.1.2 - resolution: "isstream@npm:0.1.2" - checksum: 22d9c181015226d4534a227539256897bbbcb7edd1066ca4fc4d3a06dbd976325dfdd16b3983c7d236a89f256805c1a685a772e0364e98873d3819b064ad35a1 +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" + dependencies: + "@jest/test-result": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + "@types/node": "npm:*" + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.0.0" + emittery: "npm:^0.13.1" + jest-util: "npm:^29.7.0" + string-length: "npm:^4.0.1" + checksum: 4f616e0345676631a7034b1d94971aaa719f0cd4a6041be2aa299be437ea047afd4fe05c48873b7963f5687a2f6c7cbf51244be8b14e313b97bfe32b1e127e55 languageName: node linkType: hard -"isurl@npm:^1.0.0-alpha5": - version: 1.0.0 - resolution: "isurl@npm:1.0.0" +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" dependencies: - has-to-string-tag-x: "npm:^1.2.0" - is-object: "npm:^1.0.1" - checksum: 28a96e019269d57015fa5869f19dda5a3ed1f7b21e3e0c4ff695419bd0541547db352aa32ee4a3659e811a177b0e37a5bc1a036731e71939dd16b59808ab92bd + "@types/node": "npm:*" + jest-util: "npm:^29.7.0" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.0.0" + checksum: 364cbaef00d8a2729fc760227ad34b5e60829e0869bd84976bdfbd8c0d0f9c2f22677b3e6dd8afa76ed174765351cd12bae3d4530c62eefb3791055127ca9745 languageName: node linkType: hard -"javascript-natural-sort@npm:0.7.1": - version: 0.7.1 - resolution: "javascript-natural-sort@npm:0.7.1" - checksum: 7bf6eab67871865d347f09a95aa770f9206c1ab0226bcda6fdd9edec340bf41111a7f82abac30556aa16a21cfa3b2b1ca4a362c8b73dd5ce15220e5d31f49d79 +"jest@npm:^29.7.0": + version: 29.7.0 + resolution: "jest@npm:29.7.0" + dependencies: + "@jest/core": "npm:^29.7.0" + "@jest/types": "npm:^29.6.3" + import-local: "npm:^3.0.2" + jest-cli: "npm:^29.7.0" + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + bin: + jest: bin/jest.js + checksum: 97023d78446098c586faaa467fbf2c6b07ff06e2c85a19e3926adb5b0effe9ac60c4913ae03e2719f9c01ae8ffd8d92f6b262cedb9555ceeb5d19263d8c6362a languageName: node linkType: hard -"jayson@npm:^4.1.0": - version: 4.1.0 - resolution: "jayson@npm:4.1.0" - dependencies: - "@types/connect": "npm:^3.4.33" - "@types/node": "npm:^12.12.54" - "@types/ws": "npm:^7.4.4" - JSONStream: "npm:^1.3.5" - commander: "npm:^2.20.3" - delay: "npm:^5.0.0" - es6-promisify: "npm:^5.0.0" - eyes: "npm:^0.1.8" - isomorphic-ws: "npm:^4.0.1" - json-stringify-safe: "npm:^5.0.1" - uuid: "npm:^8.3.2" - ws: "npm:^7.4.5" +"jiti@npm:^1.21.0": + version: 1.21.0 + resolution: "jiti@npm:1.21.0" bin: - jayson: bin/jayson.js - checksum: d76b3f220e14388007958b8f79e793009d6bc572b6e5ea65848a0f027b324d1950d836468986d7e38ddfb30b660e8b048b459c8bc8456e9b38dbbebc60a563b4 + jiti: bin/jiti.js + checksum: 005a0239e50381b5c9919f59dbab86128367bd64872f3376dbbde54b6523f41bd134bf22909e2a509e38fd87e1c22125ca255b9b6b53e7df0fedd23f737334cc languageName: node linkType: hard @@ -11869,6 +15672,23 @@ __metadata: languageName: node linkType: hard +"json-rpc-engine@npm:^6.1.0": + version: 6.1.0 + resolution: "json-rpc-engine@npm:6.1.0" + dependencies: + "@metamask/safe-event-emitter": "npm:^2.0.0" + eth-rpc-errors: "npm:^4.0.2" + checksum: 00d5b5228e90f126dd52176598db6e5611d295d3a3f7be21254c30c1b6555811260ef2ec2df035cd8e583e4b12096259da721e29f4ea2affb615f7dfc960a6a6 + languageName: node + linkType: hard + +"json-rpc-random-id@npm:^1.0.0, json-rpc-random-id@npm:^1.0.1": + version: 1.0.1 + resolution: "json-rpc-random-id@npm:1.0.1" + checksum: fcd2e884193a129ace4002bd65a86e9cdb206733b4693baea77bd8b372cf8de3043fbea27716a2c9a716581a908ca8d978d9dfec4847eb2cf77edb4cf4b2252c + languageName: node + linkType: hard + "json-schema-traverse@npm:^0.4.1": version: 0.4.1 resolution: "json-schema-traverse@npm:0.4.1" @@ -11897,6 +15717,18 @@ __metadata: languageName: node linkType: hard +"json-stable-stringify@npm:^1.1.1": + version: 1.1.1 + resolution: "json-stable-stringify@npm:1.1.1" + dependencies: + call-bind: "npm:^1.0.5" + isarray: "npm:^2.0.5" + jsonify: "npm:^0.0.1" + object-keys: "npm:^1.1.1" + checksum: 60853c1f63451319b5c7953465a555aa816cf84e60e3ca36b6c05225d8fdc4615127fb4ecb92f9f5ad880c552ab8cbae9a519f78b995e7788d6d89e57afafdeb + languageName: node + linkType: hard + "json-stringify-safe@npm:^5.0.1, json-stringify-safe@npm:~5.0.1": version: 5.0.1 resolution: "json-stringify-safe@npm:5.0.1" @@ -11904,6 +15736,22 @@ __metadata: languageName: node linkType: hard +"json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 1db67b853ff0de3534085d630691d3247de53a2ed1390ba0ddff681ea43e9b3e30ecbdb65c5e9aab49435e44059c23dbd6fee8ee619419ba37465bb0dd7135da + languageName: node + linkType: hard + +"jsonc-parser@npm:^3.2.0": + version: 3.2.1 + resolution: "jsonc-parser@npm:3.2.1" + checksum: fe2df6f39e21653781d52cae20c5b9e0ab62461918d97f9430b216cea9b6500efc1d8b42c6584cc0a7548b4c996055e9cdc39f09b9782fa6957af2f45306c530 + languageName: node + linkType: hard + "jsonfile@npm:^2.1.0": version: 2.4.0 resolution: "jsonfile@npm:2.4.0" @@ -11941,6 +15789,13 @@ __metadata: languageName: node linkType: hard +"jsonify@npm:^0.0.1": + version: 0.0.1 + resolution: "jsonify@npm:0.0.1" + checksum: 7b86b6f4518582ff1d8b7624ed6c6277affd5246445e864615dbdef843a4057ac58587684faf129ea111eeb80e01c15f0a4d9d03820eb3f3985fa67e81b12398 + languageName: node + linkType: hard + "jsonparse@npm:^1.2.0": version: 1.3.1 resolution: "jsonparse@npm:1.3.1" @@ -11997,6 +15852,18 @@ __metadata: languageName: node linkType: hard +"keccak@npm:^3.0.3": + version: 3.0.4 + resolution: "keccak@npm:3.0.4" + dependencies: + node-addon-api: "npm:^2.0.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:^4.2.0" + readable-stream: "npm:^3.6.0" + checksum: 45478bb0a57e44d0108646499b8360914b0fbc8b0e088f1076659cb34faaa9eb829c40f6dd9dadb3460bb86cc33153c41fed37fe5ce09465a60e71e78c23fa55 + languageName: node + linkType: hard + "keyv@npm:^4.0.0": version: 4.5.2 resolution: "keyv@npm:4.5.2" @@ -12015,6 +15882,13 @@ __metadata: languageName: node linkType: hard +"keyvaluestorage-interface@npm:^1.0.0": + version: 1.0.0 + resolution: "keyvaluestorage-interface@npm:1.0.0" + checksum: e652448bc915f9c21b9916678ed58f5314c831f0a284d190a340c0370296c71918e0cdc1156a17b12d1993941b302f0881e23fb9c395079e2065a7d2f33d0199 + languageName: node + linkType: hard + "kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": version: 6.0.3 resolution: "kind-of@npm:6.0.3" @@ -12057,6 +15931,15 @@ __metadata: languageName: node linkType: hard +"latest-version@npm:^8.0.0": + version: 8.0.0 + resolution: "latest-version@npm:8.0.0" + dependencies: + package-json: "npm:^9.0.0" + checksum: 8ef0ff0006a9df21a191991fbaee74b7bcd8f20d75dba0ac92932944a22625f1e76aadd33feb0ba1cfa45846a6238625ae603453189f71cf3c5c358989929aea + languageName: node + linkType: hard + "level-codec@npm:^9.0.0": version: 9.0.2 resolution: "level-codec@npm:9.0.2" @@ -12201,6 +16084,13 @@ __metadata: languageName: node linkType: hard +"leven@npm:^3.1.0": + version: 3.1.0 + resolution: "leven@npm:3.1.0" + checksum: 638401d534585261b6003db9d99afd244dfe82d75ddb6db5c0df412842d5ab30b2ef18de471aaec70fe69a46f17b4ae3c7f01d8a4e6580ef7adb9f4273ad1e55 + languageName: node + linkType: hard + "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" @@ -12275,6 +16165,35 @@ __metadata: languageName: node linkType: hard +"listhen@npm:^1.5.5": + version: 1.6.0 + resolution: "listhen@npm:1.6.0" + dependencies: + "@parcel/watcher": "npm:^2.4.0" + "@parcel/watcher-wasm": "npm:2.4.0" + citty: "npm:^0.1.5" + clipboardy: "npm:^4.0.0" + consola: "npm:^3.2.3" + crossws: "npm:^0.1.0" + defu: "npm:^6.1.4" + get-port-please: "npm:^3.1.2" + h3: "npm:^1.10.1" + http-shutdown: "npm:^1.2.2" + jiti: "npm:^1.21.0" + mlly: "npm:^1.5.0" + node-forge: "npm:^1.3.1" + pathe: "npm:^1.1.2" + std-env: "npm:^3.7.0" + ufo: "npm:^1.3.2" + untun: "npm:^0.1.3" + uqr: "npm:^0.1.2" + bin: + listen: bin/listhen.mjs + listhen: bin/listhen.mjs + checksum: 85fc2a6733e18e5d8071debd4a60b17c365210f46abd93f06d4f405be3f00a3e7bd3d1d7439340b3d6b90bc8aa49891fa1baa733418fdd4aff303c67d619f24a + languageName: node + linkType: hard + "listr2@npm:^4.0.5": version: 4.0.5 resolution: "listr2@npm:4.0.5" @@ -12296,6 +16215,37 @@ __metadata: languageName: node linkType: hard +"lit-element@npm:^3.3.0": + version: 3.3.3 + resolution: "lit-element@npm:3.3.3" + dependencies: + "@lit-labs/ssr-dom-shim": "npm:^1.1.0" + "@lit/reactive-element": "npm:^1.3.0" + lit-html: "npm:^2.8.0" + checksum: 7968e7f3ce3994911f27c4c54acc956488c91d8af81677cce3d6f0c2eaea45cceb79b064077159392238d6e43d46015a950269db9914fea8913566aacb17eaa1 + languageName: node + linkType: hard + +"lit-html@npm:^2.8.0": + version: 2.8.0 + resolution: "lit-html@npm:2.8.0" + dependencies: + "@types/trusted-types": "npm:^2.0.2" + checksum: 3503e55e2927c2ff94773cf041fc4128f92291869c9192f36eacb7f95132d11f6b329e5b910ab60a4456349cd2e6d23b33d83291b24d557bcd6b904d6314ac1a + languageName: node + linkType: hard + +"lit@npm:2.8.0": + version: 2.8.0 + resolution: "lit@npm:2.8.0" + dependencies: + "@lit/reactive-element": "npm:^1.6.0" + lit-element: "npm:^3.3.0" + lit-html: "npm:^2.8.0" + checksum: aa64c1136b855ba328d41157dba67657d480345aeec3c1dd829abeb67719d759c9ff2ade9903f9cfb4f9d012b16087034aaa5b33f1182e70c615765562e3251b + languageName: node + linkType: hard + "load-yaml-file@npm:^0.2.0": version: 0.2.0 resolution: "load-yaml-file@npm:0.2.0" @@ -12346,6 +16296,13 @@ __metadata: languageName: node linkType: hard +"lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: 03f39878ea1e42b3199bd3f478150ab723f93cc8730ad86fec1f2804f4a07c6e30deaac73cad53a88e9c3db33348bb8ceeb274552390e7a75d7849021c02df43 + languageName: node + linkType: hard + "lodash.camelcase@npm:^4.3.0": version: 4.3.0 resolution: "lodash.camelcase@npm:4.3.0" @@ -12353,6 +16310,13 @@ __metadata: languageName: node linkType: hard +"lodash.defaults@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.defaults@npm:4.2.0" + checksum: 6a2a9ea5ad7585aff8d76836c9e1db4528e5f5fa50fc4ad81183152ba8717d83aef8aec4fa88bf3417ed946fd4b4358f145ee08fbc77fb82736788714d3e12db + languageName: node + linkType: hard + "lodash.get@npm:^4.4.2": version: 4.4.2 resolution: "lodash.get@npm:4.4.2" @@ -12360,6 +16324,27 @@ __metadata: languageName: node linkType: hard +"lodash.isarguments@npm:^3.1.0": + version: 3.1.0 + resolution: "lodash.isarguments@npm:3.1.0" + checksum: e5186d5fe0384dcb0652501d9d04ebb984863ebc9c9faa2d4b9d5dfd81baef9ffe8e2887b9dc471d62ed092bc0788e5f1d42e45c72457a2884bbb54ac132ed92 + languageName: node + linkType: hard + +"lodash.isequal@npm:4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: 82fc58a83a1555f8df34ca9a2cd300995ff94018ac12cc47c349655f0ae1d4d92ba346db4c19bbfc90510764e0c00ddcc985a358bdcd4b3b965abf8f2a48a214 + languageName: node + linkType: hard + +"lodash.memoize@npm:4.x": + version: 4.1.2 + resolution: "lodash.memoize@npm:4.1.2" + checksum: 192b2168f310c86f303580b53acf81ab029761b9bd9caa9506a019ffea5f3363ea98d7e39e7e11e6b9917066c9d36a09a11f6fe16f812326390d8f3a54a1a6da + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -12426,7 +16411,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.0.0": +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -12476,6 +16461,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.0.2": + version: 10.2.0 + resolution: "lru-cache@npm:10.2.0" + checksum: 502ec42c3309c0eae1ce41afca471f831c278566d45a5273a0c51102dee31e0e250a62fa9029c3370988df33a14188a38e682c16143b794de78668de3643e302 + languageName: node + linkType: hard + "lru-cache@npm:^4.0.1": version: 4.1.5 resolution: "lru-cache@npm:4.1.5" @@ -12525,7 +16517,16 @@ __metadata: languageName: node linkType: hard -"make-error@npm:^1.1.1": +"make-dir@npm:^4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: bf0731a2dd3aab4db6f3de1585cea0b746bb73eb5a02e3d8d72757e376e64e6ada190b1eddcde5b2f24a81b688a9897efd5018737d05e02e2a671dda9cff8a8a + languageName: node + linkType: hard + +"make-error@npm:1.x, make-error@npm:^1.1.1": version: 1.3.6 resolution: "make-error@npm:1.3.6" checksum: b86e5e0e25f7f777b77fabd8e2cbf15737972869d852a22b7e73c17623928fccb826d8e46b9951501d3f20e51ad74ba8c59ed584f610526a48f8ccf88aaec402 @@ -12556,6 +16557,15 @@ __metadata: languageName: node linkType: hard +"makeerror@npm:1.0.12": + version: 1.0.12 + resolution: "makeerror@npm:1.0.12" + dependencies: + tmpl: "npm:1.0.5" + checksum: 4c66ddfc654537333da952c084f507fa4c30c707b1635344eb35be894d797ba44c901a9cebe914aa29a7f61357543ba09b09dddbd7f65b4aee756b450f169f40 + languageName: node + linkType: hard + "map-obj@npm:^1.0.0": version: 1.0.1 resolution: "map-obj@npm:1.0.1" @@ -12595,6 +16605,15 @@ __metadata: languageName: node linkType: hard +"media-query-parser@npm:^2.0.2": + version: 2.0.2 + resolution: "media-query-parser@npm:2.0.2" + dependencies: + "@babel/runtime": "npm:^7.12.5" + checksum: 9dff3ed135149944717a8687567f4fda1d39d28637f265c6ce7efe5ed55cd88ed49136c912ee0c7f3a6e5debc50b1ff969db609d862318f1af97f48752b08b0b + languageName: node + linkType: hard + "media-typer@npm:0.3.0": version: 0.3.0 resolution: "media-typer@npm:0.3.0" @@ -12701,6 +16720,13 @@ __metadata: languageName: node linkType: hard +"mersenne-twister@npm:^1.1.0": + version: 1.1.0 + resolution: "mersenne-twister@npm:1.1.0" + checksum: 1123526199091097102f2f91639ad7d5b3df4b098de9a4a72c835920e11ef0ce08e25737d5af1d363325a60da8804365eae8a41e03b7a46a1acc22e18fa8f261 + languageName: node + linkType: hard + "methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -12708,6 +16734,13 @@ __metadata: languageName: node linkType: hard +"micro-ftch@npm:^0.3.1": + version: 0.3.1 + resolution: "micro-ftch@npm:0.3.1" + checksum: a7ab07d25e28ec4ae492ce4542ea9b06eee85538742b3b1263b247366ee8872f2c5ce9c8651138b2f1d22c8212f691a7b8b5384fe86ead5aff1852e211f1c035 + languageName: node + linkType: hard + "micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": version: 4.0.5 resolution: "micromatch@npm:4.0.5" @@ -12755,6 +16788,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: b2d31580deb58be89adaa1877cbbf152b7604b980fd7ef8f08b9e96bfedf7d605d9c23a8ba62aa12c8580b910cd7c1d27b7331d0f40f7a14e17d5a0bbec3b49f + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -12762,6 +16804,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-fn@npm:4.0.0" + checksum: 995dcece15ee29aa16e188de6633d43a3db4611bcf93620e7e62109ec41c79c0f34277165b8ce5e361205049766e371851264c21ac64ca35499acb5421c2ba56 + languageName: node + linkType: hard + "mimic-response@npm:^1.0.0": version: 1.0.1 resolution: "mimic-response@npm:1.0.1" @@ -13017,6 +17066,18 @@ __metadata: languageName: node linkType: hard +"mlly@npm:^1.2.0, mlly@npm:^1.5.0": + version: 1.6.0 + resolution: "mlly@npm:1.6.0" + dependencies: + acorn: "npm:^8.11.3" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.0.3" + ufo: "npm:^1.3.2" + checksum: 49549b115cb18b5cdc8a0974016d4097f8be272ad7b7a78ab2962f4fdd307bfab85f89e6a797fb905a85bb0adebd1bcc72e7ba557d0117b95bb5837929aee385 + languageName: node + linkType: hard + "mnemonist@npm:^0.38.0": version: 0.38.5 resolution: "mnemonist@npm:0.38.5" @@ -13142,6 +17203,27 @@ __metadata: languageName: node linkType: hard +"motion@npm:10.16.2": + version: 10.16.2 + resolution: "motion@npm:10.16.2" + dependencies: + "@motionone/animation": "npm:^10.15.1" + "@motionone/dom": "npm:^10.16.2" + "@motionone/svelte": "npm:^10.16.2" + "@motionone/types": "npm:^10.15.1" + "@motionone/utils": "npm:^10.15.1" + "@motionone/vue": "npm:^10.16.2" + checksum: 2470f12b97371eb876337b355ad158c545622b2cc7c83b0ba540d2c02afedb49990e78898e520b8f74cccc9ecf11d366ae005a35c60e92178fadd7434860a966 + languageName: node + linkType: hard + +"mri@npm:^1.2.0": + version: 1.2.0 + resolution: "mri@npm:1.2.0" + checksum: 6775a1d2228bb9d191ead4efc220bd6be64f943ad3afd4dcb3b3ac8fc7b87034443f666e38805df38e8d047b29f910c3cc7810da0109af83e42c82c73bd3f6bc + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -13209,6 +17291,13 @@ __metadata: languageName: node linkType: hard +"multiformats@npm:^9.4.2": + version: 9.9.0 + resolution: "multiformats@npm:9.9.0" + checksum: ad55c7d480d22f4258a68fd88aa2aab744fe0cb1e68d732fc886f67d858b37e3aa6c2cec12b2960ead7730d43be690931485238569952d8a3d7f90fdc726c652 + languageName: node + linkType: hard + "multihashes@npm:^0.4.15, multihashes@npm:~0.4.15": version: 0.4.21 resolution: "multihashes@npm:0.4.21" @@ -13252,6 +17341,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^3.3.6": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: ac1eb60f615b272bccb0e2b9cd933720dad30bf9708424f691b8113826bb91aca7e9d14ef5d9415a6ba15c266b37817256f58d8ce980c82b0ba3185352565679 + languageName: node + linkType: hard + "napi-build-utils@npm:^1.0.1": version: 1.0.2 resolution: "napi-build-utils@npm:1.0.2" @@ -13266,6 +17364,13 @@ __metadata: languageName: node linkType: hard +"napi-wasm@npm:^1.1.0": + version: 1.1.0 + resolution: "napi-wasm@npm:1.1.0" + checksum: 767781f07ccaca846a6036a2df7686c9decc1b4fd6ad30ba782c94829476ec5610acc41e4caf7df94ebf0bed4abd4d34539979d0d85b025127c8a41be6259375 + languageName: node + linkType: hard + "natural-compare-lite@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare-lite@npm:1.4.0" @@ -13301,6 +17406,72 @@ __metadata: languageName: node linkType: hard +"next@npm:^13.4.19": + version: 13.5.6 + resolution: "next@npm:13.5.6" + dependencies: + "@next/env": "npm:13.5.6" + "@next/swc-darwin-arm64": "npm:13.5.6" + "@next/swc-darwin-x64": "npm:13.5.6" + "@next/swc-linux-arm64-gnu": "npm:13.5.6" + "@next/swc-linux-arm64-musl": "npm:13.5.6" + "@next/swc-linux-x64-gnu": "npm:13.5.6" + "@next/swc-linux-x64-musl": "npm:13.5.6" + "@next/swc-win32-arm64-msvc": "npm:13.5.6" + "@next/swc-win32-ia32-msvc": "npm:13.5.6" + "@next/swc-win32-x64-msvc": "npm:13.5.6" + "@swc/helpers": "npm:0.5.2" + busboy: "npm:1.6.0" + caniuse-lite: "npm:^1.0.30001406" + postcss: "npm:8.4.31" + styled-jsx: "npm:5.1.1" + watchpack: "npm:2.4.0" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: ec6defc7958b575d93306a2dcb05b7b14e27474e226ec350f412d03832407a5ae7139c21f7c7437b93cca2c8a79d7197c468308d82a903b2a86d579f84ccac9f + languageName: node + linkType: hard + +"nextjs-cors@npm:^2.1.2": + version: 2.2.0 + resolution: "nextjs-cors@npm:2.2.0" + dependencies: + cors: "npm:^2.8.5" + peerDependencies: + next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 + checksum: 9e5f1cd5d737634600259028d5ad6ee58b7d621b656f6b064bf574acb347133fcf8503bb94c5cb3e9d05fb81dd88ba6b07511cca3eadf5cd7cbe4661a207c1b4 + languageName: node + linkType: hard + "nise@npm:^5.1.1": version: 5.1.1 resolution: "nise@npm:5.1.1" @@ -13350,6 +17521,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^7.0.0": + version: 7.1.0 + resolution: "node-addon-api@npm:7.1.0" + dependencies: + node-gyp: "npm:latest" + checksum: e20487e98c76660f4957e81e85c45dfb667140d9be0bf872a3b3dfd86b4ea19c0275939116c90efebc0da7fc6af2c7b7b060512ceebe6417b1ed145a26910453 + languageName: node + linkType: hard + "node-emoji@npm:^1.10.0": version: 1.11.0 resolution: "node-emoji@npm:1.11.0" @@ -13369,6 +17549,13 @@ __metadata: languageName: node linkType: hard +"node-fetch-native@npm:^1.4.0, node-fetch-native@npm:^1.4.1, node-fetch-native@npm:^1.6.1": + version: 1.6.2 + resolution: "node-fetch-native@npm:1.6.2" + checksum: 85a3c8fb853d2abbd7e4235742ee0ff5d8ac15f982209989f7150407203dc65ad45e0c11a0f7416c3685e3cdd3d3f9ee2922e7558f201dd6a7e9c9dde3b612fd + languageName: node + linkType: hard + "node-fetch@npm:2.6.7, node-fetch@npm:^2.6.6, node-fetch@npm:^2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" @@ -13411,6 +17598,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1.3.1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 05bab6868633bf9ad4c3b1dd50ec501c22ffd69f556cdf169a00998ca1d03e8107a6032ba013852f202035372021b845603aeccd7dfcb58cdb7430013b3daa8d + languageName: node + linkType: hard + "node-gyp-build@npm:4.3.0": version: 4.3.0 resolution: "node-gyp-build@npm:4.3.0" @@ -13477,8 +17671,42 @@ __metadata: node-gyp: "npm:latest" prebuild-install: "npm:^6.0.0" bin: - hid-showdevices: src/show-devices.js - checksum: 97a9b623eb185f34c63816b4540cd1b30793e4929cf342b794a19a9abcda1635ef6a07d341736a0025e3b7b0bb17a82a9430ee1d7499bef37fd5ead93ed5c743 + hid-showdevices: src/show-devices.js + checksum: 97a9b623eb185f34c63816b4540cd1b30793e4929cf342b794a19a9abcda1635ef6a07d341736a0025e3b7b0bb17a82a9430ee1d7499bef37fd5ead93ed5c743 + languageName: node + linkType: hard + +"node-int64@npm:^0.4.0": + version: 0.4.0 + resolution: "node-int64@npm:0.4.0" + checksum: b7afc2b65e56f7035b1a2eec57ae0fbdee7d742b1cdcd0f4387562b6527a011ab1cbe9f64cc8b3cca61e3297c9637c8bf61cec2e6b8d3a711d4b5267dfafbe02 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 0f7607ec7db5ef1dc616899a5f24ae90c869b6a54c2d4f36ff6d84a282ab9343c7ff3ca3670fe4669171bb1e8a9b3e286e1ef1c131f09a83d70554f855d54f24 + languageName: node + linkType: hard + +"nodemon@npm:^3.0.3": + version: 3.0.3 + resolution: "nodemon@npm:3.0.3" + dependencies: + chokidar: "npm:^3.5.2" + debug: "npm:^4" + ignore-by-default: "npm:^1.0.1" + minimatch: "npm:^3.1.2" + pstree.remy: "npm:^1.1.8" + semver: "npm:^7.5.3" + simple-update-notifier: "npm:^2.0.0" + supports-color: "npm:^5.5.0" + touch: "npm:^3.1.0" + undefsafe: "npm:^2.0.5" + bin: + nodemon: bin/nodemon.js + checksum: 689f260e1c63b7dfdc1f8acb491c01fe1a03dbd93505b9e5dc94c6c683f9b8e0cf7d136a1274fb8f038f82b25ea59042db740e61014b8fed2f13ff441ba4b36c languageName: node linkType: hard @@ -13518,6 +17746,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:~1.0.10": + version: 1.0.10 + resolution: "nopt@npm:1.0.10" + dependencies: + abbrev: "npm:1" + bin: + nopt: ./bin/nopt.js + checksum: 4f01ad1e144883a190d70bd6003f26e2f3a899230fe1b0f3310e43779c61cab5ae0063a9209912cd52fc4c552b266b38173853aa9abe27ecb04acbdfdca2e9fc + languageName: node + linkType: hard + "normalize-package-data@npm:^2.5.0": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" @@ -13560,6 +17799,15 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^5.1.0": + version: 5.2.0 + resolution: "npm-run-path@npm:5.2.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: c5325e016014e715689c4014f7e0be16cc4cbf529f32a1723e511bc4689b5f823b704d2bca61ac152ce2bda65e0205dc8b3ba0ec0f5e4c3e162d302f6f5b9efb + languageName: node + linkType: hard + "npmlog@npm:^4.0.1": version: 4.1.2 resolution: "npmlog@npm:4.1.2" @@ -13700,6 +17948,31 @@ __metadata: languageName: node linkType: hard +"ofetch@npm:^1.3.3": + version: 1.3.3 + resolution: "ofetch@npm:1.3.3" + dependencies: + destr: "npm:^2.0.1" + node-fetch-native: "npm:^1.4.0" + ufo: "npm:^1.3.0" + checksum: d4ba1f374f3b9f3b4bd47fdca3cda47a16367e6f727545aa3ba93e9be89e615c6731dfd21158b2ef78c1788def15d2d045c233a446354099d6a17fee66e60c98 + languageName: node + linkType: hard + +"ohash@npm:^1.1.3": + version: 1.1.3 + resolution: "ohash@npm:1.1.3" + checksum: 80a3528285f61588600c8c4f091a67f55fbc141f4eec4b3c30182468053042eef5a9684780e963f98a71ec068f3de56d42920c6417bf8f79ab14aeb75ac0bb39 + languageName: node + linkType: hard + +"on-exit-leak-free@npm:^0.2.0": + version: 0.2.0 + resolution: "on-exit-leak-free@npm:0.2.0" + checksum: 36a3a1baea964dc01088884e9d87824cc1a3304ae702e7c688bdb5deec61fbb79325977dd6cba5988f60ad40fedc6ef31ec705adf65b4b042bc0d2686186c0dd + languageName: node + linkType: hard + "on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -13727,6 +18000,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 0846ce78e440841335d4e9182ef69d5762e9f38aa7499b19f42ea1c4cd40f0b4446094c455c713f9adac3f4ae86f613bb5e30c99e52652764d06a89f709b3788 + languageName: node + linkType: hard + "optionator@npm:^0.8.1": version: 0.8.3 resolution: "optionator@npm:0.8.3" @@ -13769,6 +18051,13 @@ __metadata: languageName: node linkType: hard +"outdent@npm:^0.8.0": + version: 0.8.0 + resolution: "outdent@npm:0.8.0" + checksum: a556c5c308705ad4e3441be435f2b2cf014cb5f9753a24cbd080eadc473b988c77d0d529a6a9a57c3931fb4178e5a81d668cc4bc49892b668191a5d0ba3df76e + languageName: node + linkType: hard + "p-cancelable@npm:^0.3.0": version: 0.3.0 resolution: "p-cancelable@npm:0.3.0" @@ -13817,7 +18106,7 @@ __metadata: languageName: node linkType: hard -"p-limit@npm:^3.0.2": +"p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -13913,6 +18202,18 @@ __metadata: languageName: node linkType: hard +"package-json@npm:^9.0.0": + version: 9.0.0 + resolution: "package-json@npm:9.0.0" + dependencies: + got: "npm:^13.0.0" + registry-auth-token: "npm:^5.0.2" + registry-url: "npm:^6.0.1" + semver: "npm:^7.5.4" + checksum: 7c3847b521b221a7f80264880e0c8b0f290796072771fb1c1d0dc36d0c59141e8eff6aa2b05056f8e85f07d872e2e68032378b674be4252311e3328b02df3e01 + languageName: node + linkType: hard + "pako@npm:^2.0.2": version: 2.1.0 resolution: "pako@npm:2.1.0" @@ -13997,6 +18298,13 @@ __metadata: languageName: node linkType: hard +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 8e6c314ae6d16b83e93032c61020129f6f4484590a777eed709c4a01b50e498822b00f76ceaf94bc64dbd90b327df56ceadce27da3d83393790f1219e07721d7 + languageName: node + linkType: hard + "path-parse@npm:^1.0.6, path-parse@npm:^1.0.7": version: 1.0.7 resolution: "path-parse@npm:1.0.7" @@ -14027,6 +18335,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^1.1.0, pathe@npm:^1.1.1, pathe@npm:^1.1.2": + version: 1.1.2 + resolution: "pathe@npm:1.1.2" + checksum: f201d796351bf7433d147b92c20eb154a4e0ea83512017bf4ec4e492a5d6e738fb45798be4259a61aa81270179fce11026f6ff0d3fa04173041de044defe9d80 + languageName: node + linkType: hard + "pathval@npm:^1.1.1": version: 1.1.1 resolution: "pathval@npm:1.1.1" @@ -14054,7 +18369,14 @@ __metadata: languageName: node linkType: hard -"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": +"picocolors@npm:^1.0.0": + version: 1.0.0 + resolution: "picocolors@npm:1.0.0" + checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.3, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" checksum: 60c2595003b05e4535394d1da94850f5372c9427ca4413b71210f437f7b2ca091dbd611c45e8b37d10036fa8eade25c1b8951654f9d3973bfa66a2ff4d3b08bc @@ -14070,6 +18392,13 @@ __metadata: languageName: node linkType: hard +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 668c1dc8d9fc1b34b9ce3b16ba59deb39d4dc743527bf2ed908d2b914cb8ba40aa5ba6960b27c417c241531c5aafd0598feeac2d50cb15278cf9863fa6b02a77 + languageName: node + linkType: hard + "pify@npm:^4.0.1": version: 4.0.1 resolution: "pify@npm:4.0.1" @@ -14077,6 +18406,58 @@ __metadata: languageName: node linkType: hard +"pify@npm:^5.0.0": + version: 5.0.0 + resolution: "pify@npm:5.0.0" + checksum: 443e3e198ad6bfa8c0c533764cf75c9d5bc976387a163792fb553ffe6ce923887cf14eebf5aea9b7caa8eab930da8c33612990ae85bd8c2bc18bedb9eae94ecb + languageName: node + linkType: hard + +"pino-abstract-transport@npm:v0.5.0": + version: 0.5.0 + resolution: "pino-abstract-transport@npm:0.5.0" + dependencies: + duplexify: "npm:^4.1.2" + split2: "npm:^4.0.0" + checksum: d304a104e5cb0c3fef62ea544a4a39bf2472a602cdd7ddb136b0671b9c324ad93fa7888825c4cf33e624802436e897081ba92440f40518b9f2dbdbc0c889e409 + languageName: node + linkType: hard + +"pino-std-serializers@npm:^4.0.0": + version: 4.0.0 + resolution: "pino-std-serializers@npm:4.0.0" + checksum: cec586f9634ef0e6582f62bc8fc5ca5b6e5e11ab88fe3950c66fb0fd5d6690f66bc39cd3f27216b925d2963ad5c3bba415718819ac20ebe0390c7d056cbfea1b + languageName: node + linkType: hard + +"pino@npm:7.11.0": + version: 7.11.0 + resolution: "pino@npm:7.11.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + fast-redact: "npm:^3.0.0" + on-exit-leak-free: "npm:^0.2.0" + pino-abstract-transport: "npm:v0.5.0" + pino-std-serializers: "npm:^4.0.0" + process-warning: "npm:^1.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.1.0" + safe-stable-stringify: "npm:^2.1.0" + sonic-boom: "npm:^2.2.1" + thread-stream: "npm:^0.15.1" + bin: + pino: bin.js + checksum: 1c7b4b52fea76e0bc5d8b1190a0fee24279cb16d76fdb5833b32b64256fd8a94d641574b850faba5be72514f04045206b6d902a9a3f5ceae2a4296687088e073 + languageName: node + linkType: hard + +"pirates@npm:^4.0.4": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: d02dda76f4fec1cbdf395c36c11cf26f76a644f9f9a1bfa84d3167d0d3154d5289aacc72677aa20d599bb4a6937a471de1b65c995e2aea2d8687cbcd7e43ea5f + languageName: node + linkType: hard + "pkg-dir@npm:^4.2.0": version: 4.2.0 resolution: "pkg-dir@npm:4.2.0" @@ -14086,6 +18467,17 @@ __metadata: languageName: node linkType: hard +"pkg-types@npm:^1.0.3": + version: 1.0.3 + resolution: "pkg-types@npm:1.0.3" + dependencies: + jsonc-parser: "npm:^3.2.0" + mlly: "npm:^1.2.0" + pathe: "npm:^1.1.0" + checksum: e17e1819ce579c9ea390e4c41a9ed9701d8cff14b463f9577cc4f94688da8917c66dabc40feacd47a21eb3de9b532756a78becd882b76add97053af307c1240a + languageName: node + linkType: hard + "pluralize@npm:^8.0.0": version: 8.0.0 resolution: "pluralize@npm:8.0.0" @@ -14093,6 +18485,38 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^5.0.0": + version: 5.0.0 + resolution: "pngjs@npm:5.0.0" + checksum: 345781644740779752505af2fea3e9043f6c7cc349b18e1fb8842796360d1624791f0c24d33c0f27b05658373f90ffaa177a849e932e5fea1f540cef3975f3c9 + languageName: node + linkType: hard + +"pony-cause@npm:^2.1.10": + version: 2.1.10 + resolution: "pony-cause@npm:2.1.10" + checksum: 906563565030996d0c40ba79a584e2f298391931acc59c98510f9fd583d72cd9e9c58b0fb5a25bbae19daf16840f94cb9c1ee72c7ed5ef249ecba147cee40495 + languageName: node + linkType: hard + +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: "npm:^3.3.6" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: 1a6653e72105907377f9d4f2cd341d8d90e3fde823a5ddea1e2237aaa56933ea07853f0f2758c28892a1d70c53bbaca200eb8b80f8ed55f13093003dbec5afa0 + languageName: node + linkType: hard + +"preact@npm:^10.12.0, preact@npm:^10.16.0": + version: 10.19.6 + resolution: "preact@npm:10.19.6" + checksum: 851c7d91e6899a40fdeae0ef9a792bf3217ed8365ce96f4c5630048c82b44c637fd4c0d8a4b0c3e1c8e74e243600dd9c5787520da07552d33a06c957779b4167 + languageName: node + linkType: hard + "prebuild-install@npm:^5.3.4": version: 5.3.6 resolution: "prebuild-install@npm:5.3.6" @@ -14214,6 +18638,17 @@ __metadata: languageName: node linkType: hard +"pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: dea96bc83c83cd91b2bfc55757b6b2747edcaac45b568e46de29deee80742f17bc76fe8898135a70d904f4928eafd8bb693cd1da4896e8bdd3c5e82cadf1d2bb + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -14221,6 +18656,13 @@ __metadata: languageName: node linkType: hard +"process-warning@npm:^1.0.0": + version: 1.0.0 + resolution: "process-warning@npm:1.0.0" + checksum: 8736d11d8d71c349d176e210305e84d74b13af06efb3c779377b056bfd608257d1e4e32b8fbbf90637c900f0313e40f7c9f583140884f667a21fc10a869b840c + languageName: node + linkType: hard + "process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10" @@ -14263,7 +18705,7 @@ __metadata: languageName: node linkType: hard -"prompts@npm:^2.4.2": +"prompts@npm:^2.0.1, prompts@npm:^2.4.2": version: 2.4.2 resolution: "prompts@npm:2.4.2" dependencies: @@ -14314,6 +18756,13 @@ __metadata: languageName: node linkType: hard +"proxy-compare@npm:2.5.1": + version: 2.5.1 + resolution: "proxy-compare@npm:2.5.1" + checksum: 64b6277d08d89f0b2c468a84decf43f82a4e88da7075651e6adebc69d1b87fadc17cfeb43c024c00b65faa3f0908f7ac1e61f5f6849a404a547a742e6aa527a6 + languageName: node + linkType: hard + "prr@npm:~1.0.1": version: 1.0.1 resolution: "prr@npm:1.0.1" @@ -14335,6 +18784,13 @@ __metadata: languageName: node linkType: hard +"pstree.remy@npm:^1.1.8": + version: 1.1.8 + resolution: "pstree.remy@npm:1.1.8" + checksum: ef13b1b5896b35f67dbd4fb7ba54bb2a5da1a5c317276cbad4bcad4159bf8f7b5e1748dc244bf36865f3d560d2fc952521581280a91468c9c2df166cc760c8c1 + languageName: node + linkType: hard + "pump@npm:^3.0.0": version: 3.0.0 resolution: "pump@npm:3.0.0" @@ -14366,6 +18822,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^6.0.0": + version: 6.0.4 + resolution: "pure-rand@npm:6.0.4" + checksum: 34fed0abe99d3db7ddc459c12e1eda6bff05db6a17f2017a1ae12202271ccf276fb223b442653518c719671c1b339bbf97f27ba9276dba0997c89e45c4e6a3bf + languageName: node + linkType: hard + "pvtsutils@npm:^1.3.2": version: 1.3.2 resolution: "pvtsutils@npm:1.3.2" @@ -14382,6 +18845,34 @@ __metadata: languageName: node linkType: hard +"qrcode@npm:1.5.0": + version: 1.5.0 + resolution: "qrcode@npm:1.5.0" + dependencies: + dijkstrajs: "npm:^1.0.1" + encode-utf8: "npm:^1.0.3" + pngjs: "npm:^5.0.0" + yargs: "npm:^15.3.1" + bin: + qrcode: bin/qrcode + checksum: b8d942a5fbd45c3517c095095e84566c43a5ef8654eee34957ff96957adf63467a65a3d90177013a2cc2de83932da105aa8beb62a5bc7886fe7e9920ccf02c4d + languageName: node + linkType: hard + +"qrcode@npm:1.5.3, qrcode@npm:^1.5.1": + version: 1.5.3 + resolution: "qrcode@npm:1.5.3" + dependencies: + dijkstrajs: "npm:^1.0.1" + encode-utf8: "npm:^1.0.3" + pngjs: "npm:^5.0.0" + yargs: "npm:^15.3.1" + bin: + qrcode: bin/qrcode + checksum: 823642d59a81ba5f406a1e78415fee37fd53856038f49a85c4ca7aa32ba6b8505ab059a832718ac16612bed75aa2a18584faae38cf3c25e2c90fb19b8c55fe46 + languageName: node + linkType: hard + "qs@npm:6.10.3": version: 6.10.3 resolution: "qs@npm:6.10.3" @@ -14391,6 +18882,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.11.0": + version: 6.11.0 + resolution: "qs@npm:6.11.0" + dependencies: + side-channel: "npm:^1.0.4" + checksum: 5a3bfea3e2f359ede1bfa5d2f0dbe54001aa55e40e27dc3e60fab814362d83a9b30758db057c2011b6f53a2d4e4e5150194b5bac45372652aecb3e3c0d4b256e + languageName: node + linkType: hard + "qs@npm:^6.4.0": version: 6.10.5 resolution: "qs@npm:6.10.5" @@ -14407,6 +18907,18 @@ __metadata: languageName: node linkType: hard +"query-string@npm:7.1.3": + version: 7.1.3 + resolution: "query-string@npm:7.1.3" + dependencies: + decode-uri-component: "npm:^0.2.2" + filter-obj: "npm:^1.1.0" + split-on-first: "npm:^1.0.0" + strict-uri-encode: "npm:^2.0.0" + checksum: 3b6f2c167e76ca4094c5f1a9eb276efcbb9ebfd8b1a28c413f3c4e4e7d6428c8187bf46c8cbc9f92a229369dd0015de10a7fd712c8cee98d5d84c2ac6140357e + languageName: node + linkType: hard + "query-string@npm:^5.0.1": version: 5.1.1 resolution: "query-string@npm:5.1.1" @@ -14418,6 +18930,18 @@ __metadata: languageName: node linkType: hard +"query-string@npm:^6.13.5": + version: 6.14.1 + resolution: "query-string@npm:6.14.1" + dependencies: + decode-uri-component: "npm:^0.2.0" + filter-obj: "npm:^1.1.0" + split-on-first: "npm:^1.0.0" + strict-uri-encode: "npm:^2.0.0" + checksum: 95f5a372f777b4fb5bdae5a2d85961cf3894d466cfc3a0cc799320d5ed633af935c0d96ee5d2b1652c02888e749831409ca5dd5eb388ce1014a9074024a22840 + languageName: node + linkType: hard + "querystring@npm:0.2.0": version: 0.2.0 resolution: "querystring@npm:0.2.0" @@ -14432,6 +18956,13 @@ __metadata: languageName: node linkType: hard +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 591eca457509a99368b623db05248c1193aa3cedafc9a077d7acab09495db1231017ba3ad1b5386e5633271edd0a03b312d8640a59ee585b8516a42e15438aa7 + languageName: node + linkType: hard + "quick-lru@npm:^4.0.1": version: 4.0.1 resolution: "quick-lru@npm:4.0.1" @@ -14446,6 +18977,13 @@ __metadata: languageName: node linkType: hard +"radix3@npm:^1.1.0": + version: 1.1.0 + resolution: "radix3@npm:1.1.0" + checksum: 311258ec9e8cc17613fd31aaf3138bfb2ab1ea015738e91591920961f74a1914491338554e8530f7902f1629b6c2ea2dfd66a5c068f14b76cf6535b68b5292c4 + languageName: node + linkType: hard + "randombytes@npm:^2.0.1, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -14488,6 +19026,112 @@ __metadata: languageName: node linkType: hard +"react-dom@npm:^18.2.0": + version: 18.2.0 + resolution: "react-dom@npm:18.2.0" + dependencies: + loose-envify: "npm:^1.1.0" + scheduler: "npm:^0.23.0" + peerDependencies: + react: ^18.2.0 + checksum: ca5e7762ec8c17a472a3605b6f111895c9f87ac7d43a610ab7024f68cd833d08eda0625ce02ec7178cc1f3c957cf0b9273cdc17aa2cd02da87544331c43b1d21 + languageName: node + linkType: hard + +"react-fast-compare@npm:^2.0.1": + version: 2.0.4 + resolution: "react-fast-compare@npm:2.0.4" + checksum: e4e3218c0f5c29b88e9f184a12adb77b0a93a803dbd45cb98bbb754c8310dc74e6266c53dd70b90ba4d0939e0e1b8a182cb05d081bcab22507a0390fbcd768ac + languageName: node + linkType: hard + +"react-is@npm:^16.7.0": + version: 16.13.1 + resolution: "react-is@npm:16.13.1" + checksum: 5aa564a1cde7d391ac980bedee21202fc90bdea3b399952117f54fb71a932af1e5902020144fb354b4690b2414a0c7aafe798eb617b76a3d441d956db7726fdf + languageName: node + linkType: hard + +"react-is@npm:^18.0.0": + version: 18.2.0 + resolution: "react-is@npm:18.2.0" + checksum: 200cd65bf2e0be7ba6055f647091b725a45dd2a6abef03bf2380ce701fd5edccee40b49b9d15edab7ac08a762bf83cb4081e31ec2673a5bfb549a36ba21570df + languageName: node + linkType: hard + +"react-remove-scroll-bar@npm:^2.3.3": + version: 2.3.5 + resolution: "react-remove-scroll-bar@npm:2.3.5" + dependencies: + react-style-singleton: "npm:^2.2.1" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 6d05e74ee8049b322ba0aeb398e092ae284a5b04013bc07f0c1f283824b088fd5c1b1f1514a0e0e501c063a9c3b5899373039329d0266a21121222c814052053 + languageName: node + linkType: hard + +"react-remove-scroll@npm:2.5.4": + version: 2.5.4 + resolution: "react-remove-scroll@npm:2.5.4" + dependencies: + react-remove-scroll-bar: "npm:^2.3.3" + react-style-singleton: "npm:^2.2.1" + tslib: "npm:^2.1.0" + use-callback-ref: "npm:^1.3.0" + use-sidecar: "npm:^1.1.2" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 330e3b816c1479f74701ff04a90ffff3e2ae8a996ce9d6978eb899a771eed6b9150dc4889d283053f2e4d84f66ad5550e2522d9df35f3394d211ab79d1c54fde + languageName: node + linkType: hard + +"react-style-singleton@npm:^2.2.1": + version: 2.2.1 + resolution: "react-style-singleton@npm:2.2.1" + dependencies: + get-nonce: "npm:^1.0.0" + invariant: "npm:^2.2.4" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 80c58fd6aac3594e351e2e7b048d8a5b09508adb21031a38b3c40911fe58295572eddc640d4b20a7be364842c8ed1120fe30097e22ea055316b375b88d4ff02a + languageName: node + linkType: hard + +"react-toastify@npm:^9.1.1": + version: 9.1.3 + resolution: "react-toastify@npm:9.1.3" + dependencies: + clsx: "npm:^1.1.1" + peerDependencies: + react: ">=16" + react-dom: ">=16" + checksum: 12667aa10e6cf3f74be2e3c704c2d5570dd7de66fff89ae38fbfab1122e9a9f632de1cb712fe44a9a60b8ecca7590578157cb4ca6c4e8105a8cf80936a94e181 + languageName: node + linkType: hard + +"react@npm:^18.2.0": + version: 18.2.0 + resolution: "react@npm:18.2.0" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: b9214a9bd79e99d08de55f8bef2b7fc8c39630be97c4e29d7be173d14a9a10670b5325e94485f74cd8bff4966ef3c78ee53c79a7b0b9b70cba20aa8973acc694 + languageName: node + linkType: hard + "read-pkg-up@npm:^7.0.1": version: 7.0.1 resolution: "read-pkg-up@npm:7.0.1" @@ -14585,6 +19229,13 @@ __metadata: languageName: node linkType: hard +"real-require@npm:^0.1.0": + version: 0.1.0 + resolution: "real-require@npm:0.1.0" + checksum: 0ba1c440dc9b7777d35a97f755312bf236be0847249f76cc9789c5c08d141f5d80b8564888e6a94ed0253fabf597b6892f8502c4e5658fb98f88642633a39723 + languageName: node + linkType: hard + "rechoir@npm:^0.6.2": version: 0.6.2 resolution: "rechoir@npm:0.6.2" @@ -14613,6 +19264,22 @@ __metadata: languageName: node linkType: hard +"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0": + version: 1.2.0 + resolution: "redis-errors@npm:1.2.0" + checksum: 001c11f63ddd52d7c80eb4f4ede3a9433d29a458a7eea06b9154cb37c9802a218d93b7988247aa8c958d4b5d274b18354e8853c148f1096fda87c6e675cfd3ee + languageName: node + linkType: hard + +"redis-parser@npm:^3.0.0": + version: 3.0.0 + resolution: "redis-parser@npm:3.0.0" + dependencies: + redis-errors: "npm:^1.0.0" + checksum: b10846844b4267f19ce1a6529465819c3d78c3e89db7eb0c3bb4eb19f83784797ec411274d15a77dbe08038b48f95f76014b83ca366dc955a016a3a0a0234650 + languageName: node + linkType: hard + "reduce-flatten@npm:^2.0.0": version: 2.0.0 resolution: "reduce-flatten@npm:2.0.0" @@ -14656,7 +19323,7 @@ __metadata: languageName: node linkType: hard -"registry-auth-token@npm:^5.0.1": +"registry-auth-token@npm:^5.0.1, registry-auth-token@npm:^5.0.2": version: 5.0.2 resolution: "registry-auth-token@npm:5.0.2" dependencies: @@ -14665,7 +19332,7 @@ __metadata: languageName: node linkType: hard -"registry-url@npm:^6.0.0": +"registry-url@npm:^6.0.0, registry-url@npm:^6.0.1": version: 6.0.1 resolution: "registry-url@npm:6.0.1" dependencies: @@ -14772,6 +19439,15 @@ __metadata: languageName: node linkType: hard +"resolve-cwd@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-cwd@npm:3.0.0" + dependencies: + resolve-from: "npm:^5.0.0" + checksum: 546e0816012d65778e580ad62b29e975a642989108d9a3c5beabfb2304192fa3c9f9146fbdfe213563c6ff51975ae41bac1d3c6e047dd9572c94863a057b4d81 + languageName: node + linkType: hard + "resolve-from@npm:^3.0.0": version: 3.0.0 resolution: "resolve-from@npm:3.0.0" @@ -14793,6 +19469,13 @@ __metadata: languageName: node linkType: hard +"resolve.exports@npm:^2.0.0": + version: 2.0.2 + resolution: "resolve.exports@npm:2.0.2" + checksum: f1cc0b6680f9a7e0345d783e0547f2a5110d8336b3c2a4227231dd007271ffd331fd722df934f017af90bae0373920ca0d4005da6f76cb3176c8ae426370f893 + languageName: node + linkType: hard + "resolve@npm:1.1.x": version: 1.1.7 resolution: "resolve@npm:1.1.7" @@ -14822,7 +19505,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.10.0": +"resolve@npm:^1.10.0, resolve@npm:^1.20.0": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -14864,7 +19547,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin": +"resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -15086,6 +19769,13 @@ __metadata: languageName: node linkType: hard +"safe-stable-stringify@npm:^2.1.0": + version: 2.4.3 + resolution: "safe-stable-stringify@npm:2.4.3" + checksum: a6c192bbefe47770a11072b51b500ed29be7b1c15095371c1ee1dc13e45ce48ee3c80330214c56764d006c485b88bd0b24940d868948170dddc16eed312582d8 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -15117,6 +19807,15 @@ __metadata: languageName: node linkType: hard +"scheduler@npm:^0.23.0": + version: 0.23.0 + resolution: "scheduler@npm:0.23.0" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 0c4557aa37bafca44ff21dc0ea7c92e2dbcb298bc62eae92b29a39b029134f02fb23917d6ebc8b1fa536b4184934314c20d8864d156a9f6357f3398aaf7bfda8 + languageName: node + linkType: hard + "scrypt-js@npm:2.0.4": version: 2.0.4 resolution: "scrypt-js@npm:2.0.4" @@ -15184,6 +19883,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 1ef3a85bd02a760c6ef76a45b8c1ce18226de40831e02a00bad78485390b98b6ccaa31046245fc63bba4a47a6a592b6c7eedc65cc47126e60489f9cc1ce3ed7e + languageName: node + linkType: hard + "semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": version: 7.3.7 resolution: "semver@npm:7.3.7" @@ -15195,6 +19903,17 @@ __metadata: languageName: node linkType: hard +"semver@npm:^7.3.8": + version: 7.6.0 + resolution: "semver@npm:7.6.0" + dependencies: + lru-cache: "npm:^6.0.0" + bin: + semver: bin/semver.js + checksum: 1b41018df2d8aca5a1db4729985e8e20428c650daea60fcd16e926e9383217d00f574fab92d79612771884a98d2ee2a1973f49d630829a8d54d6570defe62535 + languageName: node + linkType: hard + "semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" @@ -15312,7 +20031,7 @@ __metadata: languageName: node linkType: hard -"sha.js@npm:^2.4.0, sha.js@npm:^2.4.8": +"sha.js@npm:^2.4.0, sha.js@npm:^2.4.11, sha.js@npm:^2.4.8": version: 2.4.11 resolution: "sha.js@npm:2.4.11" dependencies: @@ -15397,6 +20116,13 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: c9fa63bbbd7431066174a48ba2dd9986dfd930c3a8b59de9c29d7b6854ec1c12a80d15310869ea5166d413b99f041bfa3dd80a7947bcd44ea8e6eb3ffeabfa1f + languageName: node + linkType: hard + "simple-concat@npm:^1.0.0": version: 1.0.1 resolution: "simple-concat@npm:1.0.1" @@ -15426,6 +20152,15 @@ __metadata: languageName: node linkType: hard +"simple-update-notifier@npm:^2.0.0": + version: 2.0.0 + resolution: "simple-update-notifier@npm:2.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 40bd4f96aa89aedbf717ae9f4ab8fca70e8f7511e8b766feb15471cca3f6fe4fe673743309b08b4ba8abfe0965c9cd927e1de46550a757b819b70fc7430cc85d + languageName: node + linkType: hard + "sinon@npm:^13.0.2": version: 13.0.2 resolution: "sinon@npm:13.0.2" @@ -15649,6 +20384,32 @@ __metadata: languageName: node linkType: hard +"sonic-boom@npm:^2.2.1": + version: 2.8.0 + resolution: "sonic-boom@npm:2.8.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + checksum: 05351d9f44bac59b2a4ab42ee22bf81b8c3bbd22db20183d78d5f2067557eb623e0eaf93b2bc0f8417bee92ca372bc26e0d83e3bdb0ffebcc33738ac1c191876 + languageName: node + linkType: hard + +"source-map-js@npm:^1.0.2": + version: 1.0.2 + resolution: "source-map-js@npm:1.0.2" + checksum: 38e2d2dd18d2e331522001fc51b54127ef4a5d473f53b1349c5cca2123562400e0986648b52e9407e348eaaed53bce49248b6e2641e6d793ca57cb2c360d6d51 + languageName: node + linkType: hard + +"source-map-support@npm:0.5.13": + version: 0.5.13 + resolution: "source-map-support@npm:0.5.13" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: d1514a922ac9c7e4786037eeff6c3322f461cd25da34bb9fefb15387b3490531774e6e31d95ab6d5b84a3e139af9c3a570ccaee6b47bd7ea262691ed3a8bc34e + languageName: node + linkType: hard + "source-map-support@npm:^0.5.13, source-map-support@npm:^0.5.16": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" @@ -15726,6 +20487,20 @@ __metadata: languageName: node linkType: hard +"split-on-first@npm:^1.0.0": + version: 1.1.0 + resolution: "split-on-first@npm:1.1.0" + checksum: 16ff85b54ddcf17f9147210a4022529b343edbcbea4ce977c8f30e38408b8d6e0f25f92cd35b86a524d4797f455e29ab89eb8db787f3c10708e0b47ebf528d30 + languageName: node + linkType: hard + +"split2@npm:^4.0.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 09bbefc11bcf03f044584c9764cd31a252d8e52cea29130950b26161287c11f519807c5e54bd9e5804c713b79c02cefe6a98f4688630993386be353e03f534ab + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -15763,6 +20538,15 @@ __metadata: languageName: node linkType: hard +"stack-utils@npm:^2.0.3": + version: 2.0.6 + resolution: "stack-utils@npm:2.0.6" + dependencies: + escape-string-regexp: "npm:^2.0.0" + checksum: cdc988acbc99075b4b036ac6014e5f1e9afa7e564482b687da6384eee6a1909d7eaffde85b0a17ffbe186c5247faf6c2b7544e802109f63b72c7be69b13151bb + languageName: node + linkType: hard + "stacktrace-parser@npm:^0.1.10": version: 0.1.10 resolution: "stacktrace-parser@npm:0.1.10" @@ -15772,6 +20556,13 @@ __metadata: languageName: node linkType: hard +"standard-as-callback@npm:^2.1.0": + version: 2.1.0 + resolution: "standard-as-callback@npm:2.1.0" + checksum: 88bec83ee220687c72d94fd86a98d5272c91d37ec64b66d830dbc0d79b62bfa6e47f53b71646011835fc9ce7fae62739545d13124262b53be4fbb3e2ebad551c + languageName: node + linkType: hard + "statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" @@ -15779,6 +20570,13 @@ __metadata: languageName: node linkType: hard +"std-env@npm:^3.7.0": + version: 3.7.0 + resolution: "std-env@npm:3.7.0" + checksum: 6ee0cca1add3fd84656b0002cfbc5bfa20340389d9ba4720569840f1caa34bce74322aef4c93f046391583e50649d0cf81a5f8fe1d411e50b659571690a45f12 + languageName: node + linkType: hard + "stealthy-require@npm:^1.1.1": version: 1.1.1 resolution: "stealthy-require@npm:1.1.1" @@ -15786,6 +20584,13 @@ __metadata: languageName: node linkType: hard +"stream-shift@npm:^1.0.0": + version: 1.0.3 + resolution: "stream-shift@npm:1.0.3" + checksum: a24c0a3f66a8f9024bd1d579a533a53be283b4475d4e6b4b3211b964031447bdf6532dd1f3c2b0ad66752554391b7c62bd7ca4559193381f766534e723d50242 + languageName: node + linkType: hard + "stream-transform@npm:^2.1.3": version: 2.1.3 resolution: "stream-transform@npm:2.1.3" @@ -15809,6 +20614,13 @@ __metadata: languageName: node linkType: hard +"strict-uri-encode@npm:^2.0.0": + version: 2.0.0 + resolution: "strict-uri-encode@npm:2.0.0" + checksum: eaac4cf978b6fbd480f1092cab8b233c9b949bcabfc9b598dd79a758f7243c28765ef7639c876fa72940dac687181b35486ea01ff7df3e65ce3848c64822c581 + languageName: node + linkType: hard + "string-argv@npm:^0.3.1": version: 0.3.1 resolution: "string-argv@npm:0.3.1" @@ -15823,6 +20635,16 @@ __metadata: languageName: node linkType: hard +"string-length@npm:^4.0.1": + version: 4.0.2 + resolution: "string-length@npm:4.0.2" + dependencies: + char-regex: "npm:^1.0.2" + strip-ansi: "npm:^6.0.0" + checksum: ce85533ef5113fcb7e522bcf9e62cb33871aa99b3729cec5595f4447f660b0cefd542ca6df4150c97a677d58b0cb727a3fe09ac1de94071d05526c73579bf505 + languageName: node + linkType: hard + "string-width@npm:^1.0.1": version: 1.0.2 resolution: "string-width@npm:1.0.2" @@ -16002,6 +20824,13 @@ __metadata: languageName: node linkType: hard +"strip-bom@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-bom@npm:4.0.0" + checksum: 9dbcfbaf503c57c06af15fe2c8176fb1bf3af5ff65003851a102749f875a6dbe0ab3b30115eccf6e805e9d756830d3e40ec508b62b3f1ddf3761a20ebe29d3f3 + languageName: node + linkType: hard + "strip-final-newline@npm:^2.0.0": version: 2.0.0 resolution: "strip-final-newline@npm:2.0.0" @@ -16009,6 +20838,13 @@ __metadata: languageName: node linkType: hard +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: 23ee263adfa2070cd0f23d1ac14e2ed2f000c9b44229aec9c799f1367ec001478469560abefd00c5c99ee6f0b31c137d53ec6029c53e9f32a93804e18c201050 + languageName: node + linkType: hard + "strip-hex-prefix@npm:1.0.0": version: 1.0.0 resolution: "strip-hex-prefix@npm:1.0.0" @@ -16041,6 +20877,22 @@ __metadata: languageName: node linkType: hard +"styled-jsx@npm:5.1.1": + version: 5.1.1 + resolution: "styled-jsx@npm:5.1.1" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 4f6a5d0010770fdeea1183d919d528fd46c484e23c0535ef3e1dd49488116f639c594f3bd4440e3bc8a8686c9f8d53c5761599870ff039ede11a5c3bfe08a4be + languageName: node + linkType: hard + "superstruct@npm:^0.14.2": version: 0.14.2 resolution: "superstruct@npm:0.14.2" @@ -16048,6 +20900,13 @@ __metadata: languageName: node linkType: hard +"superstruct@npm:^1.0.3": + version: 1.0.3 + resolution: "superstruct@npm:1.0.3" + checksum: 632b6171ac136b6750e62a55f806cc949b3dbf2b4a7dc70cc85f54adcdf19d21eab9711f04e8a643b7dd622bbd8658366ead924f467adaccb2c8005c133b7976 + languageName: node + linkType: hard + "supports-color@npm:6.0.0": version: 6.0.0 resolution: "supports-color@npm:6.0.0" @@ -16057,7 +20916,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:8.1.1": +"supports-color@npm:8.1.1, supports-color@npm:^8.0.0": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -16075,7 +20934,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^5.3.0": +"supports-color@npm:^5.3.0, supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" dependencies: @@ -16163,6 +21022,13 @@ __metadata: languageName: node linkType: hard +"system-architecture@npm:^0.1.0": + version: 0.1.0 + resolution: "system-architecture@npm:0.1.0" + checksum: ca0dd793c45c354ab57dd7fc8ce7dc9923a6e07382bd3b22eb5b08f55ddb0217c390d00767549c5155fd4ce7ef23ffdd8cfb33dd4344cbbd37837d085a50f6f0 + languageName: node + linkType: hard + "table-layout@npm:^1.0.2": version: 1.0.2 resolution: "table-layout@npm:1.0.2" @@ -16281,6 +21147,17 @@ __metadata: languageName: node linkType: hard +"test-exclude@npm:^6.0.0": + version: 6.0.0 + resolution: "test-exclude@npm:6.0.0" + dependencies: + "@istanbuljs/schema": "npm:^0.1.2" + glob: "npm:^7.1.4" + minimatch: "npm:^3.0.4" + checksum: 8fccb2cb6c8fcb6bb4115394feb833f8b6cf4b9503ec2485c2c90febf435cac62abe882a0c5c51a37b9bbe70640cdd05acf5f45e486ac4583389f4b0855f69e5 + languageName: node + linkType: hard + "text-encoding-utf-8@npm:^1.0.2": version: 1.0.2 resolution: "text-encoding-utf-8@npm:1.0.2" @@ -16314,6 +21191,15 @@ __metadata: languageName: node linkType: hard +"thread-stream@npm:^0.15.1": + version: 0.15.2 + resolution: "thread-stream@npm:0.15.2" + dependencies: + real-require: "npm:^0.1.0" + checksum: ca0a4f5bf45db88b48b41af0299455eaa8f01dd3ef8279e7ba6909c295b3ab79ddf576b595cbbceb4dbdf4012b17c6449805092926163fcbf30ac1604cb595b1 + languageName: node + linkType: hard + "through@npm:>=2.2.7 <3, through@npm:^2.3.8": version: 2.3.8 resolution: "through@npm:2.3.8" @@ -16328,6 +21214,13 @@ __metadata: languageName: node linkType: hard +"tiny-warning@npm:^1.0.2": + version: 1.0.3 + resolution: "tiny-warning@npm:1.0.3" + checksum: da62c4acac565902f0624b123eed6dd3509bc9a8d30c06e017104bedcf5d35810da8ff72864400ad19c5c7806fc0a8323c68baf3e326af7cb7d969f846100d71 + languageName: node + linkType: hard + "tmp@npm:0.0.33, tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -16337,6 +21230,13 @@ __metadata: languageName: node linkType: hard +"tmpl@npm:1.0.5": + version: 1.0.5 + resolution: "tmpl@npm:1.0.5" + checksum: cd922d9b853c00fe414c5a774817be65b058d54a2d01ebb415840960406c669a0fc632f66df885e24cb022ec812739199ccbdb8d1164c3e513f85bfca5ab2873 + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -16353,6 +21253,13 @@ __metadata: languageName: node linkType: hard +"toggle-selection@npm:^1.0.6": + version: 1.0.6 + resolution: "toggle-selection@npm:1.0.6" + checksum: 9a0ed0ecbaac72b4944888dacd79fe0a55eeea76120a4c7e46b3bb3d85b24f086e90560bb22f5a965654a25ab43d79ec47dfdb3f1850ba740b14c5a50abc7040 + languageName: node + linkType: hard + "toidentifier@npm:1.0.1": version: 1.0.1 resolution: "toidentifier@npm:1.0.1" @@ -16360,6 +21267,17 @@ __metadata: languageName: node linkType: hard +"touch@npm:^3.1.0": + version: 3.1.0 + resolution: "touch@npm:3.1.0" + dependencies: + nopt: "npm:~1.0.10" + bin: + nodetouch: ./bin/nodetouch.js + checksum: ece1d9693fbc9b73d8a6d902537b787b5685ac1aeab7562857c50e6671415a73c985055393442b518f4ac37b85c3e7a3e6c36af71142fed13b8bb04fb6664936 + languageName: node + linkType: hard + "tough-cookie@npm:^2.3.3, tough-cookie@npm:~2.5.0": version: 2.5.0 resolution: "tough-cookie@npm:2.5.0" @@ -16440,6 +21358,39 @@ __metadata: languageName: node linkType: hard +"ts-jest@npm:^29.1.2": + version: 29.1.2 + resolution: "ts-jest@npm:29.1.2" + dependencies: + bs-logger: "npm:0.x" + fast-json-stable-stringify: "npm:2.x" + jest-util: "npm:^29.0.0" + json5: "npm:^2.2.3" + lodash.memoize: "npm:4.x" + make-error: "npm:1.x" + semver: "npm:^7.5.3" + yargs-parser: "npm:^21.0.1" + peerDependencies: + "@babel/core": ">=7.0.0-beta.0 <8" + "@jest/types": ^29.0.0 + babel-jest: ^29.0.0 + jest: ^29.0.0 + typescript: ">=4.3 <6" + peerDependenciesMeta: + "@babel/core": + optional: true + "@jest/types": + optional: true + babel-jest: + optional: true + esbuild: + optional: true + bin: + ts-jest: cli.js + checksum: 5e40e7b933a1f3aa0d304d3c53913d1a7125fc79cd44e22b332f6e25dfe13008ddc7ac647066bb4f914d76083f7e8949f0bc156d793c30f3419f4ffd8180968b + languageName: node + linkType: hard + "ts-node@npm:^10.8.0": version: 10.9.1 resolution: "ts-node@npm:10.9.1" @@ -16478,13 +21429,20 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.11.1, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:1.14.1, tslib@npm:^1.11.1, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: 7dbf34e6f55c6492637adb81b555af5e3b4f9cc6b998fb440dac82d3b42bdc91560a35a5fb75e20e24a076c651438234da6743d139e4feabf0783f3cdfe1dddb languageName: node linkType: hard +"tslib@npm:^2.0.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca + languageName: node + linkType: hard + "tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0": version: 2.4.0 resolution: "tslib@npm:2.4.0" @@ -16724,7 +21682,7 @@ __metadata: languageName: node linkType: hard -"typedarray-to-buffer@npm:^3.1.5": +"typedarray-to-buffer@npm:3.1.5, typedarray-to-buffer@npm:^3.1.5": version: 3.1.5 resolution: "typedarray-to-buffer@npm:3.1.5" dependencies: @@ -16801,6 +21759,13 @@ __metadata: languageName: node linkType: hard +"ufo@npm:^1.3.0, ufo@npm:^1.3.1, ufo@npm:^1.3.2": + version: 1.4.0 + resolution: "ufo@npm:1.4.0" + checksum: b7aea8503878dc5ad797d8fc6fe39fec64d9cc7e89fb147ef86ec676e37bb462d99d67c6aad20b15f7d3e6d275d66666b29214422e268f1d98f6eaf707a207a6 + languageName: node + linkType: hard + "uglify-js@npm:^3.1.4": version: 3.16.0 resolution: "uglify-js@npm:3.16.0" @@ -16810,6 +21775,15 @@ __metadata: languageName: node linkType: hard +"uint8arrays@npm:^3.0.0, uint8arrays@npm:^3.1.0": + version: 3.1.1 + resolution: "uint8arrays@npm:3.1.1" + dependencies: + multiformats: "npm:^9.4.2" + checksum: 536e70273c040484aa7d522031a9dbca1fe8c06eb58a3ace1064ba68825b4e2764d4a0b604a1c451e7b8be0986dc94f23a419cfe9334bd116716074a2d29b33d + languageName: node + linkType: hard + "ultron@npm:~1.1.0": version: 1.1.1 resolution: "ultron@npm:1.1.1" @@ -16829,6 +21803,20 @@ __metadata: languageName: node linkType: hard +"uncrypto@npm:^0.1.3": + version: 0.1.3 + resolution: "uncrypto@npm:0.1.3" + checksum: 0020f74b0ce34723196d8982a73bb7f40cff455a41b8f88ae146b86885f4e66e41a1241fe80a887505c3bd2c7f07ed362b6ed041968370073c40a98496e6a737 + languageName: node + linkType: hard + +"undefsafe@npm:^2.0.5": + version: 2.0.5 + resolution: "undefsafe@npm:2.0.5" + checksum: f42ab3b5770fedd4ada175fc1b2eb775b78f609156f7c389106aafd231bfc210813ee49f54483d7191d7b76e483bc7f537b5d92d19ded27156baf57592eb02cc + languageName: node + linkType: hard + "undici-types@npm:~5.26.4": version: 5.26.5 resolution: "undici-types@npm:5.26.5" @@ -16845,6 +21833,19 @@ __metadata: languageName: node linkType: hard +"unenv@npm:^1.9.0": + version: 1.9.0 + resolution: "unenv@npm:1.9.0" + dependencies: + consola: "npm:^3.2.3" + defu: "npm:^6.1.3" + mime: "npm:^3.0.0" + node-fetch-native: "npm:^1.6.1" + pathe: "npm:^1.1.1" + checksum: 7b5e0f139f69ebb9d2822abc84903eccb5655bacc00a26cc3be260f25b3d84b5e19418503e038c7bf4bcc67c4f8ebcab7d55736f7eddf7a3948a311176b1d000 + languageName: node + linkType: hard + "unique-filename@npm:^1.1.1": version: 1.1.1 resolution: "unique-filename@npm:1.1.1" @@ -16884,6 +21885,97 @@ __metadata: languageName: node linkType: hard +"unstorage@npm:^1.9.0": + version: 1.10.1 + resolution: "unstorage@npm:1.10.1" + dependencies: + anymatch: "npm:^3.1.3" + chokidar: "npm:^3.5.3" + destr: "npm:^2.0.2" + h3: "npm:^1.8.2" + ioredis: "npm:^5.3.2" + listhen: "npm:^1.5.5" + lru-cache: "npm:^10.0.2" + mri: "npm:^1.2.0" + node-fetch-native: "npm:^1.4.1" + ofetch: "npm:^1.3.3" + ufo: "npm:^1.3.1" + peerDependencies: + "@azure/app-configuration": ^1.4.1 + "@azure/cosmos": ^4.0.0 + "@azure/data-tables": ^13.2.2 + "@azure/identity": ^3.3.2 + "@azure/keyvault-secrets": ^4.7.0 + "@azure/storage-blob": ^12.16.0 + "@capacitor/preferences": ^5.0.6 + "@netlify/blobs": ^6.2.0 + "@planetscale/database": ^1.11.0 + "@upstash/redis": ^1.23.4 + "@vercel/kv": ^0.2.3 + idb-keyval: ^6.2.1 + peerDependenciesMeta: + "@azure/app-configuration": + optional: true + "@azure/cosmos": + optional: true + "@azure/data-tables": + optional: true + "@azure/identity": + optional: true + "@azure/keyvault-secrets": + optional: true + "@azure/storage-blob": + optional: true + "@capacitor/preferences": + optional: true + "@netlify/blobs": + optional: true + "@planetscale/database": + optional: true + "@upstash/redis": + optional: true + "@vercel/kv": + optional: true + idb-keyval: + optional: true + checksum: 1b99782efd7f22826731da0b9fe4af18227e006c6b3f057d7cd0da5590d93a1ff3eb192d8b037bcc883a9c76de96560a2e975a0f574eb4b8f5e7207bae3de149 + languageName: node + linkType: hard + +"untun@npm:^0.1.3": + version: 0.1.3 + resolution: "untun@npm:0.1.3" + dependencies: + citty: "npm:^0.1.5" + consola: "npm:^3.2.3" + pathe: "npm:^1.1.1" + bin: + untun: bin/untun.mjs + checksum: 6a096002ca13b8442ad1d40840088888cfaa28626eefdd132cd0fd3d3b956af121a9733b7bda32647608e278fb13332d2b72e2c319a27dc55dbc8e709a2f61d4 + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.0.13": + version: 1.0.13 + resolution: "update-browserslist-db@npm:1.0.13" + dependencies: + escalade: "npm:^3.1.1" + picocolors: "npm:^1.0.0" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 9074b4ef34d2ed931f27d390aafdd391ee7c45ad83c508e8fed6aaae1eb68f81999a768ed8525c6f88d4001a4fbf1b8c0268f099d0e8e72088ec5945ac796acf + languageName: node + linkType: hard + +"uqr@npm:^0.1.2": + version: 0.1.2 + resolution: "uqr@npm:0.1.2" + checksum: 31f1fe7d7a8121a2670712234524763160985b053e7eb8af7925a131bcde0df11641e15129d988358032da603185456d08dd72b26b507897272eb9640273bfa6 + languageName: node + linkType: hard + "uri-js@npm:^4.2.2": version: 4.4.1 resolution: "uri-js@npm:4.4.1" @@ -16926,6 +22018,19 @@ __metadata: languageName: node linkType: hard +"urql@npm:^3.0.3": + version: 3.0.4 + resolution: "urql@npm:3.0.4" + dependencies: + "@urql/core": "npm:^3.2.0" + wonka: "npm:^6.0.0" + peerDependencies: + graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + react: ">= 16.8.0" + checksum: b157585cfa21d3d1fd60ff4612ffdfb1af0803e71b9a90381e20fea355671c944a478eb92d0ecb1b20967b4b42bdb099cb141f233a97684b09dd24ac619a26a5 + languageName: node + linkType: hard + "usb@npm:^1.6.3": version: 1.9.2 resolution: "usb@npm:1.9.2" @@ -16937,6 +22042,46 @@ __metadata: languageName: node linkType: hard +"use-callback-ref@npm:^1.3.0": + version: 1.3.1 + resolution: "use-callback-ref@npm:1.3.1" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 7cc68dbd8bb9890e21366f153938988967f0a17168a215bf31e24519f826a2de7de596e981f016603a363362f736f2cffad05091c3857fcafbc9c3b20a3eef1e + languageName: node + linkType: hard + +"use-sidecar@npm:^1.1.2": + version: 1.1.2 + resolution: "use-sidecar@npm:1.1.2" + dependencies: + detect-node-es: "npm:^1.1.0" + tslib: "npm:^2.0.0" + peerDependencies: + "@types/react": ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: ec99e31aefeb880f6dc4d02cb19a01d123364954f857811470ece32872f70d6c3eadbe4d073770706a9b7db6136f2a9fbf1bb803e07fbb21e936a47479281690 + languageName: node + linkType: hard + +"use-sync-external-store@npm:1.2.0, use-sync-external-store@npm:^1.2.0": + version: 1.2.0 + resolution: "use-sync-external-store@npm:1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: a676216affc203876bd47981103f201f28c2731361bb186367e12d287a7566763213a8816910c6eb88265eccd4c230426eb783d64c373c4a180905be8820ed8e + languageName: node + linkType: hard + "utf-8-validate@npm:5.0.7": version: 5.0.7 resolution: "utf-8-validate@npm:5.0.7" @@ -17046,6 +22191,17 @@ __metadata: languageName: node linkType: hard +"v8-to-istanbul@npm:^9.0.1": + version: 9.2.0 + resolution: "v8-to-istanbul@npm:9.2.0" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.12" + "@types/istanbul-lib-coverage": "npm:^2.0.1" + convert-source-map: "npm:^2.0.0" + checksum: 18dd8cebfb6790f27f4e41e7cff77c7ab1c8904085f354dd7875e2eb65f4261c4cf40939132502875779d92304bfea46b8336346ecb40b6f33c3a3979e6f5729 + languageName: node + linkType: hard + "validate-npm-package-license@npm:^3.0.1": version: 3.0.4 resolution: "validate-npm-package-license@npm:3.0.4" @@ -17056,6 +22212,24 @@ __metadata: languageName: node linkType: hard +"valtio@npm:1.11.2": + version: 1.11.2 + resolution: "valtio@npm:1.11.2" + dependencies: + proxy-compare: "npm:2.5.1" + use-sync-external-store: "npm:1.2.0" + peerDependencies: + "@types/react": ">=16.8" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + react: + optional: true + checksum: a259f5af204b801668e019855813a8f702c9558961395bb5847f583119428b997efb9b0e6feb5d6e48a76a9b541173a10fdfdb1527a7bd14477a0e0c5beba914 + languageName: node + linkType: hard + "varint@npm:^5.0.0": version: 5.0.2 resolution: "varint@npm:5.0.2" @@ -17102,6 +22276,46 @@ __metadata: languageName: node linkType: hard +"wagmi@npm:0.12.18": + version: 0.12.18 + resolution: "wagmi@npm:0.12.18" + dependencies: + "@tanstack/query-sync-storage-persister": "npm:^4.27.1" + "@tanstack/react-query": "npm:^4.28.0" + "@tanstack/react-query-persist-client": "npm:^4.28.0" + "@wagmi/core": "npm:0.10.16" + abitype: "npm:^0.3.0" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + ethers: ">=5.5.1 <6" + react: ">=17.0.0" + typescript: ">=4.9.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 1f32350b77b8614f878e8f3594dfa0dedde7d6d951898b2d066d527e0c91e7f2d210afe576d2d88a4245e4182050d377964ba11e3dbeecfeecefef37abc2af1d + languageName: node + linkType: hard + +"walker@npm:^1.0.8": + version: 1.0.8 + resolution: "walker@npm:1.0.8" + dependencies: + makeerror: "npm:1.0.12" + checksum: ad7a257ea1e662e57ef2e018f97b3c02a7240ad5093c392186ce0bcf1f1a60bbadd520d073b9beb921ed99f64f065efb63dfc8eec689a80e569f93c1c5d5e16c + languageName: node + linkType: hard + +"watchpack@npm:2.4.0": + version: 2.4.0 + resolution: "watchpack@npm:2.4.0" + dependencies: + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.1.2" + checksum: 4280b45bc4b5d45d5579113f2a4af93b67ae1b9607cc3d86ae41cdd53ead10db5d9dc3237f24256d05ef88b28c69a02712f78e434cb7ecc8edaca134a56e8cab + languageName: node + linkType: hard + "wcwidth@npm:^1.0.1": version: 1.0.1 resolution: "wcwidth@npm:1.0.1" @@ -17645,6 +22859,13 @@ __metadata: languageName: node linkType: hard +"wonka@npm:^6.0.0, wonka@npm:^6.1.2": + version: 6.3.4 + resolution: "wonka@npm:6.3.4" + checksum: 0f102630182828268b57b54102003449b97abbc2483392239baf856a2fca7b72ae9be67c208415124a3d26a320674ed64387e9bf07a8d0badedb5f607d2ccfdc + languageName: node + linkType: hard + "word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": version: 1.2.3 resolution: "word-wrap@npm:1.2.3" @@ -17716,6 +22937,16 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^4.0.2": + version: 4.0.2 + resolution: "write-file-atomic@npm:4.0.2" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^3.0.7" + checksum: 3be1f5508a46c190619d5386b1ac8f3af3dbe951ed0f7b0b4a0961eed6fc626bd84b50cf4be768dabc0a05b672f5d0c5ee7f42daa557b14415d18c3a13c7d246 + languageName: node + linkType: hard + "ws@npm:7.4.6": version: 7.4.6 resolution: "ws@npm:7.4.6" @@ -17757,7 +22988,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^7, ws@npm:^7.4.5": +"ws@npm:^7, ws@npm:^7.4.5, ws@npm:^7.5.1": version: 7.5.9 resolution: "ws@npm:7.5.9" peerDependencies: @@ -17937,7 +23168,7 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": +"yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" checksum: 9dc2c217ea3bf8d858041252d43e074f7166b53f3d010a8c711275e09cd3d62a002969a39858b92bbda2a6a63a585c7127014534a560b9c69ed2d923d113406e @@ -18000,7 +23231,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^15.1.0": +"yargs@npm:^15.1.0, yargs@npm:^15.3.1": version: 15.4.1 resolution: "yargs@npm:15.4.1" dependencies: @@ -18019,7 +23250,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.7.1, yargs@npm:^17.7.2": +"yargs@npm:^17.3.1, yargs@npm:^17.7.1, yargs@npm:^17.7.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: @@ -18054,3 +23285,40 @@ __metadata: checksum: 1c67216871808c3beaeaf2439adfc589055502665e8fc4267abf36dc4f673018cd15575e8f38a3eb9b8edb43356d91a809fc6ded3fab4b7f5d6a3982d0b97c77 languageName: node linkType: hard + +"zustand@npm:4.3.8": + version: 4.3.8 + resolution: "zustand@npm:4.3.8" + dependencies: + use-sync-external-store: "npm:1.2.0" + peerDependencies: + immer: ">=9.0" + react: ">=16.8" + peerDependenciesMeta: + immer: + optional: true + react: + optional: true + checksum: 95a5335716414c8bef3a48165226ef099ca232931ab6cd1497515ee4241e8d5a8100edf5c3cc7d7131b72a07eb0484501405aa2c3222b4b93ba690cfa2b5593d + languageName: node + linkType: hard + +"zustand@npm:^4.3.1": + version: 4.5.1 + resolution: "zustand@npm:4.5.1" + dependencies: + use-sync-external-store: "npm:1.2.0" + peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0.6" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true + react: + optional: true + checksum: c5dcd734ddcc393bc1febcf1811d606222c6d40cb4cf0e4d605be6cfa1855c8b09b6725ab6b73a395f1e020f8f617b605c800c1f19301b240c45d5042e1b2a10 + languageName: node + linkType: hard