From 7294ea963ff2e5271fb7c3fe3b267d01b10aad58 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 May 2024 12:04:51 +0000 Subject: [PATCH 01/59] Version Packages (#3752) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @hyperlane-xyz/cli@3.12.0 ### Minor Changes - cc8731985: Default to home directory for local registry - ff221f66a: Allows a developer to pass a private key or address to dry-run, and ensures HYP_KEY is only used for private keys. - eba393680: Add CLI-side submitter to use SDK submitter from CRUD and other command modules. ### Patch Changes - 2b7dfe27e: Improve defaults in chain config command - Updated dependencies [eba393680] - Updated dependencies [69de68a66] - @hyperlane-xyz/sdk@3.12.0 - @hyperlane-xyz/utils@3.12.0 ## @hyperlane-xyz/sdk@3.12.0 ### Minor Changes - 69de68a66: Implement aggregation and multisig ISM metadata encoding ### Patch Changes - eba393680: Exports submitter and transformer props types. - Updated dependencies [69de68a66] - @hyperlane-xyz/utils@3.12.0 - @hyperlane-xyz/core@3.12.0 ## @hyperlane-xyz/utils@3.12.0 ### Minor Changes - 69de68a66: Implement aggregation and multisig ISM metadata encoding ## @hyperlane-xyz/core@3.12.0 ### Patch Changes - Updated dependencies [69de68a66] - @hyperlane-xyz/utils@3.12.0 ## @hyperlane-xyz/helloworld@3.12.0 ### Patch Changes - Updated dependencies [eba393680] - Updated dependencies [69de68a66] - @hyperlane-xyz/sdk@3.12.0 - @hyperlane-xyz/core@3.12.0 ## @hyperlane-xyz/infra@3.12.0 ### Patch Changes - Updated dependencies [eba393680] - Updated dependencies [69de68a66] - @hyperlane-xyz/sdk@3.12.0 - @hyperlane-xyz/utils@3.12.0 - @hyperlane-xyz/helloworld@3.12.0 ## @hyperlane-xyz/ccip-server@3.12.0 Co-authored-by: github-actions[bot] --- .changeset/green-ads-live.md | 5 ----- .changeset/lemon-horses-swim.md | 5 ----- .changeset/olive-apricots-develop.md | 5 ----- .changeset/quiet-cheetahs-own.md | 5 ----- .changeset/sixty-avocados-double.md | 5 ----- .changeset/sour-bats-sort.md | 6 ------ solidity/CHANGELOG.md | 7 +++++++ solidity/package.json | 4 ++-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 16 ++++++++++++++++ typescript/cli/package.json | 6 +++--- typescript/cli/src/version.ts | 2 +- typescript/helloworld/CHANGELOG.md | 9 +++++++++ typescript/helloworld/package.json | 6 +++--- typescript/infra/CHANGELOG.md | 10 ++++++++++ typescript/infra/package.json | 8 ++++---- typescript/sdk/CHANGELOG.md | 13 +++++++++++++ typescript/sdk/package.json | 6 +++--- typescript/utils/CHANGELOG.md | 6 ++++++ typescript/utils/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 22 files changed, 95 insertions(+), 63 deletions(-) delete mode 100644 .changeset/green-ads-live.md delete mode 100644 .changeset/lemon-horses-swim.md delete mode 100644 .changeset/olive-apricots-develop.md delete mode 100644 .changeset/quiet-cheetahs-own.md delete mode 100644 .changeset/sixty-avocados-double.md delete mode 100644 .changeset/sour-bats-sort.md diff --git a/.changeset/green-ads-live.md b/.changeset/green-ads-live.md deleted file mode 100644 index f847f584b6..0000000000 --- a/.changeset/green-ads-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Default to home directory for local registry diff --git a/.changeset/lemon-horses-swim.md b/.changeset/lemon-horses-swim.md deleted file mode 100644 index fe5b1c9538..0000000000 --- a/.changeset/lemon-horses-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': patch ---- - -Improve defaults in chain config command diff --git a/.changeset/olive-apricots-develop.md b/.changeset/olive-apricots-develop.md deleted file mode 100644 index b1523f71d4..0000000000 --- a/.changeset/olive-apricots-develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Allows a developer to pass a private key or address to dry-run, and ensures HYP_KEY is only used for private keys. diff --git a/.changeset/quiet-cheetahs-own.md b/.changeset/quiet-cheetahs-own.md deleted file mode 100644 index df89570596..0000000000 --- a/.changeset/quiet-cheetahs-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/sdk': patch ---- - -Exports submitter and transformer props types. diff --git a/.changeset/sixty-avocados-double.md b/.changeset/sixty-avocados-double.md deleted file mode 100644 index 821e105c03..0000000000 --- a/.changeset/sixty-avocados-double.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Add CLI-side submitter to use SDK submitter from CRUD and other command modules. diff --git a/.changeset/sour-bats-sort.md b/.changeset/sour-bats-sort.md deleted file mode 100644 index 37d54bb108..0000000000 --- a/.changeset/sour-bats-sort.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/utils': minor -'@hyperlane-xyz/sdk': minor ---- - -Implement aggregation and multisig ISM metadata encoding diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index bb3548828d..e1735ad8e7 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperlane-xyz/core +## 3.12.0 + +### Patch Changes + +- Updated dependencies [69de68a66] + - @hyperlane-xyz/utils@3.12.0 + ## 3.11.1 ### Patch Changes diff --git a/solidity/package.json b/solidity/package.json index 96077c4339..4c496eb59e 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.11.1", + "version": "3.12.0", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "3.11.1", + "@hyperlane-xyz/utils": "3.12.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 561ec88454..bcee12745f 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 3.12.0 + ## 3.11.1 ## 3.11.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index 1fe18f6bcf..f2ea58a205 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "3.11.1", + "version": "3.12.0", "description": "CCIP server", "typings": "dist/index.d.ts", "typedocMain": "src/index.ts", diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index 118f939439..8f42fe354f 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,21 @@ # @hyperlane-xyz/cli +## 3.12.0 + +### Minor Changes + +- cc8731985: Default to home directory for local registry +- ff221f66a: Allows a developer to pass a private key or address to dry-run, and ensures HYP_KEY is only used for private keys. +- eba393680: Add CLI-side submitter to use SDK submitter from CRUD and other command modules. + +### Patch Changes + +- 2b7dfe27e: Improve defaults in chain config command +- Updated dependencies [eba393680] +- Updated dependencies [69de68a66] + - @hyperlane-xyz/sdk@3.12.0 + - @hyperlane-xyz/utils@3.12.0 + ## 3.11.1 ### Patch Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index b5ce7a6f13..8ee9535f00 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/cli", - "version": "3.11.1", + "version": "3.12.0", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@hyperlane-xyz/registry": "^1.0.7", - "@hyperlane-xyz/sdk": "3.11.1", - "@hyperlane-xyz/utils": "3.11.1", + "@hyperlane-xyz/sdk": "3.12.0", + "@hyperlane-xyz/utils": "3.12.0", "@inquirer/prompts": "^3.0.0", "bignumber.js": "^9.1.1", "chalk": "^5.3.0", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 68e87af9ac..e253aa91f6 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.11.1'; +export const VERSION = '3.12.0'; diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index fec1510878..979fb82851 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/helloworld +## 3.12.0 + +### Patch Changes + +- Updated dependencies [eba393680] +- Updated dependencies [69de68a66] + - @hyperlane-xyz/sdk@3.12.0 + - @hyperlane-xyz/core@3.12.0 + ## 3.11.1 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index edd483b2f7..376a7656e8 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "3.11.1", + "version": "3.12.0", "dependencies": { - "@hyperlane-xyz/core": "3.11.1", + "@hyperlane-xyz/core": "3.12.0", "@hyperlane-xyz/registry": "^1.0.7", - "@hyperlane-xyz/sdk": "3.11.1", + "@hyperlane-xyz/sdk": "3.12.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 4c249b07d8..21b608413b 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,15 @@ # @hyperlane-xyz/infra +## 3.12.0 + +### Patch Changes + +- Updated dependencies [eba393680] +- Updated dependencies [69de68a66] + - @hyperlane-xyz/sdk@3.12.0 + - @hyperlane-xyz/utils@3.12.0 + - @hyperlane-xyz/helloworld@3.12.0 + ## 3.11.1 ### Patch Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index a000f25e14..e78e76a7f3 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.11.1", + "version": "3.12.0", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -12,10 +12,10 @@ "@ethersproject/experimental": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@hyperlane-xyz/helloworld": "3.11.1", + "@hyperlane-xyz/helloworld": "3.12.0", "@hyperlane-xyz/registry": "^1.0.7", - "@hyperlane-xyz/sdk": "3.11.1", - "@hyperlane-xyz/utils": "3.11.1", + "@hyperlane-xyz/sdk": "3.12.0", + "@hyperlane-xyz/utils": "3.12.0", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@solana/web3.js": "^1.78.0", "asn1.js": "5.4.1", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index f87c403051..152e40bd61 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,18 @@ # @hyperlane-xyz/sdk +## 3.12.0 + +### Minor Changes + +- 69de68a66: Implement aggregation and multisig ISM metadata encoding + +### Patch Changes + +- eba393680: Exports submitter and transformer props types. +- Updated dependencies [69de68a66] + - @hyperlane-xyz/utils@3.12.0 + - @hyperlane-xyz/core@3.12.0 + ## 3.11.1 ### Patch Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index d543d58568..4627de39f4 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.11.1", + "version": "3.12.0", "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", - "@hyperlane-xyz/core": "3.11.1", - "@hyperlane-xyz/utils": "3.11.1", + "@hyperlane-xyz/core": "3.12.0", + "@hyperlane-xyz/utils": "3.12.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@solana/spl-token": "^0.3.8", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 2f6ec6c570..f729115183 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/utils +## 3.12.0 + +### Minor Changes + +- 69de68a66: Implement aggregation and multisig ISM metadata encoding + ## 3.11.1 ## 3.11.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 02c8cd29f9..c6db537eca 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.11.1", + "version": "3.12.0", "dependencies": { "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", diff --git a/yarn.lock b/yarn.lock index ef6de3e361..4710b1bab4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4955,8 +4955,8 @@ __metadata: resolution: "@hyperlane-xyz/cli@workspace:typescript/cli" dependencies: "@hyperlane-xyz/registry": "npm:^1.0.7" - "@hyperlane-xyz/sdk": "npm:3.11.1" - "@hyperlane-xyz/utils": "npm:3.11.1" + "@hyperlane-xyz/sdk": "npm:3.12.0" + "@hyperlane-xyz/utils": "npm:3.12.0" "@inquirer/prompts": "npm:^3.0.0" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -4983,12 +4983,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:3.11.1, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:3.12.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.11.1" + "@hyperlane-xyz/utils": "npm:3.12.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -5036,13 +5036,13 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/helloworld@npm:3.11.1, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:3.12.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.11.1" + "@hyperlane-xyz/core": "npm:3.12.0" "@hyperlane-xyz/registry": "npm:^1.0.7" - "@hyperlane-xyz/sdk": "npm:3.11.1" + "@hyperlane-xyz/sdk": "npm:3.12.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -5087,10 +5087,10 @@ __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.11.1" + "@hyperlane-xyz/helloworld": "npm:3.12.0" "@hyperlane-xyz/registry": "npm:^1.0.7" - "@hyperlane-xyz/sdk": "npm:3.11.1" - "@hyperlane-xyz/utils": "npm:3.11.1" + "@hyperlane-xyz/sdk": "npm:3.12.0" + "@hyperlane-xyz/utils": "npm:3.12.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -5150,14 +5150,14 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:3.11.1, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:3.12.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.11.1" - "@hyperlane-xyz/utils": "npm:3.11.1" + "@hyperlane-xyz/core": "npm:3.12.0" + "@hyperlane-xyz/utils": "npm:3.12.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -5225,7 +5225,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/utils@npm:3.11.1, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:3.12.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: From 3145be25434a47b7ae7c56917e6fff7281d79053 Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Fri, 17 May 2024 19:01:02 +0530 Subject: [PATCH 02/59] fix: change version to 3.12.2 (#3801) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- solidity/package.json | 4 +- typescript/ccip-server/package.json | 74 ++++++++++++++--------------- typescript/cli/package.json | 6 +-- typescript/cli/src/version.ts | 2 +- typescript/helloworld/package.json | 6 +-- typescript/infra/package.json | 8 ++-- typescript/sdk/package.json | 6 +-- typescript/utils/package.json | 2 +- yarn.lock | 28 +++++------ 9 files changed, 68 insertions(+), 68 deletions(-) diff --git a/solidity/package.json b/solidity/package.json index 4c496eb59e..3115dab148 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.12.0", + "version": "3.12.2", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "3.12.0", + "@hyperlane-xyz/utils": "3.12.2", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index f2ea58a205..f7a9829c83 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,39 +1,39 @@ { - "name": "@hyperlane-xyz/ccip-server", - "version": "3.12.0", - "description": "CCIP server", - "typings": "dist/index.d.ts", - "typedocMain": "src/index.ts", - "private": true, - "files": [ - "src" - ], - "engines": { - "node": ">=16" - }, - "scripts": { - "start": "tsx 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", - "tsx": "^4.7.1", - "typescript": "5.3.3" - }, - "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" - } + "name": "@hyperlane-xyz/ccip-server", + "version": "3.12.2", + "description": "CCIP server", + "typings": "dist/index.d.ts", + "typedocMain": "src/index.ts", + "private": true, + "files": [ + "src" + ], + "engines": { + "node": ">=16" + }, + "scripts": { + "start": "tsx 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", + "tsx": "^4.7.1", + "typescript": "5.3.3" + }, + "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/cli/package.json b/typescript/cli/package.json index 8ee9535f00..4d5ddf2dbf 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/cli", - "version": "3.12.0", + "version": "3.12.2", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@hyperlane-xyz/registry": "^1.0.7", - "@hyperlane-xyz/sdk": "3.12.0", - "@hyperlane-xyz/utils": "3.12.0", + "@hyperlane-xyz/sdk": "3.12.2", + "@hyperlane-xyz/utils": "3.12.2", "@inquirer/prompts": "^3.0.0", "bignumber.js": "^9.1.1", "chalk": "^5.3.0", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index e253aa91f6..f1447b4559 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.12.0'; +export const VERSION = '3.12.2'; diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 376a7656e8..f577cf8d88 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "3.12.0", + "version": "3.12.2", "dependencies": { - "@hyperlane-xyz/core": "3.12.0", + "@hyperlane-xyz/core": "3.12.2", "@hyperlane-xyz/registry": "^1.0.7", - "@hyperlane-xyz/sdk": "3.12.0", + "@hyperlane-xyz/sdk": "3.12.2", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/package.json b/typescript/infra/package.json index e78e76a7f3..428569c330 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.12.0", + "version": "3.12.2", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -12,10 +12,10 @@ "@ethersproject/experimental": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@hyperlane-xyz/helloworld": "3.12.0", + "@hyperlane-xyz/helloworld": "3.12.2", "@hyperlane-xyz/registry": "^1.0.7", - "@hyperlane-xyz/sdk": "3.12.0", - "@hyperlane-xyz/utils": "3.12.0", + "@hyperlane-xyz/sdk": "3.12.2", + "@hyperlane-xyz/utils": "3.12.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@solana/web3.js": "^1.78.0", "asn1.js": "5.4.1", diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 4627de39f4..c0fa21462f 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.12.0", + "version": "3.12.2", "dependencies": { "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", - "@hyperlane-xyz/core": "3.12.0", - "@hyperlane-xyz/utils": "3.12.0", + "@hyperlane-xyz/core": "3.12.2", + "@hyperlane-xyz/utils": "3.12.2", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@solana/spl-token": "^0.3.8", diff --git a/typescript/utils/package.json b/typescript/utils/package.json index c6db537eca..094b5b053f 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.12.0", + "version": "3.12.2", "dependencies": { "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", diff --git a/yarn.lock b/yarn.lock index 4710b1bab4..2adc97823e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4955,8 +4955,8 @@ __metadata: resolution: "@hyperlane-xyz/cli@workspace:typescript/cli" dependencies: "@hyperlane-xyz/registry": "npm:^1.0.7" - "@hyperlane-xyz/sdk": "npm:3.12.0" - "@hyperlane-xyz/utils": "npm:3.12.0" + "@hyperlane-xyz/sdk": "npm:3.12.2" + "@hyperlane-xyz/utils": "npm:3.12.2" "@inquirer/prompts": "npm:^3.0.0" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -4983,12 +4983,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:3.12.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:3.12.2, @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.12.0" + "@hyperlane-xyz/utils": "npm:3.12.2" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -5036,13 +5036,13 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/helloworld@npm:3.12.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:3.12.2, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: - "@hyperlane-xyz/core": "npm:3.12.0" + "@hyperlane-xyz/core": "npm:3.12.2" "@hyperlane-xyz/registry": "npm:^1.0.7" - "@hyperlane-xyz/sdk": "npm:3.12.0" + "@hyperlane-xyz/sdk": "npm:3.12.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -5087,10 +5087,10 @@ __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.12.0" + "@hyperlane-xyz/helloworld": "npm:3.12.2" "@hyperlane-xyz/registry": "npm:^1.0.7" - "@hyperlane-xyz/sdk": "npm:3.12.0" - "@hyperlane-xyz/utils": "npm:3.12.0" + "@hyperlane-xyz/sdk": "npm:3.12.2" + "@hyperlane-xyz/utils": "npm:3.12.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -5150,14 +5150,14 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:3.12.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:3.12.2, @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.12.0" - "@hyperlane-xyz/utils": "npm:3.12.0" + "@hyperlane-xyz/core": "npm:3.12.2" + "@hyperlane-xyz/utils": "npm:3.12.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -5225,7 +5225,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/utils@npm:3.12.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:3.12.2, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: From 767c20f9ee372d469f689cb1ad373b96803d1e54 Mon Sep 17 00:00:00 2001 From: Connor McEwen Date: Fri, 17 May 2024 13:11:34 -0400 Subject: [PATCH 03/59] chore: enforce conventional commits on PR titles (#3803) ### Description uses this github action: https://github.com/amannn/action-semantic-pull-request --- .github/workflows/lint-pr.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/lint-pr.yaml diff --git a/.github/workflows/lint-pr.yaml b/.github/workflows/lint-pr.yaml new file mode 100644 index 0000000000..f530f652f5 --- /dev/null +++ b/.github/workflows/lint-pr.yaml @@ -0,0 +1,20 @@ +name: 'Lint PR' + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + +permissions: + pull-requests: read + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + - uses: amannn/action-semantic-pull-request@cfb60706e18bc85e8aec535e3c577abe8f70378e + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a574b76584a816d04d84d715afc50f129690cef1 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Sun, 19 May 2024 12:32:46 +0100 Subject: [PATCH 04/59] Update clusters to use new prometheus version (#3772) ### Description GKE was complaining that the kube-state-metrics version we were running with was hitting deprecated APIs, I expect this to fix it. Some minor changes to the infra setup meant that now the push gateway has a different service name, kinda annoyingly with a double `prometheus` in the name. I updated key funder to push to the new gateway ### Drive-by changes We no longer use GKE autopilot, so started using the node exporter too ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/mainnet3/funding.ts | 2 +- .../infra/config/environments/mainnet3/infrastructure.ts | 4 ++-- .../infra/config/environments/mainnet3/liquidityLayer.ts | 2 +- typescript/infra/config/environments/testnet4/funding.ts | 2 +- .../infra/config/environments/testnet4/infrastructure.ts | 4 ++-- typescript/infra/config/environments/testnet4/middleware.ts | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index 64cf0fd66c..a8f12f503f 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -17,7 +17,7 @@ export const keyFunderConfig: KeyFunderConfig = { cronSchedule: '45 * * * *', // Every hour at the 45-minute mark namespace: environment, prometheusPushGateway: - 'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091', + 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', contextFundingFrom: Contexts.Hyperlane, contextsAndRolesToFund: { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], diff --git a/typescript/infra/config/environments/mainnet3/infrastructure.ts b/typescript/infra/config/environments/mainnet3/infrastructure.ts index 1ffdf19fcf..36c15751c7 100644 --- a/typescript/infra/config/environments/mainnet3/infrastructure.ts +++ b/typescript/infra/config/environments/mainnet3/infrastructure.ts @@ -9,7 +9,7 @@ export const infrastructure: InfrastructureConfig = { prometheus: { deployName: 'prometheus', // Node exporter does not work with GKE Autopilot - nodeExporterEnabled: false, + nodeExporterEnabled: true, helmChart: { // See https://github.com/prometheus-community/helm-charts#usage repository: { @@ -17,7 +17,7 @@ export const infrastructure: InfrastructureConfig = { url: 'https://prometheus-community.github.io/helm-charts', }, name: 'prometheus', - version: '15.0.1', + version: '25.21.0', }, }, }, diff --git a/typescript/infra/config/environments/mainnet3/liquidityLayer.ts b/typescript/infra/config/environments/mainnet3/liquidityLayer.ts index e4aec1e77d..ac311f4471 100644 --- a/typescript/infra/config/environments/mainnet3/liquidityLayer.ts +++ b/typescript/infra/config/environments/mainnet3/liquidityLayer.ts @@ -49,6 +49,6 @@ export const relayerConfig: LiquidityLayerRelayerConfig = { }, namespace: environment, prometheusPushGateway: - 'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091', + 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', connectionType: RpcConsensusType.Single, }; diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 56dbd116e6..6880dd2201 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -17,7 +17,7 @@ export const keyFunderConfig: KeyFunderConfig = { cronSchedule: '15 * * * *', // Every hour at the 15-minute mark namespace: environment, prometheusPushGateway: - 'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091', + 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', contextFundingFrom: Contexts.Hyperlane, contextsAndRolesToFund: { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], diff --git a/typescript/infra/config/environments/testnet4/infrastructure.ts b/typescript/infra/config/environments/testnet4/infrastructure.ts index 33d26d2fe7..eb747804c4 100644 --- a/typescript/infra/config/environments/testnet4/infrastructure.ts +++ b/typescript/infra/config/environments/testnet4/infrastructure.ts @@ -9,7 +9,7 @@ export const infrastructure: InfrastructureConfig = { prometheus: { deployName: 'prometheus', // Node exporter does not work with GKE Autopilot - nodeExporterEnabled: false, + nodeExporterEnabled: true, helmChart: { // See https://github.com/prometheus-community/helm-charts#usage repository: { @@ -17,7 +17,7 @@ export const infrastructure: InfrastructureConfig = { url: 'https://prometheus-community.github.io/helm-charts', }, name: 'prometheus', - version: '15.0.1', + version: '25.21.0', }, }, }, diff --git a/typescript/infra/config/environments/testnet4/middleware.ts b/typescript/infra/config/environments/testnet4/middleware.ts index e68cee52f8..607b4a3f6b 100644 --- a/typescript/infra/config/environments/testnet4/middleware.ts +++ b/typescript/infra/config/environments/testnet4/middleware.ts @@ -11,6 +11,6 @@ export const liquidityLayerRelayerConfig: LiquidityLayerRelayerConfig = { }, namespace: environment, prometheusPushGateway: - 'http://prometheus-pushgateway.monitoring.svc.cluster.local:9091', + 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', connectionType: RpcConsensusType.Single, }; From 335a0d7479afc1c98bf4079bf40bb0b83c39178a Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Tue, 21 May 2024 16:08:07 +0100 Subject: [PATCH 05/59] feat: add agent config for Osmosis Mainnet (#3825) ### Description - Added agent config description for Osmosis Mainnet - Updated infra test to allow agent config chains to be a superset of infra chains Taken from @byeongsu-hong's https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3766, but with a fixed test ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: byeongsu-hong --- rust/config/mainnet_config.json | 33 +++++++++++++++++++++ typescript/infra/test/agent-configs.test.ts | 4 ++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/rust/config/mainnet_config.json b/rust/config/mainnet_config.json index e7dd84b785..9aa3618f9d 100644 --- a/rust/config/mainnet_config.json +++ b/rust/config/mainnet_config.json @@ -892,6 +892,39 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x30f5b08e01808643221528BB2f7953bf2830Ef38" }, + "osmosis": { + "bech32Prefix": "osmo", + "blocks": { + "reorgPeriod": 1 + }, + "canonicalAsset": "uosmo", + "chainId": "osmosis-1", + "contractAddressBytes": 32, + "domainId": "875", + "gasPrice": { + "amount": "0.025", + "denom": "uosmo" + }, + "grpcUrls": [ + { + "http": "https://osmosis-grpc.publicnode.com:443" + } + ], + "index": { + "from": 14389169 + }, + "interchainGasPaymaster": "0xd20a9dcf61939fc2fe6ad501b9457b1029b3cc7ab12ed72675ea2e10d831ee5d", + "mailbox": "0x9493e39d85dd038022f97d88aba6bff98d98f9a016b4f2e498bf1d9898420172", + "merkleTreeHook": "0x8920e062ee5ed8afccbc155d13ea9049296399ee41403655864fcd243edc7388", + "name": "osmosis1", + "protocol": "cosmos", + "rpcUrls": [ + { + "http": "https://osmosis-rpc.publicnode.com:443" + } + ], + "validatorAnnounce": "0xaf867da5b09a20ee49161d57f99477c0c42d100f34eb53da0d2eb7fc6c257235" + }, "polygon": { "aggregationHook": "0x34dAb05650Cf590088bA18aF9d597f3e081bCc47", "blockExplorers": [ diff --git a/typescript/infra/test/agent-configs.test.ts b/typescript/infra/test/agent-configs.test.ts index ced36006ab..679dbfc2d6 100644 --- a/typescript/infra/test/agent-configs.test.ts +++ b/typescript/infra/test/agent-configs.test.ts @@ -43,7 +43,9 @@ describe('Agent configs', () => { const agentJsonConfigChains = Object.keys( config.agentJsonConfig.chains, ); - expect(agentJsonConfigChains).to.have.members( + // Allow for the agent JSON config to be a superset of the supported + // chain names, as AW may not always run agents for all chains. + expect(agentJsonConfigChains).to.include.members( config.supportedChainNames, ); }); From 5ae6e329ac723a2b015ef73931504ef33b216cf5 Mon Sep 17 00:00:00 2001 From: Avi Atkin <103125634+avious00@users.noreply.github.com> Date: Tue, 21 May 2024 12:14:40 -0400 Subject: [PATCH 06/59] chore: Update README to reflect v2 deprecation (#3654) --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 2d2d770c65..04551feee7 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,7 @@ Note this is the branch for Hyperlane v3. -V2 is still in operation but is not being actively developed. The code for V2 can be found in the [v2](https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/v2) branch. - -V1 has since been deprecated in favor of V2, but if you are looking for code relating to the existing V1 deployments, refer to the [v1](https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/v1) branch. +V2 is deprecated in favor of V3. The code for V2 can be found in the [v2](https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/v2) branch. For V1 code, refer to the [v1](https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/v1) branch. ## Overview From 1c4cf44c91dd9a32aace1a5de3a74e821032abe7 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 21 May 2024 17:56:57 +0100 Subject: [PATCH 07/59] fix: allow all log levels in release build (#3824) ### Description Found the reason why we never had `trace` level logs in release builds - the `tracing` crate had a feature flag enabled that would wipe out all trace calls at compile time :( ### Drive-by changes ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3823 ### Backward compatibility ### Testing --- rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0322d76292..ba33c80eb2 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -174,7 +174,7 @@ tokio = { version = "1", features = ["parking_lot"] } tokio-test = "0.4" toml_edit = "0.19.14" tonic = "0.9.2" -tracing = { version = "0.1", features = ["release_max_level_debug"] } +tracing = { version = "0.1" } tracing-error = "0.2" tracing-futures = "0.2" tracing-subscriber = { version = "0.3", default-features = false } From 6971d8da0201d194d2410e3f71036366bc049fbb Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Tue, 21 May 2024 13:55:25 -0400 Subject: [PATCH 08/59] chore: update outdated info in Readme files (#3830) --- solidity/README.md | 2 +- typescript/cli/README.md | 2 +- typescript/sdk/README.md | 15 --------------- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/solidity/README.md b/solidity/README.md index d800b4a1c4..041d45fa6a 100644 --- a/solidity/README.md +++ b/solidity/README.md @@ -28,7 +28,7 @@ yarn test ### Fixtures -Some forge tests may generate fixtures in the [fixtures](./fixtures/) directory. This allows [SDK](../typescript/sdk) tests to leverage forge fuzzing. These are git ignored and should not be committed. +Some forge tests may generate fixtures. This allows the [SDK](https://github.com/hyperlane-xyz/hyperlane-monorepo/tree/main/typescript/sdk) tests to leverage forge fuzzing. These are git ignored and should not be committed. ## License diff --git a/typescript/cli/README.md b/typescript/cli/README.md index 20fe12cda1..8493414cdf 100644 --- a/typescript/cli/README.md +++ b/typescript/cli/README.md @@ -12,7 +12,7 @@ To read more about interchain applications, how the protocol works, and how to i ## Setup -Node 16 or newer is required. +Node 18 or newer is required. **Option 1: Global install:** diff --git a/typescript/sdk/README.md b/typescript/sdk/README.md index 4c704aa123..d662581f01 100644 --- a/typescript/sdk/README.md +++ b/typescript/sdk/README.md @@ -18,12 +18,6 @@ Note, this package uses [ESM Modules](https://gist.github.com/sindresorhus/a3978 ## Contents -### Constants - -The names and relevant metadata for all Hyperlane-supported chains are included in this SDK, including public RPC and Explorer urls. It also includes the addresses for all Hyperlane core contracts and middleware. - -### Classes for development, deployment, and testing - The SDK includes various classes for building, deploying, and testing multi-chain applications. Different abstractions serve different use cases. A few common utilities include: - `MultiProvider` / `MultiProtocolProvider`: A utility for managing chain metadata, and RPC providers. @@ -32,15 +26,6 @@ The SDK includes various classes for building, deploying, and testing multi-chai - `HyperlaneDeployer`: The base class for executing multi-chain contract deployments. - `Token` & `WarpCore`: Utilities for interacting with Warp Route deployments. -### Chain Logos - -The SDK contains SVG files for all Hyperlane-supported chains. They can be imported from the `/logos` folder. - -```js -import ArbitrumBlack from '@hyperlane-xyz/sdk/logos/black/arbitrum.svg'; -import ArbitrumColor from '@hyperlane-xyz/sdk/logos/color/arbitrum.svg'; -``` - ## License Apache 2.0 From 1f9a3e9be66c1bbf6b5c75894802541aea698f24 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Tue, 21 May 2024 16:41:04 -0400 Subject: [PATCH 09/59] fix: Ensure dir exists before reading in fixture tests (#3819) ### Description - Makes SDK tests not require solidity tests have run first --- typescript/sdk/src/ism/metadata/aggregation.test.ts | 4 ++-- typescript/sdk/src/ism/metadata/multisig.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/typescript/sdk/src/ism/metadata/aggregation.test.ts b/typescript/sdk/src/ism/metadata/aggregation.test.ts index cb6c3818ca..117f059a63 100644 --- a/typescript/sdk/src/ism/metadata/aggregation.test.ts +++ b/typescript/sdk/src/ism/metadata/aggregation.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { readFileSync, readdirSync } from 'fs'; +import { existsSync, readFileSync, readdirSync } from 'fs'; import { AggregationIsmMetadata, @@ -8,7 +8,7 @@ import { import { Fixture } from './types.test.js'; const path = '../../solidity/fixtures/aggregation'; -const files = readdirSync(path); +const files = existsSync(path) ? readdirSync(path) : []; const fixtures: Fixture[] = files .map((f) => JSON.parse(readFileSync(`${path}/${f}`, 'utf8'))) .map((contents) => { diff --git a/typescript/sdk/src/ism/metadata/multisig.test.ts b/typescript/sdk/src/ism/metadata/multisig.test.ts index 1afca2bdb1..93a42e597e 100644 --- a/typescript/sdk/src/ism/metadata/multisig.test.ts +++ b/typescript/sdk/src/ism/metadata/multisig.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { readFileSync, readdirSync } from 'fs'; +import { existsSync, readFileSync, readdirSync } from 'fs'; import { SignatureLike } from '@hyperlane-xyz/utils'; @@ -9,7 +9,7 @@ import { MultisigMetadata, MultisigMetadataBuilder } from './multisig.js'; import { Fixture } from './types.test.js'; const path = '../../solidity/fixtures/multisig'; -const files = readdirSync(path); +const files = existsSync(path) ? readdirSync(path) : []; const fixtures: Fixture[] = files .map((f) => JSON.parse(readFileSync(`${path}/${f}`, 'utf8'))) .map((contents) => { From 27aabf238c460c9931e09842e951253e5a9b5313 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 22 May 2024 13:29:02 +0100 Subject: [PATCH 10/59] feat: Relayer tokio task instrumentation (#3760) ### Description - Started off adding tokio-metrics but then realised those are quite general, so while we do have instrumentation it's not exposed in our metrics endpoint - switched to adding [tokio-console](https://github.com/tokio-rs/console/tree/main), which does give insight into the lifetime of specific tasks, so we can check which ones take up a long time during relayer startup. These are only visible at the `dependencyTrace` log level, so don't affect performance in the `hyperlane` context. ### Drive-by changes ### Related issues - Helps debug https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3454 and any future performance issues - Does half the work for https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3239 (still need to expose these in the metrics endpoint and import the grafana template) ### Backward compatibility ### Testing --- rust/.cargo/config.toml | 2 + rust/Cargo.lock | 108 ++++++++++++++++-- rust/Cargo.toml | 4 +- rust/Dockerfile | 2 +- rust/agents/relayer/.cargo/config.toml | 2 + rust/agents/relayer/Cargo.toml | 2 + rust/agents/relayer/src/msg/op_submitter.rs | 65 +++++++---- rust/agents/relayer/src/msg/processor.rs | 3 +- rust/agents/relayer/src/processor.rs | 7 +- rust/agents/relayer/src/relayer.rs | 83 +++++++++++--- rust/agents/scraper/Cargo.toml | 1 + rust/agents/scraper/src/agent.rs | 1 + rust/agents/validator/Cargo.toml | 1 + rust/agents/validator/src/validator.rs | 1 + rust/hyperlane-base/Cargo.toml | 1 + rust/hyperlane-base/src/agent.rs | 12 +- rust/hyperlane-base/src/settings/trace/mod.rs | 7 +- 17 files changed, 241 insertions(+), 61 deletions(-) create mode 100644 rust/.cargo/config.toml create mode 100644 rust/agents/relayer/.cargo/config.toml diff --git a/rust/.cargo/config.toml b/rust/.cargo/config.toml new file mode 100644 index 0000000000..bff29e6e17 --- /dev/null +++ b/rust/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] diff --git a/rust/Cargo.lock b/rust/Cargo.lock index d8b69f4ea4..dd039b7d99 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1347,6 +1347,43 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "console-api" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd326812b3fd01da5bb1af7d340d0d555fd3d4b641e7f1dfcf5962a902952787" +dependencies = [ + "futures-core", + "prost 0.12.4", + "prost-types 0.12.4", + "tonic 0.10.2", + "tracing-core", +] + +[[package]] +name = "console-subscriber" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7481d4c57092cd1c19dd541b92bdce883de840df30aa5d03fd48a3935c01842e" +dependencies = [ + "console-api", + "crossbeam-channel", + "crossbeam-utils", + "futures-task", + "hdrhistogram", + "humantime", + "prost-types 0.12.4", + "serde", + "serde_json", + "thread_local", + "tokio", + "tokio-stream", + "tonic 0.10.2", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -1451,7 +1488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73c9d2043a9e617b0d602fbc0a0ecd621568edbf3a9774890a6d562389bd8e1c" dependencies = [ "prost 0.11.9", - "prost-types", + "prost-types 0.11.9", "tendermint-proto 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", "tonic 0.9.2", ] @@ -3793,6 +3830,19 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "hdrhistogram" +version = "7.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d" +dependencies = [ + "base64 0.21.7", + "byteorder", + "flate2", + "nom", + "num-traits", +] + [[package]] name = "headers" version = "0.3.9" @@ -4116,6 +4166,7 @@ dependencies = [ "bs58 0.5.0", "color-eyre", "config", + "console-subscriber", "convert_case 0.6.0", "derive-new", "derive_builder", @@ -4946,7 +4997,7 @@ dependencies = [ "cosmwasm-std", "osmosis-std-derive", "prost 0.11.9", - "prost-types", + "prost-types 0.11.9", "schemars", "serde", "serde-cw-value", @@ -6534,16 +6585,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.9", ] [[package]] name = "prost" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c289cda302b98a28d40c8b3b90498d6e526dd24ac2ecea73e4e491685b94a" +checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" dependencies = [ "bytes", + "prost-derive 0.12.5", ] [[package]] @@ -6559,6 +6611,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9554e3ab233f0a932403704f1a1d08c30d5ccd931adfdfa1e8b5a19b52c1d55a" +dependencies = [ + "anyhow", + "itertools 0.12.0", + "proc-macro2 1.0.76", + "quote 1.0.35", + "syn 2.0.48", +] + [[package]] name = "prost-types" version = "0.11.9" @@ -6568,6 +6633,15 @@ dependencies = [ "prost 0.11.9", ] +[[package]] +name = "prost-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3235c33eb02c1f1e212abdbe34c78b264b038fb58ca612664343271e36e55ffe" +dependencies = [ + "prost 0.12.4", +] + [[package]] name = "protobuf" version = "2.28.0" @@ -6944,6 +7018,7 @@ dependencies = [ "async-trait", "axum", "config", + "console-subscriber", "convert_case 0.6.0", "derive-new", "derive_more", @@ -6968,6 +7043,7 @@ dependencies = [ "strum 0.25.0", "thiserror", "tokio", + "tokio-metrics", "tokio-test", "tracing", "tracing-futures", @@ -7630,6 +7706,7 @@ version = "0.1.0" dependencies = [ "async-trait", "config", + "console-subscriber", "derive_more", "ethers", "eyre", @@ -9700,7 +9777,7 @@ dependencies = [ "num-traits", "once_cell", "prost 0.11.9", - "prost-types", + "prost-types 0.11.9", "ripemd", "serde", "serde_bytes", @@ -9739,7 +9816,7 @@ dependencies = [ "num-derive 0.3.3", "num-traits", "prost 0.11.9", - "prost-types", + "prost-types 0.11.9", "serde", "serde_bytes", "subtle-encoding", @@ -9756,7 +9833,7 @@ dependencies = [ "num-derive 0.3.3", "num-traits", "prost 0.11.9", - "prost-types", + "prost-types 0.11.9", "serde", "serde_bytes", "subtle-encoding", @@ -9956,6 +10033,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.5", "tokio-macros", + "tracing", "windows-sys 0.48.0", ] @@ -9980,6 +10058,17 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "tokio-metrics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eace09241d62c98b7eeb1107d4c5c64ca3bd7da92e8c218c153ab3a78f9be112" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio-stream", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -10217,7 +10306,7 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost 0.12.3", + "prost 0.12.4", "rustls 0.21.10", "rustls-native-certs 0.6.3", "rustls-pemfile 1.0.4", @@ -10638,6 +10727,7 @@ dependencies = [ "async-trait", "axum", "config", + "console-subscriber", "derive-new", "derive_more", "ethers", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index ba33c80eb2..5909e10bc1 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -65,6 +65,7 @@ bytes = "1" clap = "4" color-eyre = "0.6" config = "0.13.3" +console-subscriber = "0.2.0" convert_case = "0.6" cosmrs = { version = "0.14", default-features = false, features = [ "cosmwasm", @@ -170,7 +171,8 @@ tendermint-rpc = { version = "0.32.0", features = ["http-client", "tokio"] } thiserror = "1.0" time = "0.3" tiny-keccak = "2.0.2" -tokio = { version = "1", features = ["parking_lot"] } +tokio = { version = "1", features = ["parking_lot", "tracing"] } +tokio-metrics = { version = "0.3.1", default-features = false } tokio-test = "0.4" toml_edit = "0.19.14" tonic = "0.9.2" diff --git a/rust/Dockerfile b/rust/Dockerfile index f931c0fa78..7a5c882608 100644 --- a/rust/Dockerfile +++ b/rust/Dockerfile @@ -27,7 +27,7 @@ RUN \ --mount=id=cargo,type=cache,sharing=locked,target=/usr/src/target \ --mount=id=cargo-home-registry,type=cache,sharing=locked,target=/usr/local/cargo/registry \ --mount=id=cargo-home-git,type=cache,sharing=locked,target=/usr/local/cargo/git \ - cargo build --release --bin validator --bin relayer --bin scraper && \ + RUSTFLAGS="--cfg tokio_unstable" cargo build --release --bin validator --bin relayer --bin scraper && \ mkdir -p /release && \ cp /usr/src/target/release/validator /release && \ cp /usr/src/target/release/relayer /release && \ diff --git a/rust/agents/relayer/.cargo/config.toml b/rust/agents/relayer/.cargo/config.toml new file mode 100644 index 0000000000..bff29e6e17 --- /dev/null +++ b/rust/agents/relayer/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +rustflags = ["--cfg", "tokio_unstable"] diff --git a/rust/agents/relayer/Cargo.toml b/rust/agents/relayer/Cargo.toml index 59b1c74d6b..5c80952089 100644 --- a/rust/agents/relayer/Cargo.toml +++ b/rust/agents/relayer/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true async-trait.workspace = true axum.workspace = true config.workspace = true +console-subscriber.workspace = true convert_case.workspace = true derive-new.workspace = true derive_more.workspace = true @@ -32,6 +33,7 @@ serde_json.workspace = true strum.workspace = true thiserror.workspace = true tokio = { workspace = true, features = ["rt", "macros", "parking_lot", "rt-multi-thread"] } +tokio-metrics.workspace = true tracing-futures.workspace = true tracing.workspace = true diff --git a/rust/agents/relayer/src/msg/op_submitter.rs b/rust/agents/relayer/src/msg/op_submitter.rs index 4350baeff2..dc30911490 100644 --- a/rust/agents/relayer/src/msg/op_submitter.rs +++ b/rust/agents/relayer/src/msg/op_submitter.rs @@ -4,10 +4,10 @@ use derive_new::new; use futures::future::join_all; use futures_util::future::try_join_all; use prometheus::{IntCounter, IntGaugeVec}; -use tokio::spawn; use tokio::sync::mpsc; use tokio::task::JoinHandle; use tokio::time::sleep; +use tokio_metrics::TaskMonitor; use tracing::{debug, info_span, instrument, instrument::Instrumented, trace, Instrument}; use tracing::{info, warn}; @@ -82,12 +82,18 @@ pub struct SerialSubmitter { metrics: SerialSubmitterMetrics, /// Max batch size for submitting messages max_batch_size: u32, + /// tokio task monitor + task_monitor: TaskMonitor, } impl SerialSubmitter { pub fn spawn(self) -> Instrumented> { let span = info_span!("SerialSubmitter", destination=%self.domain); - spawn(async move { self.run().await }).instrument(span) + let task_monitor = self.task_monitor.clone(); + tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { + self.run().await + })) + .instrument(span) } async fn run(self) { @@ -97,6 +103,7 @@ impl SerialSubmitter { rx: rx_prepare, retry_rx, max_batch_size, + task_monitor, } = self; let prepare_queue = OpQueue::new( metrics.submitter_queue_length.clone(), @@ -115,32 +122,40 @@ impl SerialSubmitter { ); let tasks = [ - spawn(receive_task( - domain.clone(), - rx_prepare, - prepare_queue.clone(), + tokio::spawn(TaskMonitor::instrument( + &task_monitor, + receive_task(domain.clone(), rx_prepare, prepare_queue.clone()), )), - spawn(prepare_task( - domain.clone(), - prepare_queue.clone(), - submit_queue.clone(), - confirm_queue.clone(), - max_batch_size, - metrics.clone(), + tokio::spawn(TaskMonitor::instrument( + &task_monitor, + prepare_task( + domain.clone(), + prepare_queue.clone(), + submit_queue.clone(), + confirm_queue.clone(), + max_batch_size, + metrics.clone(), + ), )), - spawn(submit_task( - domain.clone(), - submit_queue, - confirm_queue.clone(), - max_batch_size, - metrics.clone(), + tokio::spawn(TaskMonitor::instrument( + &task_monitor, + submit_task( + domain.clone(), + submit_queue, + confirm_queue.clone(), + max_batch_size, + metrics.clone(), + ), )), - spawn(confirm_task( - domain.clone(), - prepare_queue, - confirm_queue, - max_batch_size, - metrics, + tokio::spawn(TaskMonitor::instrument( + &task_monitor, + confirm_task( + domain.clone(), + prepare_queue, + confirm_queue, + max_batch_size, + metrics, + ), )), ]; diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/agents/relayer/src/msg/processor.rs index 3aae0d308c..3e3b5aa61d 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/agents/relayer/src/msg/processor.rs @@ -209,6 +209,7 @@ mod test { }, time::sleep, }; + use tokio_metrics::TaskMonitor; fn dummy_processor_metrics(domain_id: u32) -> MessageProcessorMetrics { MessageProcessorMetrics { @@ -367,7 +368,7 @@ mod test { let (message_processor, mut receive_channel) = dummy_message_processor(origin_domain, destination_domain, db); - let processor = Processor::new(Box::new(message_processor)); + let processor = Processor::new(Box::new(message_processor), TaskMonitor::new()); let process_fut = processor.spawn(); let mut pending_messages = vec![]; let pending_message_accumulator = async { diff --git a/rust/agents/relayer/src/processor.rs b/rust/agents/relayer/src/processor.rs index 56dbe3eb8d..c38449cae1 100644 --- a/rust/agents/relayer/src/processor.rs +++ b/rust/agents/relayer/src/processor.rs @@ -5,6 +5,7 @@ use derive_new::new; use eyre::Result; use hyperlane_core::HyperlaneDomain; use tokio::task::JoinHandle; +use tokio_metrics::TaskMonitor; use tracing::{instrument, warn}; #[async_trait] @@ -20,11 +21,15 @@ pub trait ProcessorExt: Send + Debug { #[derive(new)] pub struct Processor { ticker: Box, + task_monitor: TaskMonitor, } impl Processor { pub fn spawn(self) -> JoinHandle<()> { - tokio::spawn(async move { self.main_loop().await }) + let task_monitor = self.task_monitor.clone(); + tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { + self.main_loop().await + })) } #[instrument(ret, skip(self), level = "info", fields(domain=%self.ticker.domain()))] diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index 581620f2f7..0496e38cac 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -25,7 +25,8 @@ use tokio::{ }, task::JoinHandle, }; -use tracing::{info, info_span, instrument::Instrumented, warn, Instrument}; +use tokio_metrics::TaskMonitor; +use tracing::{error, info, info_span, instrument::Instrumented, warn, Instrument}; use crate::{ merkle_tree::builder::MerkleTreeBuilder, @@ -79,6 +80,8 @@ pub struct Relayer { // or move them in `core_metrics`, like the validator metrics agent_metrics: AgentMetrics, chain_metrics: ChainMetrics, + /// Tokio console server + pub tokio_console_server: Option, } impl Debug for Relayer { @@ -109,6 +112,7 @@ impl BaseAgent for Relayer { core_metrics: Arc, agent_metrics: AgentMetrics, chain_metrics: ChainMetrics, + tokio_console_server: console_subscriber::Server, ) -> Result where Self: Sized, @@ -280,13 +284,26 @@ impl BaseAgent for Relayer { core_metrics, agent_metrics, chain_metrics, + tokio_console_server: Some(tokio_console_server), }) } #[allow(clippy::async_yields_async)] - async fn run(self) { + async fn run(mut self) { let mut tasks = vec![]; + let task_monitor = tokio_metrics::TaskMonitor::new(); + if let Some(tokio_console_server) = self.tokio_console_server.take() { + let console_server = + tokio::spawn(TaskMonitor::instrument(&task_monitor.clone(), async move { + info!("Starting tokio console server"); + if let Err(e) = tokio_console_server.serve().await { + error!(error=?e, "Tokio console server failed to start"); + } + })); + tasks.push(console_server.instrument(info_span!("Tokio console server"))); + } + // run server let mpmc_channel = MpmcChannel::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); let custom_routes = relayer_server::routes(mpmc_channel.sender()); @@ -318,6 +335,7 @@ impl BaseAgent for Relayer { .operation_batch_config() .map(|c| c.max_batch_size) .unwrap_or(1), + task_monitor.clone(), ), ); @@ -334,15 +352,25 @@ impl BaseAgent for Relayer { } for origin in &self.origin_chains { - tasks.push(self.run_message_sync(origin).await); - tasks.push(self.run_interchain_gas_payment_sync(origin).await); - tasks.push(self.run_merkle_tree_hook_syncs(origin).await); + tasks.push(self.run_message_sync(origin, task_monitor.clone()).await); + tasks.push( + self.run_interchain_gas_payment_sync(origin, task_monitor.clone()) + .await, + ); + tasks.push( + self.run_merkle_tree_hook_syncs(origin, task_monitor.clone()) + .await, + ); } // each message process attempts to send messages from a chain for origin in &self.origin_chains { - tasks.push(self.run_message_processor(origin, send_channels.clone())); - tasks.push(self.run_merkle_tree_processor(origin)); + tasks.push(self.run_message_processor( + origin, + send_channels.clone(), + task_monitor.clone(), + )); + tasks.push(self.run_merkle_tree_processor(origin, task_monitor.clone())); } if let Err(err) = try_join_all(tasks).await { @@ -355,22 +383,27 @@ impl BaseAgent for Relayer { } impl Relayer { - async fn run_message_sync(&self, origin: &HyperlaneDomain) -> Instrumented> { + async fn run_message_sync( + &self, + origin: &HyperlaneDomain, + task_monitor: TaskMonitor, + ) -> 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.cursor(index_settings).await; - tokio::spawn(async move { + tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { contract_sync .clone() .sync("dispatched_messages", cursor) .await - }) + })) .instrument(info_span!("MessageSync")) } async fn run_interchain_gas_payment_sync( &self, origin: &HyperlaneDomain, + task_monitor: TaskMonitor, ) -> Instrumented> { let index_settings = self.as_ref().settings.chains[origin.name()].index_settings(); let contract_sync = self @@ -379,25 +412,31 @@ impl Relayer { .unwrap() .clone(); let cursor = contract_sync.cursor(index_settings).await; - tokio::spawn(async move { contract_sync.clone().sync("gas_payments", cursor).await }) - .instrument(info_span!("IgpSync")) + tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { + contract_sync.clone().sync("gas_payments", cursor).await + })) + .instrument(info_span!("IgpSync")) } async fn run_merkle_tree_hook_syncs( &self, origin: &HyperlaneDomain, + task_monitor: TaskMonitor, ) -> 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.cursor(index_settings).await; - tokio::spawn(async move { contract_sync.clone().sync("merkle_tree_hook", cursor).await }) - .instrument(info_span!("MerkleTreeHookSync")) + tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { + contract_sync.clone().sync("merkle_tree_hook", cursor).await + })) + .instrument(info_span!("MerkleTreeHookSync")) } fn run_message_processor( &self, origin: &HyperlaneDomain, send_channels: HashMap>, + task_monitor: TaskMonitor, ) -> Instrumented> { let metrics = MessageProcessorMetrics::new( &self.core.metrics, @@ -431,12 +470,16 @@ impl Relayer { ); let span = info_span!("MessageProcessor", origin=%message_processor.domain()); - let processor = Processor::new(Box::new(message_processor)); + let processor = Processor::new(Box::new(message_processor), task_monitor.clone()); processor.spawn().instrument(span) } - fn run_merkle_tree_processor(&self, origin: &HyperlaneDomain) -> Instrumented> { + fn run_merkle_tree_processor( + &self, + origin: &HyperlaneDomain, + task_monitor: TaskMonitor, + ) -> Instrumented> { let metrics = MerkleTreeProcessorMetrics::new(); let merkle_tree_processor = MerkleTreeProcessor::new( self.dbs.get(origin).unwrap().clone(), @@ -445,7 +488,7 @@ impl Relayer { ); let span = info_span!("MerkleTreeProcessor", origin=%merkle_tree_processor.domain()); - let processor = Processor::new(Box::new(merkle_tree_processor)); + let processor = Processor::new(Box::new(merkle_tree_processor), task_monitor.clone()); processor.spawn().instrument(span) } @@ -457,6 +500,7 @@ impl Relayer { receiver: UnboundedReceiver, retry_receiver_channel: MpmcReceiver, batch_size: u32, + task_monitor: TaskMonitor, ) -> Instrumented> { let serial_submitter = SerialSubmitter::new( destination.clone(), @@ -464,10 +508,11 @@ impl Relayer { retry_receiver_channel, SerialSubmitterMetrics::new(&self.core.metrics, destination), batch_size, + task_monitor.clone(), ); let span = info_span!("SerialSubmitter", destination=%destination); let destination = destination.clone(); - tokio::spawn(async move { + tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { // Propagate task panics serial_submitter.spawn().await.unwrap_or_else(|err| { panic!( @@ -475,7 +520,7 @@ impl Relayer { destination, err ) }); - }) + })) .instrument(span) } } diff --git a/rust/agents/scraper/Cargo.toml b/rust/agents/scraper/Cargo.toml index 587a56b8dd..2348135731 100644 --- a/rust/agents/scraper/Cargo.toml +++ b/rust/agents/scraper/Cargo.toml @@ -12,6 +12,7 @@ version.workspace = true [dependencies] async-trait.workspace = true config.workspace = true +console-subscriber.workspace = true derive_more.workspace = true ethers.workspace = true eyre.workspace = true diff --git a/rust/agents/scraper/src/agent.rs b/rust/agents/scraper/src/agent.rs index cc113cacfd..d713432819 100644 --- a/rust/agents/scraper/src/agent.rs +++ b/rust/agents/scraper/src/agent.rs @@ -44,6 +44,7 @@ impl BaseAgent for Scraper { metrics: Arc, agent_metrics: AgentMetrics, chain_metrics: ChainMetrics, + _tokio_console_server: console_subscriber::Server, ) -> eyre::Result where Self: Sized, diff --git a/rust/agents/validator/Cargo.toml b/rust/agents/validator/Cargo.toml index 98a5fe6f83..e9e66eb303 100644 --- a/rust/agents/validator/Cargo.toml +++ b/rust/agents/validator/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true async-trait.workspace = true axum.workspace = true config.workspace = true +console-subscriber.workspace = true derive_more.workspace = true derive-new.workspace = true ethers.workspace = true diff --git a/rust/agents/validator/src/validator.rs b/rust/agents/validator/src/validator.rs index 8d28980098..043ac9249d 100644 --- a/rust/agents/validator/src/validator.rs +++ b/rust/agents/validator/src/validator.rs @@ -63,6 +63,7 @@ impl BaseAgent for Validator { metrics: Arc, agent_metrics: AgentMetrics, chain_metrics: ChainMetrics, + _tokio_console_server: console_subscriber::Server, ) -> Result where Self: Sized, diff --git a/rust/hyperlane-base/Cargo.toml b/rust/hyperlane-base/Cargo.toml index 97d84b6221..9f401b399f 100644 --- a/rust/hyperlane-base/Cargo.toml +++ b/rust/hyperlane-base/Cargo.toml @@ -15,6 +15,7 @@ axum.workspace = true bs58.workspace = true color-eyre = { workspace = true, optional = true } config.workspace = true +console-subscriber.workspace = true convert_case.workspace = true derive_builder.workspace = true derive-new.workspace = true diff --git a/rust/hyperlane-base/src/agent.rs b/rust/hyperlane-base/src/agent.rs index 5f6b504e9a..153526d584 100644 --- a/rust/hyperlane-base/src/agent.rs +++ b/rust/hyperlane-base/src/agent.rs @@ -44,6 +44,7 @@ pub trait BaseAgent: Send + Sync + Debug { metrics: Arc, agent_metrics: AgentMetrics, chain_metrics: ChainMetrics, + tokio_console_server: console_subscriber::Server, ) -> Result where Self: Sized; @@ -75,10 +76,17 @@ pub async fn agent_main() -> Result<()> { let core_settings: &Settings = settings.as_ref(); let metrics = settings.as_ref().metrics(A::AGENT_NAME)?; - core_settings.tracing.start_tracing(&metrics)?; + let tokio_server = core_settings.tracing.start_tracing(&metrics)?; let agent_metrics = create_agent_metrics(&metrics)?; let chain_metrics = create_chain_metrics(&metrics)?; - let agent = A::from_settings(settings, metrics.clone(), agent_metrics, chain_metrics).await?; + let agent = A::from_settings( + settings, + metrics.clone(), + agent_metrics, + chain_metrics, + tokio_server, + ) + .await?; // This await will only end if a panic happens. We won't crash, but instead gracefully shut down agent.run().await; diff --git a/rust/hyperlane-base/src/settings/trace/mod.rs b/rust/hyperlane-base/src/settings/trace/mod.rs index b0640f36ee..00d9cb4c50 100644 --- a/rust/hyperlane-base/src/settings/trace/mod.rs +++ b/rust/hyperlane-base/src/settings/trace/mod.rs @@ -60,7 +60,7 @@ pub struct TracingConfig { impl TracingConfig { /// Attempt to instantiate and register a tracing subscriber setup from /// settings. - pub fn start_tracing(&self, metrics: &CoreMetrics) -> Result<()> { + pub fn start_tracing(&self, metrics: &CoreMetrics) -> Result { let mut target_layer = Targets::new().with_default(self.level); if self.level < Level::DependencyTrace { @@ -70,6 +70,7 @@ impl TracingConfig { .with_target("rusoto_core", Level::Info) .with_target("rustls", Level::Info) .with_target("reqwest", Level::Info) + .with_target("runtime", Level::Debug) .with_target("h2", Level::Info) .with_target("tower", Level::Info) .with_target("tendermint", Level::Info) @@ -85,13 +86,15 @@ impl TracingConfig { let fmt_layer: LogOutputLayer<_> = self.fmt.into(); let err_layer = tracing_error::ErrorLayer::default(); + let (tokio_layer, tokio_server) = console_subscriber::ConsoleLayer::new(); let subscriber = tracing_subscriber::Registry::default() + .with(tokio_layer) .with(target_layer) .with(TimeSpanLifetime::new(metrics)) .with(fmt_layer) .with(err_layer); subscriber.try_init()?; - Ok(()) + Ok(tokio_server) } } From 37af3dc6bad74bea19b989115a49e1308bfd2d0a Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Wed, 22 May 2024 18:03:42 +0100 Subject: [PATCH 11/59] feat: deploy to zetachain and redstone (#3788) ### Description - Deploys to zetachain and redstone - Some learnings can be found in https://discord.com/channels/935678348330434570/1240168332813144084 Chains not yet fully connected: - inevm (Mailbox ownership situation makes this hard) - mantapacific (waiting on an update to the multisig ownership set there, then will create the tx) - non-EVM chains ### Drive-by changes - decreased ethereum cost from $2 to $1.5 following some feedback - Added Superform to the blast set - Allowed for concurrent deploys I originally had some ugly caching to avoid checking storage gas oracles so much (we check them for each route in a routing hook configuration), but I've removed this. Calling it out for the future though https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3788/commits/232695dd6cdec054e164a9ab79788b3c43e32cd1 ### Related issues ### Backward compatibility ### Testing --- .github/workflows/test.yml | 67 +- rust/config/mainnet_config.json | 183 +- .../config/environments/mainnet3/agent.ts | 46 +- .../mainnet3/aw-validators/hyperlane.json | 6 + .../mainnet3/aw-validators/rc.json | 6 + .../config/environments/mainnet3/chains.ts | 2 +- .../mainnet3/core/verification.json | 2120 ++++++++++++----- .../config/environments/mainnet3/funding.ts | 4 +- .../environments/mainnet3/gasPrices.json | 10 +- .../mainnet3/ism/verification.json | 124 + .../config/environments/mainnet3/owners.ts | 1 + .../mainnet3/supportedChainNames.ts | 14 +- .../environments/mainnet3/tokenPrices.json | 42 +- .../environments/mainnet3/validators.ts | 28 + typescript/infra/scripts/agent-utils.ts | 7 + typescript/infra/scripts/deploy.ts | 7 +- typescript/infra/src/config/gas-oracle.ts | 4 +- typescript/infra/src/deployment/deploy.ts | 7 +- .../infra/src/govern/HyperlaneAppGovernor.ts | 6 +- typescript/sdk/src/consts/multisigIsm.ts | 17 + .../sdk/src/core/HyperlaneCoreDeployer.ts | 6 +- .../sdk/src/deploy/HyperlaneDeployer.ts | 29 +- .../sdk/src/deploy/verify/ContractVerifier.ts | 4 +- .../sdk/src/hook/HyperlaneHookDeployer.ts | 16 +- 24 files changed, 2034 insertions(+), 722 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4a4727d03c..62228a44b0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,8 @@ env: LOG_FORMAT: PRETTY CARGO_TERM_COLOR: always RUST_BACKTRACE: full - REGISTRY_URI: ../../node_modules/@hyperlane-xyz/registry/dist + # Alongside the monorepo in the directory above the $GITHUB_WORKSPACE. + REGISTRY_URI: ${{ github.workspace }}/../hyperlane-registry jobs: yarn-install: @@ -81,6 +82,31 @@ jobs: - name: build run: yarn build + checkout-registry: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + repository: hyperlane-xyz/hyperlane-registry + ref: main + path: ./hyperlane-registry + + # Put alongside the monorepo in the directory above the $GITHUB_WORKSPACE. + # actions/checkout doesn't allow you to checkout a repository outside of the workspace. + # See https://github.com/actions/checkout/issues/197. + - run: mv ./hyperlane-registry ../ + + # A workaround for relative paths not being supported by actions/cache. + # See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630. + - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV + + - name: registry-cache + uses: actions/cache@v3 + with: + path: | + ${{ env.REGISTRY_URI_ABSOLUTE }} + key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} + lint-prettier: runs-on: ubuntu-latest needs: [yarn-install] @@ -113,7 +139,7 @@ jobs: yarn-test: runs-on: ubuntu-latest - needs: [yarn-build] + needs: [yarn-build, checkout-registry] steps: - uses: actions/checkout@v3 with: @@ -132,12 +158,23 @@ jobs: !./rust key: ${{ github.event.pull_request.head.sha || github.sha }} + # A workaround for relative paths not being supported by actions/cache. + # See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630. + - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV + + - name: registry-cache + uses: actions/cache@v3 + with: + path: | + ${{ env.REGISTRY_URI_ABSOLUTE }} + key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} + - name: Unit Tests run: yarn test:ci agent-configs: runs-on: ubuntu-latest - needs: [yarn-build] + needs: [yarn-build, checkout-registry] strategy: fail-fast: false matrix: @@ -164,6 +201,17 @@ jobs: !./rust key: ${{ github.event.pull_request.head.sha || github.sha }} + # A workaround for relative paths not being supported by actions/cache. + # See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630. + - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV + + - name: registry-cache + uses: actions/cache@v3 + with: + path: | + ${{ env.REGISTRY_URI_ABSOLUTE }} + key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} + - name: Generate ${{ matrix.environment }} agent config run: | cd typescript/infra @@ -177,7 +225,7 @@ jobs: e2e-matrix: runs-on: larger-runner if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') - needs: [yarn-build] + needs: [yarn-build, checkout-registry] strategy: matrix: e2e-type: [cosmwasm, non-cosmwasm] @@ -230,6 +278,17 @@ jobs: !./rust key: ${{ github.event.pull_request.head.sha || github.sha }} + # A workaround for relative paths not being supported by actions/cache. + # See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630. + - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV + + - name: registry-cache + uses: actions/cache@v3 + with: + path: | + ${{ env.REGISTRY_URI_ABSOLUTE }} + key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} + - name: cargo-cache uses: actions/cache@v3 with: diff --git a/rust/config/mainnet_config.json b/rust/config/mainnet_config.json index 9aa3618f9d..25bfbb8954 100644 --- a/rust/config/mainnet_config.json +++ b/rust/config/mainnet_config.json @@ -1,6 +1,7 @@ { "chains": { "ancient8": { + "aggregationHook": "0x1EF4ED658d542524d1D547ba2F94d3B038a55b8f", "batchContractAddress": "0x4C97D35c668EE5194a13c8DE8Afc18cce40C9F28", "blockExplorers": [ { @@ -18,6 +19,7 @@ "chainId": 888888888, "displayName": "Ancient8", "domainId": 888888888, + "domainRoutingIsm": "0xB6F0f1267B01C27326F61a4B4fe2c73751802685", "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", "fallbackRoutingHook": "0x5E01d8F34b629E3f92d69546bbc4142A7Adee7e9", "gasCurrencyCoinGeckoId": "ethereum", @@ -25,7 +27,7 @@ "from": 2507127 }, "interchainGasPaymaster": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", - "interchainSecurityModule": "0x6E3387e12C6e181BF8e712eCa9c60ccEEaBD1c67", + "interchainSecurityModule": "0xBd3C7253F08c040eDB9c54e7CD4f8a5fd1eb935D", "isTestnet": false, "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x811808Dd29ba8B0FC6C0ec0b5537035E59745162", @@ -36,6 +38,7 @@ "symbol": "ETH" }, "pausableHook": "0x66DC49405Ae2956f7E87FEAa9fE8f506C8987462", + "pausableIsm": "0xcf678903c003651DB0bb933820259A16ea9d95e4", "protocol": "ethereum", "protocolFee": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", @@ -45,6 +48,7 @@ } ], "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0xBd3C7253F08c040eDB9c54e7CD4f8a5fd1eb935D", "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", @@ -71,6 +75,7 @@ "chainId": 42161, "displayName": "Arbitrum", "domainId": 42161, + "domainRoutingIsm": "0x5d759B5CeEb1C3b0181bEc0F80fb04f820cc35D1", "domainRoutingIsmFactory": "0xa2931C37957f3079d3B21b877d56E1db930e02a5", "fallbackRoutingHook": "0x9e8fFb1c26099e75Dd5D794030e2E9AA51471c25", "gasCurrencyCoinGeckoId": "ethereum", @@ -81,7 +86,7 @@ "interchainAccountIsm": "0xfa8bfcE55B3A0631dF38257615cEF7FCD3523A48", "interchainAccountRouter": "0xCD0CFFf6eFD943b4b81f2c15847730dbcD30e3aE", "interchainGasPaymaster": "0x3b6044acd6767f017e99318AA6Ef93b7B06A5a22", - "interchainSecurityModule": "0xD0DBBF922076352cC50B285A0023536561F00EEa", + "interchainSecurityModule": "0x96845a0469363f90779f6D5cd49D79bDDAc69429", "mailbox": "0x979Ca5202784112f4738403dBec5D0F3B9daabB9", "merkleTreeHook": "0x748040afB89B8FdBb992799808215419d36A0930", "name": "arbitrum", @@ -91,15 +96,23 @@ "symbol": "ETH" }, "pausableHook": "0xEf30f29Dcd3FCB1DCcDA9C7Cbf2A5957E8Ee9Cc3", + "pausableIsm": "0x1E38556b4fE553e6249448960875883990efcf34", "protocol": "ethereum", "protocolFee": "0xD0199067DACb8526e7dc524a9a7DCBb57Cd25421", "proxyAdmin": "0x80Cebd56A65e46c474a1A101e89E76C4c51D179c", "rpcUrls": [ + { + "http": "https://arbitrum.llamarpc.com" + }, + { + "http": "https://rpc.ankr.com/arbitrum" + }, { "http": "https://arb1.arbitrum.io/rpc" } ], "staticAggregationHookFactory": "0x9B5f440bBb64Fee337F37e03362b628711Ea09C7", + "staticAggregationIsm": "0x96845a0469363f90779f6D5cd49D79bDDAc69429", "staticAggregationIsmFactory": "0xD4883084389fC1Eeb4dAfB2ADcFc36B711c310EB", "staticMerkleRootMultisigIsmFactory": "0x3C330D4A2e2b8443AFaB8E326E64ab4251B7Eae0", "staticMessageIdMultisigIsmFactory": "0x12Df53079d399a47e9E730df095b712B0FDFA791", @@ -128,6 +141,7 @@ "chainId": 43114, "displayName": "Avalanche", "domainId": 43114, + "domainRoutingIsm": "0x9f68F961ba2dF53b1cB3EbCC0b08e89790C6E2f6", "domainRoutingIsmFactory": "0x28F7907911C7E321c596686AE6D1F20516450037", "fallbackRoutingHook": "0x61D15D571D5f7A9eF0D1938f072f430bBF024747", "gasCurrencyCoinGeckoId": "avalanche-2", @@ -138,7 +152,7 @@ "interchainAccountIsm": "0x786c26C1857032617c215f265509d6E44e44Bfe3", "interchainAccountRouter": "0xA967A6CE0e73fAf672843DECaA372511996E8852", "interchainGasPaymaster": "0x95519ba800BBd0d34eeAE026fEc620AD978176C0", - "interchainSecurityModule": "0xA36B02a83564f52d9244310Ea439ee6F6AfeFb60", + "interchainSecurityModule": "0xe7a61510EA7197281b49e5bdf1798608d5132595", "mailbox": "0xFf06aFcaABaDDd1fb08371f9ccA15D73D51FeBD6", "merkleTreeHook": "0x84eea61D679F42D92145fA052C89900CBAccE95A", "name": "avalanche", @@ -148,10 +162,14 @@ "symbol": "AVAX" }, "pausableHook": "0x239eB860770F1C48ABAC9bE9825d20e3E7c018df", + "pausableIsm": "0xd76080269C641e1adb786b72ae60Ddac3b6b8ed0", "protocol": "ethereum", "protocolFee": "0xEc4AdA26E51f2685279F37C8aE62BeAd8212D597", "proxyAdmin": "0xd7CF8c05fd81b8cA7CfF8E6C49B08a9D63265c9B", "rpcUrls": [ + { + "http": "https://rpc.ankr.com/avalanche" + }, { "http": "https://api.avax.network/ext/bc/C/rpc", "pagination": { @@ -161,6 +179,7 @@ } ], "staticAggregationHookFactory": "0x3bF6Ac986C7Af9A9Ac356C0e99C0041EFd8D96e7", + "staticAggregationIsm": "0xe7a61510EA7197281b49e5bdf1798608d5132595", "staticAggregationIsmFactory": "0xa5E13796eB7d2EDCc88012c8cfF90D69B51FcF9f", "staticMerkleRootMultisigIsmFactory": "0x896cF1D1B66cD211633eDd589fF158E8Cfaf9B54", "staticMessageIdMultisigIsmFactory": "0x8819D653DF5b1FC0DdB32189a2704E471AF8483c", @@ -188,6 +207,7 @@ "chainId": 8453, "displayName": "Base", "domainId": 8453, + "domainRoutingIsm": "0x80C8F6394c0FcF7bAB16ac08b85484361eCe5888", "domainRoutingIsmFactory": "0x7E27456a839BFF31CA642c060a2b68414Cb6e503", "fallbackRoutingHook": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "gasCurrencyCoinGeckoId": "ethereum", @@ -198,7 +218,7 @@ "interchainAccountIsm": "0x861908E6c8F992537F557da5Fb5876836036b347", "interchainAccountRouter": "0xa85F9e4fdA2FFF1c07f2726a630443af3faDF830", "interchainGasPaymaster": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", - "interchainSecurityModule": "0x5D1e7D7c5B9e6dDC8439F67F10c578f2A1084f6F", + "interchainSecurityModule": "0x77bE0b5aE400675063Ce2B2B0d692D9341f4b193", "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", "merkleTreeHook": "0x19dc38aeae620380430C200a6E990D5Af5480117", "name": "base", @@ -208,6 +228,7 @@ "symbol": "ETH" }, "pausableHook": "0x46fa3A5780e5B90Eaf34BDED554d5353B5ABE9E7", + "pausableIsm": "0x2AF32cF8e3Cf42d221eDa0c843818fA5ee129E27", "protocol": "ethereum", "protocolFee": "0x99ca8c74cE7Cfa9d72A51fbb05F9821f5f826b3a", "proxyAdmin": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", @@ -223,14 +244,17 @@ } ], "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsm": "0x77bE0b5aE400675063Ce2B2B0d692D9341f4b193", "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "storageGasOracle": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "testRecipient": "0xb7C9307fE90B9AB093c6D3EdeE3259f5378D5f03", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x182E8d7c5F1B06201b102123FC7dF0EaeB445a7B" }, "blast": { + "aggregationHook": "0x012278333Ce0A845AE9bD7302867a59Bd5D3635d", "blockExplorers": [ { "apiUrl": "https://api.routescan.io/v2/network/mainnet/evm/81457/etherscan/api", @@ -247,6 +271,7 @@ "chainId": 81457, "displayName": "Blast", "domainId": 81457, + "domainRoutingIsm": "0x0296D16d371a49F631143612020138896b3eA421", "domainRoutingIsmFactory": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "fallbackRoutingHook": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "gasCurrencyCoinGeckoId": "ethereum", @@ -254,7 +279,7 @@ "from": 2496427 }, "interchainGasPaymaster": "0xB3fCcD379ad66CED0c91028520C64226611A48c9", - "interchainSecurityModule": "0x0986f6D82A47045788b0ce8EF68f6C0D77726854", + "interchainSecurityModule": "0x208263bB303B2a737642fB13C765F106a2591be8", "mailbox": "0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7", "merkleTreeHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "name": "blast", @@ -264,6 +289,7 @@ "symbol": "ETH" }, "pausableHook": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "pausableIsm": "0x4C97D35c668EE5194a13c8DE8Afc18cce40C9F28", "protocol": "ethereum", "protocolFee": "0x12582c7B0f43c6A667CBaA7fA8b112F7fb1E69F0", "proxyAdmin": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", @@ -273,6 +299,7 @@ } ], "staticAggregationHookFactory": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "staticAggregationIsm": "0x208263bB303B2a737642fB13C765F106a2591be8", "staticAggregationIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", "staticMerkleRootMultisigIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", "staticMessageIdMultisigIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", @@ -300,6 +327,7 @@ "displayName": "Binance Smart Chain", "displayNameShort": "Binance", "domainId": 56, + "domainRoutingIsm": "0xBc3Af0D4930502Ff0f6a8416a7a184c7BFFe19E7", "domainRoutingIsmFactory": "0xe6Af5720d34213C805C08e2470aea979e3F72F75", "fallbackRoutingHook": "0x237E81f87F57Badad9e09f13CC676D986cA852e7", "gasCurrencyCoinGeckoId": "binancecoin", @@ -310,7 +338,7 @@ "interchainAccountIsm": "0xB274Bbbc1df5f1d1763216A93d473fde6f5de043", "interchainAccountRouter": "0x4BBd67dC995572b40Dc6B3eB6CdE5185a5373868", "interchainGasPaymaster": "0x78E25e7f84416e69b9339B0A6336EB6EFfF6b451", - "interchainSecurityModule": "0xab3df354baBee6c2B88E2CeD3b2e030e31aA5e61", + "interchainSecurityModule": "0xfA360ff588623A026BF19A1801F2A8F1f045fa33", "mailbox": "0x2971b9Aec44bE4eb673DF1B88cDB57b96eefe8a4", "merkleTreeHook": "0xFDb9Cd5f9daAA2E4474019405A328a88E7484f26", "name": "bsc", @@ -320,6 +348,7 @@ "symbol": "BNB" }, "pausableHook": "0x7DBdAd1b4A922B65d37d7258a4227b6658344b7f", + "pausableIsm": "0x25dB01caDf91CfD2f7e6dD829Ce81698217F9151", "protocol": "ethereum", "protocolFee": "0xA8Aa5f14a5463a78E45CC068F11c867949F3E367", "proxyAdmin": "0x65993Af9D0D3a64ec77590db7ba362D6eB78eF70", @@ -335,6 +364,7 @@ } ], "staticAggregationHookFactory": "0xe70E86a7D1e001D419D71F960Cb6CaD59b6A3dB6", + "staticAggregationIsm": "0xfA360ff588623A026BF19A1801F2A8F1f045fa33", "staticAggregationIsmFactory": "0x38B3878c4fb44d201DA924c4a04bae3EE728c065", "staticMerkleRootMultisigIsmFactory": "0xfADBc81Ca8A957F1Bf7c78bCc575b28DBDE042b6", "staticMessageIdMultisigIsmFactory": "0x4B1d8352E35e3BDE36dF5ED2e73C24E35c4a96b7", @@ -374,14 +404,14 @@ "domainRoutingIsm": "0xf18E32428dad0802C5D6F723cB80A6Da889777c4", "domainRoutingIsmFactory": "0x2A2c22B0a8615ad24839fA6Af302E896Af32d1a3", "fallbackRoutingHook": "0xDC98a856fb9112894c2fE32267DA8bF35645FAF3", - "gnosisSafeTransactionServiceUrl": "https://mainnet-tx-svc.celo-safe-prod.celo-networks-dev.org/", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-celo.safe.global/", "index": { "from": 22102340 }, "interchainAccountIsm": "0x30a8DEc5318e2aAa9ad5b069fC606c4CfF6f5676", "interchainAccountRouter": "0x4ED23E3885e1651E62564F78817D91865beba575", "interchainGasPaymaster": "0x571f1435613381208477ac5d6974310d88AC7cB7", - "interchainSecurityModule": "0x99e8E56Dce3402D6E09A82718937fc1cA2A9491E", + "interchainSecurityModule": "0x0dcb01D4ABfa73fadB17C4B0e8cd52A38BD52c66", "mailbox": "0x50da3B3907A08a24fe4999F4Dcf337E8dC7954bb", "merkleTreeHook": "0x04dB778f05854f26E67e0a66b740BBbE9070D366", "name": "celo", @@ -435,8 +465,10 @@ "chainId": 1, "displayName": "Ethereum", "domainId": 1, + "domainRoutingIsm": "0xBA328338044e0C0AFd0591FB6E5e2F83C4e8F742", "domainRoutingIsmFactory": "0x28fA9552F19039b450498B0d8e5DEAe0d0aAc559", "fallbackRoutingHook": "0x571f1435613381208477ac5d6974310d88AC7cB7", + "gasCurrencyCoinGeckoId": "ethereum", "gnosisSafeTransactionServiceUrl": "https://safe-transaction-mainnet.safe.global/", "index": { "from": 18422581 @@ -444,7 +476,7 @@ "interchainAccountIsm": "0x609707355a53d2aAb6366f48E2b607C599D26B29", "interchainAccountRouter": "0x8dBae9B1616c46A20591fE0006Bf015E28ca5cC9", "interchainGasPaymaster": "0x9e6B1022bE9BBF5aFd152483DAD9b88911bC8611", - "interchainSecurityModule": "0xB42b88243F749F47697F01Ae1cbBCA9d4763902a", + "interchainSecurityModule": "0x8CE0c6cAf18DbF5882b35F26E28412f3E9AbDeca", "mailbox": "0xc005dc82818d67AF737725bD4bf75435d065D239", "merkleTreeHook": "0x48e6c30B97748d1e2e03bf3e9FbE3890ca5f8CCA", "name": "ethereum", @@ -454,6 +486,7 @@ "symbol": "ETH" }, "pausableHook": "0x3A66Dc852e56d3748838b3C27CF381105b83705b", + "pausableIsm": "0xDC98a856fb9112894c2fE32267DA8bF35645FAF3", "protocol": "ethereum", "protocolFee": "0x8B05BF30F6247a90006c5837eA63C7905D79e6d8", "proxyAdmin": "0x75EE15Ee1B4A75Fa3e2fDF5DF3253c25599cc659", @@ -466,6 +499,7 @@ } ], "staticAggregationHookFactory": "0x6D2555A8ba483CcF4409C39013F5e9a3285D3C9E", + "staticAggregationIsm": "0x5447cdC0f4B1Afd827BF9d2F6b6cE7668d5dc284", "staticAggregationIsmFactory": "0x46FA191Ad972D9674Ed752B69f9659A0d7b22846", "staticMerkleRootMultisigIsmFactory": "0x47e8aF9e30C32Ab91060ED587894288786761B45", "staticMessageIdMultisigIsmFactory": "0xfA21D9628ADce86531854C2B7ef00F07394B0B69", @@ -497,6 +531,7 @@ "chainId": 100, "displayName": "Gnosis", "domainId": 100, + "domainRoutingIsm": "0x83873DB8B4982091D0781B4eDF108DCb98075C39", "domainRoutingIsmFactory": "0xbB5Df000113e767dE11343A16f83De733e5bCC0F", "fallbackRoutingHook": "0x24f5E353dD03E103Ba2372F7D6FC0cf3A66f849c", "gasCurrencyCoinGeckoId": "xdai", @@ -507,7 +542,7 @@ "interchainAccountIsm": "0x5a56dff3D92D635372718f86e6dF09C1129CFf53", "interchainAccountRouter": "0x5E59EBAedeB691408EBAcF6C37218fa2cFcaC9f2", "interchainGasPaymaster": "0xDd260B99d302f0A3fF885728c086f729c06f227f", - "interchainSecurityModule": "0x8e1aa0687B6d939D5a44304D13B7c922ebB012f1", + "interchainSecurityModule": "0x5DB7edF8C1CF91e34895dB2e4b28d8b9C68ddC7B", "mailbox": "0xaD09d78f4c6b9dA2Ae82b1D34107802d380Bb74f", "merkleTreeHook": "0x2684C6F89E901987E1FdB7649dC5Be0c57C61645", "name": "gnosis", @@ -517,6 +552,7 @@ "symbol": "xDai" }, "pausableHook": "0xf728C884De5275a608dEC222dACd0f2BF2E23AB6", + "pausableIsm": "0x223F7D3f27E6272266AE4B5B91Fd5C7A2d798cD8", "protocol": "ethereum", "protocolFee": "0x9c2214467Daf9e2e1F45b36d08ce0b9C65BFeA88", "proxyAdmin": "0x81a92A1a272cb09d7b4970b07548463dC7aE0cB7", @@ -530,6 +566,7 @@ } ], "staticAggregationHookFactory": "0xbC8AA096dabDf4A0200BB9f8D4Cbb644C3D86d7B", + "staticAggregationIsm": "0xe640167B9a283C8b4039fA33f3ac7be6e7E788c5", "staticAggregationIsmFactory": "0x11EF91d17c5ad3330DbCa709a8841743d3Af6819", "staticMerkleRootMultisigIsmFactory": "0x8E273260EAd8B72A085B19346A676d355740e875", "staticMessageIdMultisigIsmFactory": "0x603f46cc520d2fc22957b81e206408590808F02F", @@ -567,7 +604,7 @@ "interchainAccountIsm": "0x31894E7a734540B343d67E491148EB4FC9f7A45B", "interchainAccountRouter": "0x4E55aDA3ef1942049EA43E904EB01F4A0a9c39bd", "interchainGasPaymaster": "0x19dc38aeae620380430C200a6E990D5Af5480117", - "interchainSecurityModule": "0x3052aD50De54aAAc5D364d80bBE681d29e924597", + "interchainSecurityModule": "0x440f7AD246F3e75df88a6338E8A33e91DA4B2B05", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x0972954923a1e2b2aAb04Fa0c4a0797e5989Cd65", "name": "inevm", @@ -652,6 +689,7 @@ "domainRoutingIsmFactory": "0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147", "fallbackRoutingHook": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807", "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://transaction.safe.manta.network", "index": { "from": 437300 }, @@ -943,6 +981,7 @@ "chainId": 137, "displayName": "Polygon", "domainId": 137, + "domainRoutingIsm": "0xBcb9d74E1D2549fc1939023433aaAB11587bc338", "domainRoutingIsmFactory": "0x0d0E816eE4557689d34fAd5885C53b9393C1D9fA", "fallbackRoutingHook": "0xca4cCe24E7e06241846F5EA0cda9947F0507C40C", "gasCurrencyCoinGeckoId": "matic-network", @@ -953,7 +992,7 @@ "interchainAccountIsm": "0x90384bC552e3C48af51Ef7D9473A9bF87431f5c7", "interchainAccountRouter": "0x5e80f3474825B61183c0F0f0726796F589082420", "interchainGasPaymaster": "0x0071740Bf129b05C4684abfbBeD248D80971cce2", - "interchainSecurityModule": "0x9a795fB62f86146ec06e2377e3C95Af65c7C20eB", + "interchainSecurityModule": "0xe289bD204Dbb4F3aaFA27Dbe5751C71e101CFD80", "mailbox": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", "merkleTreeHook": "0x73FbD25c3e817DC4B4Cd9d00eff6D83dcde2DfF6", "name": "polygon", @@ -963,6 +1002,7 @@ "symbol": "ETH" }, "pausableHook": "0x748040afB89B8FdBb992799808215419d36A0930", + "pausableIsm": "0x6741e91fFDC31c7786E3684427c628dad06299B0", "protocol": "ethereum", "protocolFee": "0xF8F3629e308b4758F8396606405989F8D8C9c578", "proxyAdmin": "0xC4F7590C5d30BE959225dC75640657954A86b980", @@ -978,6 +1018,7 @@ } ], "staticAggregationHookFactory": "0xFeeB86e70e4a640cDd29636CCE19BD6fe8628135", + "staticAggregationIsm": "0xe289bD204Dbb4F3aaFA27Dbe5751C71e101CFD80", "staticAggregationIsmFactory": "0x81AdDD9Ca89105063DaDEBd5B4408551Ce850E22", "staticMerkleRootMultisigIsmFactory": "0xa9E0E18E78b098c2DE36c42E4DDEA13ce214c592", "staticMessageIdMultisigIsmFactory": "0xEa5Be2AD66BB1BA321B7aCf0A079fBE304B09Ca0", @@ -986,7 +1027,7 @@ "testTokenRecipient": "0x85ac1164878e017b67660a74ff1f41f3D05C02Bb", "timelockController": "0x0000000000000000000000000000000000000000", "transactionOverrides": { - "maxFeePerGas": 800000000000, + "maxFeePerGas": 550000000000, "maxPriorityFeePerGas": 50000000000 }, "validatorAnnounce": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5" @@ -1013,6 +1054,7 @@ "domainRoutingIsmFactory": "0xe4057c5B0c43Dc18E36b08C39B419F190D29Ac2d", "fallbackRoutingHook": "0x01aE937A7B05d187bBCBE80F44F41879D3D335a4", "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://safe-transaction-zkevm.safe.global/", "index": { "from": 6577743 }, @@ -1048,6 +1090,56 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9" }, + "redstone": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.redstone.xyz/api", + "family": "blockscout", + "name": "Redstone Explorer", + "url": "https://explorer.redstone.xyz" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 2, + "reorgPeriod": 0 + }, + "chainId": 690, + "displayName": "Redstone", + "domainId": 690, + "domainRoutingIsmFactory": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "fallbackRoutingHook": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", + "gasCurrencyCoinGeckoId": "ethereum", + "index": { + "from": 1797579 + }, + "interchainGasPaymaster": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "interchainSecurityModule": "0xF4689C7fA4920C91a6EEEd59630C9C8da7a77D40", + "mailbox": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "merkleTreeHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", + "name": "redstone", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "protocol": "ethereum", + "protocolFee": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4", + "proxyAdmin": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "rpcUrls": [ + { + "http": "https://rpc.redstonechain.com" + } + ], + "staticAggregationHookFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "staticAggregationIsmFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticMerkleRootMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "staticMessageIdMultisigIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "storageGasOracle": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "testRecipient": "0x1Ab68dC4f7b6cfcd00218D4b761b7F3b5a724555", + "validatorAnnounce": "0x12582c7B0f43c6A667CBaA7fA8b112F7fb1E69F0" + }, "scroll": { "aggregationHook": "0x9Bc0FAf446E128a618A88a2F28960Fb2Ca169faE", "blockExplorers": [ @@ -1069,6 +1161,7 @@ "domainRoutingIsmFactory": "0xe03dad16074BC5EEA9A9311257BF02Eb0B6AAA2b", "fallbackRoutingHook": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", "gasCurrencyCoinGeckoId": "ethereum", + "gnosisSafeTransactionServiceUrl": "https://transaction.safe.scroll.xyz", "index": { "chunk": 999, "from": 271840 @@ -1106,6 +1199,7 @@ "validatorAnnounce": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638" }, "viction": { + "aggregationHook": "0x5c7890FAf9c99dC55926F00d624D7Bc6D7ac6834", "blockExplorers": [ { "apiUrl": "https://www.vicscan.xyz/api", @@ -1122,7 +1216,9 @@ "chainId": 88, "displayName": "Viction", "domainId": 88, + "domainRoutingIsm": "0x477145b11E1a71fEb658d96A0E27F19495121504", "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", "gasCurrencyCoinGeckoId": "tomochain", "index": { "chunk": 999, @@ -1131,7 +1227,7 @@ "interchainAccountIsm": "0xD1E267d2d7876e97E217BfE61c34AB50FEF52807", "interchainAccountRouter": "0x1956848601549de5aa0c887892061fA5aB4f6fC4", "interchainGasPaymaster": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "interchainSecurityModule": "0xBD70Ea9D599a0FC8158B026797177773C3445730", + "interchainSecurityModule": "0xf8F3AF5F6B8f319364c339c0b8cA5975481901eD", "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", "merkleTreeHook": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "name": "viction", @@ -1140,6 +1236,8 @@ "name": "Viction", "symbol": "VIC" }, + "pausableHook": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", + "pausableIsm": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", "protocol": "ethereum", "protocolFee": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", @@ -1152,6 +1250,7 @@ } ], "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsm": "0x60586f0b79426f8F406C807a59c7b6478e8bBa0C", "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", @@ -1160,6 +1259,62 @@ "testTokenRecipient": "0xe042D1fbDf59828dd16b9649Ede7abFc856F7a6c", "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9" + }, + "zetachain": { + "blockExplorers": [ + { + "apiUrl": "https://explorer.zetachain.com", + "family": "other", + "name": "ZetaScan", + "url": "https://explorer.zetachain.com" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 6, + "reorgPeriod": 0 + }, + "chainId": 7000, + "displayName": "ZetaChain", + "domainId": 7000, + "domainRoutingIsmFactory": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "fallbackRoutingHook": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", + "gasCurrencyCoinGeckoId": "zetachain", + "index": { + "from": 3068132 + }, + "interchainGasPaymaster": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "interchainSecurityModule": "0x8dfE6790DbB2Ecc1bEdb0eECfc1Ff467Ae5d8C89", + "mailbox": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "merkleTreeHook": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", + "name": "zetachain", + "nativeToken": { + "decimals": 18, + "name": "ZetaChain", + "symbol": "ZETA" + }, + "pausableHook": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", + "protocol": "ethereum", + "protocolFee": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4", + "proxyAdmin": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "rpcUrls": [ + { + "http": "https://zetachain-evm.blockpi.network/v1/rpc/public" + }, + { + "http": "https://zetachain-athens-evm.blockpi.network/v1/rpc/public" + }, + { + "http": "https://zetachain-mainnet-archive.allthatnode.com:8545" + } + ], + "staticAggregationHookFactory": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "staticAggregationIsmFactory": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "staticMerkleRootMultisigIsmFactory": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "staticMessageIdMultisigIsmFactory": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "storageGasOracle": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "testRecipient": "0x12582c7B0f43c6A667CBaA7fA8b112F7fb1E69F0", + "validatorAnnounce": "0x48083C69f5a42c6B69ABbAd48AE195BD36770ee2" } }, "defaultRpcConsensusType": "fallback" diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 2b4d53e572..971e305305 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -53,18 +53,20 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = { bsc: true, celo: true, ethereum: true, - neutron: true, + gnosis: true, + injective: true, + inevm: true, mantapacific: true, mode: true, moonbeam: true, + neutron: true, optimism: true, polygon: true, - gnosis: true, - scroll: true, polygonzkevm: true, - injective: true, - inevm: true, + redstone: true, + scroll: true, viction: true, + zetachain: true, }, [Role.Relayer]: { arbitrum: true, @@ -75,19 +77,21 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = { bsc: true, celo: true, ethereum: true, - // At the moment, we only relay between Neutron and Manta Pacific on the neutron context. - neutron: false, + gnosis: true, + injective: true, + inevm: true, mantapacific: true, mode: true, moonbeam: true, + // At the moment, we only relay between Neutron and Manta Pacific on the neutron context. + neutron: false, optimism: true, polygon: true, - gnosis: true, - scroll: true, polygonzkevm: true, - injective: true, - inevm: true, + redstone: true, + scroll: true, viction: true, + zetachain: true, }, [Role.Scraper]: { arbitrum: true, @@ -98,21 +102,23 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = { bsc: true, celo: true, ethereum: true, + gnosis: true, // Cannot scrape non-EVM chains - neutron: false, + injective: false, + inevm: true, mantapacific: true, mode: true, moonbeam: true, + // Cannot scrape non-EVM chains + neutron: false, optimism: true, polygon: true, - gnosis: true, - scroll: true, polygonzkevm: true, - // Cannot scrape non-EVM chains - injective: false, - inevm: true, + redstone: true, + scroll: true, // Has RPC non-compliance that breaks scraping. viction: false, + zetachain: true, }, }; @@ -203,7 +209,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'c9c5d37-20240510-014327', + tag: 'd6bb976-20240520-164138', }, gasPaymentEnforcement: gasPaymentEnforcement, metricAppContexts, @@ -211,7 +217,7 @@ const hyperlane: RootAgentConfig = { validators: { docker: { repo, - tag: 'c9c5d37-20240510-014327', + tag: 'de8c2a7-20240515-135254', }, rpcConsensusType: RpcConsensusType.Quorum, chains: validatorChainConfig(Contexts.Hyperlane), @@ -220,7 +226,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'c9c5d37-20240510-014327', + tag: 'd6bb976-20240520-164138', }, }, }; diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json index 550918b8de..a519362328 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/hyperlane.json @@ -109,6 +109,9 @@ "0x6a1da2e0b7ae26aaece1377c0a4dbe25b85fa3ca" ] }, + "redstone": { + "validators": ["0x1400b9737007f7978d8b4bbafb4a69c83f0641a7"] + }, "scroll": { "validators": [ "0xad557170a9f2f21c35e03de07cb30dcbcc3dff63", @@ -118,5 +121,8 @@ }, "viction": { "validators": ["0x1f87c368f8e05a85ef9126d984a980a20930cb9c"] + }, + "zetachain": { + "validators": ["0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef"] } } diff --git a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json index 112dc4a777..31f01e7aa1 100644 --- a/typescript/infra/config/environments/mainnet3/aw-validators/rc.json +++ b/typescript/infra/config/environments/mainnet3/aw-validators/rc.json @@ -106,6 +106,9 @@ "0x1cd73544c000fd519784f56e59bc380a5fef53d6" ] }, + "redstone": { + "validators": ["0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5"] + }, "scroll": { "validators": [ "0x11387d89856219cf685f22781bf4e85e00468d54", @@ -119,5 +122,8 @@ "0xad94659e2383214e4a1c4e8d3c17caffb75bc31b", "0x0f9e5775ac4d3b73dd28e5a3f8394443186cb70c" ] + }, + "zetachain": { + "validators": ["0xa13d146b47242671466e4041f5fe68d22a2ffe09"] } } diff --git a/typescript/infra/config/environments/mainnet3/chains.ts b/typescript/infra/config/environments/mainnet3/chains.ts index 29f33e9365..0e5d4144ef 100644 --- a/typescript/infra/config/environments/mainnet3/chains.ts +++ b/typescript/infra/config/environments/mainnet3/chains.ts @@ -30,7 +30,7 @@ export const ethereumMainnetConfigs: ChainMap = { transactionOverrides: { // A very high max fee per gas is used as Polygon is susceptible // to large swings in gas prices. - maxFeePerGas: 800 * 10 ** 9, // 800 gwei + maxFeePerGas: 550 * 10 ** 9, // 550 gwei maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei }, }, diff --git a/typescript/infra/config/environments/mainnet3/core/verification.json b/typescript/infra/config/environments/mainnet3/core/verification.json index b496235409..aa032b48a1 100644 --- a/typescript/infra/config/environments/mainnet3/core/verification.json +++ b/typescript/infra/config/environments/mainnet3/core/verification.json @@ -683,6 +683,12 @@ "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" + }, + { + "address": "0xcf678903c003651DB0bb933820259A16ea9d95e4", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "arbitrum": [ @@ -865,6 +871,18 @@ "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "PausableIsm" + }, + { + "address": "0xecE63bD3561a8d2daF5763804B91a772183793aF", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + }, + { + "address": "0x1E38556b4fE553e6249448960875883990efcf34", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "avalanche": [ @@ -1035,6 +1053,12 @@ "constructorArguments": "", "isProxy": false, "name": "PausableHook" + }, + { + "address": "0xd76080269C641e1adb786b72ae60Ddac3b6b8ed0", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "base": [ @@ -1181,6 +1205,12 @@ "constructorArguments": "", "isProxy": false, "name": "PausableHook" + }, + { + "address": "0x2AF32cF8e3Cf42d221eDa0c843818fA5ee129E27", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "blast": [ @@ -1945,6 +1975,12 @@ "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "PausableIsm" + }, + { + "address": "0x4C97D35c668EE5194a13c8DE8Afc18cce40C9F28", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "bsc": [ @@ -1995,6 +2031,12 @@ "constructorArguments": "0000000000000000000000002971b9aec44be4eb673df1b88cdb57b96eefe8a4", "isProxy": false, "name": "ValidatorAnnounce" + }, + { + "address": "0x25dB01caDf91CfD2f7e6dD829Ce81698217F9151", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "celo": [ @@ -2299,6 +2341,12 @@ "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "PausableIsm" + }, + { + "address": "0xDC98a856fb9112894c2fE32267DA8bF35645FAF3", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "gnosis": [ @@ -2391,6 +2439,12 @@ "constructorArguments": "", "isProxy": false, "name": "PausableHook" + }, + { + "address": "0x223F7D3f27E6272266AE4B5B91Fd5C7A2d798cD8", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" } ], "inevm": [ @@ -3357,1805 +3411,2593 @@ "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" - }, - { - "address": "0x086eF95a2F74582Ee30E7D698518a872fb18301f", - "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", - "isProxy": false, - "name": "PausableIsm" } ], - "mode": [ - { - "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", - "constructorArguments": "", - "isProxy": false, - "name": "ProxyAdmin" - }, + "moonbeam": [ { - "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "constructorArguments": "000000000000000000000000000000000000000000000000000000000000868b", + "address": "0xeE064c4Dd3d476676a40b7cab94Ef651444175c0", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000504", "isProxy": false, "name": "Mailbox" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", + "constructorArguments": "000000000000000000000000ee064c4dd3d476676a40b7cab94ef651444175c00000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x61DDB465eEA5bc3708Cf8B53156aC91a77A2f029", - "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", - "isProxy": false, - "name": "PausableIsm" - }, - { - "address": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000e2ee936bea8e42671c400ac96de198e06f2ba2a6", + "address": "0xeE064c4Dd3d476676a40b7cab94Ef651444175c0", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000504", "isProxy": false, - "name": "FallbackRoutingHook" + "name": "Mailbox" }, { - "address": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", - "constructorArguments": "", - "isProxy": false, - "name": "PausableHook" + "address": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", + "constructorArguments": "000000000000000000000000ee064c4dd3d476676a40b7cab94ef651444175c00000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false, - "name": "StorageGasOracle" + "name": "MerkleTreeHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", + "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false, - "name": "StorageGasOracle" + "name": "MerkleTreeHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", + "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0xCd3e29A9D293DcC7341295996a118913F7c582c0", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, - "name": "StorageGasOracle" + "name": "ProtocolFee" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", - "constructorArguments": "", + "address": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false, - "name": "InterchainGasPaymaster" - }, - { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "name": "TransparentUpgradeableProxy" + "name": "ValidatorAnnounce" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false, - "name": "StorageGasOracle" + "name": "MerkleTreeHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", + "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false, - "name": "StorageGasOracle" + "name": "MerkleTreeHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", + "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0xCd3e29A9D293DcC7341295996a118913F7c582c0", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, - "name": "StorageGasOracle" + "name": "ProtocolFee" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", - "constructorArguments": "", + "address": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", "isProxy": false, - "name": "InterchainGasPaymaster" + "name": "ValidatorAnnounce" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "name": "TransparentUpgradeableProxy" + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", "constructorArguments": "", "isProxy": false, - "name": "StorageGasOracle" + "name": "PausableHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", - "constructorArguments": "", + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", "isProxy": false, - "name": "InterchainGasPaymaster" + "name": "FallbackRoutingHook" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "name": "TransparentUpgradeableProxy" + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", "isProxy": false, - "name": "StorageGasOracle" + "name": "FallbackRoutingHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", "constructorArguments": "", "isProxy": false, - "name": "InterchainGasPaymaster" + "name": "PausableHook" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "name": "TransparentUpgradeableProxy" + "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", + "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "isProxy": false, + "name": "FallbackRoutingHook" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", "constructorArguments": "", "isProxy": false, - "name": "StorageGasOracle" - }, + "name": "PausableHook" + } + ], + "optimism": [ { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", - "constructorArguments": "", + "address": "0xF00824861e4bFe5dFC769295A50006BA203BBc29", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000000a", "isProxy": false, - "name": "InterchainGasPaymaster" + "name": "Mailbox" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", + "constructorArguments": "000000000000000000000000f00824861e4bfe5dfc769295a50006ba203bbc29000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", "isProxy": false, - "name": "StorageGasOracle" + "name": "MerkleTreeHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", + "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", - "constructorArguments": "", + "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", "isProxy": false, - "name": "StorageGasOracle" + "name": "MerkleTreeHook" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", + "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, + { + "address": "0xD71Ff941120e8f935b8b1E2C1eD72F5d140FF458", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0xF00824861e4bFe5dFC769295A50006BA203BBc29", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000000a", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", + "constructorArguments": "000000000000000000000000f00824861e4bfe5dfc769295a50006ba203bbc29000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", + "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", + "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xD71Ff941120e8f935b8b1E2C1eD72F5d140FF458", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", + "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0xD59a200cCEc5b3b1bF544dD7439De452D718f594", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + }, + { + "address": "0x33D3803215a32B84BFb6b1627367231EcD6F138F", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + } + ], + "polygon": [ + { + "address": "0xA3Ae1C7dBAc1C9658708E6aCD271bfB93d87f8A3", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000089", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", + "constructorArguments": "000000000000000000000000a3ae1c7dbac1c9658708e6acd271bfb93d87f8a3000000000000000000000000c4f7590c5d30be959225dc75640657954a86b98000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xA3Ae1C7dBAc1C9658708E6aCD271bfB93d87f8A3", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000089", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", + "constructorArguments": "000000000000000000000000a3ae1c7dbac1c9658708e6acd271bfb93d87f8a3000000000000000000000000c4f7590c5d30be959225dc75640657954a86b98000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xF8F3629e308b4758F8396606405989F8D8C9c578", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", + "constructorArguments": "0000000000000000000000005d934f4e2f797775e53561bb72aca21ba36b96bb", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0xF8F3629e308b4758F8396606405989F8D8C9c578", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", + "constructorArguments": "0000000000000000000000005d934f4e2f797775e53561bb72aca21ba36b96bb", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0x6741e91fFDC31c7786E3684427c628dad06299B0", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + } + ], + "polygonzkevm": [ + { + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000044d", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "000000000000000000000000000000000000000000000000000000000000044d", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "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, + "name": "PausableHook" + } + ], + "redstone": [ + { + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "00000000000000000000000000000000000000000000000000000000000002b2", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a70000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x79b3D752cc9494eCB93800712471a7a62954C8AE", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + }, + { + "address": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000008f1e22d309baa69d398a03cc88e9b46037e988aa", + "isProxy": false, + "name": "FallbackRoutingHook" + }, { "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", "isProxy": false, - "name": "MerkleTreeHook" + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { "address": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000e2ee936bea8e42671c400ac96de198e06f2ba2a6", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d", "isProxy": false, - "name": "FallbackRoutingHook" + "name": "MerkleTreeHook" }, { "address": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000008f1e22d309baa69d398a03cc88e9b46037e988aa", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "PausableHook" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "address": "0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "address": "0xBDa330Ea8F3005C421C8088e638fBB64fA71b9e0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", - "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x2Fa570E83009eaEef3a1cbd496a9a30F05266634", + "constructorArguments": "000000000000000000000000bda330ea8f3005c421c8088e638fbb64fa71b9e00000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded100000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4", + "address": "0x26f32245fCF5Ad53159E875d5Cae62aEcf19c2d4", "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "ProtocolFee" }, { - "address": "0x48083C69f5a42c6B69ABbAd48AE195BD36770ee2", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "address": "0x12582c7B0f43c6A667CBaA7fA8b112F7fb1E69F0", + "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d", "isProxy": false, "name": "ValidatorAnnounce" } ], - "moonbeam": [ + "scroll": [ { - "address": "0xeE064c4Dd3d476676a40b7cab94Ef651444175c0", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000504", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" - }, - { - "address": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", - "constructorArguments": "000000000000000000000000ee064c4dd3d476676a40b7cab94ef651444175c00000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "name": "TransparentUpgradeableProxy" + "name": "ProxyAdmin" }, { - "address": "0xeE064c4Dd3d476676a40b7cab94Ef651444175c0", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000504", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000082750", "isProxy": false, "name": "Mailbox" }, { - "address": "0x094d03E751f49908080EFf000Dd6FD177fd44CC3", - "constructorArguments": "000000000000000000000000ee064c4dd3d476676a40b7cab94ef651444175c00000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", + "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", + "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", + "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", + "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xCd3e29A9D293DcC7341295996a118913F7c582c0", + "address": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "ProtocolFee" }, { - "address": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" }, { - "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000082750", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", + "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x87403b85f6f316e7ba91ba1fa6C3Fb7dD4095547", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", + "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x89e8C8735f3C3956168BAd6C31e95ecE19CaF507", + "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x14760E32C0746094cF14D97124865BC7F0F7368F", - "constructorArguments": "00000000000000000000000089e8c8735f3c3956168bad6c31e95ece19caf5070000000000000000000000006a9cda3dd1f593983bfd142eb35e6ce4137bd5ce00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xCd3e29A9D293DcC7341295996a118913F7c582c0", + "address": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "ProtocolFee" }, { - "address": "0x8c1001eBee6F25b31863A55EadfF149aF88B356F", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" }, { - "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "constructorArguments": "", "isProxy": false, "name": "PausableHook" }, { - "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "constructorArguments": "", "isProxy": false, "name": "PausableHook" }, { - "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "constructorArguments": "", "isProxy": false, "name": "PausableHook" }, { - "address": "0x6C2D6eA0969F7Aa0A850CCA88c7BFACa563B2361", - "constructorArguments": "000000000000000000000000094d03e751f49908080eff000dd6fd177fd44cc3000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000087403b85f6f316e7ba91ba1fa6c3fb7dd4095547", + "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xe28f2AEEB42ee83CAd068D9A9a449c8b868C137f", + "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", "constructorArguments": "", "isProxy": false, "name": "PausableHook" } ], - "optimism": [ + "viction": [ + { + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false, + "name": "ValidatorAnnounce" + }, + { + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, { - "address": "0xF00824861e4bFe5dFC769295A50006BA203BBc29", - "constructorArguments": "000000000000000000000000000000000000000000000000000000000000000a", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", "isProxy": false, "name": "Mailbox" }, { - "address": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", - "constructorArguments": "000000000000000000000000f00824861e4bfe5dfc769295a50006ba203bbc29000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xD71Ff941120e8f935b8b1E2C1eD72F5d140FF458", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "ProtocolFee" }, { - "address": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" }, { - "address": "0xF00824861e4bFe5dFC769295A50006BA203BBc29", - "constructorArguments": "000000000000000000000000000000000000000000000000000000000000000a", + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", "isProxy": false, "name": "Mailbox" }, { - "address": "0xd4C1905BB1D26BC93DAC913e13CaCC278CdCC80D", - "constructorArguments": "000000000000000000000000f00824861e4bfe5dfc769295a50006ba203bbc29000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x68eE9bec9B4dbB61f69D9D293Ae26a5AACb2e28f", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x9b27988D926673fe99126DF4eed42A4aae8Bc01F", + "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xD8A76C4D91fCbB7Cc8eA795DFDF870E48368995C", - "constructorArguments": "0000000000000000000000009b27988d926673fe99126df4eed42a4aae8bc01f000000000000000000000000e047cb95fb3b7117989e911c6afb34771183fc3500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", + "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xD71Ff941120e8f935b8b1E2C1eD72F5d140FF458", + "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "ProtocolFee" }, { - "address": "0x30f5b08e01808643221528BB2f7953bf2830Ef38", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d", + "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" }, { - "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "address": "0x92cdbF0Ccdf8E93467FA858fb986fa650A02f2A8", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + }, + { + "address": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f112", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", "constructorArguments": "", "isProxy": false, "name": "PausableHook" }, { - "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "address": "0x5d69BC38eF3eDb491c0b7186BEc4eC45c4013f93", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f112", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "address": "0xDab56C5A1EffFdd23f6BD1243E457B1575984Bc6", "constructorArguments": "", "isProxy": false, "name": "PausableHook" + } + ], + "zetachain": [ + { + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" }, { - "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000001b58", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x61DDB465eEA5bc3708Cf8B53156aC91a77A2f029", + "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "isProxy": false, + "name": "PausableIsm" + }, + { + "address": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000e2ee936bea8e42671c400ac96de198e06f2ba2a6", "isProxy": false, "name": "FallbackRoutingHook" }, { - "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "address": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", "constructorArguments": "", "isProxy": false, "name": "PausableHook" }, { - "address": "0xD4b132C6d4AA93A4247F1A91e1ED929c0572a43d", - "constructorArguments": "000000000000000000000000d4c1905bb1d26bc93dac913e13cacc278cdcc80d000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000068ee9bec9b4dbb61f69d9d293ae26a5aacb2e28f", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "FallbackRoutingHook" + "name": "StorageGasOracle" }, { - "address": "0xf753CA2269c8A7693ce1808b5709Fbf36a65D47A", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "InterchainGasPaymaster" }, { - "address": "0xD59a200cCEc5b3b1bF544dD7439De452D718f594", - "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "PausableIsm" + "name": "StorageGasOracle" }, { - "address": "0x33D3803215a32B84BFb6b1627367231EcD6F138F", - "constructorArguments": "000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "PausableIsm" - } - ], - "polygon": [ + "name": "InterchainGasPaymaster" + }, { - "address": "0xA3Ae1C7dBAc1C9658708E6aCD271bfB93d87f8A3", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000089", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "StorageGasOracle" }, { - "address": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", - "constructorArguments": "000000000000000000000000a3ae1c7dbac1c9658708e6acd271bfb93d87f8a3000000000000000000000000c4f7590c5d30be959225dc75640657954a86b98000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xA3Ae1C7dBAc1C9658708E6aCD271bfB93d87f8A3", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000089", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "StorageGasOracle" }, { - "address": "0x5d934f4e2f797775e53561bB72aca21ba36B96BB", - "constructorArguments": "000000000000000000000000a3ae1c7dbac1c9658708e6acd271bfb93d87f8a3000000000000000000000000c4f7590c5d30be959225dc75640657954a86b98000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xF8F3629e308b4758F8396606405989F8D8C9c578", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", - "constructorArguments": "0000000000000000000000005d934f4e2f797775e53561bb72aca21ba36b96bb", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0xF8F3629e308b4758F8396606405989F8D8C9c578", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0x454E1a1E1CA8B51506090f1b5399083658eA4Fc5", - "constructorArguments": "0000000000000000000000005d934f4e2f797775e53561bb72aca21ba36b96bb", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" - } - ], - "polygonzkevm": [ + "name": "InterchainGasPaymaster" + }, + { + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "ProxyAdmin" + "name": "StorageGasOracle" }, { - "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", - "constructorArguments": "000000000000000000000000000000000000000000000000000000000000044d", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "InterchainGasPaymaster" }, { - "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", - "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", - "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "ProxyAdmin" + "name": "StorageGasOracle" }, { - "address": "0xeA87ae93Fa0019a82A727bfd3eBd1cFCa8f64f1D", - "constructorArguments": "000000000000000000000000000000000000000000000000000000000000044d", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "InterchainGasPaymaster" }, { - "address": "0x3a464f746D23Ab22155710f44dB16dcA53e0775E", - "constructorArguments": "000000000000000000000000ea87ae93fa0019a82a727bfd3ebd1cfca8f64f1d0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a700000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", - "constructorArguments": "0000000000000000000000003a464f746d23ab22155710f44db16dca53e0775e", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "StorageGasOracle" }, { - "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "InterchainGasPaymaster" }, { - "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "StorageGasOracle" }, { - "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "InterchainGasPaymaster" }, { - "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "StorageGasOracle" }, { - "address": "0xc2FbB9411186AB3b1a6AFCCA702D1a80B48b197c", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" - } - ], - "scroll": [ + "name": "InterchainGasPaymaster" + }, { - "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "ProxyAdmin" + "name": "StorageGasOracle" }, { - "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000082750", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "InterchainGasPaymaster" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", + "address": "0xE2ee936bEa8e42671c400aC96dE198E06F2bA2A6", "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "MerkleTreeHook" }, { - "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "address": "0x8F1E22d309baa69D398a03cc88E9b46037e988AA", + "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000e2ee936bea8e42671c400ac96de198e06f2ba2a6", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xA1ac41d8A663fd317cc3BD94C7de92dC4BA4a882", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", - "constructorArguments": "", - "isProxy": false, - "name": "ProxyAdmin" + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000082750", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "StorageGasOracle" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", - "isProxy": true, - "name": "TransparentUpgradeableProxy" + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" }, { - "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x6119E37Bd66406A1Db74920aC79C15fB8411Ba76", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x481171eb1aad17eDE6a56005B7F1aB00C581ef13", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", - "constructorArguments": "000000000000000000000000149db7afd694722747035d5aec7007ccb6f8f1120000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xc3F23848Ed2e04C0c6d41bd7804fa8f89F940B94", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", - "isProxy": false, - "name": "FallbackRoutingHook" + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" - }, - { - "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", - "isProxy": false, - "name": "FallbackRoutingHook" + "name": "StorageGasOracle" }, { - "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "InterchainGasPaymaster" }, { - "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", - "isProxy": false, - "name": "FallbackRoutingHook" + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" }, { - "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "PausableHook" + "name": "StorageGasOracle" }, { - "address": "0xDa7cECb05C4aeB02c1aFDE277d4306a2da7Bd762", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba0000000000000000000000006119e37bd66406a1db74920ac79c15fb8411ba76", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "FallbackRoutingHook" + "name": "InterchainGasPaymaster" }, { - "address": "0x4Eb82Ee35b0a1c1d776E3a3B547f9A9bA6FCC9f2", - "constructorArguments": "", - "isProxy": false, - "name": "PausableHook" - } - ], - "viction": [ + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, { - "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "ProxyAdmin" + "name": "StorageGasOracle" }, { - "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "InterchainGasPaymaster" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "ProxyAdmin" + "name": "StorageGasOracle" }, { - "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "InterchainGasPaymaster" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", - "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", + "constructorArguments": "", "isProxy": false, - "name": "ProtocolFee" + "name": "StorageGasOracle" }, { - "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "ValidatorAnnounce" + "name": "InterchainGasPaymaster" }, { - "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, - "name": "ProxyAdmin" + "name": "StorageGasOracle" }, { - "address": "0x4Ed7d626f1E96cD1C0401607Bf70D95243E3dEd1", - "constructorArguments": "0000000000000000000000000000000000000000000000000000000000000058", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", + "constructorArguments": "", "isProxy": false, - "name": "Mailbox" + "name": "InterchainGasPaymaster" }, { - "address": "0x2f2aFaE1139Ce54feFC03593FeE8AB2aDF4a85A7", - "constructorArguments": "0000000000000000000000004ed7d626f1e96cd1c0401607bf70d95243e3ded10000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0x149db7afD694722747035d5AEC7007ccb6F8f112", - "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", - "isProxy": false, - "name": "MerkleTreeHook" - }, - { - "address": "0x19dc38aeae620380430C200a6E990D5Af5480117", + "address": "0xC9B8ea6230d6687a4b13fD3C0b8f0Ec607B26465", "constructorArguments": "", "isProxy": false, "name": "StorageGasOracle" }, { - "address": "0xBF12ef4B9f307463D3FB59c3604F294dDCe287E2", + "address": "0xE0C452DDA7506f0F4dE5C8C1d383F7aD866eA4F0", "constructorArguments": "", "isProxy": false, "name": "InterchainGasPaymaster" }, { - "address": "0x0D63128D887159d63De29497dfa45AFc7C699AE4", - "constructorArguments": "000000000000000000000000bf12ef4b9f307463d3fb59c3604f294ddce287e20000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", + "address": "0x931dFCc8c1141D6F532FD023bd87DAe0080c835d", + "constructorArguments": "000000000000000000000000e0c452dda7506f0f4de5c8c1d383f7ad866ea4f00000000000000000000000000761b0827849abbf7b0cc09ce14e1c93d87f500400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba00000000000000000000000000000000000000000000000000000000", "isProxy": true, "name": "TransparentUpgradeableProxy" }, { - "address": "0xd83A4F747fE80Ed98839e05079B1B7Fe037b1638", + "address": "0xea820f9BCFD5E16a0dd42071EB61A29874Ad81A4", "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba000000000000000000000000a7eccdb9be08178f896c26b7bbd8c3d4e844d9ba", "isProxy": false, "name": "ProtocolFee" }, { - "address": "0x2fa5F5C96419C222cDbCeC797D696e6cE428A7A9", + "address": "0x48083C69f5a42c6B69ABbAd48AE195BD36770ee2", "constructorArguments": "0000000000000000000000002f2afae1139ce54fefc03593fee8ab2adf4a85a7", "isProxy": false, "name": "ValidatorAnnounce" diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index a8f12f503f..9b5fa1523c 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.js'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '5d1391c-20240418-100607', + tag: '375ec39-20240520-160456', }, // We're currently using the same deployer/key funder key as mainnet2. // To minimize nonce clobbering we offset the key funder cron @@ -44,6 +44,8 @@ export const keyFunderConfig: KeyFunderConfig = { polygonzkevm: '0.5', scroll: '0.5', ancient8: '0.5', + redstone: '0.2', + zetachain: '20', }, desiredKathyBalancePerChain: { arbitrum: '0.1', diff --git a/typescript/infra/config/environments/mainnet3/gasPrices.json b/typescript/infra/config/environments/mainnet3/gasPrices.json index 7d34ef96f2..8fd3ca2aac 100644 --- a/typescript/infra/config/environments/mainnet3/gasPrices.json +++ b/typescript/infra/config/environments/mainnet3/gasPrices.json @@ -28,7 +28,7 @@ "decimals": 9 }, "ethereum": { - "amount": "26.346912847", + "amount": "20", "decimals": 9 }, "mantapacific": { @@ -63,6 +63,10 @@ "amount": "3.95", "decimals": 9 }, + "redstone": { + "amount": "0.0003", + "decimals": 9 + }, "inevm": { "amount": "0.1", "decimals": 9 @@ -78,5 +82,9 @@ "injective": { "amount": "700000000", "decimals": 1 + }, + "zetachain": { + "amount": "0.0001", + "decimals": 9 } } diff --git a/typescript/infra/config/environments/mainnet3/ism/verification.json b/typescript/infra/config/environments/mainnet3/ism/verification.json index fbbaaf72dc..cd76f9f2be 100644 --- a/typescript/infra/config/environments/mainnet3/ism/verification.json +++ b/typescript/infra/config/environments/mainnet3/ism/verification.json @@ -3043,6 +3043,68 @@ "name": "DomaingRoutingIsm" } ], + "redstone": [ + { + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" + }, + { + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" + }, + { + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" + }, + { + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" + }, + { + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationIsmFactory" + }, + { + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" + }, + { + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationHookFactory" + }, + { + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" + }, + { + "address": "0x0761b0827849abbf7b0cC09CE14e1C93D87f5004", + "constructorArguments": "", + "isProxy": false, + "name": "DomainRoutingIsmFactory" + }, + { + "address": "0x3b9f24fD2ecfed0d3A88fa7f0E4e5747671981D7", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" + } + ], "scroll": [ { "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", @@ -3352,5 +3414,67 @@ "isProxy": true, "name": "DomaingRoutingIsm" } + ], + "zetachain": [ + { + "address": "0x2C1FAbEcd7bFBdEBF27CcdB67baADB38b6Df90fC", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" + }, + { + "address": "0x4725F7b8037513915aAf6D6CBDE2920E28540dDc", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" + }, + { + "address": "0x8b83fefd896fAa52057798f6426E9f0B080FCCcE", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" + }, + { + "address": "0xAF03386044373E2fe26C5b1dCedF5a7e854a7a3F", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" + }, + { + "address": "0x8F7454AC98228f3504Bb91eA3D8Adafe6406110A", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationIsmFactory" + }, + { + "address": "0x882CD0C5D50b6dD74b36Da4BDb059507fddEDdf2", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" + }, + { + "address": "0xEb9FcFDC9EfDC17c1EC5E1dc085B98485da213D6", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationHookFactory" + }, + { + "address": "0x19930232E9aFC4f4F09d09fe2375680fAc2100D0", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" + }, + { + "address": "0x1052eF3419f26Bec74Ed7CEf4a4FA6812Bc09908", + "constructorArguments": "", + "isProxy": false, + "name": "DomainRoutingIsmFactory" + }, + { + "address": "0x12Ed1BbA182CbC63692F813651BD493B7445C874", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" + } ] } diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts index 3b2d89d7ae..bf1239efc3 100644 --- a/typescript/infra/config/environments/mainnet3/owners.ts +++ b/typescript/infra/config/environments/mainnet3/owners.ts @@ -55,6 +55,7 @@ export const owners: ChainMap = Object.fromEntries( proxyAdmin: timelocks[local] ?? safes[local] ?? DEPLOYER, validatorAnnounce: DEPLOYER, // unused testRecipient: DEPLOYER, + fallbackRoutingHook: DEPLOYER, }, }, ]), diff --git a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts index 03a7d1655d..5a0042c8ee 100644 --- a/typescript/infra/config/environments/mainnet3/supportedChainNames.ts +++ b/typescript/infra/config/environments/mainnet3/supportedChainNames.ts @@ -4,21 +4,23 @@ export const supportedChainNames = [ 'arbitrum', 'ancient8', 'avalanche', + 'base', 'blast', 'bsc', 'celo', 'ethereum', - 'neutron', + 'gnosis', + 'inevm', + 'injective', 'mantapacific', 'mode', 'moonbeam', + 'neutron', 'optimism', 'polygon', - 'gnosis', - 'base', - 'scroll', 'polygonzkevm', - 'injective', - 'inevm', + 'redstone', + 'scroll', 'viction', + 'zetachain', ]; diff --git a/typescript/infra/config/environments/mainnet3/tokenPrices.json b/typescript/infra/config/environments/mainnet3/tokenPrices.json index eeb0b6bcfb..dac9798f6e 100644 --- a/typescript/infra/config/environments/mainnet3/tokenPrices.json +++ b/typescript/infra/config/environments/mainnet3/tokenPrices.json @@ -1,22 +1,24 @@ { - "arbitrum": "3174.87", - "ancient8": "3174.87", - "avalanche": "38.39", - "base": "3174.87", - "blast": "3174.87", - "bsc": "609.32", - "celo": "0.860923", - "ethereum": "3174.87", - "mantapacific": "3174.87", - "mode": "3174.87", - "moonbeam": "0.338118", - "optimism": "3174.87", - "polygon": "0.730041", - "gnosis": "0.993981", - "scroll": "3174.87", - "polygonzkevm": "3174.87", - "inevm": "28.12", - "viction": "0.775722", - "neutron": "0.842639", - "injective": "28.12" + "arbitrum": "2919.87", + "ancient8": "2919.87", + "avalanche": "33.19", + "base": "2919.87", + "blast": "2919.87", + "bsc": "570.1", + "celo": "0.738559", + "ethereum": "2919.87", + "gnosis": "1.005", + "inevm": "21.59", + "mantapacific": "2919.87", + "mode": "2919.87", + "moonbeam": "0.253144", + "optimism": "2919.87", + "polygon": "0.663051", + "polygonzkevm": "2919.87", + "redstone": "2919.87", + "scroll": "2919.87", + "viction": "0.424231", + "zetachain": "1.53", + "injective": "21.59", + "neutron": "0.606906" } diff --git a/typescript/infra/config/environments/mainnet3/validators.ts b/typescript/infra/config/environments/mainnet3/validators.ts index 45d7a469af..444bc70f25 100644 --- a/typescript/infra/config/environments/mainnet3/validators.ts +++ b/typescript/infra/config/environments/mainnet3/validators.ts @@ -380,5 +380,33 @@ export const validatorChainConfig = ( 'mode', ), }, + redstone: { + interval: 5, + reorgPeriod: getReorgPeriod('redstone'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x1400b9737007f7978d8b4bbafb4a69c83f0641a7'], + [Contexts.ReleaseCandidate]: [ + '0x51ed7127c0afc0513a0f141e910c5e02b2a9a4b5', + ], + [Contexts.Neutron]: [], + }, + 'redstone', + ), + }, + zetachain: { + interval: 5, + reorgPeriod: getReorgPeriod('zetachain'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef'], + [Contexts.ReleaseCandidate]: [ + '0xa13d146b47242671466e4041f5fe68d22a2ffe09', + ], + [Contexts.Neutron]: [], + }, + 'zetachain', + ), + }, }; }; diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index 7226bdf0b1..d027d5aad0 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -164,6 +164,13 @@ export function withBuildArtifactPath(args: Argv) { .alias('b', 'buildArtifactPath'); } +export function withConcurrentDeploy(args: Argv) { + return args + .describe('concurrentDeploy', 'If enabled, runs all deploys concurrently') + .boolean('concurrentDeploy') + .default('concurrentDeploy', false); +} + // not requiring to build coreConfig to get agentConfig export async function getAgentConfigsBasedOnArgs(argv?: { environment: DeployEnvironment; diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index c761c59c14..733bfd819e 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -42,6 +42,7 @@ import { getArgs, getModuleDirectory, withBuildArtifactPath, + withConcurrentDeploy, withContext, withModuleAndFork, withNetwork, @@ -56,8 +57,11 @@ async function main() { environment, network, buildArtifactPath, + concurrentDeploy, } = await withContext( - withNetwork(withModuleAndFork(withBuildArtifactPath(getArgs()))), + withConcurrentDeploy( + withNetwork(withModuleAndFork(withBuildArtifactPath(getArgs()))), + ), ).argv; const envConfig = getEnvironmentConfig(environment); @@ -113,6 +117,7 @@ async function main() { multiProvider, ismFactory, contractVerifier, + concurrentDeploy, ); } else if (module === Modules.WARP) { const ismFactory = HyperlaneIsmFactory.fromAddressesMap( diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index 6ca80b39d1..1994e91ce5 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -129,9 +129,9 @@ function getMinUsdCost(local: ChainName, remote: ChainName): number { // By default, min cost is 20 cents let minUsdCost = 0.2; - // For Ethereum local, min cost is 2 USD + // For Ethereum local, min cost is 1.5 USD if (local === 'ethereum') { - minUsdCost = Math.max(minUsdCost, 2); + minUsdCost = Math.max(minUsdCost, 1.5); } const remoteMinCostOverrides: ChainMap = { diff --git a/typescript/infra/src/deployment/deploy.ts b/typescript/infra/src/deployment/deploy.ts index 7812dec3a9..4a418def79 100644 --- a/typescript/infra/src/deployment/deploy.ts +++ b/typescript/infra/src/deployment/deploy.ts @@ -142,7 +142,12 @@ export async function writeAgentConfig( const deployedBlock = await mailbox.deployedBlock(); return deployedBlock.toNumber(); } catch (err) { - console.error('Failed to get deployed block for', chain, err); + console.error( + 'Failed to get deployed block, defaulting to 0. Chain:', + chain, + 'Error:', + err, + ); return 0; } }), diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index 31eb1dc38b..a2449ec434 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -115,7 +115,9 @@ export abstract class HyperlaneAppGovernor< if (calls.length > 0) { const confirmed = await summarizeCalls(submissionType, calls); if (confirmed) { - console.log(`Submitting calls on ${chain} via ${submissionType}`); + console.log( + `Submitting calls on ${chain} via ${SubmissionType[submissionType]}`, + ); await multiSend.sendTransactions( calls.map((call) => ({ to: call.to, @@ -125,7 +127,7 @@ export abstract class HyperlaneAppGovernor< ); } else { console.log( - `Skipping submission of calls on ${chain} via ${submissionType}`, + `Skipping submission of calls on ${chain} via ${SubmissionType[submissionType]}`, ); } } diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index d384e073d3..c6f8d8568e 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -57,6 +57,7 @@ export const defaultMultisigConfigs: ChainMap = { validators: [ '0xf20c0b09f597597c8d2430d3d72dfddaf09177d1', '0x4f977a59fdc2d9e39f6d780a84d5b4add1495a36', // mitosis + '0xae53467a5c2a9d9420c188d10fef5e1d9b9a5b80', // superform ], }, @@ -231,6 +232,14 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + redstone: { + threshold: 2, + validators: [ + '0x1400b9737007f7978d8b4bbafb4a69c83f0641a7', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + ], + }, + scroll: { threshold: 2, validators: [ @@ -281,4 +290,12 @@ export const defaultMultisigConfigs: ChainMap = { '0x1f87c368f8e05a85ef9126d984a980a20930cb9c', ], }, + + zetachain: { + threshold: 2, + validators: [ + '0xa3bca0b80317dbf9c7dce16a16ac89f4ff2b23ef', + '0xcf0211fafbb91fd9d06d7e306b30032dc3a1934f', // merkly + ], + }, }; diff --git a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts index dc36a05b9b..9edcfe5f8b 100644 --- a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts +++ b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts @@ -32,12 +32,14 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< multiProvider: MultiProvider, readonly ismFactory: HyperlaneIsmFactory, contractVerifier?: ContractVerifier, + concurrentDeploy: boolean = false, ) { super(multiProvider, coreFactories, { logger: rootLogger.child({ module: 'CoreDeployer' }), chainTimeoutMs: 1000 * 60 * 10, // 10 minutes ismFactory, contractVerifier, + concurrentDeploy, }); this.hookDeployer = new HyperlaneHookDeployer( multiProvider, @@ -133,7 +135,9 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< !e.message.includes('Reverted 0x08c379a') && // Handle situation where the gas estimation fails on the call function, // then the real error reason is not available in `e.message`, but rather in `e.error.reason` - !e.error?.reason?.includes('already initialized') + !e.error?.reason?.includes('already initialized') && + // Some providers, like on Viction, return a generic error message for all revert reasons + !e.message.includes('always failing transaction') ) { throw e; } diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index 46f4db67ea..0598d48072 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -60,6 +60,7 @@ export interface DeployerOptions { ismFactory?: HyperlaneIsmFactory; icaApp?: InterchainAccount; contractVerifier?: ContractVerifier; + concurrentDeploy?: boolean; } export abstract class HyperlaneDeployer< @@ -82,7 +83,7 @@ export abstract class HyperlaneDeployer< protected readonly icaAddresses = {}, ) { this.logger = options?.logger ?? rootLogger.child({ module: 'deployer' }); - this.chainTimeoutMs = options?.chainTimeoutMs ?? 5 * 60 * 1000; // 5 minute timeout per chain + this.chainTimeoutMs = options?.chainTimeoutMs ?? 15 * 60 * 1000; // 15 minute timeout per chain this.options.ismFactory?.setDeployer(this); if (Object.keys(icaAddresses).length > 0) { this.options.icaApp = InterchainAccount.fromAddressesMap( @@ -125,6 +126,8 @@ export abstract class HyperlaneDeployer< ).intersection; this.logger.debug(`Start deploy to ${targetChains}`); + + const deployPromises = []; for (const chain of targetChains) { const signerUrl = await this.multiProvider.tryGetExplorerAddressUrl( chain, @@ -135,11 +138,31 @@ export abstract class HyperlaneDeployer< this.startingBlockNumbers[chain] = await this.multiProvider .getProvider(chain) .getBlockNumber(); - await runWithTimeout(this.chainTimeoutMs, async () => { + + const deployPromise = runWithTimeout(this.chainTimeoutMs, async () => { const contracts = await this.deployContracts(chain, configMap[chain]); this.addDeployedContracts(chain, contracts); + this.logger.info({ chain }, 'Successfully deployed contracts'); }); + if (this.options.concurrentDeploy) { + deployPromises.push(deployPromise); + } else { + await deployPromise; + } } + + // Await all deploy promises. If concurrent deploy is not enabled, this will be a no-op. + const deployResults = await Promise.allSettled(deployPromises); + for (const [i, result] of deployResults.entries()) { + if (result.status === 'rejected') { + this.logger.error( + { chain: targetChains[i], error: result.reason }, + 'Deployment failed', + ); + throw result.reason; + } + } + return this.deployedContracts; } @@ -715,7 +738,7 @@ export abstract class HyperlaneDeployer< ); if (!eqAddress(current, owner)) { this.logger.debug( - { contractName }, + { contractName, current, desiredOwner: owner }, 'Current owner and config owner do not match', ); const receipt = await this.runIfOwner(chain, ownable, () => { diff --git a/typescript/sdk/src/deploy/verify/ContractVerifier.ts b/typescript/sdk/src/deploy/verify/ContractVerifier.ts index f6baa1c169..e43646396e 100644 --- a/typescript/sdk/src/deploy/verify/ContractVerifier.ts +++ b/typescript/sdk/src/deploy/verify/ContractVerifier.ts @@ -137,7 +137,7 @@ export class ContractVerifier { break; default: errorMessage = `Verification failed. ${ - result.result ?? response.statusText + JSON.stringify(result.result) ?? response.statusText }`; break; } @@ -155,7 +155,7 @@ export class ContractVerifier { if (result.result === ExplorerApiErrors.UNABLE_TO_VERIFY) { const errorMessage = `Verification failed. ${ - result.result ?? response.statusText + JSON.stringify(result.result) ?? response.statusText }`; verificationLogger.debug(errorMessage); throw new Error(`[${chain}] ${errorMessage}`); diff --git a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts index 3e521b094a..a426e079bb 100644 --- a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts +++ b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts @@ -313,12 +313,20 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< } const overrides = this.multiProvider.getTransactionOverrides(chain); - await this.runIfOwner(chain, routingHook, async () => - this.multiProvider.handleTx( + await this.runIfOwner(chain, routingHook, async () => { + this.logger.debug( + { + chain, + routingHookAddress: routingHook.address, + routingConfigs, + }, + 'Setting routing hooks', + ); + return this.multiProvider.handleTx( chain, routingHook.setHooks(routingConfigs, overrides), - ), - ); + ); + }); await this.transferOwnershipOfContracts(chain, config, { [config.type]: routingHook, From 3f2216574c22fbddc5444092e49faaa9c4a84d98 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 23 May 2024 11:06:45 +0100 Subject: [PATCH 12/59] fix: fix CI tests to correctly use registry from `main` (#3840) ### Description As part of #3788, we moved to using the registry's `main` branch in CI. I accidentally didn't include this for other non-required tests that I didn't realize were failing, and then automerge merged that PR. This ensures the registry is usable by these other tests ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .github/workflows/test.yml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 62228a44b0..b6bb18d5ff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -326,7 +326,7 @@ jobs: cli-e2e: runs-on: larger-runner if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') - needs: [yarn-build] + needs: [yarn-build, checkout-registry] strategy: matrix: include: @@ -382,6 +382,17 @@ jobs: !./rust key: ${{ github.event.pull_request.head.sha || github.sha }} + # A workaround for relative paths not being supported by actions/cache. + # See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630. + - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV + + - name: registry-cache + uses: actions/cache@v3 + with: + path: | + ${{ env.REGISTRY_URI_ABSOLUTE }} + key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} + - name: cargo-cache uses: actions/cache@v3 with: @@ -394,7 +405,7 @@ jobs: env-test: runs-on: ubuntu-latest - needs: [yarn-build] + needs: [yarn-build, checkout-registry] strategy: fail-fast: false matrix: @@ -422,6 +433,17 @@ jobs: !./rust key: ${{ github.event.pull_request.head.sha || github.sha }} + # A workaround for relative paths not being supported by actions/cache. + # See https://github.com/actions/upload-artifact/issues/176#issuecomment-1367855630. + - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV + + - name: registry-cache + uses: actions/cache@v3 + with: + path: | + ${{ env.REGISTRY_URI_ABSOLUTE }} + key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} + - name: Fork test ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} deployment run: cd typescript/infra && ./fork.sh ${{ matrix.environment }} ${{ matrix.module }} ${{ matrix.chain }} From fdd14215401de8e9b39eb2879fdeee58667669ee Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Thu, 23 May 2024 12:29:25 +0100 Subject: [PATCH 13/59] fix: fetch the registry's `main` branch in the monorepo Docker image (#3841) ### Description Infra services require the presence of a registry. This is expected to be found at the env var REGISTRY_URI. This PR fetches the latest registry from `main` and uses that as the registry in the monorepo image for infra services / scripts to make use of. To prevent caching of this git clone, we use a build-time arg such that if the arg changes, any later layers in the image will be ran. See https://stackoverflow.com/a/73501716 ### Drive-by changes ### Related issues - Partially fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3827 - we'll need a deploy to fully close it out ### Backward compatibility ### Testing --- .github/workflows/monorepo-docker.yml | 5 +++++ Dockerfile | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/monorepo-docker.yml b/.github/workflows/monorepo-docker.yml index 72a0fc1f1c..b9d90db2b2 100644 --- a/.github/workflows/monorepo-docker.yml +++ b/.github/workflows/monorepo-docker.yml @@ -8,6 +8,8 @@ on: paths: # For now, because this image is only used to use `infra`, we just build for infra changes - 'typescript/infra/**' + - 'Dockerfile' + - '.dockerignore' concurrency: group: build-push-monorepo-${{ github.ref }} cancel-in-progress: true @@ -74,3 +76,6 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + # To always fetch the latest registry, we use the date as the cache key + build-args: | + REGISTRY_CACHE=${{ steps.taggen.outputs.TAG_DATE }} diff --git a/Dockerfile b/Dockerfile index 4a01616b8c..763e1186f9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,3 +27,10 @@ COPY typescript ./typescript COPY solidity ./solidity RUN yarn build + +ENV REGISTRY_URI="/hyperlane-registry" +# To allow us to avoid caching the registry clone, we use a build-time arg to force +# the below steps to be re-run if this arg is changed. +ARG REGISTRY_CACHE="default" + +RUN git clone https://github.com/hyperlane-xyz/hyperlane-registry.git "$REGISTRY_URI" From b22a0f453854938a59e44407fb47287434b25e3e Mon Sep 17 00:00:00 2001 From: Ali Alaoui Date: Thu, 23 May 2024 15:07:43 +0100 Subject: [PATCH 14/59] feat: CLI command to get validator address by S3 bucket or KMS key ID (#3795) ### Description Create the `hyperlane validator address` command, making it easy for users to get the address of their validator key. ### Related issues Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3341 ### Backward compatibility Yes ### Testing Manual testing under all conditions/edge cases --------- Co-authored-by: J M Rossy --- .changeset/neat-ducks-own.md | 5 + typescript/cli/cli.ts | 2 + typescript/cli/package.json | 3 + typescript/cli/src/commands/options.ts | 32 + typescript/cli/src/commands/validator.ts | 51 + typescript/cli/src/utils/env.ts | 3 + typescript/cli/src/validator/address.ts | 166 +++ yarn.lock | 1332 +++++++++++++++++++++- 8 files changed, 1592 insertions(+), 2 deletions(-) create mode 100644 .changeset/neat-ducks-own.md create mode 100644 typescript/cli/src/commands/validator.ts create mode 100644 typescript/cli/src/validator/address.ts diff --git a/.changeset/neat-ducks-own.md b/.changeset/neat-ducks-own.md new file mode 100644 index 0000000000..af030bd5e6 --- /dev/null +++ b/.changeset/neat-ducks-own.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +Add hyperlane validator address command to retrieve validator address from AWS diff --git a/typescript/cli/cli.ts b/typescript/cli/cli.ts index 0ced1ef0fd..39cc3669fe 100644 --- a/typescript/cli/cli.ts +++ b/typescript/cli/cli.ts @@ -20,6 +20,7 @@ import { } from './src/commands/options.js'; import { sendCommand } from './src/commands/send.js'; import { statusCommand } from './src/commands/status.js'; +import { validatorCommand } from './src/commands/validator.js'; import { contextMiddleware } from './src/context/context.js'; import { configureLogger, errorRed } from './src/logger.js'; import { checkVersion } from './src/utils/version-check.js'; @@ -55,6 +56,7 @@ try { .command(ismCommand) .command(sendCommand) .command(statusCommand) + .command(validatorCommand) .version(VERSION) .demandCommand() .strict() diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 4d5ddf2dbf..47d0a1b11b 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -3,10 +3,13 @@ "version": "3.12.2", "description": "A command-line utility for common Hyperlane operations", "dependencies": { + "@aws-sdk/client-kms": "^3.577.0", + "@aws-sdk/client-s3": "^3.577.0", "@hyperlane-xyz/registry": "^1.0.7", "@hyperlane-xyz/sdk": "3.12.2", "@hyperlane-xyz/utils": "3.12.2", "@inquirer/prompts": "^3.0.0", + "asn1.js": "^5.4.1", "bignumber.js": "^9.1.1", "chalk": "^5.3.0", "ethers": "^5.7.2", diff --git a/typescript/cli/src/commands/options.ts b/typescript/cli/src/commands/options.ts index aa2b7e0366..3774b43cb4 100644 --- a/typescript/cli/src/commands/options.ts +++ b/typescript/cli/src/commands/options.ts @@ -146,3 +146,35 @@ export const addressCommandOption = ( description, demandOption, }); + +/* Validator options */ +export const awsAccessKeyCommandOption: Options = { + type: 'string', + description: 'AWS access key of IAM user associated with validator', + default: ENV.AWS_ACCESS_KEY_ID, + defaultDescription: 'process.env.AWS_ACCESS_KEY_ID', +}; + +export const awsSecretKeyCommandOption: Options = { + type: 'string', + description: 'AWS secret access key of IAM user associated with validator', + default: ENV.AWS_SECRET_ACCESS_KEY, + defaultDescription: 'process.env.AWS_SECRET_ACCESS_KEY', +}; + +export const awsRegionCommandOption: Options = { + type: 'string', + describe: 'AWS region associated with validator', + default: ENV.AWS_REGION, + defaultDescription: 'process.env.AWS_REGION', +}; + +export const awsBucketCommandOption: Options = { + type: 'string', + describe: 'AWS S3 bucket containing validator signatures and announcement', +}; + +export const awsKeyIdCommandOption: Options = { + type: 'string', + describe: 'Key ID from AWS KMS', +}; diff --git a/typescript/cli/src/commands/validator.ts b/typescript/cli/src/commands/validator.ts new file mode 100644 index 0000000000..973c0cd25e --- /dev/null +++ b/typescript/cli/src/commands/validator.ts @@ -0,0 +1,51 @@ +import { CommandModule } from 'yargs'; + +import { CommandModuleWithContext } from '../context/types.js'; +import { log } from '../logger.js'; +import { getValidatorAddress } from '../validator/address.js'; + +import { + awsAccessKeyCommandOption, + awsBucketCommandOption, + awsKeyIdCommandOption, + awsRegionCommandOption, + awsSecretKeyCommandOption, +} from './options.js'; + +// Parent command to help configure and set up Hyperlane validators +export const validatorCommand: CommandModule = { + command: 'validator', + describe: 'Configure and manage Hyperlane validators', + builder: (yargs) => yargs.command(addressCommand).demandCommand(), + handler: () => log('Command required'), +}; + +// If AWS access key needed for future validator commands, move to context +const addressCommand: CommandModuleWithContext<{ + accessKey: string; + secretKey: string; + region: string; + bucket: string; + keyId: string; +}> = { + command: 'address', + describe: 'Get the validator address from S3 bucket or KMS key ID', + builder: { + 'access-key': awsAccessKeyCommandOption, + 'secret-key': awsSecretKeyCommandOption, + region: awsRegionCommandOption, + bucket: awsBucketCommandOption, + 'key-id': awsKeyIdCommandOption, + }, + handler: async ({ context, accessKey, secretKey, region, bucket, keyId }) => { + await getValidatorAddress({ + context, + accessKey, + secretKey, + region, + bucket, + keyId, + }); + process.exit(0); + }, +}; diff --git a/typescript/cli/src/utils/env.ts b/typescript/cli/src/utils/env.ts index 9a3e74ccdc..51ab1ce352 100644 --- a/typescript/cli/src/utils/env.ts +++ b/typescript/cli/src/utils/env.ts @@ -4,6 +4,9 @@ const envScheme = z.object({ HYP_KEY: z.string().optional(), ANVIL_IP_ADDR: z.string().optional(), ANVIL_PORT: z.number().optional(), + AWS_ACCESS_KEY_ID: z.string().optional(), + AWS_SECRET_ACCESS_KEY: z.string().optional(), + AWS_REGION: z.string().optional(), }); const parsedEnv = envScheme.safeParse(process.env); diff --git a/typescript/cli/src/validator/address.ts b/typescript/cli/src/validator/address.ts new file mode 100644 index 0000000000..d816fcb1f5 --- /dev/null +++ b/typescript/cli/src/validator/address.ts @@ -0,0 +1,166 @@ +import { GetPublicKeyCommand, KMSClient } from '@aws-sdk/client-kms'; +import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'; +import { input } from '@inquirer/prompts'; +// @ts-ignore +import asn1 from 'asn1.js'; +import { ethers } from 'ethers'; + +import { assert } from '@hyperlane-xyz/utils'; + +import { CommandContext } from '../context/types.js'; +import { log, logBlue } from '../logger.js'; + +export async function getValidatorAddress({ + context, + accessKey, + secretKey, + region, + bucket, + keyId, +}: { + context: CommandContext; + accessKey?: string; + secretKey?: string; + region?: string; + bucket?: string; + keyId?: string; +}) { + if (!bucket && !keyId) { + throw new Error('Must provide either an S3 bucket or a KMS Key ID.'); + } + + // Query user for AWS parameters if not passed in or stored as .env variables + accessKey ||= await getAccessKeyId(context.skipConfirmation); + secretKey ||= await getSecretAccessKey(context.skipConfirmation); + region ||= await getRegion(context.skipConfirmation); + + assert(accessKey, 'No access key ID set.'); + assert(secretKey, 'No secret access key set.'); + assert(region, 'No AWS region set.'); + + let validatorAddress; + if (bucket) { + validatorAddress = await getAddressFromBucket( + bucket, + accessKey, + secretKey, + region, + ); + } else { + validatorAddress = await getAddressFromKey( + keyId!, + accessKey, + secretKey, + region, + ); + } + + logBlue('Validator address is: '); + log(validatorAddress); +} + +/** + * Displays validator key address from + * validator announcement S3 bucket. + */ +async function getAddressFromBucket( + bucket: string, + accessKeyId: string, + secretAccessKey: string, + region: string, +) { + const s3Client = new S3Client({ + region: region, + credentials: { + accessKeyId, + secretAccessKey, + }, + }); + + const { Body } = await s3Client.send( + new GetObjectCommand({ + Bucket: bucket, + Key: 'announcement.json', + }), + ); + + if (Body) { + const announcement = JSON.parse(await Body?.transformToString()); + return announcement['value']['validator']; + } else { + throw new Error('Announcement file announcement.json not found in bucket'); + } +} + +/** + * Logs validator key address using AWS KMS key ID. + * Taken from github.com/tkporter/get-aws-kms-address/ + */ +async function getAddressFromKey( + keyId: string, + accessKeyId: string, + secretAccessKey: string, + region: string, +) { + const client = new KMSClient({ + region: region, + credentials: { + accessKeyId, + secretAccessKey, + }, + }); + + const publicKeyResponse = await client.send( + new GetPublicKeyCommand({ KeyId: keyId }), + ); + + return getEthereumAddress(Buffer.from(publicKeyResponse.PublicKey!)); +} + +const EcdsaPubKey = asn1.define('EcdsaPubKey', function (this: any) { + this.seq().obj( + this.key('algo').seq().obj(this.key('a').objid(), this.key('b').objid()), + this.key('pubKey').bitstr(), + ); +}); + +function getEthereumAddress(publicKey: Buffer): string { + // The public key is ASN1 encoded in a format according to + // https://tools.ietf.org/html/rfc5480#section-2 + const res = EcdsaPubKey.decode(publicKey, 'der'); + let pubKeyBuffer: Buffer = res.pubKey.data; + + // The public key starts with a 0x04 prefix that needs to be removed + // more info: https://www.oreilly.com/library/view/mastering-ethereum/9781491971932/ch04.html + pubKeyBuffer = pubKeyBuffer.slice(1, pubKeyBuffer.length); + + const address = ethers.utils.keccak256(pubKeyBuffer); // keccak256 hash of publicKey + return `0x${address.slice(-40)}`; // take last 20 bytes as ethereum address +} + +async function getAccessKeyId(skipConfirmation: boolean) { + if (skipConfirmation) throw new Error('No AWS access key ID set.'); + else + return await input({ + message: + 'Please enter AWS access key ID or use the AWS_ACCESS_KEY_ID environment variable.', + }); +} + +async function getSecretAccessKey(skipConfirmation: boolean) { + if (skipConfirmation) throw new Error('No AWS secret access key set.'); + else + return await input({ + message: + 'Please enter AWS secret access key or use the AWS_SECRET_ACCESS_KEY environment variable.', + }); +} + +async function getRegion(skipConfirmation: boolean) { + if (skipConfirmation) throw new Error('No AWS region set.'); + else + return await input({ + message: + 'Please enter AWS region or use the AWS_REGION environment variable.', + }); +} diff --git a/yarn.lock b/yarn.lock index 2adc97823e..009df5a75b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -52,6 +52,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32@npm:3.0.0" + dependencies: + "@aws-crypto/util": "npm:^3.0.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^1.11.1" + checksum: 672d593fd98a88709a1b488db92aabf584b6dad3e8099e04b6d2870e34a2ee668cbbe0e5406e60c0d776b9c34a91cfc427999230ad959518fed56a3db037704c + languageName: node + linkType: hard + "@aws-crypto/crc32c@npm:2.0.0": version: 2.0.0 resolution: "@aws-crypto/crc32c@npm:2.0.0" @@ -63,6 +74,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/crc32c@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/crc32c@npm:3.0.0" + dependencies: + "@aws-crypto/util": "npm:^3.0.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^1.11.1" + checksum: 3e604ad7a8d3fb10e5fe11597d593d0ae8e1d6dc06a06b8d882d5732a6e181f6a77fd4f92fb3ae9002a2007121d49e40bc6b78d83af62d36deb1b457b7f1d977 + languageName: node + linkType: hard + "@aws-crypto/ie11-detection@npm:^2.0.0": version: 2.0.0 resolution: "@aws-crypto/ie11-detection@npm:2.0.0" @@ -72,6 +94,15 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/ie11-detection@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/ie11-detection@npm:3.0.0" + dependencies: + tslib: "npm:^1.11.1" + checksum: f5aee4a11a113ab9640474e75d398c99538aa30775f484cd519f0de0096ae0d4a6b68d2f0c685f24bd6f2425067c565bc20592c36c0dc1f4d28c1b4751a40734 + languageName: node + linkType: hard + "@aws-crypto/sha1-browser@npm:2.0.0": version: 2.0.0 resolution: "@aws-crypto/sha1-browser@npm:2.0.0" @@ -86,6 +117,21 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/sha1-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha1-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": "npm:^3.0.0" + "@aws-crypto/supports-web-crypto": "npm:^3.0.0" + "@aws-crypto/util": "npm:^3.0.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@aws-sdk/util-utf8-browser": "npm:^3.0.0" + tslib: "npm:^1.11.1" + checksum: 8c30fa1e427bf2c295077b007835b0dd9af6beb6250e0aa775cecd42a1f517ef211751e7e12c2423f39d9b1c6748b99eb7b73207eb69165abc696cc470d8659e + languageName: node + linkType: hard + "@aws-crypto/sha256-browser@npm:2.0.0": version: 2.0.0 resolution: "@aws-crypto/sha256-browser@npm:2.0.0" @@ -102,6 +148,22 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/sha256-browser@npm:3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-browser@npm:3.0.0" + dependencies: + "@aws-crypto/ie11-detection": "npm:^3.0.0" + "@aws-crypto/sha256-js": "npm:^3.0.0" + "@aws-crypto/supports-web-crypto": "npm:^3.0.0" + "@aws-crypto/util": "npm:^3.0.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@aws-sdk/util-utf8-browser": "npm:^3.0.0" + tslib: "npm:^1.11.1" + checksum: 4e075906c48a46bbb8babb60db3e6b280db405a88c68b77c1496c26218292d5ea509beae3ccc19366ca6bc944c6d37fe347d0917909900dbac86f054a19c71c7 + languageName: node + linkType: hard + "@aws-crypto/sha256-js@npm:1.2.2": version: 1.2.2 resolution: "@aws-crypto/sha256-js@npm:1.2.2" @@ -124,6 +186,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/sha256-js@npm:3.0.0, @aws-crypto/sha256-js@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/sha256-js@npm:3.0.0" + dependencies: + "@aws-crypto/util": "npm:^3.0.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^1.11.1" + checksum: f9fc2d51631950434d0f91f51c2ce17845d4e8e75971806e21604987e3186ee1e54de8a89e5349585b91cb36e56d5f058d6a45004e1bfbce1351dbb40f479152 + languageName: node + linkType: hard + "@aws-crypto/sha256-js@npm:^2.0.0": version: 2.0.1 resolution: "@aws-crypto/sha256-js@npm:2.0.1" @@ -144,6 +217,15 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/supports-web-crypto@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/supports-web-crypto@npm:3.0.0" + dependencies: + tslib: "npm:^1.11.1" + checksum: 8a48788d2866e391354f256aa79b577b2ba1474b50184cbe690467de7e64a79928afece95007ab69a1556f99da97ea129487db091d94489847e14decdc7c9a6f + languageName: node + linkType: hard + "@aws-crypto/util@npm:^1.2.2": version: 1.2.2 resolution: "@aws-crypto/util@npm:1.2.2" @@ -166,6 +248,17 @@ __metadata: languageName: node linkType: hard +"@aws-crypto/util@npm:^3.0.0": + version: 3.0.0 + resolution: "@aws-crypto/util@npm:3.0.0" + dependencies: + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-utf8-browser": "npm:^3.0.0" + tslib: "npm:^1.11.1" + checksum: 92c835b83d7a888b37b2f2a37c82e58bb8fabb617e371173c488d2a71b916c69ee566f0ea0b3f7f4e16296226c49793f95b3d59fc07a7ca00af91f8f9f29e6c4 + languageName: node + linkType: hard + "@aws-sdk/abort-controller@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/abort-controller@npm:3.127.0" @@ -343,6 +436,121 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-kms@npm:^3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/client-kms@npm:3.577.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:3.0.0" + "@aws-crypto/sha256-js": "npm:3.0.0" + "@aws-sdk/client-sso-oidc": "npm:3.577.0" + "@aws-sdk/client-sts": "npm:3.577.0" + "@aws-sdk/core": "npm:3.576.0" + "@aws-sdk/credential-provider-node": "npm:3.577.0" + "@aws-sdk/middleware-host-header": "npm:3.577.0" + "@aws-sdk/middleware-logger": "npm:3.577.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.577.0" + "@aws-sdk/middleware-user-agent": "npm:3.577.0" + "@aws-sdk/region-config-resolver": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-endpoints": "npm:3.577.0" + "@aws-sdk/util-user-agent-browser": "npm:3.577.0" + "@aws-sdk/util-user-agent-node": "npm:3.577.0" + "@smithy/config-resolver": "npm:^3.0.0" + "@smithy/core": "npm:^2.0.0" + "@smithy/fetch-http-handler": "npm:^3.0.0" + "@smithy/hash-node": "npm:^3.0.0" + "@smithy/invalid-dependency": "npm:^3.0.0" + "@smithy/middleware-content-length": "npm:^3.0.0" + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-retry": "npm:^3.0.0" + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/middleware-stack": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.0" + "@smithy/util-defaults-mode-node": "npm:^3.0.0" + "@smithy/util-endpoints": "npm:^2.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + "@smithy/util-retry": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 2d4e4bb8c05e711c588cca4c7fa2737ffcb0f778c013cb97093b9b8d71b7e0e6a67d3354f50a0d230e2c0d54abcf7c19288c4750c71b734bd0b01f24d816ebcf + languageName: node + linkType: hard + +"@aws-sdk/client-s3@npm:^3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/client-s3@npm:3.577.0" + dependencies: + "@aws-crypto/sha1-browser": "npm:3.0.0" + "@aws-crypto/sha256-browser": "npm:3.0.0" + "@aws-crypto/sha256-js": "npm:3.0.0" + "@aws-sdk/client-sso-oidc": "npm:3.577.0" + "@aws-sdk/client-sts": "npm:3.577.0" + "@aws-sdk/core": "npm:3.576.0" + "@aws-sdk/credential-provider-node": "npm:3.577.0" + "@aws-sdk/middleware-bucket-endpoint": "npm:3.577.0" + "@aws-sdk/middleware-expect-continue": "npm:3.577.0" + "@aws-sdk/middleware-flexible-checksums": "npm:3.577.0" + "@aws-sdk/middleware-host-header": "npm:3.577.0" + "@aws-sdk/middleware-location-constraint": "npm:3.577.0" + "@aws-sdk/middleware-logger": "npm:3.577.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.577.0" + "@aws-sdk/middleware-sdk-s3": "npm:3.577.0" + "@aws-sdk/middleware-signing": "npm:3.577.0" + "@aws-sdk/middleware-ssec": "npm:3.577.0" + "@aws-sdk/middleware-user-agent": "npm:3.577.0" + "@aws-sdk/region-config-resolver": "npm:3.577.0" + "@aws-sdk/signature-v4-multi-region": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-endpoints": "npm:3.577.0" + "@aws-sdk/util-user-agent-browser": "npm:3.577.0" + "@aws-sdk/util-user-agent-node": "npm:3.577.0" + "@aws-sdk/xml-builder": "npm:3.575.0" + "@smithy/config-resolver": "npm:^3.0.0" + "@smithy/core": "npm:^2.0.0" + "@smithy/eventstream-serde-browser": "npm:^3.0.0" + "@smithy/eventstream-serde-config-resolver": "npm:^3.0.0" + "@smithy/eventstream-serde-node": "npm:^3.0.0" + "@smithy/fetch-http-handler": "npm:^3.0.0" + "@smithy/hash-blob-browser": "npm:^3.0.0" + "@smithy/hash-node": "npm:^3.0.0" + "@smithy/hash-stream-node": "npm:^3.0.0" + "@smithy/invalid-dependency": "npm:^3.0.0" + "@smithy/md5-js": "npm:^3.0.0" + "@smithy/middleware-content-length": "npm:^3.0.0" + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-retry": "npm:^3.0.0" + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/middleware-stack": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.0" + "@smithy/util-defaults-mode-node": "npm:^3.0.0" + "@smithy/util-endpoints": "npm:^2.0.0" + "@smithy/util-retry": "npm:^3.0.0" + "@smithy/util-stream": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + "@smithy/util-waiter": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 0b52b3cec27544b8c528ad63d9c80803ba7bae4b0336028faee1e30d8e45ce9f0832b381cc2cf8f122a017c20a2ca8c8b20365b42ec6c6b27b9b58df36d96279 + languageName: node + linkType: hard + "@aws-sdk/client-s3@npm:^3.74.0": version: 3.107.0 resolution: "@aws-sdk/client-s3@npm:3.107.0" @@ -405,6 +613,54 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-sso-oidc@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.577.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:3.0.0" + "@aws-crypto/sha256-js": "npm:3.0.0" + "@aws-sdk/client-sts": "npm:3.577.0" + "@aws-sdk/core": "npm:3.576.0" + "@aws-sdk/credential-provider-node": "npm:3.577.0" + "@aws-sdk/middleware-host-header": "npm:3.577.0" + "@aws-sdk/middleware-logger": "npm:3.577.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.577.0" + "@aws-sdk/middleware-user-agent": "npm:3.577.0" + "@aws-sdk/region-config-resolver": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-endpoints": "npm:3.577.0" + "@aws-sdk/util-user-agent-browser": "npm:3.577.0" + "@aws-sdk/util-user-agent-node": "npm:3.577.0" + "@smithy/config-resolver": "npm:^3.0.0" + "@smithy/core": "npm:^2.0.0" + "@smithy/fetch-http-handler": "npm:^3.0.0" + "@smithy/hash-node": "npm:^3.0.0" + "@smithy/invalid-dependency": "npm:^3.0.0" + "@smithy/middleware-content-length": "npm:^3.0.0" + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-retry": "npm:^3.0.0" + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/middleware-stack": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.0" + "@smithy/util-defaults-mode-node": "npm:^3.0.0" + "@smithy/util-endpoints": "npm:^2.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + "@smithy/util-retry": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 498ae3ed9b7b5ff5c2d5312f24e5f58eb7e64f41184958ba7fda907c9277807790f4299f0622374c4372c23bf7967b2fb9d8a73495e7503cf4ee2fc9dc711e43 + languageName: node + linkType: hard + "@aws-sdk/client-sso@npm:3.105.0": version: 3.105.0 resolution: "@aws-sdk/client-sso@npm:3.105.0" @@ -521,6 +777,52 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-sso@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/client-sso@npm:3.577.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:3.0.0" + "@aws-crypto/sha256-js": "npm:3.0.0" + "@aws-sdk/core": "npm:3.576.0" + "@aws-sdk/middleware-host-header": "npm:3.577.0" + "@aws-sdk/middleware-logger": "npm:3.577.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.577.0" + "@aws-sdk/middleware-user-agent": "npm:3.577.0" + "@aws-sdk/region-config-resolver": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-endpoints": "npm:3.577.0" + "@aws-sdk/util-user-agent-browser": "npm:3.577.0" + "@aws-sdk/util-user-agent-node": "npm:3.577.0" + "@smithy/config-resolver": "npm:^3.0.0" + "@smithy/core": "npm:^2.0.0" + "@smithy/fetch-http-handler": "npm:^3.0.0" + "@smithy/hash-node": "npm:^3.0.0" + "@smithy/invalid-dependency": "npm:^3.0.0" + "@smithy/middleware-content-length": "npm:^3.0.0" + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-retry": "npm:^3.0.0" + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/middleware-stack": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.0" + "@smithy/util-defaults-mode-node": "npm:^3.0.0" + "@smithy/util-endpoints": "npm:^2.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + "@smithy/util-retry": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: c7a6786e469d278d86579f2c74e334e4aac038becc881a60fb30fd175495e46ee4f41ed0d011445fc312bebcd2754531482180aa7b505d0768cdb472b3e4f633 + languageName: node + linkType: hard + "@aws-sdk/client-sts@npm:3.105.0": version: 3.105.0 resolution: "@aws-sdk/client-sts@npm:3.105.0" @@ -652,6 +954,54 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/client-sts@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/client-sts@npm:3.577.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:3.0.0" + "@aws-crypto/sha256-js": "npm:3.0.0" + "@aws-sdk/client-sso-oidc": "npm:3.577.0" + "@aws-sdk/core": "npm:3.576.0" + "@aws-sdk/credential-provider-node": "npm:3.577.0" + "@aws-sdk/middleware-host-header": "npm:3.577.0" + "@aws-sdk/middleware-logger": "npm:3.577.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.577.0" + "@aws-sdk/middleware-user-agent": "npm:3.577.0" + "@aws-sdk/region-config-resolver": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-endpoints": "npm:3.577.0" + "@aws-sdk/util-user-agent-browser": "npm:3.577.0" + "@aws-sdk/util-user-agent-node": "npm:3.577.0" + "@smithy/config-resolver": "npm:^3.0.0" + "@smithy/core": "npm:^2.0.0" + "@smithy/fetch-http-handler": "npm:^3.0.0" + "@smithy/hash-node": "npm:^3.0.0" + "@smithy/invalid-dependency": "npm:^3.0.0" + "@smithy/middleware-content-length": "npm:^3.0.0" + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-retry": "npm:^3.0.0" + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/middleware-stack": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.0" + "@smithy/util-defaults-mode-node": "npm:^3.0.0" + "@smithy/util-endpoints": "npm:^2.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + "@smithy/util-retry": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 7eb6a1b463d4173295f22dbe496c046bc32e5f9fc5bafbe02975d0dd215c46050e40a3251fd92fff16024abba4c1ae5f1da3e82933b944d1d21eadb315037201 + languageName: node + linkType: hard + "@aws-sdk/config-resolver@npm:3.130.0": version: 3.130.0 resolution: "@aws-sdk/config-resolver@npm:3.130.0" @@ -690,6 +1040,21 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/core@npm:3.576.0": + version: 3.576.0 + resolution: "@aws-sdk/core@npm:3.576.0" + dependencies: + "@smithy/core": "npm:^2.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/signature-v4": "npm:^3.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + fast-xml-parser: "npm:4.2.5" + tslib: "npm:^2.6.2" + checksum: 9bf9d28b380a929c1f8be0c67f61529a5d3b21815b5a0c0e22608767ee8d420a1d81c461f17f60af98f09a1600a4fe105e411b0b56d132c69fb831f0f9f5fdfe + languageName: node + linkType: hard + "@aws-sdk/credential-provider-env@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/credential-provider-env@npm:3.127.0" @@ -712,6 +1077,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-env@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 1fb3db7f2490661488dd4c72e55af7680782335c69534527f0185b97fb554a15f88721c25484c772f2ba498f734e6629b27dfac7d375f4ea7794cc814bc88e8e + languageName: node + linkType: hard + "@aws-sdk/credential-provider-env@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/credential-provider-env@npm:3.78.0" @@ -723,6 +1100,23 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-http@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/fetch-http-handler": "npm:^3.0.0" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-stream": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 55299138b472e4cc63914442b89fa7db72bbe5f4c3d631e6a63024db95239a711e242b138352b11cfcb9ccb15ede0632940e811f23b9f2ac1b9f6bb5ebe847a5 + languageName: node + linkType: hard + "@aws-sdk/credential-provider-imds@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/credential-provider-imds@npm:3.127.0" @@ -811,6 +1205,26 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-ini@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.577.0" + dependencies: + "@aws-sdk/credential-provider-env": "npm:3.577.0" + "@aws-sdk/credential-provider-process": "npm:3.577.0" + "@aws-sdk/credential-provider-sso": "npm:3.577.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@smithy/credential-provider-imds": "npm:^3.0.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sts": ^3.577.0 + checksum: 505373cdd7577be8d5d31c11d0c35de5b9660dcfe550a92d421340c30d090d5f35575902e486146b49b2a5e8842c003e2a8aa1feb196a31074945e99e9fa9295 + languageName: node + linkType: hard + "@aws-sdk/credential-provider-node@npm:3.105.0": version: 3.105.0 resolution: "@aws-sdk/credential-provider-node@npm:3.105.0" @@ -866,6 +1280,26 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-node@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.577.0" + dependencies: + "@aws-sdk/credential-provider-env": "npm:3.577.0" + "@aws-sdk/credential-provider-http": "npm:3.577.0" + "@aws-sdk/credential-provider-ini": "npm:3.577.0" + "@aws-sdk/credential-provider-process": "npm:3.577.0" + "@aws-sdk/credential-provider-sso": "npm:3.577.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@smithy/credential-provider-imds": "npm:^3.0.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 61e7b82a98f97ab62a1c167727f015e3a12e51c351c2dc26a7e942a72a2ef5d2f36759aa8e3b3b2799f2de1bcbeaeccee4f8fe1f36626dfd3a0648147af35d78 + languageName: node + linkType: hard + "@aws-sdk/credential-provider-process@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/credential-provider-process@npm:3.127.0" @@ -891,6 +1325,19 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-process@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: deb5b00cd052440705ca86df618d77862dc024c50efcc74a80975523076e634ad9570cdd78ada35dad486f721e8aaed95f0a3e9bf99f249e9d722eb8f4b434f7 + languageName: node + linkType: hard + "@aws-sdk/credential-provider-process@npm:3.80.0": version: 3.80.0 resolution: "@aws-sdk/credential-provider-process@npm:3.80.0" @@ -943,6 +1390,21 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-sso@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.577.0" + dependencies: + "@aws-sdk/client-sso": "npm:3.577.0" + "@aws-sdk/token-providers": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 43ca5e4c1843301d77d6987b94484730fcf8bd0ce7913a49a0763138da9e911e5b259a195e3a2a674520c80aef9f1bfbb7a6b843bd61e6310a52828fe455b63f + languageName: node + linkType: hard + "@aws-sdk/credential-provider-web-identity@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/credential-provider-web-identity@npm:3.127.0" @@ -965,6 +1427,20 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/credential-provider-web-identity@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sts": ^3.577.0 + checksum: f7f297c2fdae913c6671b83ae9e89864c27941699b443993dc4c1eb20648adac335fa07ebf0aefffb9bec3e0cb78a5e6a93a778b65d0d192dad4d3a0942f38fe + languageName: node + linkType: hard + "@aws-sdk/credential-provider-web-identity@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/credential-provider-web-identity@npm:3.78.0" @@ -1187,6 +1663,21 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-bucket-endpoint@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-arn-parser": "npm:3.568.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-config-provider": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 87e3822883c2295a170a98db36e9fe275559a5dd1b5e2baf4c1b69bb6d3147a0c12e2e4991af4db68ee823d3b7dc44089448a5b846de0384e9a19424f78e4bd9 + languageName: node + linkType: hard + "@aws-sdk/middleware-bucket-endpoint@npm:3.80.0": version: 3.80.0 resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.80.0" @@ -1233,6 +1724,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-expect-continue@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 81f076341de3e5dd5c6dc1c23be223e42dbad5118ac423e95b3cd783ee39a9085c94fe710f17329520f52807373795636a43ac9ea9fb373455e5ce5df9a63291 + languageName: node + linkType: hard + "@aws-sdk/middleware-expect-continue@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-expect-continue@npm:3.78.0" @@ -1245,6 +1748,22 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-flexible-checksums@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.577.0" + dependencies: + "@aws-crypto/crc32": "npm:3.0.0" + "@aws-crypto/crc32c": "npm:3.0.0" + "@aws-sdk/types": "npm:3.577.0" + "@smithy/is-array-buffer": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: f219bcfda7bacd8fa5b0038f69a1ba4bac04460ca33105849b5f88dfe9df4d46464d66d44d43a7ed246be2eea5e8ecad62be78476bc6efdd9e5db22bc1547231 + languageName: node + linkType: hard + "@aws-sdk/middleware-flexible-checksums@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.78.0" @@ -1292,6 +1811,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-host-header@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 83e28305f05d88450f93ffc2ea49288b407413054cb29dd969313a4b0df4910090fd6495314820fa2e59f951e21399ed73e7425a66ef98d777c1702ba5c103f4 + languageName: node + linkType: hard + "@aws-sdk/middleware-host-header@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-host-header@npm:3.78.0" @@ -1303,6 +1834,17 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-location-constraint@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: deeecf19c78f8aefe17c205104482288f851baa4c3eaa9424f59d3d5b371b8fb6e764f21377721d50ad8e0121cb005a7dbace44d7bab22d1fe95a46a2d4340d3 + languageName: node + linkType: hard + "@aws-sdk/middleware-location-constraint@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-location-constraint@npm:3.78.0" @@ -1333,6 +1875,17 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-logger@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-logger@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: b1dd50f776aac1391fc6a114d06bea175cb7e6b7e2300125e624fccf32a28176910fc32746f70cef1ddfec8d9d4d66729dad0ecb4fd3b14d48e0a1dda7192754 + languageName: node + linkType: hard + "@aws-sdk/middleware-logger@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-logger@npm:3.78.0" @@ -1365,6 +1918,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-recursion-detection@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 375b9dd945c10f941e7f86697c38f1401cd225dd28ae264a21d0f2c88c5c89585af18b759687f139b4441c6402278c634a4f8baacb53070299cee18ecea47dba + languageName: node + linkType: hard + "@aws-sdk/middleware-retry@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/middleware-retry@npm:3.127.0" @@ -1418,6 +1983,23 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-sdk-s3@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-arn-parser": "npm:3.568.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/signature-v4": "npm:^3.0.0" + "@smithy/smithy-client": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-config-provider": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 60b654d8e6167f2b4081f2ccbe7c36d77eae56b4005438e9a41f33f17120f11a00ed647a2827b5188aff25053f30f55ae3d5db955ee5a066a5c54fc100105264 + languageName: node + linkType: hard + "@aws-sdk/middleware-sdk-sts@npm:3.130.0": version: 3.130.0 resolution: "@aws-sdk/middleware-sdk-sts@npm:3.130.0" @@ -1516,6 +2098,21 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-signing@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-signing@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/signature-v4": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 51c0e9b82d01ed0c8d2b5b9b5299eec15e6bc64b7b448be730ef861c3d9334ce49fccc889d913864f836da9db6f28f3538c8385493be3a432a5d8517b0ace551 + languageName: node + linkType: hard + "@aws-sdk/middleware-signing@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-signing@npm:3.78.0" @@ -1529,6 +2126,17 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-ssec@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 92f6a68e12b9ecdb644cf30a5bf076517496203223642cdcd0f12faa5882118100a1ae67de2b44ec26c7b393cdcd1d5c79af42ec6ac523f94c478cd7a546e335 + languageName: node + linkType: hard + "@aws-sdk/middleware-ssec@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-ssec@npm:3.78.0" @@ -1588,6 +2196,19 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/middleware-user-agent@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@aws-sdk/util-endpoints": "npm:3.577.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 1419935c015d0a23dceb597f9fff2ef04c6dbb9f211c3c954bed1bacd85d9a662f7f86e948ebb6d1099231084528fb753f70057a90daec8a3e4f2b0f32f31075 + languageName: node + linkType: hard + "@aws-sdk/middleware-user-agent@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/middleware-user-agent@npm:3.78.0" @@ -1797,6 +2418,20 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/region-config-resolver@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-config-provider": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: c17be35a8a9f9a92a77190e38db5f218d12ada04bff531d72c888ee81f87c8faaab87756f80b312fab9aec51ba7662b0222c4562b0950fe8a751e04365516606 + languageName: node + linkType: hard + "@aws-sdk/service-error-classification@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/service-error-classification@npm:3.127.0" @@ -1845,6 +2480,20 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/signature-v4-multi-region@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.577.0" + dependencies: + "@aws-sdk/middleware-sdk-s3": "npm:3.577.0" + "@aws-sdk/types": "npm:3.577.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/signature-v4": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 9f8784abdc1a95d2ce91accf0be038d4b08f959ea0ae1ed3ef5fb868fdd3d65b1aa1fca37a94ff2d1517c50a1213d64b1daedc0b891c1daf9bdef738ce68f994 + languageName: node + linkType: hard + "@aws-sdk/signature-v4-multi-region@npm:3.88.0": version: 3.88.0 resolution: "@aws-sdk/signature-v4-multi-region@npm:3.88.0" @@ -1937,6 +2586,21 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/token-providers@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/token-providers@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sso-oidc": ^3.577.0 + checksum: 94e0e6e1cea556bbba4abf574bdc24590ebcf86a75d675e9f54283c764bec9b0da3b1c9bd76c6eee2e3fd6eeefa59012f64832fc2d96761f064119320ab78529 + languageName: node + linkType: hard + "@aws-sdk/types@npm:3.127.0": version: 3.127.0 resolution: "@aws-sdk/types@npm:3.127.0" @@ -1951,6 +2615,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/types@npm:3.577.0, @aws-sdk/types@npm:^3.222.0": + version: 3.577.0 + resolution: "@aws-sdk/types@npm:3.577.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: a384ea32fcd0ae02a8058aa1f0d370de4c54f38a5e88bf649090e85bde09ef36efca49351f88669152626826441a7de8a3b1dfd0d6886553eeb2234d5205a196 + languageName: node + linkType: hard + "@aws-sdk/types@npm:3.78.0, @aws-sdk/types@npm:^3.1.0": version: 3.78.0 resolution: "@aws-sdk/types@npm:3.78.0" @@ -2000,6 +2674,15 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-arn-parser@npm:3.568.0": + version: 3.568.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.568.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: b1a7f93b4f47136ee8d71bcbbd2d5d19581007f0684aff252d3bee6b9ccc7c56e765255bb1bea847171b40cdbd2eca0fb102f24cba857d1c79c54747e8ee0855 + languageName: node + linkType: hard + "@aws-sdk/util-base64-browser@npm:3.109.0": version: 3.109.0 resolution: "@aws-sdk/util-base64-browser@npm:3.109.0" @@ -2218,6 +2901,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-endpoints@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/util-endpoints@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-endpoints": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: e5aac9f02b4ec24c6392b4e9fa8646771d39b8c4feedc7ac129cf5467585391bb17f11c27b04105113304d3fc97c3424ad0ff29dc4253de0b3ef459749dc87d6 + languageName: node + linkType: hard + "@aws-sdk/util-hex-encoding@npm:3.109.0": version: 3.109.0 resolution: "@aws-sdk/util-hex-encoding@npm:3.109.0" @@ -2332,6 +3027,18 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-user-agent-browser@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/types": "npm:^3.0.0" + bowser: "npm:^2.11.0" + tslib: "npm:^2.6.2" + checksum: 11a0f949a0f3ff09e557d4d1c0ccc384db1753d5029d0aaa325f12482294199225e6027c9e62aab7273a84429630ad3c94d69fd7c87678ac22da896e75f4e992 + languageName: node + linkType: hard + "@aws-sdk/util-user-agent-browser@npm:3.78.0": version: 3.78.0 resolution: "@aws-sdk/util-user-agent-browser@npm:3.78.0" @@ -2370,6 +3077,23 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/util-user-agent-node@npm:3.577.0": + version: 3.577.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.577.0" + dependencies: + "@aws-sdk/types": "npm:3.577.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 494bae9a69d903fae1725ab833e39af207da50642f86ec3ae554694a548f66b0c8ca24fcc1967b8d197b71bf87f5f82b95b4782ca1d331fdefdeb3207b40a9b1 + languageName: node + linkType: hard + "@aws-sdk/util-user-agent-node@npm:3.80.0": version: 3.80.0 resolution: "@aws-sdk/util-user-agent-node@npm:3.80.0" @@ -2458,6 +3182,16 @@ __metadata: languageName: node linkType: hard +"@aws-sdk/xml-builder@npm:3.575.0": + version: 3.575.0 + resolution: "@aws-sdk/xml-builder@npm:3.575.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: b1ca3cca6d49a10453d9b82242a4fbf2e5867d22c6238ea44f4210bdb64a2a48996b36d3662718995a76308bf4af7a834df7ba8498fba4d5aaabd2f7362f36f1 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0": version: 7.16.7 resolution: "@babel/code-frame@npm:7.16.7" @@ -4954,6 +5688,8 @@ __metadata: version: 0.0.0-use.local resolution: "@hyperlane-xyz/cli@workspace:typescript/cli" dependencies: + "@aws-sdk/client-kms": "npm:^3.577.0" + "@aws-sdk/client-s3": "npm:^3.577.0" "@hyperlane-xyz/registry": "npm:^1.0.7" "@hyperlane-xyz/sdk": "npm:3.12.2" "@hyperlane-xyz/utils": "npm:3.12.2" @@ -4963,6 +5699,7 @@ __metadata: "@types/yargs": "npm:^17.0.24" "@typescript-eslint/eslint-plugin": "npm:^7.4.0" "@typescript-eslint/parser": "npm:^7.4.0" + asn1.js: "npm:^5.4.1" bignumber.js: "npm:^9.1.1" chai: "npm:^4.3.6" chalk: "npm:^5.3.0" @@ -7578,6 +8315,570 @@ __metadata: languageName: node linkType: hard +"@smithy/abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/abort-controller@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 08bf21e79226c60f3654767683a767b34dd7b30d7fd73dfecd4cb13d172a7225f83f45553fd4af2692c95d38bdf2adbe5b132fac0affb4ecece6a41f066d49ba + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader-native@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/chunked-blob-reader-native@npm:3.0.0" + dependencies: + "@smithy/util-base64": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 424aa83f4fc081625a03ec6c64e74ae38c740c0b202d0b998f2bf341b935613491b39c7bf701790a0625219424340d5cfb042b701bfdff4c1cbedc57ee3f2500 + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/chunked-blob-reader@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 1c7955ae693aa098dd0839d7e8f9e742ab963de4ededa92f201f1982552c35ba625c1b90cf761de81deddd5002ed10f081ad46f6e0a5150066cee8b00f3f6058 + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/config-resolver@npm:3.0.0" + dependencies: + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-config-provider": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 78d6a13cb7d8c64ffe4aff675f5fb7114355b406be307576b7f77f880769417ba8e02830dcbb0991dd933c00e9e1e6248706a60e97c98bcf302577bd79ec52e0 + languageName: node + linkType: hard + +"@smithy/core@npm:^2.0.0": + version: 2.0.1 + resolution: "@smithy/core@npm:2.0.1" + dependencies: + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-retry": "npm:^3.0.1" + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.0.1" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 3c951d33e0b7c402d7e6b1f6f16bf0a29625b0edba6c68dc7f991e4e90a9c80bb9951e9fe1e1865939d37fedfd7bb0674352a8f973493ab4aa0d313c1c1427ac + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/credential-provider-imds@npm:3.0.0" + dependencies: + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 793e826a6d2ea1d407a4a0329662b41bc30d2e052520af27845bbd4345f454e1974e389fce622c26b06501c7d5a3c4b3844ec99baedb27e8f89d947d2c28fee6 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/eventstream-codec@npm:3.0.0" + dependencies: + "@aws-crypto/crc32": "npm:3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-hex-encoding": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 66ec273253d59c78ff7967d1fcd56c2479dc5807af360773dcad3d669b75a75682f3cd3c6647bda0ecc3c42cce6ea7d6261fd109e9531b209a51b9b6e93d7c2c + languageName: node + linkType: hard + +"@smithy/eventstream-serde-browser@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/eventstream-serde-browser@npm:3.0.0" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: a04c6a5207e670ce0c5508d92a70f5372a5fca076e3a7d76e5383753622525042225fdfe8c3cc0b3ee723ce0e7b568b7d2603ed83337538d471a8817c8d4306d + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/eventstream-serde-config-resolver@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: c07f698072bc1ddec3fc54cecbe03fece2b86445e9c18183f2621c528258d120a63d3b02b7d6d92fe3e0a73d29275ce18d85a253ebbdb06973666f885586ce72 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/eventstream-serde-node@npm:3.0.0" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 46921fae3e47926ef31879dffc3e9743bf2b696a7d4b083c3ddfd06568a30d4fe021a70847d8c8561b9637ba01c4cdfdbd7ba3c418977096945a6cb604b041ea + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/eventstream-serde-universal@npm:3.0.0" + dependencies: + "@smithy/eventstream-codec": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 6bca5785416e8674a032cffab6fd7f93e8a71ba048d300f46e9da82973750e8709c5981c6aa7de2ebe82e01d22eb1df2383b5c5093beedda5f3e600482e84559 + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^3.0.0, @smithy/fetch-http-handler@npm:^3.0.1": + version: 3.0.1 + resolution: "@smithy/fetch-http-handler@npm:3.0.1" + dependencies: + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/querystring-builder": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 46da52bf2320cd279f11585fd2ce414557f3fdf283b969a4b9ed1b0093d23b099bc93edf992f97e99d5c672e5ebfae5072595f8da1fc2738b8f2ea3ead200a90 + languageName: node + linkType: hard + +"@smithy/hash-blob-browser@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/hash-blob-browser@npm:3.0.0" + dependencies: + "@smithy/chunked-blob-reader": "npm:^3.0.0" + "@smithy/chunked-blob-reader-native": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: c8f6d76d8dae37bd48334bcd6060195d8d8f6b14d0af7d326cbd7f3dd4db648691bf755f3795e5d6b4ca9b85fbc580a4b010cce6fe9735204e8a667f145f11bb + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/hash-node@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + "@smithy/util-buffer-from": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 99c65bc992bc3adedb68d4304845bf0acd3e55d3cd851874605a937be5dd4da4eeab01e99e971b59d43d6fb4364dab655530c3a89eb32eac0803f6d07179a63b + languageName: node + linkType: hard + +"@smithy/hash-stream-node@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/hash-stream-node@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 1e67ad794267dcbf2f5008938409b4081e43cbf302d44508f444aaa051ca167e1739418333122b9cce4b98b0815f618326e9c2d55fd5579751ad22ac7e02c9d2 + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/invalid-dependency@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: e78e9cbe1cc8ad04be0dffbc1094eb15294d29b86389ae62c55f0afb96b7354c615fc20f34affed362f857d497e7b34e04b51e732f0b045c2870b749ecc5a2f4 + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/is-array-buffer@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: cab1fd4033d9863dcd95ff058463eb591574bd47e6b61e36aaaf4c0d0da9ed966a54e1d33ec4db7d67aa85df7d274203e934e04dbb40323d01ef4815f63997fc + languageName: node + linkType: hard + +"@smithy/md5-js@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/md5-js@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 098b849ee76c83fb33624dac8d3980a50873564de6fae4e159bac90a6aa9abe0b9fe0fce9a150e5ff438e0a8af2010c50cdc08dd2a8d02b7db2ebb89802743b9 + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/middleware-content-length@npm:3.0.0" + dependencies: + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 2d1dc5766ac83604d43b75b788d4b1f61d8095c9081fe060d5bb21d69b59c4da52869d38eb4f9e13ca8001974b3bce63619f1d8bddfc553041e5b264162fdac9 + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/middleware-endpoint@npm:3.0.0" + dependencies: + "@smithy/middleware-serde": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/url-parser": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: b39b8a3c8ddc4295ab265ce861360a7a842c94af7fb75d81aba4a89000715e50598138f1a7da4979675738d391472189e9854d35cae10a9e994245ad69c2682f + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^3.0.0, @smithy/middleware-retry@npm:^3.0.1": + version: 3.0.1 + resolution: "@smithy/middleware-retry@npm:3.0.1" + dependencies: + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/service-error-classification": "npm:^3.0.0" + "@smithy/smithy-client": "npm:^3.0.1" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + "@smithy/util-retry": "npm:^3.0.0" + tslib: "npm:^2.6.2" + uuid: "npm:^9.0.1" + checksum: 637cadce7fd0b2a22358d43fd3601129e8b725b5f742d835be187882a46690173986d3d7172a00ddbea31c69e4ecef2ed911c457acdb1d3c96adfb56475cbe23 + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/middleware-serde@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 7ca5256fe9290b6ae097fdb9c0180e5219e6d3cb39084fadee007d9e698073498d200c32c439486902e386ab76739176765f64d23673882a08aa0e8de837dc8a + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/middleware-stack@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: e85695b2d2d96230f03500b7111f9917abaab516e1850ec90021db7e984718965e05f7afccda084a7ba96a6bbb9d195a7d6e7882b48d7ccec97239101a2978bc + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/node-config-provider@npm:3.0.0" + dependencies: + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/shared-ini-file-loader": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 6f5326162484f27c6709796e6f11aaa1cd624cb0632a09340f2f2126c20c64dd10f9ed96400f1e65afdfa11e877f69910951ea2b36264141cc513c51461ac656 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/node-http-handler@npm:3.0.0" + dependencies: + "@smithy/abort-controller": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/querystring-builder": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 3d2d0fff55ebeabeca4bb9a8b1cc7dd3a9c810281817232eb546b75cfae53da3c9571d25f203232f42cef608f28b795a1cadec3dbfd44aad2029a6141d146ecf + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/property-provider@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 37c9b949f0df60240b51a4a6e60a772e4ffc5f50de7fb51c74aec4336f46ba7c424f81181487ba6c7a15b5a43f13d82f7609836e96cfc61728e1c26425a5a2b4 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^4.0.0": + version: 4.0.0 + resolution: "@smithy/protocol-http@npm:4.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 0e663013be49ca6867e4d03d2759bae5a72653918617a0184c0f7ecf84043ebaf0f3e6a174f7f6f81934720f90bfce89cecc56510d572cd8d93f423483b74d93 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/querystring-builder@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + "@smithy/util-uri-escape": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: bca3e4c32127f444d7d7812c6afc6cc0dadbbd52a6359f09bf4ba04d2a7f6a09395f61c981b4cf64d714e6010a93ba4a729989f869e4cc32c065aca86bd8f2fc + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/querystring-parser@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: c0258dd552819ffd584abc858d702428da4d6d850eeaa47b29bd15972d428e5b6d62cc9a6609c83ad58e1fedcc38a9189093568163eac6ecf24ea38a96e31779 + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/service-error-classification@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + checksum: b7922ac401773fe4ff500378d8731e9fe8b7dceb6707a48ea93051c0158f2cec7195c718dd80b940af57ef584e36982792f1fe7d31d52c4173c1c495775075a0 + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/shared-ini-file-loader@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 29b2fda4aa6a5688453dd025a1acf867461c9b59db52998e2bac41b7acca8aea45aa41b275cfac27443446a90e9e22da794fb7fd64c2a244cdce80e0c373237f + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/signature-v4@npm:3.0.0" + dependencies: + "@smithy/is-array-buffer": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-hex-encoding": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.0" + "@smithy/util-uri-escape": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 528461766bc6a941216a17331ef61ecc72a2e0171b10c6b40bfafb33e3c83a77f1003541a9986a3c5b61320cc28c95c2aff7c3fa650c6e70a62cb765327e9a9e + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^3.0.0, @smithy/smithy-client@npm:^3.0.1": + version: 3.0.1 + resolution: "@smithy/smithy-client@npm:3.0.1" + dependencies: + "@smithy/middleware-endpoint": "npm:^3.0.0" + "@smithy/middleware-stack": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-stream": "npm:^3.0.1" + tslib: "npm:^2.6.2" + checksum: c9813aa7de2b11d4eb93482b42a52467d1b1fa94e18678ed343ecdb9929880c7526722c22e68993b9f238763cf43e21f266e7c51d3041a93ebaba1112e27ac0f + languageName: node + linkType: hard + +"@smithy/types@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/types@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 8b9a45fc24e2e9702bc9614facbb7ad7c5b3b7a7b438afeeae770e25e62182827e3ea24367e466705f25e4f83e89ff89d0acbcd4c42195fba847821b649205db + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/url-parser@npm:3.0.0" + dependencies: + "@smithy/querystring-parser": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: f88c1a2537593dd8c9643d42fbfde313c630bbb3f2dc9d202d58df298504534c4cedc4595173b1a290ada9220c97096d2653eed9024a00053a08452621db3a9a + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-base64@npm:3.0.0" + dependencies: + "@smithy/util-buffer-from": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 3c883d63a33e1cfeebf7f846f167dfc54c832d1f5514d014fbfff06de3aecd5919f01637fc93668dca8a1029752f3a6fab0a94f455dcb7c88f1d472bde294eef + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-body-length-browser@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: a0ab6a6d414a62d18e57f3581769379a54bb1fd93606c69bc8d96a3566fdecb8db7b57da9446568d03eef9f004f2a89d7e94bdda79ef280f28b19a71803c0309 + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-body-length-node@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: aabac66d7111612fd375d67150f8787c5cdc828f7e6acb40ef0b18b4c354e64e0ef2e4b8da2d7f01e8abe931ff2ef8109a408164ce7e5662fd41b470c462f1e4 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-buffer-from@npm:3.0.0" + dependencies: + "@smithy/is-array-buffer": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 7e6596b38855c07869f7e8f7b0ad9b70c5e658f4a06c7db71c6134a9a785ac1fdaa84f8b3358c4a572767838498df118daad1fa937237d1fb4b9fce735cf8bb0 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-config-provider@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 614c321c5a5a220d7d72d36359c41b4e390566719f7e01cefebffbe7034aae78b4533b27ab2030f93186c5f22893ddf056a3a2376a077d70ce89275f31e1ac46 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^3.0.0": + version: 3.0.1 + resolution: "@smithy/util-defaults-mode-browser@npm:3.0.1" + dependencies: + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/smithy-client": "npm:^3.0.1" + "@smithy/types": "npm:^3.0.0" + bowser: "npm:^2.11.0" + tslib: "npm:^2.6.2" + checksum: 87ce3e6d9a935fa19b8a99bb91739093679a6cf2fbd2167b324f24da07a00120b4f2206c709261eddacc5c681a0096a53ceadbf446d0691f44a3651097794f43 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^3.0.0": + version: 3.0.1 + resolution: "@smithy/util-defaults-mode-node@npm:3.0.1" + dependencies: + "@smithy/config-resolver": "npm:^3.0.0" + "@smithy/credential-provider-imds": "npm:^3.0.0" + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/property-provider": "npm:^3.0.0" + "@smithy/smithy-client": "npm:^3.0.1" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: cda4d67f0b9d72278488e06d1f1d003e3660273fcb4bbe98ce6436adc37e300858773cc3d55689bad481699aaf123c728c06280af5307928500ff3ea5180cc79 + languageName: node + linkType: hard + +"@smithy/util-endpoints@npm:^2.0.0": + version: 2.0.0 + resolution: "@smithy/util-endpoints@npm:2.0.0" + dependencies: + "@smithy/node-config-provider": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 49e897e8b1c19a79f71edfa1b5fa58f90b3244e5026e38c32c3bd2ff2672f4a2de9dbb0c0cf7dfaf8ae6de25db3c8ea76cfbbfc0db8415935721863bcda527bd + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-hex-encoding@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: d9f8c4c676410ca51bdbcec5d986883bad0028e26b098fc50e2b57bc81e8a5ce20e160786d08c8552ca0ba662c88ca16f33857ff24a0d183174325b2b40e3c8f + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-middleware@npm:3.0.0" + dependencies: + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: e9878f85326859b8025df7e2cf7aba5b9fb8ec59be2189c61b0082947c967d888d6894ce6e2152a28eda3e03c207453a94fba7dbf084d755e2ada2df5a58cbb5 + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-retry@npm:3.0.0" + dependencies: + "@smithy/service-error-classification": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 9e38115e47f99bd86360864ed4dd84a266a4e8bc528c6cc834760339cfef857263eb557b85e060776775c1a70b0f03ded0133b9b23c31095d41e51d247a2b1a3 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^3.0.0, @smithy/util-stream@npm:^3.0.1": + version: 3.0.1 + resolution: "@smithy/util-stream@npm:3.0.1" + dependencies: + "@smithy/fetch-http-handler": "npm:^3.0.1" + "@smithy/node-http-handler": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-buffer-from": "npm:^3.0.0" + "@smithy/util-hex-encoding": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: da1d1a6d3ccb5d27e117d6d9331ba7a74501150eecda6cd4625f6a615ff3388f805ee8dd87366edd16fbf335f817cb27f7d4655c6629d10025240952a77913b0 + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-uri-escape@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: d44522339325b0f1fe2c5bf1a3f01d5a699eb8718d800dee24378a1a1b301683756dcfd4be4c32db4d6a00cad85893494778ae39fb246a03aef27d06c9852a67 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-utf8@npm:3.0.0" + dependencies: + "@smithy/util-buffer-from": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 1aead297d835af419f75e0c3113c021aa0da671110d1b498035530d5f35d8030092cad5147edaa7ca458aafe27c9383399ccd8176d342942465a2d8357e5cbf4 + languageName: node + linkType: hard + +"@smithy/util-waiter@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-waiter@npm:3.0.0" + dependencies: + "@smithy/abort-controller": "npm:^3.0.0" + "@smithy/types": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: d206c9f6613e1c43675a48214dd762cb7f85ba57182d2dbcff80392a1983a7f6b06bd537c89949017100bf641d71a32d0c62299d172c52480240c5a431b797ac + languageName: node + linkType: hard + "@solana/buffer-layout-utils@npm:^0.2.0": version: 0.2.0 resolution: "@solana/buffer-layout-utils@npm:0.2.0" @@ -10001,7 +11302,7 @@ __metadata: languageName: node linkType: hard -"asn1.js@npm:5.4.1": +"asn1.js@npm:5.4.1, asn1.js@npm:^5.4.1": version: 5.4.1 resolution: "asn1.js@npm:5.4.1" dependencies: @@ -13748,6 +15049,17 @@ __metadata: languageName: node linkType: hard +"fast-xml-parser@npm:4.2.5": + version: 4.2.5 + resolution: "fast-xml-parser@npm:4.2.5" + dependencies: + strnum: "npm:^1.0.5" + bin: + fxparser: src/cli/cli.js + checksum: 4be7ebe24d6a9a60c278e1423cd86a7da9a77ec64c95563e2c552363caf7a777e0c87c9de1255c2f4e8dea9bce8905dc2bdc58a34e9f2b73c4693654456ad284 + languageName: node + linkType: hard + "fastq@npm:^1.6.0": version: 1.13.0 resolution: "fastq@npm:1.13.0" @@ -22370,6 +23682,13 @@ __metadata: languageName: node linkType: hard +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: d3117975db8372d4d7b2c07601ed2f65bf21cc48d741f37a8617b76370d228f2ec26336e53791ebc3638264d23ca54e6c241f57f8c69bd4941c63c79440525ca + languageName: node + linkType: hard + "styled-jsx@npm:5.1.1": version: 5.1.1 resolution: "styled-jsx@npm:5.1.1" @@ -22947,7 +24266,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0": +"tslib@npm:^2.0.0, tslib@npm:^2.6.2": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca @@ -23729,6 +25048,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 9d0b6adb72b736e36f2b1b53da0d559125ba3e39d913b6072f6f033e0c87835b414f0836b45bcfaf2bdf698f92297fea1c3cc19b0b258bc182c9c43cc0fab9f2 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" From 39ea7cdef9b106b440f5165b6f1efdc27fa140e8 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Thu, 23 May 2024 13:20:26 -0400 Subject: [PATCH 15/59] feat: multi collateral warp routes (#3820) ### Description Adds multicollateral warp route support to the CLI. Dedupes SDK warp route types and zod schemas (via type inference). ### Drive-by changes - Removes scattered ICA logic from deployers in favor of config time resolution ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3639 ### Backward compatibility - No ### Testing - E2E Tests --- .changeset/nice-rivers-own.md | 7 + typescript/cli/src/commands/config.ts | 2 +- typescript/cli/src/config/chain.ts | 6 +- typescript/cli/src/config/warp.ts | 182 +++++--- typescript/cli/src/deploy/utils.ts | 30 -- typescript/cli/src/deploy/warp.ts | 230 +++------ typescript/cli/src/send/message.ts | 9 +- typescript/cli/src/send/transfer.ts | 9 +- typescript/cli/src/tests/deployTestErc20.ts | 9 +- typescript/cli/src/utils/chains.ts | 16 +- typescript/infra/config/warp.ts | 9 +- .../infra/src/govern/HyperlaneAppGovernor.ts | 8 +- typescript/infra/test/govern.hardhat-test.ts | 16 +- .../sdk/src/core/HyperlaneCoreDeployer.ts | 11 +- .../sdk/src/deploy/HyperlaneDeployer.ts | 27 +- typescript/sdk/src/deploy/schemas.ts | 4 +- typescript/sdk/src/deploy/types.ts | 2 +- .../sdk/src/hook/HyperlaneHookDeployer.ts | 2 +- typescript/sdk/src/index.ts | 33 +- typescript/sdk/src/ism/schemas.test.ts | 3 +- typescript/sdk/src/ism/schemas.ts | 2 +- .../sdk/src/middleware/account/schemas.ts | 3 +- .../sdk/src/router/GasRouterDeployer.ts | 9 +- .../sdk/src/router/ProxiedRouterDeployer.ts | 37 +- typescript/sdk/src/router/schemas.ts | 24 +- typescript/sdk/src/router/types.ts | 28 +- .../sdk/src/token/EvmERC20WarpRouteReader.ts | 99 ++-- .../token/adapters/CosmWasmTokenAdapter.ts | 4 +- .../src/token/adapters/CosmosTokenAdapter.ts | 4 +- .../sdk/src/token/adapters/EvmTokenAdapter.ts | 13 +- .../sdk/src/token/adapters/ITokenAdapter.ts | 4 +- .../token/adapters/SealevelTokenAdapter.ts | 13 +- typescript/sdk/src/token/app.ts | 8 +- typescript/sdk/src/token/checker.ts | 15 +- typescript/sdk/src/token/config.ts | 99 +--- typescript/sdk/src/token/contracts.ts | 10 +- .../sdk/src/token/deploy.hardhat-test.ts | 121 ++--- typescript/sdk/src/token/deploy.ts | 436 +++++------------- typescript/sdk/src/token/schemas.test.ts | 69 ++- typescript/sdk/src/token/schemas.ts | 92 ++-- typescript/sdk/src/token/types.ts | 2 + typescript/sdk/src/utils/schemas.ts | 6 + 42 files changed, 633 insertions(+), 1080 deletions(-) create mode 100644 .changeset/nice-rivers-own.md create mode 100644 typescript/sdk/src/utils/schemas.ts diff --git a/.changeset/nice-rivers-own.md b/.changeset/nice-rivers-own.md new file mode 100644 index 0000000000..8000edf814 --- /dev/null +++ b/.changeset/nice-rivers-own.md @@ -0,0 +1,7 @@ +--- +'@hyperlane-xyz/infra': minor +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +--- + +Implement multi collateral warp routes diff --git a/typescript/cli/src/commands/config.ts b/typescript/cli/src/commands/config.ts index b498db4b96..bf30e78ee8 100644 --- a/typescript/cli/src/commands/config.ts +++ b/typescript/cli/src/commands/config.ts @@ -170,7 +170,7 @@ const validateWarpCommand: CommandModuleWithContext<{ path: string }> = { path: inputFileCommandOption, }, handler: async ({ path }) => { - readWarpRouteDeployConfig(path); + await readWarpRouteDeployConfig(path); logGreen('Config is valid'); process.exit(0); }, diff --git a/typescript/cli/src/config/chain.ts b/typescript/cli/src/config/chain.ts index 232205dc04..c2655cab31 100644 --- a/typescript/cli/src/config/chain.ts +++ b/typescript/cli/src/config/chain.ts @@ -46,8 +46,8 @@ export async function createChainConfig({ await new ethers.providers.JsonRpcProvider().getNetwork(); return ethers.providers.JsonRpcProvider.defaultUrl(); }, - 'rpc url', 'Enter http or https', + 'rpc url', ); const provider = new ethers.providers.JsonRpcProvider(rpcUrl); @@ -58,8 +58,8 @@ export async function createChainConfig({ const client = clientName.split('/')[0]; return `${client}${port}`; }, - 'chain name', 'Enter (one word, lower case)', + 'chain name', ); const chainId = parseInt( @@ -68,8 +68,8 @@ export async function createChainConfig({ const network = await provider.getNetwork(); return network.chainId.toString(); }, - 'chain id', 'Enter a (number)', + 'chain id', ), 10, ); diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 19f1a1fbde..189d5198cf 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -1,30 +1,87 @@ -import { confirm, input } from '@inquirer/prompts'; -import { ethers } from 'ethers'; +import { input, select } from '@inquirer/prompts'; import { - ChainMetadata, + ChainMap, + MailboxClientConfig, TokenType, WarpCoreConfig, WarpCoreConfigSchema, WarpRouteDeployConfig, WarpRouteDeployConfigSchema, } from '@hyperlane-xyz/sdk'; -import { objFilter } from '@hyperlane-xyz/utils'; +import { assert, objMap, promiseObjAll } from '@hyperlane-xyz/utils'; import { CommandContext } from '../context/types.js'; import { errorRed, logBlue, logGreen } from '../logger.js'; import { + detectAndConfirmOrPrompt, runMultiChainSelectionStep, - runSingleChainSelectionStep, } from '../utils/chains.js'; import { readYamlOrJson, writeYamlOrJson } from '../utils/files.js'; -export function readWarpRouteDeployConfig( +const TYPE_DESCRIPTIONS: Record = { + [TokenType.synthetic]: 'A new ERC20 with remote transfer functionality', + [TokenType.collateral]: + 'Extends an existing ERC20 with remote transfer functionality', + [TokenType.native]: + 'Extends the native token with remote transfer functionality', + [TokenType.collateralVault]: + 'Extends an existing ERC4626 with remote transfer functionality', + [TokenType.collateralFiat]: + 'Extends an existing FiatToken with remote transfer functionality', + [TokenType.collateralXERC20]: + 'Extends an existing xERC20 with Warp Route functionality', + // TODO: describe + [TokenType.fastSynthetic]: '', + [TokenType.syntheticUri]: '', + [TokenType.fastCollateral]: '', + [TokenType.collateralUri]: '', + [TokenType.nativeScaled]: '', +}; + +const TYPE_CHOICES = Object.values(TokenType).map((type) => ({ + name: type, + value: type, + description: TYPE_DESCRIPTIONS[type], +})); + +async function fillDefaults( + context: CommandContext, + config: ChainMap>, +): Promise> { + return promiseObjAll( + objMap(config, async (chain, config): Promise => { + let mailbox = config.mailbox; + if (!mailbox) { + const addresses = await context.registry.getChainAddresses(chain); + assert(addresses, `No addresses found for chain ${chain}`); + mailbox = addresses.mailbox; + } + let owner = config.owner; + if (!owner) { + owner = + (await context.signer?.getAddress()) ?? + (await context.multiProvider.getSignerAddress(chain)); + } + return { + owner, + mailbox, + ...config, + }; + }), + ); +} + +export async function readWarpRouteDeployConfig( filePath: string, -): WarpRouteDeployConfig { - const config = readYamlOrJson(filePath); + context?: CommandContext, +): Promise { + let config = readYamlOrJson(filePath); if (!config) throw new Error(`No warp route deploy config found at ${filePath}`); + if (context) { + config = await fillDefaults(context, config as any); + } return WarpRouteDeployConfigSchema.parse(config); } @@ -40,75 +97,70 @@ export async function createWarpRouteDeployConfig({ outPath: string; }) { logBlue('Creating a new warp route deployment config'); - const baseChain = await runSingleChainSelectionStep( - context.chainMetadata, - 'Select base chain with the original token to warp', + + const owner = await detectAndConfirmOrPrompt( + async () => context.signer?.getAddress(), + 'Enter the desired', + 'owner address', ); - const isNative = await confirm({ - message: - 'Are you creating a route for the native token of the base chain (e.g. Ether on Ethereum)?', - }); - - const isNft = isNative - ? false - : await confirm({ message: 'Is this an NFT (i.e. ERC-721)?' }); - const isYieldBearing = - isNative || isNft - ? false - : await confirm({ - message: - 'Do you want this warp route to be yield-bearing (i.e. deposits into ERC-4626 vault)?', - }); - - const addressMessage = `Enter the ${ - isYieldBearing ? 'ERC-4626 vault' : 'collateral token' - } address`; - const baseAddress = isNative - ? ethers.constants.AddressZero - : await input({ message: addressMessage }); - - const metadataWithoutBase = objFilter( + const warpChains = await runMultiChainSelectionStep( context.chainMetadata, - (chain, _): _ is ChainMetadata => chain !== baseChain, - ); - const syntheticChains = await runMultiChainSelectionStep( - metadataWithoutBase, - 'Select chains to which the base token will be connected', + 'Select chains to connect', ); - // TODO add more prompts here to support customizing the token metadata - let result: WarpRouteDeployConfig; - if (isNative) { - result = { - [baseChain]: { - type: TokenType.native, - }, - }; - } else { - result = { - [baseChain]: { - type: isYieldBearing ? TokenType.collateralVault : TokenType.collateral, - token: baseAddress, - isNft, + const result: WarpRouteDeployConfig = {}; + for (const chain of warpChains) { + logBlue(`Configuring warp route for chain ${chain}`); + const type = await select({ + message: `Select ${chain}'s token type`, + choices: TYPE_CHOICES, + }); + + // TODO: restore NFT prompting + const isNft = + type === TokenType.syntheticUri || type === TokenType.collateralUri; + + const mailbox = await detectAndConfirmOrPrompt( + async () => { + const addresses = await context.registry.getChainAddresses(chain); + return addresses?.mailbox; }, - }; - } + `For ${chain}, enter the`, + 'mailbox address', + ); - syntheticChains.map((chain) => { - result[chain] = { - type: TokenType.synthetic, - }; - }); + switch (type) { + case TokenType.collateral: + case TokenType.collateralXERC20: + case TokenType.collateralFiat: + case TokenType.collateralUri: + case TokenType.fastCollateral: + case TokenType.collateralVault: + result[chain] = { + mailbox, + type, + owner, + isNft, + token: await input({ + message: `Enter the existing token address on chain ${chain}`, + }), + }; + break; + default: + result[chain] = { mailbox, type, owner, isNft }; + } + } - if (isValidWarpRouteDeployConfig(result)) { + try { + const parsed = WarpRouteDeployConfigSchema.parse(result); logGreen(`Warp Route config is valid, writing to file ${outPath}`); - writeYamlOrJson(outPath, result); - } else { + writeYamlOrJson(outPath, parsed); + } catch (e) { errorRed( `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'); + throw e; } } diff --git a/typescript/cli/src/deploy/utils.ts b/typescript/cli/src/deploy/utils.ts index a2f411d2ec..f1082b3013 100644 --- a/typescript/cli/src/deploy/utils.ts +++ b/typescript/cli/src/deploy/utils.ts @@ -18,36 +18,6 @@ import { assertSigner } from '../utils/keys.js'; import { completeDryRun } from './dry-run.js'; -export async function runPreflightChecks({ - context, - origin, - remotes, - minGas, - chainsToGasCheck, -}: { - context: WriteCommandContext; - origin: ChainName; - remotes: ChainName[]; - minGas: string; - chainsToGasCheck?: ChainName[]; -}) { - log('Running pre-flight checks...'); - - if (!origin || !remotes?.length) throw new Error('Invalid chain selection'); - logGreen('✅ Chain selections are valid'); - - if (remotes.includes(origin)) - throw new Error('Origin and remotes must be distinct'); - logGreen('✅ Origin and remote are distinct'); - - return runPreflightChecksForChains({ - context, - chains: [origin, ...remotes], - minGas, - chainsToGasCheck, - }); -} - export async function runPreflightChecksForChains({ context, chains, diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index a4a92c465a..eb07f189af 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -1,38 +1,35 @@ -import { confirm, input } from '@inquirer/prompts'; +import { confirm } from '@inquirer/prompts'; import { - ChainMap, - ChainName, - ConnectionClientConfig, - EvmTokenAdapter, HypERC20Deployer, HypERC721Deployer, HyperlaneContractsMap, - MinimalTokenMetadata, - MultiProtocolProvider, - MultiProvider, - RouterConfig, TOKEN_TYPE_TO_STANDARD, - TokenConfig, TokenFactories, - TokenRouterConfig, TokenType, WarpCoreConfig, WarpRouteDeployConfig, getTokenConnectionId, - isCollateralConfig, - isNativeConfig, - isSyntheticConfig, + isTokenMetadata, } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; import { readWarpRouteDeployConfig } from '../config/warp.js'; import { MINIMUM_WARP_DEPLOY_GAS } from '../consts.js'; import { WriteCommandContext } from '../context/types.js'; -import { log, logBlue, logGray, logGreen } from '../logger.js'; +import { log, logBlue, logGray, logGreen, logTable } from '../logger.js'; import { isFile, runFileSelectionStep } from '../utils/files.js'; -import { completeDeploy, prepareDeploy, runPreflightChecks } from './utils.js'; +import { + completeDeploy, + prepareDeploy, + runPreflightChecksForChains, +} from './utils.js'; + +interface DeployParams { + context: WriteCommandContext; + configMap: WarpRouteDeployConfig; +} export async function runWarpRouteDeploy({ context, @@ -59,30 +56,28 @@ export async function runWarpRouteDeploy({ `Using warp route deployment config at ${warpRouteDeploymentConfigPath}`, ); } - const warpRouteConfig = readWarpRouteDeployConfig( + const warpRouteConfig = await readWarpRouteDeployConfig( warpRouteDeploymentConfigPath, - ); - - const configs = await runBuildConfigStep({ context, - warpRouteConfig, - }); + ); const deploymentParams = { context, - ...configs, + configMap: warpRouteConfig, }; logBlue('Warp route deployment plan'); await runDeployPlanStep(deploymentParams); - await runPreflightChecks({ - ...deploymentParams, + const chains = Object.keys(warpRouteConfig); + + await runPreflightChecksForChains({ + context, + chains, minGas: MINIMUM_WARP_DEPLOY_GAS, }); const userAddress = await signer.getAddress(); - const chains = [deploymentParams.origin, ...configs.remotes]; const initialBalances = await prepareDeploy(context, userAddress, chains); @@ -91,111 +86,13 @@ export async function runWarpRouteDeploy({ await completeDeploy(context, 'warp', initialBalances, userAddress, chains); } -async function runBuildConfigStep({ - context, - warpRouteConfig, -}: { - context: WriteCommandContext; - warpRouteConfig: WarpRouteDeployConfig; -}) { - const { registry, signer, multiProvider, skipConfirmation } = context; - log('Assembling token configs'); - const chainAddresses = await registry.getAddresses(); - const owner = await signer.getAddress(); - const requiredRouterFields: Array = ['mailbox']; - const remotes: string[] = []; - - /// @dev This will keep track of the base collateral metadata which can get overwritten if there are multiple collaterals. - /// These 'base' variables are used to derive synthetic fields - /// @todo Remove this artifact when multi-collateral is enabled - let baseChainName = ''; - let baseMetadata = {} as MinimalTokenMetadata; - // Define configs that coalesce together values from the config file - for (const [chain, config] of Object.entries(warpRouteConfig)) { - // the artifacts, and the SDK as a fallback - config.owner = owner; - config.mailbox = config.mailbox || chainAddresses[chain]?.mailbox; - config.interchainSecurityModule = - config.interchainSecurityModule || - chainAddresses[chain]?.interchainSecurityModule || - chainAddresses[chain]?.multisigIsm; - // config.ismFactory: chainAddresses[baseChainName].domainRoutingIsmFactory, // TODO fix when updating from routingIsm - - if (isCollateralConfig(config) || isNativeConfig(config)) { - // Store the base metadata - baseChainName = chain; - baseMetadata = await fetchBaseTokenMetadata(chain, config, multiProvider); - log( - `Using token metadata: Name: ${baseMetadata.name}, Symbol: ${baseMetadata.symbol}, Decimals: ${baseMetadata.decimals}`, - ); - if (isCollateralConfig(config)) { - config.name = baseMetadata.name; - config.symbol = baseMetadata.symbol; - config.decimals = baseMetadata.decimals; - } - } else if (isSyntheticConfig(config)) { - // Use the config, or baseMetadata - config.name = config.name || baseMetadata.name; - config.symbol = config.symbol || baseMetadata.symbol; - config.totalSupply = config.totalSupply || 0; - remotes.push(chain); - } - - let hasShownInfo = false; - // Request input for any address fields that are missing - for (const field of requiredRouterFields) { - if (config[field]) continue; - if (skipConfirmation) - throw new Error(`Field ${field} for token on ${chain} required`); - if (!hasShownInfo) { - logBlue( - 'Some router fields are missing. Please enter them now, add them to your warp config, or use the --core flag to use deployment artifacts.', - ); - hasShownInfo = true; - } - const value = await input({ - message: `Enter ${field} for ${getTokenName(config)} token on ${chain}`, - }); - if (!value) throw new Error(`Field ${field} required`); - config[field] = value.trim(); - } - } - - log('Token configs ready'); - return { - configMap: warpRouteConfig, - origin: baseChainName, - metadata: baseMetadata, - remotes, - }; -} +async function runDeployPlanStep({ context, configMap }: DeployParams) { + const { skipConfirmation } = context; -interface DeployParams { - context: WriteCommandContext; - configMap: WarpRouteDeployConfig; - metadata: MinimalTokenMetadata; - origin: ChainName; - remotes: ChainName[]; -} - -async function runDeployPlanStep({ - context, - configMap, - origin, - remotes, -}: DeployParams) { - const { signer, skipConfirmation } = context; - const address = await signer.getAddress(); - const baseToken = configMap[origin]; - - const baseName = getTokenName(baseToken); logBlue('\nDeployment plan'); logGray('==============='); - log(`Collateral type will be ${baseToken.type}`); - log(`Transaction signer and owner of new contracts will be ${address}`); - log(`Deploying a warp route with a base of ${baseName} token on ${origin}`); - log(`Connecting it to new synthetic tokens on ${remotes.join(', ')}`); log(`Using token standard ${configMap.isNft ? 'ERC721' : 'ERC20'}`); + logTable(configMap); if (skipConfirmation) return; @@ -210,80 +107,65 @@ async function executeDeploy(params: DeployParams) { const { configMap, - context: { registry, multiProvider, isDryRun }, + context: { registry, multiProvider, isDryRun, dryRunChain }, } = params; const deployer = configMap.isNft ? new HypERC721Deployer(multiProvider) : new HypERC20Deployer(multiProvider); - const config = isDryRun - ? { [params.origin]: configMap[params.origin] } - : configMap; + const config: WarpRouteDeployConfig = + isDryRun && dryRunChain + ? { [dryRunChain]: configMap[dryRunChain] } + : configMap; - const deployedContracts = await deployer.deploy( - config as ChainMap, - ); /// @todo remove ChainMap once Hyperlane deployers are refactored + const deployedContracts = await deployer.deploy(config); logGreen('✅ Hyp token deployments complete'); if (!isDryRun) log('Writing deployment artifacts'); - const warpCoreConfig = getWarpCoreConfig(params, deployedContracts); + const warpCoreConfig = await getWarpCoreConfig(params, deployedContracts); await registry.addWarpRoute(warpCoreConfig); log(JSON.stringify(warpCoreConfig, null, 2)); logBlue('Deployment is complete!'); } -async function fetchBaseTokenMetadata( - chain: string, - config: TokenRouterConfig, - multiProvider: MultiProvider, -): Promise { - if (config.type === TokenType.native) { - // If it's a native token, use the chain's native token metadata - const chainNativeToken = multiProvider.getChainMetadata(chain).nativeToken; - if (chainNativeToken) return chainNativeToken; - else throw new Error(`No native token metadata for ${chain}`); - } else if ( - config.type === TokenType.collateralVault || - config.type === TokenType.collateral - ) { - // If it's a collateral type, use a TokenAdapter to query for its metadata - log(`Fetching token metadata for ${config.token} on ${chain}`); - const adapter = new EvmTokenAdapter( - chain, - MultiProtocolProvider.fromMultiProvider(multiProvider), - { token: config.token }, - ); - return adapter.getMetadata(); - } else { - throw new Error( - `Unsupported token: ${config.type}. Consider setting token metadata in your deployment config.`, - ); - } -} - -function getTokenName(token: TokenConfig) { - return token.type === TokenType.native ? 'native' : token.name; -} - -function getWarpCoreConfig( - { configMap, metadata }: DeployParams, +async function getWarpCoreConfig( + { configMap, context }: DeployParams, contracts: HyperlaneContractsMap, -): WarpCoreConfig { +): Promise { const warpCoreConfig: WarpCoreConfig = { tokens: [] }; + // TODO: replace with warp read + const tokenMetadata = await HypERC20Deployer.deriveTokenMetadata( + context.multiProvider, + configMap, + ); + // First pass, create token configs for (const [chainName, contract] of Object.entries(contracts)) { const config = configMap[chainName]; + const metadata = { + ...tokenMetadata, + ...config, + }; + + if (!isTokenMetadata(metadata)) { + throw new Error('Missing required token metadata'); + } + + const { decimals } = metadata; + if (!decimals) { + throw new Error('Missing decimals on token metadata'); + } + 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, + ...metadata, + decimals, addressOrDenom: contract[configMap[chainName].type as keyof TokenFactories].address, collateralAddressOrDenom, diff --git a/typescript/cli/src/send/message.ts b/typescript/cli/src/send/message.ts index e48e712376..102592bbc8 100644 --- a/typescript/cli/src/send/message.ts +++ b/typescript/cli/src/send/message.ts @@ -5,7 +5,7 @@ import { addressToBytes32, timeout } from '@hyperlane-xyz/utils'; import { MINIMUM_TEST_SEND_GAS } from '../consts.js'; import { CommandContext, WriteCommandContext } from '../context/types.js'; -import { runPreflightChecks } from '../deploy/utils.js'; +import { runPreflightChecksForChains } from '../deploy/utils.js'; import { errorRed, log, logBlue, logGreen } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; @@ -42,12 +42,11 @@ export async function sendTestMessage({ ); } - await runPreflightChecks({ + await runPreflightChecksForChains({ context, - origin, - remotes: [destination], - minGas: MINIMUM_TEST_SEND_GAS, + chains: [origin, destination], chainsToGasCheck: [origin], + minGas: MINIMUM_TEST_SEND_GAS, }); await timeout( diff --git a/typescript/cli/src/send/transfer.ts b/typescript/cli/src/send/transfer.ts index dcd3aa6fd7..23cd5ba521 100644 --- a/typescript/cli/src/send/transfer.ts +++ b/typescript/cli/src/send/transfer.ts @@ -13,7 +13,7 @@ import { timeout } from '@hyperlane-xyz/utils'; import { readWarpRouteConfig } from '../config/warp.js'; import { MINIMUM_TEST_SEND_GAS } from '../consts.js'; import { WriteCommandContext } from '../context/types.js'; -import { runPreflightChecks } from '../deploy/utils.js'; +import { runPreflightChecksForChains } from '../deploy/utils.js'; import { logBlue, logGreen, logRed } from '../logger.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { runTokenSelectionStep } from '../utils/tokens.js'; @@ -57,12 +57,11 @@ export async function sendTestTransfer({ ); } - await runPreflightChecks({ + await runPreflightChecksForChains({ context, - origin, - remotes: [destination], - minGas: MINIMUM_TEST_SEND_GAS, + chains: [origin, destination], chainsToGasCheck: [origin], + minGas: MINIMUM_TEST_SEND_GAS, }); await timeout( diff --git a/typescript/cli/src/tests/deployTestErc20.ts b/typescript/cli/src/tests/deployTestErc20.ts index 517668a599..be49a13daf 100644 --- a/typescript/cli/src/tests/deployTestErc20.ts +++ b/typescript/cli/src/tests/deployTestErc20.ts @@ -2,7 +2,7 @@ import { Wallet, providers } from 'ethers'; import fs from 'fs'; import { ERC20Test__factory } from '@hyperlane-xyz/core'; -import { TokenType, WarpRouteDeployConfig } from '@hyperlane-xyz/sdk'; +import { TokenType } from '@hyperlane-xyz/sdk'; async function deployERC20() { const [rpcUrl, chain1, chain2, privateKey, outPath] = process.argv.slice(2); @@ -19,13 +19,14 @@ async function deployERC20() { await contract.deployed(); console.log('Test ERC20 contract deployed', contract.address); - const warpDeploymentConfig: WarpRouteDeployConfig = { + const warpDeploymentConfig = { [chain1]: { type: TokenType.collateral, token: contract.address, - isNft: false, }, - [chain2]: { type: TokenType.synthetic }, + [chain2]: { + type: TokenType.synthetic, + }, }; console.log('Writing deployment config to', outPath); diff --git a/typescript/cli/src/utils/chains.ts b/typescript/cli/src/utils/chains.ts index 470f0a20da..3c0e7477d6 100644 --- a/typescript/cli/src/utils/chains.ts +++ b/typescript/cli/src/utils/chains.ts @@ -75,18 +75,20 @@ function handleNewChain(chainNames: string[]) { } export async function detectAndConfirmOrPrompt( - detect: () => Promise, - label: string, + detect: () => Promise, prompt: string, + label: string, ): Promise { let detectedValue: string | undefined; try { detectedValue = await detect(); - const confirmed = await confirm({ - message: `Detected ${label} as ${detectedValue}, is this correct?`, - }); - if (confirmed) { - return detectedValue; + if (detectedValue) { + const confirmed = await confirm({ + message: `Detected ${label} as ${detectedValue}, is this correct?`, + }); + if (confirmed) { + return detectedValue; + } } // eslint-disable-next-line no-empty } catch (e) {} diff --git a/typescript/infra/config/warp.ts b/typescript/infra/config/warp.ts index a878cfc247..43fe0beda1 100644 --- a/typescript/infra/config/warp.ts +++ b/typescript/infra/config/warp.ts @@ -4,8 +4,7 @@ import { ChainMap, HyperlaneIsmFactory, MultiProvider, - RouterConfig, - TokenConfig, + TokenRouterConfig, TokenType, buildAggregationIsmConfigs, defaultMultisigConfigs, @@ -21,7 +20,7 @@ import { DEPLOYER } from './environments/mainnet3/owners.js'; export async function getWarpConfig( multiProvider: MultiProvider, envConfig: EnvironmentConfig, -): Promise> { +): Promise> { const { core } = await getHyperlaneCore(envConfig.environment, multiProvider); const ismFactory = HyperlaneIsmFactory.fromAddressesMap( getAddresses(envConfig.environment, Modules.PROXY_FACTORY), @@ -43,7 +42,7 @@ export async function getWarpConfig( const routerConfig = core.getRouterConfig(envConfig.owners); - const ethereum: TokenConfig & RouterConfig = { + const ethereum: TokenRouterConfig = { ...routerConfig.ethereum, type: TokenType.collateral, token: tokens.ethereum.USDC, @@ -59,7 +58,7 @@ export async function getWarpConfig( // TokenMetadata, but in practice these are actually inferred from a // collateral config. To avoid needing to specify the TokenMetadata, just // ts-ignore for synthetic tokens. - const ancient8: TokenConfig & RouterConfig = { + const ancient8: TokenRouterConfig = { ...routerConfig.ancient8, type: TokenType.synthetic, // Uses the default ISM diff --git a/typescript/infra/src/govern/HyperlaneAppGovernor.ts b/typescript/infra/src/govern/HyperlaneAppGovernor.ts index a2449ec434..cfe2243a87 100644 --- a/typescript/infra/src/govern/HyperlaneAppGovernor.ts +++ b/typescript/infra/src/govern/HyperlaneAppGovernor.ts @@ -3,7 +3,6 @@ import prompts from 'prompts'; import { Ownable__factory } from '@hyperlane-xyz/core'; import { - AccountConfig, ChainMap, ChainName, HyperlaneApp, @@ -137,12 +136,7 @@ export abstract class HyperlaneAppGovernor< SubmissionType.SIGNER, new SignerMultiSend(this.checker.multiProvider, chain), ); - let safeOwner: Address; - if (typeof this.checker.configMap[chain].owner === 'string') { - safeOwner = this.checker.configMap[chain].owner as Address; - } else { - safeOwner = (this.checker.configMap[chain].owner as AccountConfig).owner; - } + const safeOwner = this.checker.configMap[chain].owner; await sendCallsForType( SubmissionType.SAFE, new SafeMultiSend(this.checker.multiProvider, chain, safeOwner), diff --git a/typescript/infra/test/govern.hardhat-test.ts b/typescript/infra/test/govern.hardhat-test.ts index 07f94c0cef..e5852d1f3b 100644 --- a/typescript/infra/test/govern.hardhat-test.ts +++ b/typescript/infra/test/govern.hardhat-test.ts @@ -134,6 +134,12 @@ describe('ICA governance', async () => { localRouter: remote.address, }; + accountOwner = await resolveOrDeployAccountOwner( + multiProvider, + remoteChain, + accountConfig, + ); + const recipientF = new TestRecipient__factory(signer); recipient = await recipientF.deploy(); @@ -145,23 +151,15 @@ describe('ICA governance', async () => { recipient, }, }; - // missing ica const configMap = { [localChain]: { owner: signer.address }, - [remoteChain]: { - owner: { origin: TestChainName.test1, owner: signer.address }, - }, + [remoteChain]: { owner: accountOwner }, }; const app = new TestApp(contractsMap, multiProvider); const checker = new TestChecker(multiProvider, app, configMap); governor = new HyperlaneTestGovernor(checker, icaApp); - accountOwner = await resolveOrDeployAccountOwner( - multiProvider, - remoteChain, - accountConfig, - ); await recipient.transferOwnership(accountOwner); }); diff --git a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts index 9edcfe5f8b..6a06fa88f6 100644 --- a/typescript/sdk/src/core/HyperlaneCoreDeployer.ts +++ b/typescript/sdk/src/core/HyperlaneCoreDeployer.ts @@ -71,11 +71,6 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< proxyAdmin, [domain], ); - // resolve the owner account so that the subsequent calls terminate early - config.owner = await this.resolveInterchainAccountAsOwner( - chain, - config.owner, - ); let defaultIsm = await mailbox.defaultIsm(); const matches = await moduleMatchesConfig( @@ -113,15 +108,11 @@ export class HyperlaneCoreDeployer extends HyperlaneDeployer< // configure mailbox try { - const owner = await this.resolveInterchainAccountAsOwner( - chain, - config.owner, - ); this.logger.debug('Initializing mailbox'); await this.multiProvider.handleTx( chain, mailbox.initialize( - owner, + config.owner, defaultIsm, defaultHook.address, requiredHook.address, diff --git a/typescript/sdk/src/deploy/HyperlaneDeployer.ts b/typescript/sdk/src/deploy/HyperlaneDeployer.ts index 0598d48072..d9fefa0e1e 100644 --- a/typescript/sdk/src/deploy/HyperlaneDeployer.ts +++ b/typescript/sdk/src/deploy/HyperlaneDeployer.ts @@ -43,7 +43,7 @@ import { proxyConstructorArgs, proxyImplementation, } from './proxy.js'; -import { OwnableConfig, Owner } from './types.js'; +import { OwnableConfig } from './types.js'; import { ContractVerifier } from './verify/ContractVerifier.js'; import { ContractVerificationInput, @@ -732,10 +732,7 @@ export abstract class HyperlaneDeployer< continue; } const current = await ownable.owner(); - const owner = await this.resolveInterchainAccountAsOwner( - chain, - config.ownerOverrides?.[contractName as K] ?? config.owner, - ); + const owner = config.ownerOverrides?.[contractName as K] ?? config.owner; if (!eqAddress(current, owner)) { this.logger.debug( { contractName, current, desiredOwner: owner }, @@ -759,24 +756,4 @@ export abstract class HyperlaneDeployer< return receipts.filter((x) => !!x) as ethers.ContractReceipt[]; } - - protected async resolveInterchainAccountAsOwner( - chain: ChainName, - owner: Owner, - ): Promise
{ - if (typeof owner === 'string') { - return owner; - } else { - const routerAddress = this.options.icaApp?.routerAddress(chain); - if (!routerAddress) { - throw new Error('InterchainAccountRouter not deployed'); - } - const router = InterchainAccount.fromAddressesMap( - { chain: { router: routerAddress } }, - this.multiProvider, - ); - // submits network transaction to deploy the account iff it doesn't exist - return router.deployAccount(chain, owner); - } - } } diff --git a/typescript/sdk/src/deploy/schemas.ts b/typescript/sdk/src/deploy/schemas.ts index 7d965f0bc1..99b1066525 100644 --- a/typescript/sdk/src/deploy/schemas.ts +++ b/typescript/sdk/src/deploy/schemas.ts @@ -1,8 +1,6 @@ import { z } from 'zod'; -import { AccountConfigSchema } from '../middleware/account/schemas.js'; - -export const OwnerSchema = z.union([z.string(), AccountConfigSchema]); +export const OwnerSchema = z.string(); export const OwnableConfigSchema = z.object({ owner: OwnerSchema, diff --git a/typescript/sdk/src/deploy/types.ts b/typescript/sdk/src/deploy/types.ts index 084979d0e5..5c74845a4c 100644 --- a/typescript/sdk/src/deploy/types.ts +++ b/typescript/sdk/src/deploy/types.ts @@ -39,7 +39,7 @@ export async function resolveOrDeployAccountOwner( throw new Error('localRouter is required for AccountConfig'); } // submits a transaction to deploy an interchain account if the owner is an AccountConfig and the ICA isn't not deployed yet - return await deployInterchainAccount(multiProvider, chain, owner); + return deployInterchainAccount(multiProvider, chain, owner); } } diff --git a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts index a426e079bb..d1877c9309 100644 --- a/typescript/sdk/src/hook/HyperlaneHookDeployer.ts +++ b/typescript/sdk/src/hook/HyperlaneHookDeployer.ts @@ -115,7 +115,7 @@ export class HyperlaneHookDeployer extends HyperlaneDeployer< config.maxProtocolFee, config.protocolFee, config.beneficiary, - await this.resolveInterchainAccountAsOwner(chain, config.owner), + config.owner, ]); } diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index fd338351e7..aceb6509ed 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -340,8 +340,6 @@ export { MailboxClientConfig as ConnectionClientConfig, ClientViolation as ConnectionClientViolation, ClientViolationType as ConnectionClientViolationType, - ForeignDeploymentConfig, - GasConfig, GasRouterConfig, MailboxClientConfig, ProxiedFactories, @@ -419,27 +417,7 @@ export { } from './token/adapters/serialization.js'; export { HypERC20App } from './token/app.js'; export { HypERC20Checker } from './token/checker.js'; -export { - CollateralConfig, - ERC20Metadata, - ERC20RouterConfig, - ERC721RouterConfig, - HypERC20CollateralConfig, - HypERC20Config, - HypERC721CollateralConfig, - HypERC721Config, - HypNativeConfig, - MinimalTokenMetadata, - NativeConfig, - SyntheticConfig, - TokenConfig, - TokenMetadata, - TokenType, - isCollateralConfig, - isNativeConfig, - isSyntheticConfig, - isUriConfig, -} from './token/config.js'; +export { TokenType } from './token/config.js'; export { HypERC20Factories, HypERC721Factories, @@ -480,8 +458,15 @@ export { AggregationIsmConfigSchema } from './ism/schemas.js'; export { MailboxClientConfigSchema as mailboxClientConfigSchema } from './router/schemas.js'; export { WarpRouteDeployConfigSchema, - TokenRouterConfigSchema as tokenRouterConfigSchema, + TokenRouterConfigSchema, + CollateralConfig, + NativeConfig, + isCollateralConfig, + isNativeConfig, + isSyntheticConfig, + isTokenMetadata, } from './token/schemas.js'; +export { isCompliant } from './utils/schemas.js'; export { TokenRouterConfig, WarpRouteDeployConfig } from './token/types.js'; // prettier-ignore diff --git a/typescript/sdk/src/ism/schemas.test.ts b/typescript/sdk/src/ism/schemas.test.ts index db44109034..7605382c24 100644 --- a/typescript/sdk/src/ism/schemas.test.ts +++ b/typescript/sdk/src/ism/schemas.test.ts @@ -1,8 +1,7 @@ import { expect } from 'chai'; import { ethers } from 'ethers'; -import { AggregationIsmConfigSchema } from '@hyperlane-xyz/sdk'; - +import { AggregationIsmConfigSchema } from './schemas.js'; import { IsmType } from './types.js'; const SOME_ADDRESS = ethers.Wallet.createRandom().address; diff --git a/typescript/sdk/src/ism/schemas.ts b/typescript/sdk/src/ism/schemas.ts index f07df3f256..c449e0e42e 100644 --- a/typescript/sdk/src/ism/schemas.ts +++ b/typescript/sdk/src/ism/schemas.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; import { OwnableConfigSchema } from '../deploy/schemas.js'; -import { ZHash } from '../index.js'; +import { ZHash } from '../metadata/customZodTypes.js'; import { AggregationIsmConfig, IsmConfig, IsmType } from './types.js'; diff --git a/typescript/sdk/src/middleware/account/schemas.ts b/typescript/sdk/src/middleware/account/schemas.ts index fd0e91d2ce..5b95b13d15 100644 --- a/typescript/sdk/src/middleware/account/schemas.ts +++ b/typescript/sdk/src/middleware/account/schemas.ts @@ -1,7 +1,6 @@ import { z } from 'zod'; -import { ZHash } from '../../index.js'; -import { ZChainName } from '../../metadata/customZodTypes.js'; +import { ZChainName, ZHash } from '../../metadata/customZodTypes.js'; export const AccountConfigSchema = z.object({ origin: ZChainName, diff --git a/typescript/sdk/src/router/GasRouterDeployer.ts b/typescript/sdk/src/router/GasRouterDeployer.ts index 8c7d0a52e6..2c929123a4 100644 --- a/typescript/sdk/src/router/GasRouterDeployer.ts +++ b/typescript/sdk/src/router/GasRouterDeployer.ts @@ -4,15 +4,18 @@ import { Address } from '@hyperlane-xyz/utils'; import { HyperlaneContracts, HyperlaneContractsMap, + HyperlaneFactories, } from '../contracts/types.js'; import { ChainMap } from '../types.js'; import { ProxiedRouterDeployer } from './ProxiedRouterDeployer.js'; -import { GasRouterConfig, ProxiedFactories } from './types.js'; +import { GasRouterConfig } from './types.js'; + +const DEFAULT_GAS_OVERHEAD = 100_000; export abstract class GasRouterDeployer< Config extends GasRouterConfig, - Factories extends ProxiedFactories, + Factories extends HyperlaneFactories, > extends ProxiedRouterDeployer { abstract router(contracts: HyperlaneContracts): GasRouter; @@ -37,7 +40,7 @@ export abstract class GasRouterDeployer< const remoteConfigs = remoteDomains .map((domain, i) => ({ domain, - gas: configMap[remoteChains[i]].gas, + gas: configMap[remoteChains[i]].gas ?? DEFAULT_GAS_OVERHEAD, })) .filter(({ gas }, index) => !currentConfigs[index].eq(gas)); if (remoteConfigs.length == 0) { diff --git a/typescript/sdk/src/router/ProxiedRouterDeployer.ts b/typescript/sdk/src/router/ProxiedRouterDeployer.ts index f8a5608045..335a421e27 100644 --- a/typescript/sdk/src/router/ProxiedRouterDeployer.ts +++ b/typescript/sdk/src/router/ProxiedRouterDeployer.ts @@ -7,18 +7,41 @@ import { } from '@hyperlane-xyz/core'; import { eqAddress } from '@hyperlane-xyz/utils'; -import { HyperlaneContracts } from '../contracts/types.js'; +import { HyperlaneContracts, HyperlaneFactories } from '../contracts/types.js'; +import { DeployerOptions } from '../deploy/HyperlaneDeployer.js'; import { resolveOrDeployAccountOwner } from '../deploy/types.js'; +import { MultiProvider } from '../providers/MultiProvider.js'; import { ChainName } from '../types.js'; import { HyperlaneRouterDeployer } from './HyperlaneRouterDeployer.js'; -import { ProxiedFactories, ProxiedRouterConfig } from './types.js'; +import { + ProxiedFactories, + ProxiedRouterConfig, + proxiedFactories, +} from './types.js'; export abstract class ProxiedRouterDeployer< Config extends ProxiedRouterConfig, - Factories extends ProxiedFactories, -> extends HyperlaneRouterDeployer { - abstract router(contracts: HyperlaneContracts): Router; + Factories extends HyperlaneFactories, +> extends HyperlaneRouterDeployer { + constructor( + multiProvider: MultiProvider, + factories: Factories, + options?: DeployerOptions, + ) { + super( + multiProvider, + { + ...factories, + ...proxiedFactories, + }, + options, + ); + } + + abstract router( + contracts: HyperlaneContracts, + ): Router; /** * Returns the contract name @@ -59,7 +82,7 @@ export abstract class ProxiedRouterDeployer< async deployContracts( chain: ChainName, config: Config, - ): Promise> { + ): Promise> { const proxyAdmin = await this.deployContractFromFactory( chain, this.factories.proxyAdmin, @@ -112,6 +135,6 @@ export abstract class ProxiedRouterDeployer< [this.routerContractKey(config)]: proxiedRouter, proxyAdmin, timelockController, - } as HyperlaneContracts; + } as HyperlaneContracts; } } diff --git a/typescript/sdk/src/router/schemas.ts b/typescript/sdk/src/router/schemas.ts index e186c4e1cf..6c6ffa0311 100644 --- a/typescript/sdk/src/router/schemas.ts +++ b/typescript/sdk/src/router/schemas.ts @@ -1,21 +1,23 @@ import { z } from 'zod'; import { OwnableConfigSchema } from '../deploy/schemas.js'; -import { ZHash } from '../index.js'; import { IsmConfigSchema } from '../ism/schemas.js'; +import { ZHash } from '../metadata/customZodTypes.js'; -export const ForeignDeploymentConfigSchema = z.object({ - foreignDeployment: z.string().optional(), -}); - -export const MailboxClientConfigSchema = z.object({ +export const MailboxClientConfigSchema = OwnableConfigSchema.extend({ mailbox: ZHash, hook: ZHash.optional(), interchainSecurityModule: IsmConfigSchema.optional(), }); -export const routerConfigSchema = MailboxClientConfigSchema.merge( - OwnableConfigSchema, -) - .merge(ForeignDeploymentConfigSchema) - .deepPartial(); +export const ForeignDeploymentConfigSchema = z.object({ + foreignDeployment: z.string().optional(), +}); + +export const RouterConfigSchema = MailboxClientConfigSchema.merge( + ForeignDeploymentConfigSchema, +); + +export const GasRouterConfigSchema = RouterConfigSchema.extend({ + gas: z.number().optional(), +}); diff --git a/typescript/sdk/src/router/types.ts b/typescript/sdk/src/router/types.ts index f1c7ab4480..1d4202e05b 100644 --- a/typescript/sdk/src/router/types.ts +++ b/typescript/sdk/src/router/types.ts @@ -6,37 +6,27 @@ import { Router, TimelockController__factory, } from '@hyperlane-xyz/core'; +import { Address } from '@hyperlane-xyz/utils'; -import type { Address } from '../../../utils/dist/index.js'; import { HyperlaneFactories } from '../contracts/types.js'; import { UpgradeConfig } from '../deploy/proxy.js'; -import { CheckerViolation, OwnableConfig } from '../deploy/types.js'; +import { CheckerViolation } from '../deploy/types.js'; import { - ForeignDeploymentConfigSchema, + GasRouterConfigSchema, MailboxClientConfigSchema, + RouterConfigSchema, } from './schemas.js'; export type RouterAddress = { router: Address; }; -export type ForeignDeploymentConfig = z.infer< - typeof ForeignDeploymentConfigSchema ->; - -export type RouterConfig = MailboxClientConfig & - OwnableConfig & - ForeignDeploymentConfig; +export type MailboxClientConfig = z.infer; +export type RouterConfig = z.infer; +export type GasRouterConfig = z.infer; export type ProxiedRouterConfig = RouterConfig & Partial; - -export type GasConfig = { - gas: number; -}; - -export type GasRouterConfig = RouterConfig & GasConfig; - export type ProxiedFactories = HyperlaneFactories & { proxyAdmin: ProxyAdmin__factory; timelockController: TimelockController__factory; @@ -47,10 +37,6 @@ export const proxiedFactories: ProxiedFactories = { timelockController: new TimelockController__factory(), }; -// TODO: merge with kunal's hook deployer - -export type MailboxClientConfig = z.infer; - export enum ClientViolationType { InterchainSecurityModule = 'ClientIsm', Mailbox = 'ClientMailbox', diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 0699fe5c45..d62285705a 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -3,24 +3,24 @@ import { ethers, providers } from 'ethers'; import { ERC20__factory, HypERC20Collateral__factory, + MailboxClient__factory, } from '@hyperlane-xyz/core'; -import { ERC20Metadata, ERC20RouterConfig } from '@hyperlane-xyz/sdk'; -import { Address } from '@hyperlane-xyz/utils'; +import { Address, eqAddress } from '@hyperlane-xyz/utils'; import { DEFAULT_CONTRACT_READ_CONCURRENCY } from '../consts/concurrency.js'; import { EvmHookReader } from '../hook/EvmHookReader.js'; import { EvmIsmReader } from '../ism/EvmIsmReader.js'; import { MultiProvider } from '../providers/MultiProvider.js'; +import { MailboxClientConfig } from '../router/types.js'; import { ChainName } from '../types.js'; -type WarpRouteBaseMetadata = Record< - 'mailbox' | 'owner' | 'token' | 'hook' | 'interchainSecurityModule', - string ->; +import { TokenType } from './config.js'; +import { TokenRouterConfig } from './schemas.js'; +import { TokenMetadata } from './types.js'; -type DerivedERC20WarpRouteConfig = Omit; +const { AddressZero } = ethers.constants; -export class EvmERC20WarpRouteReader { +export class EvmWarpRouteReader { provider: providers.Provider; evmHookReader: EvmHookReader; evmIsmReader: EvmIsmReader; @@ -44,34 +44,32 @@ export class EvmERC20WarpRouteReader { */ async deriveWarpRouteConfig( address: Address, - ): Promise { - const fetchedBaseMetadata = await this.fetchBaseMetadata(address); - const fetchedTokenMetadata = await this.fetchTokenMetadata( - fetchedBaseMetadata.token, - ); - - const results: DerivedERC20WarpRouteConfig = { - ...fetchedBaseMetadata, - ...fetchedTokenMetadata, - }; + type = TokenType.collateral, + ): Promise { + const mailboxClientConfig = await this.fetchMailboxClientConfig(address); - if ( - fetchedBaseMetadata.interchainSecurityModule !== - ethers.constants.AddressZero - ) { - results.interchainSecurityModule = - await this.evmIsmReader.deriveIsmConfig( - fetchedBaseMetadata.interchainSecurityModule, - ); + let token: Address; + switch (type) { + case TokenType.collateral: + token = await HypERC20Collateral__factory.connect( + address, + this.provider, + ).wrappedToken(); + break; + case TokenType.synthetic: + token = address; + break; + default: + throw new Error(`Invalid token type: ${type}`); } - // @todo add after https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3667 is fixed - // if (fetchedBaseMetadata.hook !== ethers.constants.AddressZero) { - // results.hook = await this.evmHookReader.deriveHookConfig( - // fetchedBaseMetadata.hook, - // ); - // } + const fetchedTokenMetadata = await this.fetchTokenMetadata(token); - return results; + return { + type, + token: TokenType.collateral === type ? token : undefined, + ...mailboxClientConfig, + ...fetchedTokenMetadata, + } as TokenRouterConfig; } /** @@ -80,28 +78,31 @@ export class EvmERC20WarpRouteReader { * @param routerAddress - The address of the Warp Route contract. * @returns The base metadata for the Warp Route contract, including the mailbox, owner, wrapped token address, hook, and interchain security module. */ - async fetchBaseMetadata( + async fetchMailboxClientConfig( routerAddress: Address, - ): Promise { - const warpRoute = HypERC20Collateral__factory.connect( + ): Promise { + const warpRoute = MailboxClient__factory.connect( routerAddress, this.provider, ); - const [mailbox, owner, token, hook, interchainSecurityModule] = - await Promise.all([ - warpRoute.mailbox(), - warpRoute.owner(), - warpRoute.wrappedToken(), - warpRoute.hook(), - warpRoute.interchainSecurityModule(), - ]); + const [mailbox, owner, hook, ism] = await Promise.all([ + warpRoute.mailbox(), + warpRoute.owner(), + warpRoute.hook(), + warpRoute.interchainSecurityModule(), + ]); + + const derivedIsm = eqAddress(ism, AddressZero) + ? undefined + : await this.evmIsmReader.deriveIsmConfig(ism); + // TODO: add after https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3667 is fixed + const derivedHook = eqAddress(hook, AddressZero) ? undefined : hook; return { mailbox, owner, - token, - hook, - interchainSecurityModule, + hook: derivedHook, + interchainSecurityModule: derivedIsm, }; } @@ -111,7 +112,7 @@ export class EvmERC20WarpRouteReader { * @param tokenAddress - The address of the token. * @returns A partial ERC20 metadata object containing the token name, symbol, total supply, and decimals. */ - async fetchTokenMetadata(tokenAddress: Address): Promise { + async fetchTokenMetadata(tokenAddress: Address): Promise { const erc20 = ERC20__factory.connect(tokenAddress, this.provider); const [name, symbol, totalSupply, decimals] = await Promise.all([ erc20.name(), @@ -120,6 +121,6 @@ export class EvmERC20WarpRouteReader { erc20.decimals(), ]); - return { name, symbol, totalSupply, decimals }; + return { name, symbol, totalSupply: totalSupply.toString(), decimals }; } } diff --git a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts index 9ee5fba389..e1b476520d 100644 --- a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts @@ -30,7 +30,7 @@ import { } from '../../cw-types/WarpCw20.types.js'; import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; import { ChainName } from '../../types.js'; -import { ERC20Metadata } from '../config.js'; +import { TokenMetadata } from '../types.js'; import { IHypTokenAdapter, @@ -92,7 +92,7 @@ export class CwNativeTokenAdapter } } -export type CW20Metadata = ERC20Metadata; +export type CW20Metadata = TokenMetadata; type CW20Response = TokenInfoResponse | BalanceResponse; // Interacts with CW20/721 contracts diff --git a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts index de0bfd2ee7..ea85d05aa9 100644 --- a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts @@ -6,7 +6,7 @@ import { Address, Domain, assert } from '@hyperlane-xyz/utils'; import { BaseCosmosAdapter } from '../../app/MultiProtocolApp.js'; import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; import { ChainName } from '../../types.js'; -import { MinimalTokenMetadata } from '../config.js'; +import { TokenMetadata } from '../types.js'; import { CwHypCollateralAdapter } from './CosmWasmTokenAdapter.js'; import { @@ -43,7 +43,7 @@ export class CosmNativeTokenAdapter return BigInt(coin.amount); } - getMetadata(): Promise { + getMetadata(): Promise { throw new Error('Metadata not available to native tokens'); } diff --git a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts index febbd57a90..3fc25ade31 100644 --- a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts @@ -21,7 +21,7 @@ import { import { BaseEvmAdapter } from '../../app/MultiProtocolApp.js'; import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider.js'; import { ChainName } from '../../types.js'; -import { MinimalTokenMetadata } from '../config.js'; +import { TokenMetadata } from '../types.js'; import { IHypTokenAdapter, @@ -45,7 +45,7 @@ export class EvmNativeTokenAdapter return BigInt(balance.toString()); } - async getMetadata(): Promise { + async getMetadata(): Promise { // TODO get metadata from chainMetadata config throw new Error('Metadata not available to native tokens'); } @@ -98,13 +98,14 @@ export class EvmTokenAdapter return BigInt(balance.toString()); } - override async getMetadata(isNft?: boolean): Promise { - const [decimals, symbol, name] = await Promise.all([ + override async getMetadata(isNft?: boolean): Promise { + const [decimals, symbol, name, totalSupply] = await Promise.all([ isNft ? 0 : this.contract.decimals(), this.contract.symbol(), this.contract.name(), + this.contract.totalSupply(), ]); - return { decimals, symbol, name }; + return { decimals, symbol, name, totalSupply: totalSupply.toString() }; } override async isApproveRequired( @@ -247,7 +248,7 @@ export class EvmHypCollateralAdapter }); } - override getMetadata(isNft?: boolean): Promise { + override getMetadata(isNft?: boolean): Promise { return this.getWrappedTokenAdapter().then((t) => t.getMetadata(isNft)); } diff --git a/typescript/sdk/src/token/adapters/ITokenAdapter.ts b/typescript/sdk/src/token/adapters/ITokenAdapter.ts index f5dfd906a4..67bd1a0f4f 100644 --- a/typescript/sdk/src/token/adapters/ITokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/ITokenAdapter.ts @@ -1,6 +1,6 @@ import { Address, Domain, Numberish } from '@hyperlane-xyz/utils'; -import { MinimalTokenMetadata } from '../config.js'; +import { TokenMetadata } from '../types.js'; export interface TransferParams { weiAmountOrId: Numberish; @@ -23,7 +23,7 @@ export interface InterchainGasQuote { export interface ITokenAdapter { getBalance(address: Address): Promise; - getMetadata(isNft?: boolean): Promise; + getMetadata(isNft?: boolean): Promise; isApproveRequired( owner: Address, spender: Address, diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index a924bc92c1..5cae1811f2 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -33,7 +33,7 @@ import { SealevelAccountDataWrapper, SealevelInstructionWrapper, } from '../../utils/sealevelSerialization.js'; -import { MinimalTokenMetadata } from '../config.js'; +import { TokenMetadata } from '../types.js'; import { IHypTokenAdapter, @@ -61,7 +61,7 @@ export class SealevelNativeTokenAdapter return BigInt(balance.toString()); } - async getMetadata(): Promise { + async getMetadata(): Promise { throw new Error('Metadata not available to native tokens'); } @@ -115,9 +115,9 @@ export class SealevelTokenAdapter return BigInt(response.value.amount); } - async getMetadata(_isNft?: boolean): Promise { + async getMetadata(_isNft?: boolean): Promise { // TODO solana support - return { decimals: 9, symbol: 'SPL', name: 'SPL Token' }; + return { decimals: 9, symbol: 'SPL', name: 'SPL Token', totalSupply: '' }; } async isApproveRequired(): Promise { @@ -212,11 +212,12 @@ export abstract class SealevelHypTokenAdapter return this.cachedTokenAccountData; } - override async getMetadata(): Promise { + override async getMetadata(): Promise { const tokenData = await this.getTokenAccountData(); // TODO full token metadata support return { decimals: tokenData.decimals, + totalSupply: '0', symbol: 'HYP', name: 'Unknown Hyp Token', }; @@ -506,7 +507,7 @@ export class SealevelHypNativeAdapter extends SealevelHypTokenAdapter { return this.wrappedNative.getBalance(owner); } - override async getMetadata(): Promise { + override async getMetadata(): Promise { return this.wrappedNative.getMetadata(); } diff --git a/typescript/sdk/src/token/app.ts b/typescript/sdk/src/token/app.ts index 9ea8141fa5..0eeb41dbca 100644 --- a/typescript/sdk/src/token/app.ts +++ b/typescript/sdk/src/token/app.ts @@ -10,11 +10,7 @@ import { import { MultiProvider } from '../providers/MultiProvider.js'; import { GasRouterApp } from '../router/RouterApps.js'; -import { - HypERC20Factories, - hypERC20Tokenfactories, - hypERC20factories, -} from './contracts.js'; +import { HypERC20Factories, hypERC20factories } from './contracts.js'; export class HypERC20App extends GasRouterApp { constructor( @@ -25,7 +21,7 @@ export class HypERC20App extends GasRouterApp { } router(contracts: HyperlaneContracts): TokenRouter { - for (const key of objKeys(hypERC20Tokenfactories)) { + for (const key of objKeys(hypERC20factories)) { if (contracts[key]) { return contracts[key] as unknown as TokenRouter; } diff --git a/typescript/sdk/src/token/checker.ts b/typescript/sdk/src/token/checker.ts index 9b0e366f8e..48b38841fb 100644 --- a/typescript/sdk/src/token/checker.ts +++ b/typescript/sdk/src/token/checker.ts @@ -8,20 +8,19 @@ import { HyperlaneRouterChecker } from '../router/HyperlaneRouterChecker.js'; import { ChainName } from '../types.js'; import { HypERC20App } from './app.js'; +import { HypERC20Factories } from './contracts.js'; import { - ERC20RouterConfig, - HypERC20Config, - TokenMetadata, + TokenRouterConfig, isCollateralConfig, isNativeConfig, isSyntheticConfig, -} from './config.js'; -import { HypERC20Factories } from './contracts.js'; +} from './schemas.js'; +import { TokenMetadata } from './types.js'; export class HypERC20Checker extends HyperlaneRouterChecker< HypERC20Factories, HypERC20App, - ERC20RouterConfig + TokenRouterConfig > { async checkChain(chain: ChainName): Promise { await super.checkChain(chain); @@ -31,10 +30,10 @@ export class HypERC20Checker extends HyperlaneRouterChecker< async checkToken(chain: ChainName): Promise { const checkERC20 = async ( token: ERC20, - config: HypERC20Config, + config: TokenRouterConfig, ): Promise => { const checks: { - method: keyof TokenMetadata | 'decimals'; + method: keyof ERC20 & keyof TokenMetadata; violationType: string; }[] = [ { method: 'symbol', violationType: 'TokenSymbolMismatch' }, diff --git a/typescript/sdk/src/token/config.ts b/typescript/sdk/src/token/config.ts index e8d57a4a7d..59a2e597dc 100644 --- a/typescript/sdk/src/token/config.ts +++ b/typescript/sdk/src/token/config.ts @@ -1,10 +1,3 @@ -import { ethers } from 'ethers'; -import z from 'zod'; - -import { GasRouterConfig } from '../router/types.js'; - -import { SyntheticConfigSchema } from './schemas.js'; - export enum TokenType { synthetic = 'synthetic', fastSynthetic = 'fastSynthetic', @@ -19,86 +12,14 @@ export enum TokenType { nativeScaled = 'nativeScaled', } -export type TokenMetadata = { - name: string; - symbol: string; - totalSupply: ethers.BigNumberish; -}; - -export type TokenDecimals = { - decimals: number; - scale?: number; +export const gasOverhead = (tokenType: TokenType) => { + switch (tokenType) { + case TokenType.fastSynthetic: + case TokenType.synthetic: + return 64_000; + case TokenType.native: + return 44_000; + default: + return 68_000; + } }; - -export type ERC20Metadata = TokenMetadata & TokenDecimals; -export type MinimalTokenMetadata = Omit; - -export const isTokenMetadata = (metadata: any): metadata is TokenMetadata => - metadata.name && metadata.symbol && metadata.totalSupply !== undefined; // totalSupply can be 0 - -export const isErc20Metadata = (metadata: any): metadata is ERC20Metadata => - metadata.decimals && isTokenMetadata(metadata); - -export type SyntheticConfig = z.infer; -export type CollateralConfig = { - type: - | TokenType.collateral - | TokenType.collateralXERC20 - | TokenType.collateralFiat - | TokenType.collateralUri - | TokenType.fastCollateral - | TokenType.fastSynthetic - | TokenType.collateralVault; - token: string; -} & Partial; -export type NativeConfig = { - type: TokenType.native; -} & Partial; - -export type TokenConfig = SyntheticConfig | CollateralConfig | NativeConfig; - -export const isCollateralConfig = ( - config: TokenConfig, -): config is CollateralConfig => - config.type === TokenType.collateral || - config.type === TokenType.collateralXERC20 || - config.type === TokenType.collateralFiat || - config.type === TokenType.collateralUri || - config.type === TokenType.fastCollateral || - config.type == TokenType.collateralVault; - -export const isCollateralVaultConfig = ( - config: TokenConfig, -): config is CollateralConfig => config.type === TokenType.collateralVault; - -export const isSyntheticConfig = ( - config: TokenConfig, -): config is SyntheticConfig => - config.type === TokenType.synthetic || - config.type === TokenType.syntheticUri || - config.type === TokenType.fastSynthetic; - -export const isNativeConfig = (config: TokenConfig): config is NativeConfig => - config.type === TokenType.native; - -export const isUriConfig = (config: TokenConfig): boolean => - config.type === TokenType.syntheticUri || - config.type === TokenType.collateralUri; - -export const isFastConfig = (config: TokenConfig): boolean => - config.type === TokenType.fastSynthetic || - config.type === TokenType.fastCollateral; - -export type HypERC20Config = GasRouterConfig & SyntheticConfig & ERC20Metadata; -export type HypERC20CollateralConfig = GasRouterConfig & - CollateralConfig & - Partial; -export type HypNativeConfig = GasRouterConfig & NativeConfig; -export type ERC20RouterConfig = - | HypERC20Config - | HypERC20CollateralConfig - | HypNativeConfig; - -export type HypERC721Config = GasRouterConfig & SyntheticConfig; -export type HypERC721CollateralConfig = GasRouterConfig & CollateralConfig; -export type ERC721RouterConfig = HypERC721Config | HypERC721CollateralConfig; diff --git a/typescript/sdk/src/token/contracts.ts b/typescript/sdk/src/token/contracts.ts index 50ef36dbb0..57e45a7bd5 100644 --- a/typescript/sdk/src/token/contracts.ts +++ b/typescript/sdk/src/token/contracts.ts @@ -14,8 +14,6 @@ import { HypXERC20Collateral__factory, } from '@hyperlane-xyz/core'; -import { proxiedFactories } from '../router/types.js'; - import { TokenType } from './config.js'; export const hypERC20contracts = { @@ -31,7 +29,7 @@ export const hypERC20contracts = { }; export type HypERC20contracts = typeof hypERC20contracts; -export const hypERC20Tokenfactories = { +export const hypERC20factories = { [TokenType.fastCollateral]: new FastHypERC20Collateral__factory(), [TokenType.fastSynthetic]: new FastHypERC20__factory(), [TokenType.synthetic]: new HypERC20__factory(), @@ -42,11 +40,6 @@ export const hypERC20Tokenfactories = { [TokenType.native]: new HypNative__factory(), [TokenType.nativeScaled]: new HypNativeScaled__factory(), }; - -export const hypERC20factories = { - ...hypERC20Tokenfactories, - ...proxiedFactories, -}; export type HypERC20Factories = typeof hypERC20factories; export const hypERC721contracts = { @@ -63,7 +56,6 @@ export const hypERC721factories = { [TokenType.collateral]: new HypERC721Collateral__factory(), [TokenType.syntheticUri]: new HypERC721URIStorage__factory(), [TokenType.synthetic]: new HypERC721__factory(), - ...proxiedFactories, }; export type HypERC721Factories = typeof hypERC721factories; diff --git a/typescript/sdk/src/token/deploy.hardhat-test.ts b/typescript/sdk/src/token/deploy.hardhat-test.ts index 25ed9e50bf..49323191d1 100644 --- a/typescript/sdk/src/token/deploy.hardhat-test.ts +++ b/typescript/sdk/src/token/deploy.hardhat-test.ts @@ -2,38 +2,31 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers.js'; import { expect } from 'chai'; import hre from 'hardhat'; -import { - ERC20Test, - ERC20Test__factory, - Mailbox__factory, -} from '@hyperlane-xyz/core'; -import { RouterConfig, TestChainName } from '@hyperlane-xyz/sdk'; -import { objMap } from '@hyperlane-xyz/utils'; +import { ERC20Test__factory } from '@hyperlane-xyz/core'; +import { Address, objMap } from '@hyperlane-xyz/utils'; +import { TestChainName } from '../consts/testChains.js'; import { TestCoreApp } from '../core/TestCoreApp.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { MultiProvider } from '../providers/MultiProvider.js'; -import { ChainMap } from '../types.js'; -import { EvmERC20WarpRouteReader } from './EvmERC20WarpRouteReader.js'; -import { - HypERC20CollateralConfig, - HypERC20Config, - TokenConfig, - TokenType, -} from './config.js'; +import { EvmWarpRouteReader } from './EvmERC20WarpRouteReader.js'; +import { TokenType } from './config.js'; import { HypERC20Deployer } from './deploy.js'; +import { TokenRouterConfig } from './schemas.js'; import { WarpRouteDeployConfig } from './types.js'; +const chain = TestChainName.test1; + describe('TokenDeployer', async () => { let signer: SignerWithAddress; let deployer: HypERC20Deployer; let multiProvider: MultiProvider; let coreApp: TestCoreApp; - let routerConfigMap: ChainMap; let config: WarpRouteDeployConfig; + let token: Address; before(async () => { [signer] = await hre.ethers.getSigners(); @@ -44,19 +37,27 @@ describe('TokenDeployer', async () => { ); const ismFactory = new HyperlaneIsmFactory(factories, multiProvider); coreApp = await new TestCoreDeployer(multiProvider, ismFactory).deployApp(); - routerConfigMap = coreApp.getRouterConfig(signer.address); + const routerConfigMap = coreApp.getRouterConfig(signer.address); config = objMap( routerConfigMap, - (chain, c): HypERC20Config => ({ + (chain, c): TokenRouterConfig => ({ type: TokenType.synthetic, name: chain, symbol: `u${chain}`, decimals: 18, - totalSupply: 100_000, - gas: 65_000, + totalSupply: '100000', ...c, }), ); + + const { name, decimals, symbol, totalSupply } = config[chain]; + const contract = await new ERC20Test__factory(signer).deploy( + name!, + symbol!, + totalSupply!, + decimals!, + ); + token = contract.address; }); beforeEach(async () => { @@ -64,66 +65,36 @@ describe('TokenDeployer', async () => { }); it('deploys', async () => { - await deployer.deploy(config as ChainMap); + await deployer.deploy(config); }); - describe('ERC20WarpRouterReader', async () => { - const TOKEN_NAME = 'fake'; - const TOKEN_SUPPLY = '100000000000000000000'; - const TOKEN_DECIMALS = 18; - let erc20Factory: ERC20Test__factory; - let token: ERC20Test; + for (const type of [TokenType.collateral, TokenType.synthetic]) { + describe('ERC20WarpRouterReader', async () => { + let reader: EvmWarpRouteReader; + let routerAddress: Address; - before(async () => { - erc20Factory = new ERC20Test__factory(signer); - token = await erc20Factory.deploy( - TOKEN_NAME, - TOKEN_NAME, - TOKEN_SUPPLY, - TOKEN_DECIMALS, - ); - }); - async function deriveWarpConfig(chainName: string, address: string) { - return new EvmERC20WarpRouteReader( - multiProvider, - chainName, - ).deriveWarpRouteConfig(address); - } - it('should derive ERC20RouterConfig from collateral correctly', async () => { - const baseConfig = routerConfigMap[TestChainName.test1]; - const mailbox = Mailbox__factory.connect(baseConfig.mailbox, signer); + before(() => { + reader = new EvmWarpRouteReader(multiProvider, TestChainName.test1); + }); - // Create config - const config: { [key: string]: any } = { - [TestChainName.test1]: { - type: TokenType.collateral, - token: token.address, - hook: await mailbox.defaultHook(), - gas: 65_000, - ...baseConfig, - }, - }; - // Deploy with config - const warpRoute = await deployer.deploy( - config as ChainMap, - ); + beforeEach(async () => { + config[chain] = { + ...config[chain], + type, + // @ts-ignore + token: type === TokenType.collateral ? token : undefined, + }; + const warpRoute = await deployer.deploy(config); + routerAddress = warpRoute[chain][type].address; + }); - // Derive config and check if each value matches - const derivedConfig: Partial = - await deriveWarpConfig( - TestChainName.test1, - warpRoute[TestChainName.test1].collateral.address, + it(`should derive TokenRouterConfig from ${type} correctly`, async () => { + const derivedConfig = await reader.deriveWarpRouteConfig( + routerAddress, + type, ); - - for (const [key, value] of Object.entries(derivedConfig)) { - const deployedValue = config[TestChainName.test1][key]; - if (deployedValue) expect(deployedValue).to.equal(value); - } - - // Check if token values matches - expect(derivedConfig.name).to.equal(TOKEN_NAME); - expect(derivedConfig.symbol).to.equal(TOKEN_NAME); - expect(derivedConfig.decimals).to.equal(TOKEN_DECIMALS); + expect(derivedConfig).to.include(config[chain]); + }); }); - }); + } }); diff --git a/typescript/sdk/src/token/deploy.ts b/typescript/sdk/src/token/deploy.ts index f4f4e93b99..ad865c4f02 100644 --- a/typescript/sdk/src/token/deploy.ts +++ b/typescript/sdk/src/token/deploy.ts @@ -1,92 +1,70 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { constants, providers } from 'ethers'; +import { constants } from 'ethers'; import { ERC20__factory, - ERC721EnumerableUpgradeable__factory, + ERC721Enumerable__factory, GasRouter, - MailboxClient, } from '@hyperlane-xyz/core'; -import { objKeys, objMap, rootLogger } from '@hyperlane-xyz/utils'; +import { assert, objKeys, objMap, rootLogger } from '@hyperlane-xyz/utils'; import { HyperlaneContracts } from '../contracts/types.js'; import { ContractVerifier } from '../deploy/verify/ContractVerifier.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { GasRouterDeployer } from '../router/GasRouterDeployer.js'; -import { GasConfig, RouterConfig } from '../router/types.js'; -import { ChainMap, ChainName } from '../types.js'; +import { ChainName } from '../types.js'; -import { - CollateralConfig, - ERC20Metadata, - ERC20RouterConfig, - ERC721RouterConfig, - HypERC20Config, - HypERC721Config, - TokenConfig, - TokenMetadata, - TokenType, - isCollateralConfig, - isErc20Metadata, - isNativeConfig, - isSyntheticConfig, - isTokenMetadata, - isUriConfig, -} from './config.js'; +import { gasOverhead } from './config.js'; import { HypERC20Factories, - HypERC20contracts, HypERC721Factories, - HypERC721contracts, + TokenFactories, hypERC20contracts, hypERC20factories, hypERC721contracts, hypERC721factories, } from './contracts.js'; +import { + TokenRouterConfig, + isCollateralConfig, + isNativeConfig, + isSyntheticConfig, + isTokenMetadata, +} from './schemas.js'; +import { TokenMetadata, WarpRouteDeployConfig } from './types.js'; -export class HypERC20Deployer extends GasRouterDeployer< - ERC20RouterConfig, - HypERC20Factories -> { +abstract class TokenDeployer< + Factories extends TokenFactories, +> extends GasRouterDeployer { constructor( multiProvider: MultiProvider, + factories: Factories, + loggerName: string, ismFactory?: HyperlaneIsmFactory, contractVerifier?: ContractVerifier, ) { - super(multiProvider, hypERC20factories, { - logger: rootLogger.child({ module: 'HypERC20Deployer' }), + super(multiProvider, factories, { + logger: rootLogger.child({ module: loggerName }), ismFactory, contractVerifier, }); // factories not used in deploy } - routerContractName(config: ERC20RouterConfig): string { - return hypERC20contracts[this.routerContractKey(config)]; - } - - routerContractKey(config: ERC20RouterConfig) { - return config.type as keyof HypERC20contracts; - } - - async constructorArgs( - _: ChainName, - config: ERC20RouterConfig, - ): Promise> { + async constructorArgs(_: ChainName, config: TokenRouterConfig): Promise { if (isCollateralConfig(config)) { - return [config.token, config.mailbox] as any; + return [config.token, config.mailbox]; } else if (isNativeConfig(config)) { - return config.scale - ? [config.scale, config.mailbox] - : ([config.mailbox] as any); + return config.scale ? [config.scale, config.mailbox] : [config.mailbox]; } else if (isSyntheticConfig(config)) { - return [config.decimals, config.mailbox] as any; + assert(config.decimals); // decimals must be defined by this point + return [config.decimals, config.mailbox]; } else { - throw new Error('Unknown collateral type when constructing arguments'); + throw new Error('Unknown token type when constructing arguments'); } } - async initializeArgs(_: ChainName, config: HypERC20Config): Promise { + async initializeArgs(_: ChainName, config: TokenRouterConfig): Promise { // ISM config can be an object, but is not supported right now. if (typeof config.interchainSecurityModule === 'object') { throw new Error('Token deployer does not support ISM objects currently'); @@ -96,318 +74,144 @@ export class HypERC20Deployer extends GasRouterDeployer< config.interchainSecurityModule ?? constants.AddressZero, config.owner, ]; - if (isCollateralConfig(config)) { - return defaultArgs as any; - } else if (isNativeConfig(config)) { - return defaultArgs as any; + if (isCollateralConfig(config) || isNativeConfig(config)) { + return defaultArgs; } else if (isSyntheticConfig(config)) { - return [ - config.totalSupply, - config.name, - config.symbol, - ...defaultArgs, - ] as any; + return [config.totalSupply, config.name, config.symbol, ...defaultArgs]; } else { throw new Error('Unknown collateral type when initializing arguments'); } } - static async fetchMetadata( - provider: providers.Provider, - config: CollateralConfig, - ): Promise { - const erc20 = ERC20__factory.connect(config.token, provider); - - const [name, symbol, totalSupply, decimals] = await Promise.all([ - erc20.name(), - erc20.symbol(), - erc20.totalSupply(), - erc20.decimals(), - ]); - - return { name, symbol, totalSupply, decimals }; - } - - static gasOverheadDefault(config: TokenConfig): number { - switch (config.type) { - case 'fastSynthetic': - return 64_000; - case 'synthetic': - return 64_000; - case 'native': - return 44_000; - case 'collateral': - case 'fastCollateral': - default: - return 68_000; - } - } - - // Gets the metadata for a collateral token, favoring the config - // and getting any on-chain metadata that is missing. - async getCollateralMetadata( - chain: ChainName, - config: CollateralConfig, - ): Promise { - const metadata = { - name: config.name, - symbol: config.symbol, - decimals: config.decimals, - totalSupply: 0, - }; - - if ( - metadata.name && - metadata.symbol && - metadata.decimals !== undefined && - metadata.decimals !== null - ) { - return metadata as ERC20Metadata; - } - const fetchedMetadata = await HypERC20Deployer.fetchMetadata( - this.multiProvider.getProvider(chain), - config, - ); - // Filter out undefined values - const definedConfigMetadata = Object.fromEntries( - Object.entries(metadata).filter(([k, v]) => !!k && !!v), - ); - return { - ...fetchedMetadata, - ...definedConfigMetadata, - } as ERC20Metadata; - } - - router(contracts: HyperlaneContracts) { - for (const key of objKeys(hypERC20factories)) { - if (contracts[key]) { - return contracts[key] as GasRouter; + static async deriveTokenMetadata( + multiProvider: MultiProvider, + configMap: WarpRouteDeployConfig, + ): Promise { + for (const [chain, config] of Object.entries(configMap)) { + if (isTokenMetadata(config)) { + return config; } - } - throw new Error('No matching contract found'); - } - async deployContracts(chain: ChainName, config: HypERC20Config) { - const deployedContracts = await super.deployContracts(chain, config); - const router = deployedContracts[this.routerContractKey(config)]; - await this.configureClient(chain, router, config); - return { - [config.type]: router, - ...deployedContracts, - } as any; - } - - async buildTokenMetadata( - configMap: ChainMap, - ): Promise> { - let tokenMetadata: ERC20Metadata | undefined; + if (isNativeConfig(config)) { + const nativeToken = multiProvider.getChainMetadata(chain).nativeToken; + if (nativeToken) { + return { totalSupply: 0, ...nativeToken }; + } + } - for (const [chain, config] of Object.entries(configMap)) { if (isCollateralConfig(config)) { - const collateralMetadata = await this.getCollateralMetadata( - chain, - config, - ); - tokenMetadata = { - ...collateralMetadata, - totalSupply: 0, - }; - } else if (isNativeConfig(config)) { - const chainMetadata = this.multiProvider.getChainMetadata(chain); - if (chainMetadata.nativeToken) { - tokenMetadata = { - ...chainMetadata.nativeToken, - totalSupply: 0, - }; - } else { - throw new Error( - `Warp route config specifies native token but chain metadata for ${chain} does not provide native token details`, + const provider = multiProvider.getProvider(chain); + + if (config.isNft) { + const erc721 = ERC721Enumerable__factory.connect( + config.token, + provider, ); + const [name, symbol, totalSupply] = await Promise.all([ + erc721.name(), + erc721.symbol(), + erc721.totalSupply(), + ]); + return { + name, + symbol, + totalSupply: totalSupply.toString(), + }; } - } else if (isErc20Metadata(config)) { - tokenMetadata = config; - } - } - if (!isErc20Metadata(tokenMetadata)) { - throw new Error('Invalid ERC20 token metadata'); + const erc20 = ERC20__factory.connect(config.token, provider); + const [name, symbol, totalSupply, decimals] = await Promise.all([ + erc20.name(), + erc20.symbol(), + erc20.totalSupply(), + erc20.decimals(), + ]); + + return { name, symbol, totalSupply: totalSupply.toString(), decimals }; + } } - return objMap(configMap, () => tokenMetadata!); + return undefined; } - buildGasOverhead(configMap: ChainMap): ChainMap { - return objMap(configMap, (_, config) => ({ - gas: HypERC20Deployer.gasOverheadDefault(config), + async deploy(configMap: WarpRouteDeployConfig) { + const tokenMetadata = await TokenDeployer.deriveTokenMetadata( + this.multiProvider, + configMap, + ); + const resolvedConfigMap = objMap(configMap, (_, config) => ({ + ...tokenMetadata, + gas: gasOverhead(config.type), + ...config, })); - } - - async deploy(configMap: ChainMap) { - const tokenMetadata = await this.buildTokenMetadata(configMap); - const gasOverhead = this.buildGasOverhead(configMap); - const mergedConfig = objMap(configMap, (chain, config) => { - return { - ...tokenMetadata[chain], - ...gasOverhead[chain], - ...config, - }; - }) as ChainMap; - - return super.deploy(mergedConfig); + return super.deploy(resolvedConfigMap); } } -export class HypERC721Deployer extends GasRouterDeployer< - ERC721RouterConfig, - HypERC721Factories -> { +export class HypERC20Deployer extends TokenDeployer { constructor( multiProvider: MultiProvider, + ismFactory?: HyperlaneIsmFactory, contractVerifier?: ContractVerifier, ) { - super(multiProvider, hypERC721factories, { - logger: rootLogger.child({ module: 'HypERC721Deployer' }), + super( + multiProvider, + hypERC20factories, + 'HypERC20Deployer', + ismFactory, contractVerifier, - }); - } - routerContractName(config: ERC721RouterConfig): string { - return hypERC721contracts[this.routerContractKey(config)]; + ); } - routerContractKey( - config: ERC721RouterConfig, - ): K { - if (isCollateralConfig(config)) { - return ( - isUriConfig(config) ? TokenType.collateralUri : TokenType.collateral - ) as K; - } else { - // if isSyntheticConfig - return ( - isUriConfig(config) ? TokenType.syntheticUri : TokenType.synthetic - ) as K; + router(contracts: HyperlaneContracts): GasRouter { + for (const key of objKeys(hypERC20factories)) { + if (contracts[key]) { + return contracts[key]; + } } + throw new Error('No matching contract found'); } - async constructorArgs( - _: ChainName, - config: ERC721RouterConfig, - ): Promise { - if (isCollateralConfig(config)) { - return [config.token, config.mailbox]; - } else if (isSyntheticConfig(config)) { - return [config.mailbox]; - } else { - throw new Error('Unknown collateral type when constructing arguments'); - } + routerContractKey(config: TokenRouterConfig): keyof HypERC20Factories { + assert(config.type in hypERC20factories, 'Invalid ERC20 token type'); + return config.type as keyof HypERC20Factories; } - async initializeArgs(_: ChainName, config: ERC721RouterConfig): Promise { - const defaultArgs = [ - config.hook ?? constants.AddressZero, - config.interchainSecurityModule ?? constants.AddressZero, - config.owner, - ]; - if (isCollateralConfig(config)) { - return defaultArgs; - } else if (isSyntheticConfig(config)) { - return [config.totalSupply, config.name, config.symbol, ...defaultArgs]; - } else { - throw new Error('Unknown collateral type when initializing arguments'); - } + routerContractName(config: TokenRouterConfig): string { + return hypERC20contracts[this.routerContractKey(config)]; } +} - static async fetchMetadata( - provider: providers.Provider, - config: CollateralConfig, - ): Promise { - const erc721 = ERC721EnumerableUpgradeable__factory.connect( - config.token, - provider, +export class HypERC721Deployer extends TokenDeployer { + constructor( + multiProvider: MultiProvider, + ismFactory?: HyperlaneIsmFactory, + contractVerifier?: ContractVerifier, + ) { + super( + multiProvider, + hypERC721factories, + 'HypERC721Deployer', + ismFactory, + contractVerifier, ); - const [name, symbol, totalSupply] = await Promise.all([ - erc721.name(), - erc721.symbol(), - erc721.totalSupply(), - ]); - - return { name, symbol, totalSupply }; } - static gasOverheadDefault(config: TokenConfig): number { - switch (config.type) { - case 'synthetic': - return 160_000; - case 'syntheticUri': - return 163_000; - case 'collateral': - case 'collateralUri': - default: - return 80_000; - } - } - - router(contracts: HyperlaneContracts) { + router(contracts: HyperlaneContracts): GasRouter { for (const key of objKeys(hypERC721factories)) { if (contracts[key]) { - return contracts[key] as GasRouter; + return contracts[key]; } } throw new Error('No matching contract found'); } - async deployContracts(chain: ChainName, config: HypERC721Config) { - const { [this.routerContractKey(config)]: router } = - await super.deployContracts(chain, config); - - await this.configureClient(chain, router as MailboxClient, config); - return { [config.type]: router } as any; - } - - async buildTokenMetadata( - configMap: ChainMap, - ): Promise> { - let tokenMetadata: TokenMetadata | undefined; - - for (const [chain, config] of Object.entries(configMap)) { - if (isCollateralConfig(config)) { - const collateralMetadata = await HypERC721Deployer.fetchMetadata( - this.multiProvider.getProvider(chain), - config, - ); - tokenMetadata = { - ...collateralMetadata, - totalSupply: 0, - }; - } else if (isTokenMetadata(config)) { - tokenMetadata = config; - } - } - - if (!isTokenMetadata(tokenMetadata)) { - throw new Error('Invalid ERC721 token metadata'); - } - - return objMap(configMap, () => tokenMetadata!); - } - - buildGasOverhead(configMap: ChainMap): ChainMap { - return objMap(configMap, (_, config) => ({ - gas: HypERC721Deployer.gasOverheadDefault(config), - })); + routerContractKey(config: TokenRouterConfig): keyof HypERC721Factories { + assert(config.type in hypERC721factories, 'Invalid ERC721 token type'); + return config.type as keyof HypERC721Factories; } - async deploy(configMap: ChainMap) { - const tokenMetadata = await this.buildTokenMetadata(configMap); - const gasOverhead = this.buildGasOverhead(configMap); - const mergedConfig = objMap(configMap, (chain, config) => { - return { - ...tokenMetadata[chain], - ...gasOverhead[chain], - ...config, - }; - }) as ChainMap; - - return super.deploy(mergedConfig); + routerContractName(config: TokenRouterConfig): string { + return hypERC721contracts[this.routerContractKey(config)]; } } diff --git a/typescript/sdk/src/token/schemas.test.ts b/typescript/sdk/src/token/schemas.test.ts index 394b4c9fb2..65f780cd03 100644 --- a/typescript/sdk/src/token/schemas.test.ts +++ b/typescript/sdk/src/token/schemas.test.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; import { ethers } from 'ethers'; -import { constants } from 'ethers'; -import { TokenType, WarpRouteDeployConfigSchema } from '@hyperlane-xyz/sdk'; +import { TokenType } from './config.js'; +import { WarpRouteDeployConfigSchema } from './schemas.js'; const SOME_ADDRESS = ethers.Wallet.createRandom().address; const COLLATERAL_TYPES = [ @@ -16,58 +16,43 @@ const NON_COLLATERAL_TYPES = [ TokenType.synthetic, TokenType.syntheticUri, TokenType.fastSynthetic, - TokenType.native, ]; describe('WarpRouteDeployConfigSchema refine', () => { - it('should require type address', () => { - const config: any = { + let config: any; + beforeEach(() => { + config = { arbitrum: { type: TokenType.collateral, token: SOME_ADDRESS, + owner: SOME_ADDRESS, + mailbox: SOME_ADDRESS, }, }; + }); + + it('should require token type', () => { expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; delete config.arbitrum.type; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; }); it('should require token address', () => { - const config: any = { - arbitrum: { - type: TokenType.collateral, - token: SOME_ADDRESS, - }, - }; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; delete config.arbitrum.token; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; }); - it('should allow mailbox to be optional', () => { - const config: any = { - arbitrum: { - type: TokenType.collateral, - token: constants.AddressZero, - mailbox: SOME_ADDRESS, - }, - }; + it('should require mailbox address', () => { expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; delete config.arbitrum.mailbox; - expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; + expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; }); it('should throw if collateral type and token is empty', async () => { for (const type of COLLATERAL_TYPES) { - const config: any = { - arbitrum: { - type, - mailbox: SOME_ADDRESS, - name: 'Arby Coin', - symbol: 'ARBY', - totalSupply: '10000', - }, - }; + config.arbitrum.type = type; + config.arbitrum.token = undefined; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; // Set to some address @@ -76,17 +61,23 @@ describe('WarpRouteDeployConfigSchema refine', () => { } }); - it('should succeed if non-collateral type and token is empty', async () => { + it('should accept native type if token is empty', async () => { + config.arbitrum.type = TokenType.native; + config.arbitrum.token = undefined; + expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; + }); + + it('should succeed if non-collateral type, token is empty, metadata is defined', async () => { + delete config.arbitrum.token; + config.arbitrum.totalSupply = '0'; + config.arbitrum.name = 'name'; + for (const type of NON_COLLATERAL_TYPES) { - const config: any = { - arbitrum: { - type, - mailbox: SOME_ADDRESS, - name: 'Arby Coin', - symbol: 'ARBY', - totalSupply: '10000', - }, - }; + config.arbitrum.type = type; + config.arbitrum.symbol = undefined; + expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.false; + + config.arbitrum.symbol = 'symbol'; expect(WarpRouteDeployConfigSchema.safeParse(config).success).to.be.true; } }); diff --git a/typescript/sdk/src/token/schemas.ts b/typescript/sdk/src/token/schemas.ts index 2890261a57..3ec33bf8f3 100644 --- a/typescript/sdk/src/token/schemas.ts +++ b/typescript/sdk/src/token/schemas.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; -import { routerConfigSchema } from '../router/schemas.js'; +import { GasRouterConfigSchema } from '../router/schemas.js'; +import { isCompliant } from '../utils/schemas.js'; import { TokenType } from './config.js'; @@ -8,49 +9,36 @@ export const TokenMetadataSchema = z.object({ name: z.string(), symbol: z.string(), totalSupply: z.string().or(z.number()), -}); - -export const TokenDecimalsSchema = z.object({ - decimals: z.number(), + decimals: z.number().optional(), scale: z.number().optional(), -}); - -export const ERC20MetadataSchema = - TokenMetadataSchema.merge(TokenDecimalsSchema).partial(); - -export const ERC721MetadataSchema = z.object({ isNft: z.boolean().optional(), }); -export const CollateralConfigSchema = ERC721MetadataSchema.merge( - ERC20MetadataSchema, -).merge( - z.object({ - type: z.enum([ - TokenType.collateral, - TokenType.collateralUri, - TokenType.fastCollateral, - TokenType.collateralVault, - ]), - token: z.string(), - }), -); +export const CollateralConfigSchema = TokenMetadataSchema.partial().extend({ + type: z.enum([ + TokenType.collateral, + TokenType.collateralXERC20, + TokenType.collateralFiat, + TokenType.collateralUri, + TokenType.fastCollateral, + TokenType.collateralVault, + ]), + token: z + .string() + .describe('Existing token address to extend with Warp Route functionality'), +}); -export const NativeConfigSchema = TokenDecimalsSchema.partial().merge( - z.object({ - type: z.enum([TokenType.native]), - }), -); +export const NativeConfigSchema = TokenMetadataSchema.partial().extend({ + type: z.enum([TokenType.native, TokenType.nativeScaled]), +}); -export const SyntheticConfigSchema = TokenMetadataSchema.partial().merge( - z.object({ - type: z.enum([ - TokenType.synthetic, - TokenType.syntheticUri, - TokenType.fastSynthetic, - ]), - }), -); +export const SyntheticConfigSchema = TokenMetadataSchema.partial().extend({ + type: z.enum([ + TokenType.synthetic, + TokenType.syntheticUri, + TokenType.fastSynthetic, + ]), +}); /** * @remarks @@ -63,12 +51,26 @@ export const TokenConfigSchema = z.discriminatedUnion('type', [ SyntheticConfigSchema, ]); -export const TokenRouterConfigSchema = z.intersection( - TokenConfigSchema, - routerConfigSchema, +export const TokenRouterConfigSchema = TokenConfigSchema.and( + GasRouterConfigSchema, ); -export const WarpRouteDeployConfigSchema = z.record( - z.string(), - TokenRouterConfigSchema, -); +export type TokenRouterConfig = z.infer; +export type NativeConfig = z.infer; +export type CollateralConfig = z.infer; + +export const isSyntheticConfig = isCompliant(SyntheticConfigSchema); +export const isCollateralConfig = isCompliant(CollateralConfigSchema); +export const isNativeConfig = isCompliant(NativeConfigSchema); +export const isTokenMetadata = isCompliant(TokenMetadataSchema); + +export const WarpRouteDeployConfigSchema = z + .record(TokenRouterConfigSchema) + .refine((configMap) => { + const entries = Object.entries(configMap); + return ( + entries.some( + ([_, config]) => isCollateralConfig(config) || isNativeConfig(config), + ) || entries.every(([_, config]) => isTokenMetadata(config)) + ); + }, `Config must include Native or Collateral OR all synthetics must define token metadata`); diff --git a/typescript/sdk/src/token/types.ts b/typescript/sdk/src/token/types.ts index 0d0a70800f..8cbead3317 100644 --- a/typescript/sdk/src/token/types.ts +++ b/typescript/sdk/src/token/types.ts @@ -1,9 +1,11 @@ import { z } from 'zod'; import { + TokenMetadataSchema, TokenRouterConfigSchema, WarpRouteDeployConfigSchema, } from './schemas.js'; +export type TokenMetadata = z.infer; export type TokenRouterConfig = z.infer; export type WarpRouteDeployConfig = z.infer; diff --git a/typescript/sdk/src/utils/schemas.ts b/typescript/sdk/src/utils/schemas.ts new file mode 100644 index 0000000000..2babea6c0e --- /dev/null +++ b/typescript/sdk/src/utils/schemas.ts @@ -0,0 +1,6 @@ +import { z } from 'zod'; + +export function isCompliant(schema: S) { + return (config: unknown): config is z.infer => + schema.safeParse(config).success; +} From f2dc925cb3a6ed3a1e6c930ab9e3f0bd0da11d09 Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Fri, 24 May 2024 14:18:39 +0100 Subject: [PATCH 16/59] Remove nambrot from codeowners (#3846) It had to happen #RIP, my getting review requested ended up too noisy to miss actual tags in issues --- .github/CODEOWNERS | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6a5ccb733a..ab8ed10d1c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,9 +1,9 @@ # File extension owners *.sol @yorhodes @tkporter @aroralanuk @nbayindirli -*.ts @yorhodes @jmrossy @nambrot @nbayindirli +*.ts @yorhodes @jmrossy @nbayindirli *.rs @tkporter @daniel-savu -*.md @Skunkchain @nambrot @avious00 +*.md @Skunkchain @avious00 # Package owners @@ -20,10 +20,10 @@ typescript/sdk @yorhodes @jmrossy typescript/token @yorhodes @jmrossy @tkporter @aroralanuk @nbayindirli ## Hello World -typescript/helloworld @yorhodes @nambrot +typescript/helloworld @yorhodes ## CLI typescript/cli @jmrossy @yorhodes @aroralanuk @nbayindirli ## Infra -typescript/infra @tkporter @nambrot +typescript/infra @tkporter From 704675c7e133d403ea5208a6adf46281a3be668a Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Fri, 24 May 2024 14:58:41 +0100 Subject: [PATCH 17/59] chore: update infra service images (key funder, kathy) (#3843) ### Description - Updates key funder and kathy images on testnet4 and mainnet3 - Sorts desired balances and is more explicit about requirements for kathy chains (https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3732 will expand on this to make sure we keep this up to date) - I found it surprising that if the kathy balance wasn't specified in the config, then key funder would default to funding the full relayer amount. Made this not be the case ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --------- Co-authored-by: Paul Balaji --- .../config/environments/mainnet3/funding.ts | 24 ++++++++++++------- .../environments/mainnet3/helloworld.ts | 4 ++-- .../config/environments/testnet4/funding.ts | 10 +++++--- .../environments/testnet4/helloworld.ts | 4 ++-- .../funding/fund-keys-from-deployer.ts | 16 +++++++++---- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index 9b5fa1523c..f9f15f0a84 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.js'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '375ec39-20240520-160456', + tag: 'b22a0f4-20240523-140812', }, // We're currently using the same deployer/key funder key as mainnet2. // To minimize nonce clobbering we offset the key funder cron @@ -26,41 +26,47 @@ export const keyFunderConfig: KeyFunderConfig = { connectionType: RpcConsensusType.Fallback, // desired balance config desiredBalancePerChain: { + arbitrum: '0.5', + ancient8: '0.5', avalanche: '5', - bsc: '5', + base: '0.5', blast: '0.2', + bsc: '5', celo: '3', ethereum: '0.5', gnosis: '5', inevm: '3', + mantapacific: '0.2', mode: '0.2', 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', + polygon: '20', polygonzkevm: '0.5', - scroll: '0.5', - ancient8: '0.5', redstone: '0.2', + scroll: '0.5', + viction: '3', zetachain: '20', }, desiredKathyBalancePerChain: { arbitrum: '0.1', + ancient8: '0', avalanche: '6', base: '0.05', + blast: '0', bsc: '0.35', celo: '150', ethereum: '0.4', gnosis: '100', inevm: '0.05', + mantapacific: '0', + mode: '0', moonbeam: '250', optimism: '0.1', polygon: '85', polygonzkevm: '0.05', + redstone: '0', scroll: '0.05', viction: '0.05', + zetachain: '0', }, }; diff --git a/typescript/infra/config/environments/mainnet3/helloworld.ts b/typescript/infra/config/environments/mainnet3/helloworld.ts index c4d1920cab..4f15ba9126 100644 --- a/typescript/infra/config/environments/mainnet3/helloworld.ts +++ b/typescript/infra/config/environments/mainnet3/helloworld.ts @@ -15,7 +15,7 @@ export const hyperlane: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '86b7f98-20231207-153806', + tag: 'b22a0f4-20240523-140812', }, chainsToSkip: [], runEnv: environment, @@ -36,7 +36,7 @@ export const releaseCandidate: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '0e3f73f-20240206-160718', + tag: 'b22a0f4-20240523-140812', }, chainsToSkip: [], runEnv: environment, diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 6880dd2201..42d9c75c94 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.js'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'b0811ba-20240411-151216', + tag: 'b22a0f4-20240523-140812', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron @@ -30,11 +30,15 @@ export const keyFunderConfig: KeyFunderConfig = { bsctestnet: '5', fuji: '5', plumetestnet: '0.2', - sepolia: '5', - // Funder boosts itself upto 5x balance on L2 before dispersing funds scrollsepolia: '1', + sepolia: '5', }, desiredKathyBalancePerChain: { + alfajores: '1', + bsctestnet: '1', + fuji: '1', plumetestnet: '0.05', + scrollsepolia: '1', + sepolia: '1', }, }; diff --git a/typescript/infra/config/environments/testnet4/helloworld.ts b/typescript/infra/config/environments/testnet4/helloworld.ts index 96903af850..e6f094c7d9 100644 --- a/typescript/infra/config/environments/testnet4/helloworld.ts +++ b/typescript/infra/config/environments/testnet4/helloworld.ts @@ -15,7 +15,7 @@ export const hyperlaneHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '17ac515-20240402-171932', + tag: 'b22a0f4-20240523-140812', }, chainsToSkip: [], runEnv: environment, @@ -35,7 +35,7 @@ export const releaseCandidateHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '17ac515-20240402-171932', + tag: 'b22a0f4-20240523-140812', }, chainsToSkip: [], runEnv: environment, diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index 5ee9aba1ce..02b15a978c 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -575,10 +575,18 @@ class ContextFunder { } private getDesiredBalanceForRole(chain: ChainName, role: Role): BigNumber { - const desiredBalanceEther = - role === Role.Kathy && this.desiredKathyBalancePerChain[chain] - ? this.desiredKathyBalancePerChain[chain] - : this.desiredBalancePerChain[chain]; + let desiredBalanceEther: string | undefined; + if (role === Role.Kathy) { + const desiredKathyBalance = this.desiredKathyBalancePerChain[chain]; + if (desiredKathyBalance === undefined) { + logger.warn({ chain }, 'No desired balance for Kathy, not funding'); + desiredBalanceEther = '0'; + } else { + desiredBalanceEther = this.desiredKathyBalancePerChain[chain]; + } + } else { + desiredBalanceEther = this.desiredBalancePerChain[chain]; + } let desiredBalance = ethers.utils.parseEther(desiredBalanceEther ?? '0'); if (this.context === Contexts.ReleaseCandidate) { desiredBalance = desiredBalance From 214f503e53634403b051da575d2c63547114f747 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 24 May 2024 19:19:25 +0100 Subject: [PATCH 18/59] feat: forward-backward message processor (#3775) ### Description - adds logic for iterating message nonces in the processor task in forward-backward fashion - removes all manual calls that were updating the next nonce to query. This is done automatically in the iterator struct now - stores the highest known message nonce in the db, which is used to initialize the iterator ### Drive-by changes - Converts the concrete HyperlaneDb type stored in the processor to a trait obj, to enable mocking db responses ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3796 - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3816 ### Backward compatibility Yes - if there is no db entry for the new prefix, the processor starts from nonce zero, so no migration is required ### Testing Unit testing + e2e --- rust/Cargo.lock | 1 + rust/agents/relayer/Cargo.toml | 1 + rust/agents/relayer/src/msg/processor.rs | 342 +++++++++++++++--- .../src/contracts/mailbox.rs | 2 +- .../cursors/sequence_aware/backward.rs | 26 +- .../cursors/sequence_aware/forward.rs | 22 +- .../src/db/rocks/hyperlane_db.rs | 58 ++- 7 files changed, 404 insertions(+), 48 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index dd039b7d99..f29c2c80ea 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -7032,6 +7032,7 @@ dependencies = [ "hyperlane-ethereum", "hyperlane-test", "itertools 0.12.0", + "mockall", "num-derive 0.4.1", "num-traits", "once_cell", diff --git a/rust/agents/relayer/Cargo.toml b/rust/agents/relayer/Cargo.toml index 5c80952089..2df8f54d9a 100644 --- a/rust/agents/relayer/Cargo.toml +++ b/rust/agents/relayer/Cargo.toml @@ -43,6 +43,7 @@ hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } [dev-dependencies] once_cell.workspace = true +mockall.worksapce = true tokio-test.workspace = true hyperlane-test = { path = "../../hyperlane-test" } hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/agents/relayer/src/msg/processor.rs index 3e3b5aa61d..166ee6561f 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/agents/relayer/src/msg/processor.rs @@ -1,4 +1,5 @@ use std::{ + cmp::max, collections::HashMap, fmt::{Debug, Formatter}, sync::Arc, @@ -8,11 +9,14 @@ use std::{ use async_trait::async_trait; use derive_new::new; use eyre::Result; -use hyperlane_base::{db::HyperlaneRocksDB, CoreMetrics}; +use hyperlane_base::{ + db::{HyperlaneRocksDB, ProcessMessage}, + CoreMetrics, +}; use hyperlane_core::{HyperlaneDomain, HyperlaneMessage}; use prometheus::IntGauge; use tokio::sync::mpsc::UnboundedSender; -use tracing::{debug, trace}; +use tracing::{debug, instrument, trace}; use super::{metadata::AppContextClassifier, op_queue::QueueOperation, pending_message::*}; use crate::{processor::ProcessorExt, settings::matching_list::MatchingList}; @@ -20,9 +24,7 @@ use crate::{processor::ProcessorExt, settings::matching_list::MatchingList}; /// Finds unprocessed messages from an origin and submits then through a channel /// for to the appropriate destination. #[allow(clippy::too_many_arguments)] -#[derive(new)] pub struct MessageProcessor { - db: HyperlaneRocksDB, whitelist: Arc, blacklist: Arc, metrics: MessageProcessorMetrics, @@ -32,16 +34,187 @@ pub struct MessageProcessor { /// Needed context to send a message for each destination chain destination_ctxs: HashMap>, metric_app_contexts: Vec<(MatchingList, String)>, - #[new(default)] - message_nonce: u32, + nonce_iterator: ForwardBackwardIterator, +} + +#[derive(Debug)] +struct ForwardBackwardIterator { + low_nonce_iter: DirectionalNonceIterator, + high_nonce_iter: DirectionalNonceIterator, + // here for debugging purposes + _domain: String, +} + +impl ForwardBackwardIterator { + #[instrument(skip(db), ret)] + fn new(db: Arc) -> Self { + let high_nonce = db.retrieve_highest_seen_message_nonce().ok().flatten(); + let domain = db.domain().name().to_owned(); + let high_nonce_iter = DirectionalNonceIterator::new( + // If the high nonce is None, we start from the beginning + high_nonce.unwrap_or_default().into(), + NonceDirection::High, + db.clone(), + domain.clone(), + ); + let mut low_nonce_iter = + DirectionalNonceIterator::new(high_nonce, NonceDirection::Low, db, domain.clone()); + // Decrement the low nonce to avoid processing the same message twice, which causes double counts in metrics + low_nonce_iter.iterate(); + debug!( + ?low_nonce_iter, + ?high_nonce_iter, + ?domain, + "Initialized ForwardBackwardIterator" + ); + Self { + low_nonce_iter, + high_nonce_iter, + _domain: domain, + } + } + + async fn try_get_next_message( + &mut self, + metrics: &MessageProcessorMetrics, + ) -> Result> { + loop { + let high_nonce_message_status = self.high_nonce_iter.try_get_next_nonce(metrics)?; + let low_nonce_message_status = self.low_nonce_iter.try_get_next_nonce(metrics)?; + // Always prioritize the high nonce message + match (high_nonce_message_status, low_nonce_message_status) { + // Keep iterating if only processed messages are found + (MessageStatus::Processed, _) => { + self.high_nonce_iter.iterate(); + } + (_, MessageStatus::Processed) => { + self.low_nonce_iter.iterate(); + } + // Otherwise return - either a processable message or nothing to process + (MessageStatus::Processable(high_nonce_message), _) => { + self.high_nonce_iter.iterate(); + return Ok(Some(high_nonce_message)); + } + (_, MessageStatus::Processable(low_nonce_message)) => { + self.low_nonce_iter.iterate(); + return Ok(Some(low_nonce_message)); + } + (MessageStatus::Unindexed, MessageStatus::Unindexed) => return Ok(None), + } + // This loop may iterate through millions of processed messages, blocking the runtime. + // So, to avoid starving other futures in this task, yield to the runtime + // on each iteration + tokio::task::yield_now().await; + } + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq)] +enum NonceDirection { + #[default] + High, + Low, +} + +#[derive(new)] +struct DirectionalNonceIterator { + nonce: Option, + direction: NonceDirection, + db: Arc, + domain_name: String, +} + +impl Debug for DirectionalNonceIterator { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "DirectionalNonceIterator {{ nonce: {:?}, direction: {:?}, domain: {:?} }}", + self.nonce, self.direction, self.domain_name + ) + } +} + +impl DirectionalNonceIterator { + #[instrument] + fn iterate(&mut self) { + match self.direction { + NonceDirection::High => self.nonce = self.nonce.map(|n| n.saturating_add(1)), + NonceDirection::Low => { + if let Some(nonce) = self.nonce { + // once the message with nonce zero is processed, we should stop going backwards + self.nonce = nonce.checked_sub(1); + } + } + } + } + + fn try_get_next_nonce( + &mut self, + metrics: &MessageProcessorMetrics, + ) -> Result> { + if let Some(message) = self.indexed_message_with_nonce()? { + Self::update_max_nonce_gauge(&message, metrics); + if !self.is_message_processed()? { + return Ok(MessageStatus::Processable(message)); + } else { + return Ok(MessageStatus::Processed); + } + } + Ok(MessageStatus::Unindexed) + } + + fn update_max_nonce_gauge(message: &HyperlaneMessage, metrics: &MessageProcessorMetrics) { + let current_max = metrics.max_last_known_message_nonce_gauge.get(); + metrics + .max_last_known_message_nonce_gauge + .set(max(current_max, message.nonce as i64)); + if let Some(metrics) = metrics.get(message.destination) { + metrics.set(message.nonce as i64); + } + } + + fn indexed_message_with_nonce(&self) -> Result> { + match self.nonce { + Some(nonce) => { + let msg = self.db.retrieve_message_by_nonce(nonce)?; + Ok(msg) + } + None => Ok(None), + } + } + + fn is_message_processed(&self) -> Result { + let Some(nonce) = self.nonce else { + return Ok(false); + }; + let processed = self.db.retrieve_processed_by_nonce(nonce)?.unwrap_or(false); + if processed { + trace!( + nonce, + domain = self.db.domain().name(), + "Message already marked as processed in DB" + ); + } + Ok(processed) + } +} + +#[derive(Debug)] +enum MessageStatus { + /// The message wasn't indexed yet so can't be processed. + Unindexed, + // The message was indexed and is ready to be processed. + Processable(T), + // The message was indexed and already processed. + Processed, } impl Debug for MessageProcessor { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, - "MessageProcessor {{ whitelist: {:?}, blacklist: {:?}, message_nonce: {:?} }}", - self.whitelist, self.blacklist, self.message_nonce + "MessageProcessor {{ whitelist: {:?}, blacklist: {:?}, nonce_iterator: {:?}}}", + self.whitelist, self.blacklist, self.nonce_iterator ) } } @@ -50,7 +223,7 @@ impl Debug for MessageProcessor { impl ProcessorExt for MessageProcessor { /// The domain this processor is getting messages from. fn domain(&self) -> &HyperlaneDomain { - self.db.domain() + self.nonce_iterator.high_nonce_iter.db.domain() } /// One round of processing, extracted from infinite work loop for @@ -61,35 +234,31 @@ impl ProcessorExt for MessageProcessor { // self.tx_msg and then continue the scan at the next highest // nonce. // Scan until we find next nonce without delivery confirmation. - if let Some(msg) = self.try_get_unprocessed_message()? { + if let Some(msg) = self.try_get_unprocessed_message().await? { debug!(?msg, "Processor working on message"); let destination = msg.destination; // Skip if not whitelisted. if !self.whitelist.msg_matches(&msg, true) { debug!(?msg, whitelist=?self.whitelist, "Message not whitelisted, skipping"); - self.message_nonce += 1; return Ok(()); } // Skip if the message is blacklisted if self.blacklist.msg_matches(&msg, false) { debug!(?msg, blacklist=?self.blacklist, "Message blacklisted, skipping"); - self.message_nonce += 1; return Ok(()); } // Skip if the message is intended for this origin if destination == self.domain().id() { debug!(?msg, "Message destined for self, skipping"); - self.message_nonce += 1; return Ok(()); } // Skip if the message is intended for a destination we do not service if !self.send_channels.contains_key(&destination) { debug!(?msg, "Message destined for unknown domain, skipping"); - self.message_nonce += 1; return Ok(()); } @@ -106,7 +275,6 @@ impl ProcessorExt for MessageProcessor { app_context, ); self.send_channels[&destination].send(Box::new(pending_msg) as QueueOperation)?; - self.message_nonce += 1; } else { tokio::time::sleep(Duration::from_secs(1)).await; } @@ -115,34 +283,36 @@ impl ProcessorExt for MessageProcessor { } impl MessageProcessor { - fn try_get_unprocessed_message(&mut self) -> Result> { - loop { - // First, see if we can find the message so we can update the gauge. - if let Some(message) = self.db.retrieve_message_by_nonce(self.message_nonce)? { - // Update the latest nonce gauges - self.metrics - .max_last_known_message_nonce_gauge - .set(message.nonce as i64); - if let Some(metrics) = self.metrics.get(message.destination) { - metrics.set(message.nonce as i64); - } + pub fn new( + db: HyperlaneRocksDB, + whitelist: Arc, + blacklist: Arc, + metrics: MessageProcessorMetrics, + send_channels: HashMap>, + destination_ctxs: HashMap>, + metric_app_contexts: Vec<(MatchingList, String)>, + ) -> Self { + Self { + whitelist, + blacklist, + metrics, + send_channels, + destination_ctxs, + metric_app_contexts, + nonce_iterator: ForwardBackwardIterator::new(Arc::new(db) as Arc), + } + } - // If this message has already been processed, on to the next one. - if !self - .db - .retrieve_processed_by_nonce(&self.message_nonce)? - .unwrap_or(false) - { - return Ok(Some(message)); - } else { - debug!(nonce=?self.message_nonce, "Message already marked as processed in DB"); - self.message_nonce += 1; - } - } else { - trace!(nonce=?self.message_nonce, "No message found in DB for nonce"); - return Ok(None); - } + async fn try_get_unprocessed_message(&mut self) -> Result> { + trace!(nonce_iterator=?self.nonce_iterator, "Trying to get the next processor message"); + let next_message = self + .nonce_iterator + .try_get_next_message(&self.metrics) + .await?; + if next_message.is_none() { + trace!(nonce_iterator=?self.nonce_iterator, "No message found in DB for nonce"); } + Ok(next_message) } } @@ -197,7 +367,7 @@ mod test { use super::*; use hyperlane_base::{ - db::{test_utils, HyperlaneRocksDB}, + db::{test_utils, DbResult, HyperlaneRocksDB}, settings::{ChainConf, ChainConnectionConf, Settings}, }; use hyperlane_test::mocks::{MockMailboxContract, MockValidatorAnnounceContract}; @@ -387,6 +557,21 @@ mod test { pending_messages } + mockall::mock! { + pub Db {} + + impl Debug for Db { + fn fmt<'a>(&self, f: &mut std::fmt::Formatter<'a>) -> std::fmt::Result; + } + + impl ProcessMessage for Db { + fn retrieve_highest_seen_message_nonce(&self) -> DbResult>; + fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult>; + fn retrieve_processed_by_nonce(&self, nonce: u32) -> DbResult>; + fn domain(&self) -> &HyperlaneDomain; + } + } + #[tokio::test] async fn test_full_pending_message_persistence_flow() { test_utils::run_test_db(|db| async move { @@ -441,4 +626,77 @@ mod test { }) .await; } + + #[tokio::test] + async fn test_forward_backward_iterator() { + let mut mock_db = MockDb::new(); + const MAX_ONCHAIN_NONCE: u32 = 4; + const MOCK_HIGHEST_SEEN_NONCE: u32 = 2; + + // How many times the db was queried for the max onchain nonce message + let mut retrieve_calls_for_max_onchain_nonce = 0; + + mock_db + .expect_domain() + .return_const(dummy_domain(0, "dummy_domain")); + mock_db + .expect_retrieve_highest_seen_message_nonce() + .returning(|| Ok(Some(MOCK_HIGHEST_SEEN_NONCE))); + mock_db + .expect_retrieve_message_by_nonce() + .returning(move |nonce| { + // return `None` the first time we get a query for the last message + // (the `MAX_ONCHAIN_NONCE`th one), to simulate an ongoing indexing that hasn't finished + if nonce == MAX_ONCHAIN_NONCE && retrieve_calls_for_max_onchain_nonce == 0 { + retrieve_calls_for_max_onchain_nonce += 1; + return Ok(None); + } + + // otherwise return a message for every nonce in the closed + // interval [0, MAX_ONCHAIN_NONCE] + if nonce > MAX_ONCHAIN_NONCE { + Ok(None) + } else { + Ok(Some(dummy_hyperlane_message( + &dummy_domain(1, "dummy_domain"), + nonce, + ))) + } + }); + + // The messages must be marked as "not processed" in the db for them to be returned + // when the iterator queries them + mock_db + .expect_retrieve_processed_by_nonce() + .returning(|_| Ok(Some(false))); + let dummy_metrics = dummy_processor_metrics(0); + let db = Arc::new(mock_db); + + let mut forward_backward_iterator = ForwardBackwardIterator::new(db.clone()); + + let mut messages = vec![]; + while let Some(msg) = forward_backward_iterator + .try_get_next_message(&dummy_metrics) + .await + .unwrap() + { + messages.push(msg.nonce); + } + + // we start with 2 (MOCK_HIGHEST_SEEN_NONCE) as the highest seen nonce, + // so we go forward and get 3. + // then we try going forward again but get a `None` (not indexed yet), for nonce 4 (MAX_ONCHAIN_NONCE). + // then we go backwards once and get 1. + // then retry the forward iteration, which should return a message the second time, for nonce 4. + // finally, going forward again returns None so we go backward and get 0. + assert_eq!(messages, vec![2, 3, 1, 4, 0]); + + // the final bounds of the iterator are (None, MAX_ONCHAIN_NONCE + 1), where None means + // the backward iterator has reached the beginning (iterated past nonce 0) + assert_eq!(forward_backward_iterator.low_nonce_iter.nonce, None); + assert_eq!( + forward_backward_iterator.high_nonce_iter.nonce, + Some(MAX_ONCHAIN_NONCE + 1) + ); + } } diff --git a/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs index fd6a6b2808..d70c2bfc7d 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -164,7 +164,7 @@ impl SequenceAwareIndexer for EthereumMailboxIndexer where M: Middleware + 'static, { - #[instrument(err, skip(self))] + #[instrument(err, skip(self), ret)] 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?; 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 index e217d4bb42..3efd04a8d3 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs @@ -9,12 +9,11 @@ use hyperlane_core::{ HyperlaneSequenceAwareIndexerStoreReader, IndexMode, Indexed, LogMeta, SequenceIndexed, }; use itertools::Itertools; -use tracing::{debug, warn}; +use tracing::{debug, instrument, 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. @@ -34,6 +33,11 @@ pub(crate) struct BackwardSequenceAwareSyncCursor { } impl BackwardSequenceAwareSyncCursor { + #[instrument( + skip(db), + fields(chunk_size, next_sequence, start_block, index_mode), + ret + )] pub fn new( chunk_size: u32, db: Arc>, @@ -61,6 +65,7 @@ impl BackwardSequenceAwareSyncCursor { /// 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. + #[instrument(ret)] pub async fn get_next_range(&mut self) -> Result>> { // Skip any already indexed logs. self.skip_indexed().await?; @@ -129,6 +134,11 @@ impl BackwardSequenceAwareSyncCursor { // If the sequence hasn't been indexed, break out of the loop. break; } + // We've noticed that this loop can run for a long time because the `await` + // points never yield. + // So, to avoid starving other futures in this task, yield to the runtime + // on each iteration + tokio::task::yield_now().await; } Ok(()) @@ -299,6 +309,17 @@ impl BackwardSequenceAwareSyncCursor { } } +impl Debug for BackwardSequenceAwareSyncCursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BackwardSequenceAwareSyncCursor") + .field("chunk_size", &self.chunk_size) + .field("current_indexing_snapshot", &self.current_indexing_snapshot) + .field("last_indexed_snapshot", &self.last_indexed_snapshot) + .field("index_mode", &self.index_mode) + .finish() + } +} + #[async_trait] impl ContractSyncCursor for BackwardSequenceAwareSyncCursor @@ -329,6 +350,7 @@ impl ContractSyncCursor /// ## logs /// The logs to ingest. If any logs are duplicated or their sequence is higher than the current indexing snapshot, /// they are filtered out. + #[instrument(err, ret, skip(logs), fields(range=?range, logs=?logs.iter().map(|(log, _)| log.sequence).collect::>()))] async fn update( &mut self, logs: Vec<(Indexed, LogMeta)>, diff --git a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs index aef515b2b6..374b4b797c 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs @@ -13,12 +13,11 @@ use hyperlane_core::{ SequenceIndexed, }; use itertools::Itertools; -use tracing::{debug, warn}; +use tracing::{debug, instrument, 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. @@ -43,6 +42,11 @@ pub(crate) struct ForwardSequenceAwareSyncCursor { } impl ForwardSequenceAwareSyncCursor { + #[instrument( + skip(db, latest_sequence_querier), + fields(chunk_size, next_sequence, start_block, index_mode), + ret + )] pub fn new( chunk_size: u32, latest_sequence_querier: Arc>, @@ -76,6 +80,7 @@ impl ForwardSequenceAwareSyncCursor { /// 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. + #[instrument(ret)] pub async fn get_next_range(&mut self) -> Result>> { // Skip any already indexed logs. self.skip_indexed().await?; @@ -386,6 +391,18 @@ impl ForwardSequenceAwareSyncCursor { } } +impl Debug for ForwardSequenceAwareSyncCursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ForwardSequenceAwareSyncCursor") + .field("chunk_size", &self.chunk_size) + .field("current_indexing_snapshot", &self.current_indexing_snapshot) + .field("last_indexed_snapshot", &self.last_indexed_snapshot) + .field("target_snapshot", &self.target_snapshot) + .field("index_mode", &self.index_mode) + .finish() + } +} + #[async_trait] impl ContractSyncCursor for ForwardSequenceAwareSyncCursor @@ -420,6 +437,7 @@ impl ContractSyncCursor /// - 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. + #[instrument(err, ret, skip(logs), fields(range=?range, logs=?logs.iter().map(|(log, _)| log.sequence).collect::>()))] async fn update( &mut self, logs: Vec<(Indexed, LogMeta)>, diff --git a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs index da61a26ce5..3d164ce269 100644 --- a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -23,6 +23,7 @@ const MESSAGE_DISPATCHED_BLOCK_NUMBER: &str = "message_dispatched_block_number_" const MESSAGE: &str = "message_"; const NONCE_PROCESSED: &str = "nonce_processed_"; const GAS_PAYMENT_BY_SEQUENCE: &str = "gas_payment_by_sequence_"; +const HIGHEST_SEEN_MESSAGE_NONCE: &str = "highest_seen_message_nonce_"; const GAS_PAYMENT_FOR_MESSAGE_ID: &str = "gas_payment_sequence_for_message_id_v2_"; const GAS_PAYMENT_META_PROCESSED: &str = "gas_payment_meta_processed_v3_"; const GAS_EXPENDITURE_FOR_MESSAGE_ID: &str = "gas_expenditure_for_message_id_v2_"; @@ -34,7 +35,8 @@ const MERKLE_TREE_INSERTION_BLOCK_NUMBER_BY_LEAF_INDEX: &str = "merkle_tree_insertion_block_number_by_leaf_index_"; const LATEST_INDEXED_GAS_PAYMENT_BLOCK: &str = "latest_indexed_gas_payment_block"; -type DbResult = std::result::Result; +/// Rocks DB result type +pub type DbResult = std::result::Result; /// DB handle for storing data tied to a specific Mailbox. #[derive(Debug, Clone)] @@ -94,6 +96,8 @@ impl HyperlaneRocksDB { self.store_message_by_id(&id, message)?; // - `nonce` --> `id` self.store_message_id_by_nonce(&message.nonce, &id)?; + // Update the max seen nonce to allow forward-backward iteration in the processor + self.try_update_max_seen_message_nonce(message.nonce)?; // - `nonce` --> `dispatched block number` self.store_dispatched_block_number_by_nonce(&message.nonce, &dispatched_block_number)?; Ok(true) @@ -108,6 +112,22 @@ impl HyperlaneRocksDB { } } + /// Update the nonce of the highest processed message we're aware of + pub fn try_update_max_seen_message_nonce(&self, nonce: u32) -> DbResult<()> { + let current_max = self + .retrieve_highest_seen_message_nonce()? + .unwrap_or_default(); + if nonce >= current_max { + self.store_highest_seen_message_nonce_number(&Default::default(), &nonce)?; + } + Ok(()) + } + + /// Retrieve the nonce of the highest processed message we're aware of + pub fn retrieve_highest_seen_message_nonce(&self) -> DbResult> { + self.retrieve_highest_seen_message_nonce_number(&Default::default()) + } + /// If the provided gas payment, identified by its metadata, has not been /// processed, processes the gas payment and records it as processed. /// Returns whether the gas payment was processed for the first time. @@ -416,6 +436,39 @@ impl HyperlaneWatermarkedLogStore for HyperlaneRocksDB { } } +/// Database interface required for processing messages +pub trait ProcessMessage: Send + Sync { + /// Retrieve the nonce of the highest processed message we're aware of + fn retrieve_highest_seen_message_nonce(&self) -> DbResult>; + + /// Retrieve a message by its nonce + fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult>; + + /// Retrieve whether a message has been processed + fn retrieve_processed_by_nonce(&self, nonce: u32) -> DbResult>; + + /// Get the origin domain of the database + fn domain(&self) -> &HyperlaneDomain; +} + +impl ProcessMessage for HyperlaneRocksDB { + fn retrieve_highest_seen_message_nonce(&self) -> DbResult> { + self.retrieve_highest_seen_message_nonce() + } + + fn retrieve_message_by_nonce(&self, nonce: u32) -> DbResult> { + self.retrieve_message_by_nonce(nonce) + } + + fn retrieve_processed_by_nonce(&self, nonce: u32) -> DbResult> { + self.retrieve_processed_by_nonce(&nonce) + } + + fn domain(&self) -> &HyperlaneDomain { + self.domain() + } +} + /// Generate a call to ChainSetup for the given builder macro_rules! make_store_and_retrieve { ($vis:vis, $name_suffix:ident, $key_prefix: ident, $key_ty:ty, $val_ty:ty$(,)?) => { @@ -479,3 +532,6 @@ make_store_and_retrieve!( u32, u64 ); +// There's no unit struct Encode/Decode impl, so just use `bool`, have visibility be private (by omitting the first argument), and wrap +// with a function that always uses the `Default::default()` key +make_store_and_retrieve!(, highest_seen_message_nonce_number, HIGHEST_SEEN_MESSAGE_NONCE, bool, u32); From b6b26e2bb8530565c4d87037420c8831b1820760 Mon Sep 17 00:00:00 2001 From: Connor McEwen Date: Sat, 25 May 2024 12:43:22 -0400 Subject: [PATCH 19/59] fix: minor change was breaking in registry export (#3821) ### Description Fixes compat with with breaking changes in registry 1.3.0 --------- Co-authored-by: Yorke Rhodes Co-authored-by: J M Rossy --- .changeset/many-rice-wave.md | 7 + typescript/cli/package.json | 2 +- typescript/cli/src/context/context.ts | 35 ++-- typescript/cli/src/deploy/core.ts | 12 +- typescript/cli/src/deploy/warp.ts | 6 +- typescript/cli/src/registry/MergedRegistry.ts | 156 ------------------ typescript/helloworld/package.json | 2 +- typescript/infra/config/registry.ts | 22 ++- typescript/infra/package.json | 2 +- yarn.lock | 14 +- 10 files changed, 70 insertions(+), 188 deletions(-) create mode 100644 .changeset/many-rice-wave.md delete mode 100644 typescript/cli/src/registry/MergedRegistry.ts diff --git a/.changeset/many-rice-wave.md b/.changeset/many-rice-wave.md new file mode 100644 index 0000000000..46b8eabc33 --- /dev/null +++ b/.changeset/many-rice-wave.md @@ -0,0 +1,7 @@ +--- +"@hyperlane-xyz/cli": patch +"@hyperlane-xyz/helloworld": patch +"@hyperlane-xyz/infra": patch +--- + +fix: minor change was breaking in registry export diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 47d0a1b11b..311f505858 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -5,7 +5,7 @@ "dependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", - "@hyperlane-xyz/registry": "^1.0.7", + "@hyperlane-xyz/registry": "1.3.0", "@hyperlane-xyz/sdk": "3.12.2", "@hyperlane-xyz/utils": "3.12.2", "@inquirer/prompts": "^3.0.0", diff --git a/typescript/cli/src/context/context.ts b/typescript/cli/src/context/context.ts index b153854df2..fba159a696 100644 --- a/typescript/cli/src/context/context.ts +++ b/typescript/cli/src/context/context.ts @@ -1,13 +1,17 @@ import { ethers } from 'ethers'; -import { IRegistry } from '@hyperlane-xyz/registry'; +import { + GithubRegistry, + IRegistry, + MergedRegistry, +} from '@hyperlane-xyz/registry'; +import { FileSystemRegistry } from '@hyperlane-xyz/registry/fs'; import { ChainName, MultiProvider } from '@hyperlane-xyz/sdk'; -import { isNullish } from '@hyperlane-xyz/utils'; +import { isHttpsUrl, isNullish, rootLogger } from '@hyperlane-xyz/utils'; import { isSignCommand } from '../commands/signCommands.js'; import { forkNetworkToMultiProvider, verifyAnvil } from '../deploy/dry-run.js'; import { logBlue } from '../logger.js'; -import { MergedRegistry } from '../registry/MergedRegistry.js'; import { runSingleChainSelectionStep } from '../utils/chains.js'; import { getImpersonatedSigner, getSigner } from '../utils/keys.js'; @@ -81,7 +85,7 @@ export async function getDryRunContext( }: ContextSettings, chain?: ChainName, ): Promise { - const registry = getRegistry(registryUri, registryOverrideUri, true); + const registry = getRegistry(registryUri, registryOverrideUri); const chainMetadata = await registry.getMetadata(); if (!chain) { @@ -127,14 +131,25 @@ export async function getDryRunContext( function getRegistry( primaryRegistryUri: string, overrideRegistryUri: string, - isDryRun?: boolean, ): IRegistry { - const registryUris = [primaryRegistryUri, overrideRegistryUri] - .map((r) => r.trim()) - .filter((r) => !!r); + const logger = rootLogger.child({ module: 'MergedRegistry' }); + const registries = [primaryRegistryUri, overrideRegistryUri] + .map((uri) => uri.trim()) + .filter((uri) => !!uri) + .map((uri, index) => { + const childLogger = logger.child({ uri, index }); + if (isHttpsUrl(uri)) { + return new GithubRegistry({ uri, logger: childLogger }); + } else { + return new FileSystemRegistry({ + uri, + logger: childLogger, + }); + } + }); return new MergedRegistry({ - registryUris, - isDryRun, + registries, + logger, }); } diff --git a/typescript/cli/src/deploy/core.ts b/typescript/cli/src/deploy/core.ts index f3da40e48b..8f8bf20439 100644 --- a/typescript/cli/src/deploy/core.ts +++ b/typescript/cli/src/deploy/core.ts @@ -258,6 +258,7 @@ async function executeDeploy({ registry, ismFactoryContracts, artifacts, + context.isDryRun, ); logGreen('ISM factory contracts deployed'); @@ -297,7 +298,12 @@ async function executeDeploy({ }; } artifacts = objMerge(artifacts, isms); - artifacts = await updateChainAddresses(registry, coreContracts, artifacts); + artifacts = await updateChainAddresses( + registry, + coreContracts, + artifacts, + context.isDryRun, + ); logGreen('✅ Core contracts deployed'); log(JSON.stringify(artifacts, null, 2)); @@ -395,6 +401,7 @@ async function updateChainAddresses( registry: IRegistry, newContracts: HyperlaneContractsMap, otherAddresses: HyperlaneAddressesMap, + isDryRun?: boolean, ) { let newAddresses = serializeContractsMap(newContracts); // The HyperlaneCoreDeployer is returning a nested object with ISM addresses @@ -407,6 +414,9 @@ async function updateChainAddresses( ); }); const mergedAddresses = objMerge(otherAddresses, newAddresses); + + if (isDryRun) return mergedAddresses; + for (const chainName of Object.keys(newContracts)) { await registry.updateChain({ chainName, diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index eb07f189af..0968f0d994 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -123,9 +123,11 @@ async function executeDeploy(params: DeployParams) { logGreen('✅ Hyp token deployments complete'); - if (!isDryRun) log('Writing deployment artifacts'); const warpCoreConfig = await getWarpCoreConfig(params, deployedContracts); - await registry.addWarpRoute(warpCoreConfig); + if (!isDryRun) { + log('Writing deployment artifacts'); + await registry.addWarpRoute(warpCoreConfig); + } log(JSON.stringify(warpCoreConfig, null, 2)); logBlue('Deployment is complete!'); } diff --git a/typescript/cli/src/registry/MergedRegistry.ts b/typescript/cli/src/registry/MergedRegistry.ts deleted file mode 100644 index fe6737cf83..0000000000 --- a/typescript/cli/src/registry/MergedRegistry.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { Logger } from 'pino'; - -import { - BaseRegistry, - ChainAddresses, - GithubRegistry, - IRegistry, - RegistryContent, - RegistryType, -} from '@hyperlane-xyz/registry'; -import { LocalRegistry } from '@hyperlane-xyz/registry/local'; -import { - ChainMap, - ChainMetadata, - ChainName, - WarpCoreConfig, -} from '@hyperlane-xyz/sdk'; -import { - isHttpsUrl, - objKeys, - objMerge, - rootLogger, -} from '@hyperlane-xyz/utils'; - -export interface MergedRegistryOptions { - registryUris: Array; - isDryRun?: boolean; - logger?: Logger; -} - -export class MergedRegistry extends BaseRegistry implements IRegistry { - public readonly type = RegistryType.Local; - public readonly registries: Array; - public readonly isDryRun: boolean; - - constructor({ registryUris, logger, isDryRun }: MergedRegistryOptions) { - logger ||= rootLogger.child({ module: 'MergedRegistry' }); - super({ uri: '__merged_registry__', logger }); - - if (!registryUris.length) - throw new Error('At least one registry URI is required'); - - this.registries = registryUris.map((uri, index) => { - if (isHttpsUrl(uri)) { - return new GithubRegistry({ uri, logger: logger!.child({ index }) }); - } else { - return new LocalRegistry({ uri, logger: logger!.child({ index }) }); - } - }); - - this.isDryRun = !!isDryRun; - } - - async listRegistryContent(): Promise { - const results = await this.multiRegistryRead((r) => - r.listRegistryContent(), - ); - return results.reduce((acc, content) => objMerge(acc, content), { - chains: {}, - deployments: {}, - }); - } - - async getChains(): Promise> { - return objKeys(await this.getMetadata); - } - - async getMetadata(): Promise> { - const results = await this.multiRegistryRead((r) => r.getMetadata()); - return results.reduce((acc, content) => objMerge(acc, content), {}); - } - - async getChainMetadata(chainName: ChainName): Promise { - return (await this.getMetadata())[chainName] || null; - } - - async getAddresses(): Promise> { - const results = await this.multiRegistryRead((r) => r.getAddresses()); - return results.reduce((acc, content) => objMerge(acc, content), {}); - } - - async getChainAddresses( - chainName: ChainName, - ): Promise { - return (await this.getAddresses())[chainName] || null; - } - - async addChain(chain: { - chainName: ChainName; - metadata?: ChainMetadata; - addresses?: ChainAddresses; - }): Promise { - return this.multiRegistryWrite( - async (registry) => await registry.addChain(chain), - `adding chain ${chain.chainName}`, - ); - } - - async updateChain(chain: { - chainName: ChainName; - metadata?: ChainMetadata; - addresses?: ChainAddresses; - }): Promise { - return this.multiRegistryWrite( - async (registry) => await registry.updateChain(chain), - `updating chain ${chain.chainName}`, - ); - } - - async removeChain(chain: ChainName): Promise { - return this.multiRegistryWrite( - async (registry) => await registry.removeChain(chain), - `removing chain ${chain}`, - ); - } - - async addWarpRoute(config: WarpCoreConfig): Promise { - return this.multiRegistryWrite( - async (registry) => await registry.addWarpRoute(config), - 'adding warp route', - ); - } - - protected multiRegistryRead( - readFn: (registry: IRegistry) => Promise | R, - ) { - return Promise.all(this.registries.map(readFn)); - } - - protected async multiRegistryWrite( - writeFn: (registry: IRegistry) => Promise, - logMsg: string, - ): Promise { - if (this.isDryRun) return; - for (const registry of this.registries) { - // TODO remove this when GithubRegistry supports write methods - if (registry.type === RegistryType.Github) { - this.logger.warn(`skipping ${logMsg} at ${registry.type} registry`); - continue; - } - try { - this.logger.info( - `${logMsg} at ${registry.type} registry at ${registry.uri}`, - ); - await writeFn(registry); - this.logger.info(`done ${logMsg} at ${registry.type} registry`); - } catch (error) { - // To prevent loss of artifacts, MergedRegistry write methods are failure tolerant - this.logger.error( - `failure ${logMsg} at ${registry.type} registry`, - error, - ); - } - } - } -} diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index f577cf8d88..64d3140aab 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -4,7 +4,7 @@ "version": "3.12.2", "dependencies": { "@hyperlane-xyz/core": "3.12.2", - "@hyperlane-xyz/registry": "^1.0.7", + "@hyperlane-xyz/registry": "1.3.0", "@hyperlane-xyz/sdk": "3.12.2", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" diff --git a/typescript/infra/config/registry.ts b/typescript/infra/config/registry.ts index 91abea01ce..a854880146 100644 --- a/typescript/infra/config/registry.ts +++ b/typescript/infra/config/registry.ts @@ -2,7 +2,7 @@ import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; import { ChainAddresses } from '@hyperlane-xyz/registry'; -import { LocalRegistry } from '@hyperlane-xyz/registry/local'; +import { FileSystemRegistry } from '@hyperlane-xyz/registry/fs'; import { ChainMap, ChainMetadata, @@ -10,7 +10,7 @@ import { getDomainId as resolveDomainId, getReorgPeriod as resolveReorgPeriod, } from '@hyperlane-xyz/sdk'; -import { objFilter, rootLogger } from '@hyperlane-xyz/utils'; +import { assert, objFilter, rootLogger } from '@hyperlane-xyz/utils'; import type { DeployEnvironment } from '../src/config/environment.js'; @@ -29,17 +29,17 @@ const DEFAULT_REGISTRY_URI = join( // A global Registry singleton // All uses of chain metadata or chain address artifacts should go through this registry. -let registry: LocalRegistry; +let registry: FileSystemRegistry; -export function setRegistry(reg: LocalRegistry) { +export function setRegistry(reg: FileSystemRegistry) { registry = reg; } -export function getRegistry(): LocalRegistry { +export function getRegistry(): FileSystemRegistry { if (!registry) { const registryUri = process.env.REGISTRY_URI || DEFAULT_REGISTRY_URI; rootLogger.info('Using registry URI:', registryUri); - registry = new LocalRegistry({ + registry = new FileSystemRegistry({ uri: registryUri, logger: rootLogger.child({ module: 'infra-registry' }), }); @@ -55,15 +55,19 @@ export function getChain(chainName: ChainName): ChainMetadata { if (testChains.includes(chainName)) { return testChainMetadata[chainName]; } - return getRegistry().getChainMetadata(chainName); + const chain = getRegistry().getChainMetadata(chainName); + assert(chain, `Chain not found: ${chainName}`); + return chain; } export function getDomainId(chainName: ChainName): number { - return resolveDomainId(getChain(chainName)); + const chain = getChain(chainName); + return resolveDomainId(chain); } export function getReorgPeriod(chainName: ChainName): number { - return resolveReorgPeriod(getChain(chainName)); + const chain = getChain(chainName); + return resolveReorgPeriod(chain); } export function getChainMetadata(): ChainMap { diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 428569c330..4f8fcb014d 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -13,7 +13,7 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@hyperlane-xyz/helloworld": "3.12.2", - "@hyperlane-xyz/registry": "^1.0.7", + "@hyperlane-xyz/registry": "1.3.0", "@hyperlane-xyz/sdk": "3.12.2", "@hyperlane-xyz/utils": "3.12.2", "@nomiclabs/hardhat-etherscan": "^3.0.3", diff --git a/yarn.lock b/yarn.lock index 009df5a75b..fd3c714eec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5690,7 +5690,7 @@ __metadata: dependencies: "@aws-sdk/client-kms": "npm:^3.577.0" "@aws-sdk/client-s3": "npm:^3.577.0" - "@hyperlane-xyz/registry": "npm:^1.0.7" + "@hyperlane-xyz/registry": "npm:1.3.0" "@hyperlane-xyz/sdk": "npm:3.12.2" "@hyperlane-xyz/utils": "npm:3.12.2" "@inquirer/prompts": "npm:^3.0.0" @@ -5778,7 +5778,7 @@ __metadata: resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: "@hyperlane-xyz/core": "npm:3.12.2" - "@hyperlane-xyz/registry": "npm:^1.0.7" + "@hyperlane-xyz/registry": "npm:1.3.0" "@hyperlane-xyz/sdk": "npm:3.12.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -5825,7 +5825,7 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" "@hyperlane-xyz/helloworld": "npm:3.12.2" - "@hyperlane-xyz/registry": "npm:^1.0.7" + "@hyperlane-xyz/registry": "npm:1.3.0" "@hyperlane-xyz/sdk": "npm:3.12.2" "@hyperlane-xyz/utils": "npm:3.12.2" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -5877,13 +5877,13 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/registry@npm:^1.0.7": - version: 1.0.7 - resolution: "@hyperlane-xyz/registry@npm:1.0.7" +"@hyperlane-xyz/registry@npm:1.3.0": + version: 1.3.0 + resolution: "@hyperlane-xyz/registry@npm:1.3.0" dependencies: yaml: "npm:^2" zod: "npm:^3.21.2" - checksum: fb112e2a1fdec539c6ef457ab44b9b5a835b719f2d4cc5cb0efb4413e2647402ed826d597f4c63b552f86fcf91bf69d4d1e5934aee8498f83dac61007a9d5650 + checksum: 2cbdfd9e8958d0babde7104dfb0c98def7edb5f87f5f4679b09467a6a9b531884f187fcbc16fd85b00e304ef8fa3beb0a0779555b2c3edc1936541a0e878a73d languageName: node linkType: hard From babe816f86a6efaefe6c89f626914bdd9a26c2d3 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Sun, 26 May 2024 12:04:31 -0400 Subject: [PATCH 20/59] feat: support xerc20 and xerc20Lockbox in CLI (#3753) ### Description - Supports xERC20 and xERC20Lockbox in CLI and UI ### Drive-by changes - Rename XERC20Collateral to XERC20 ### Backward compatibility - Yes ### Testing - Manual --- .changeset/perfect-seahorses-add.md | 7 +++ solidity/.solhint.json | 1 - solidity/.solhintignore | 1 + solidity/contracts/test/ERC20Test.sol | 12 ++++ ...atTokenCollateral.sol => HypFiatToken.sol} | 2 +- ...{HypXERC20Collateral.sol => HypXERC20.sol} | 2 +- .../token/extensions/HypXERC20Lockbox.sol | 54 ++++++++++++++++ .../contracts/token/interfaces/IXERC20.sol | 15 +++++ .../token/interfaces/IXERC20Lockbox.sol | 61 +++++++++++++++++++ solidity/test/token/HypERC20.t.sol | 22 +++---- typescript/cli/src/config/warp.ts | 7 ++- typescript/sdk/src/token/Token.test.ts | 27 -------- typescript/sdk/src/token/Token.ts | 7 +-- typescript/sdk/src/token/TokenStandard.ts | 17 ++---- typescript/sdk/src/token/config.ts | 3 +- typescript/sdk/src/token/contracts.ts | 15 +++-- typescript/sdk/src/token/schemas.ts | 7 ++- 17 files changed, 188 insertions(+), 72 deletions(-) create mode 100644 .changeset/perfect-seahorses-add.md rename solidity/contracts/token/extensions/{HypFiatTokenCollateral.sol => HypFiatToken.sol} (94%) rename solidity/contracts/token/extensions/{HypXERC20Collateral.sol => HypXERC20.sol} (93%) create mode 100644 solidity/contracts/token/extensions/HypXERC20Lockbox.sol create mode 100644 solidity/contracts/token/interfaces/IXERC20Lockbox.sol diff --git a/.changeset/perfect-seahorses-add.md b/.changeset/perfect-seahorses-add.md new file mode 100644 index 0000000000..372ca45e87 --- /dev/null +++ b/.changeset/perfect-seahorses-add.md @@ -0,0 +1,7 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/sdk': minor +'@hyperlane-xyz/core': minor +--- + +Support xERC20 and xERC20 Lockbox in SDK and CLI diff --git a/solidity/.solhint.json b/solidity/.solhint.json index a4cc08a3bc..177b098d2f 100644 --- a/solidity/.solhint.json +++ b/solidity/.solhint.json @@ -11,7 +11,6 @@ "func-name-mixedcase": "off", "reason-string": ["warn",{"maxLength":64}], "prettier/prettier": "error", - "custom-errors": "off", "gas-custom-errors": "off" }, "plugins": ["prettier"] diff --git a/solidity/.solhintignore b/solidity/.solhintignore index 9a4af1ecb1..a1c868d03f 100644 --- a/solidity/.solhintignore +++ b/solidity/.solhintignore @@ -1,2 +1,3 @@ contracts/mock contracts/test +contracts/interfaces/avs/vendored diff --git a/solidity/contracts/test/ERC20Test.sol b/solidity/contracts/test/ERC20Test.sol index 87f38420e5..8d4580c248 100644 --- a/solidity/contracts/test/ERC20Test.sol +++ b/solidity/contracts/test/ERC20Test.sol @@ -65,4 +65,16 @@ contract XERC20Test is ERC20Test, IXERC20 { function burn(address account, uint256 amount) public override { _burn(account, amount); } + + function setLimits( + address _bridge, + uint256 _mintingLimit, + uint256 _burningLimit + ) external { + require(false); + } + + function owner() external returns (address) { + return address(0x0); + } } diff --git a/solidity/contracts/token/extensions/HypFiatTokenCollateral.sol b/solidity/contracts/token/extensions/HypFiatToken.sol similarity index 94% rename from solidity/contracts/token/extensions/HypFiatTokenCollateral.sol rename to solidity/contracts/token/extensions/HypFiatToken.sol index ab043e6413..c31351abcf 100644 --- a/solidity/contracts/token/extensions/HypFiatTokenCollateral.sol +++ b/solidity/contracts/token/extensions/HypFiatToken.sol @@ -5,7 +5,7 @@ import {IFiatToken} from "../interfaces/IFiatToken.sol"; import {HypERC20Collateral} from "../HypERC20Collateral.sol"; // see https://github.com/circlefin/stablecoin-evm/blob/master/doc/tokendesign.md#issuing-and-destroying-tokens -contract HypFiatTokenCollateral is HypERC20Collateral { +contract HypFiatToken is HypERC20Collateral { constructor( address _fiatToken, address _mailbox diff --git a/solidity/contracts/token/extensions/HypXERC20Collateral.sol b/solidity/contracts/token/extensions/HypXERC20.sol similarity index 93% rename from solidity/contracts/token/extensions/HypXERC20Collateral.sol rename to solidity/contracts/token/extensions/HypXERC20.sol index f58b526ba3..9f50b4537f 100644 --- a/solidity/contracts/token/extensions/HypXERC20Collateral.sol +++ b/solidity/contracts/token/extensions/HypXERC20.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.0; import {IXERC20} from "../interfaces/IXERC20.sol"; import {HypERC20Collateral} from "../HypERC20Collateral.sol"; -contract HypXERC20Collateral is HypERC20Collateral { +contract HypXERC20 is HypERC20Collateral { constructor( address _xerc20, address _mailbox diff --git a/solidity/contracts/token/extensions/HypXERC20Lockbox.sol b/solidity/contracts/token/extensions/HypXERC20Lockbox.sol new file mode 100644 index 0000000000..f4a8609179 --- /dev/null +++ b/solidity/contracts/token/extensions/HypXERC20Lockbox.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import {IXERC20Lockbox} from "../interfaces/IXERC20Lockbox.sol"; +import {IXERC20, IERC20} from "../interfaces/IXERC20.sol"; +import {HypERC20Collateral} from "../HypERC20Collateral.sol"; + +contract HypXERC20Lockbox is HypERC20Collateral { + uint256 constant MAX_INT = 2 ** 256 - 1; + + IXERC20Lockbox public immutable lockbox; + IXERC20 public immutable xERC20; + + constructor( + address _lockbox, + address _mailbox + ) HypERC20Collateral(address(IXERC20Lockbox(_lockbox).ERC20()), _mailbox) { + lockbox = IXERC20Lockbox(_lockbox); + xERC20 = lockbox.XERC20(); + + // grant infinite approvals to lockbox + require( + IERC20(wrappedToken).approve(_lockbox, MAX_INT), + "erc20 lockbox approve failed" + ); + require( + xERC20.approve(_lockbox, MAX_INT), + "xerc20 lockbox approve failed" + ); + } + + function _transferFromSender( + uint256 _amount + ) internal override returns (bytes memory) { + // transfer erc20 from sender + super._transferFromSender(_amount); + // convert erc20 to xERC20 + lockbox.deposit(_amount); + // burn xERC20 + xERC20.burn(address(this), _amount); + return bytes(""); + } + + function _transferTo( + address _recipient, + uint256 _amount, + bytes calldata /*metadata*/ + ) internal override { + // mint xERC20 + xERC20.mint(address(this), _amount); + // convert xERC20 to erc20 + lockbox.withdrawTo(_recipient, _amount); + } +} diff --git a/solidity/contracts/token/interfaces/IXERC20.sol b/solidity/contracts/token/interfaces/IXERC20.sol index 3f63c477a4..2c9bad49a5 100644 --- a/solidity/contracts/token/interfaces/IXERC20.sol +++ b/solidity/contracts/token/interfaces/IXERC20.sol @@ -21,4 +21,19 @@ interface IXERC20 is IERC20 { * @param _amount The amount of tokens being burned */ function burn(address _user, uint256 _amount) external; + + /** + * @notice Updates the limits of any bridge + * @dev Can only be called by the owner + * @param _mintingLimit The updated minting limit we are setting to the bridge + * @param _burningLimit The updated burning limit we are setting to the bridge + * @param _bridge The address of the bridge we are setting the limits too + */ + function setLimits( + address _bridge, + uint256 _mintingLimit, + uint256 _burningLimit + ) external; + + function owner() external returns (address); } diff --git a/solidity/contracts/token/interfaces/IXERC20Lockbox.sol b/solidity/contracts/token/interfaces/IXERC20Lockbox.sol new file mode 100644 index 0000000000..ba01f7bc4c --- /dev/null +++ b/solidity/contracts/token/interfaces/IXERC20Lockbox.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.4 <0.9.0; + +// adapted from https://github.com/defi-wonderland/xERC20 + +import {IXERC20} from "./IXERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IXERC20Lockbox { + /** + * @notice The XERC20 token of this contract + */ + function XERC20() external returns (IXERC20); + + /** + * @notice The ERC20 token of this contract + */ + function ERC20() external returns (IERC20); + + /** + * @notice Deposit ERC20 tokens into the lockbox + * + * @param _amount The amount of tokens to deposit + */ + + function deposit(uint256 _amount) external; + + /** + * @notice Deposit ERC20 tokens into the lockbox, and send the XERC20 to a user + * + * @param _user The user to send the XERC20 to + * @param _amount The amount of tokens to deposit + */ + + function depositTo(address _user, uint256 _amount) external; + + /** + * @notice Deposit the native asset into the lockbox, and send the XERC20 to a user + * + * @param _user The user to send the XERC20 to + */ + + function depositNativeTo(address _user) external payable; + + /** + * @notice Withdraw ERC20 tokens from the lockbox + * + * @param _amount The amount of tokens to withdraw + */ + + function withdraw(uint256 _amount) external; + + /** + * @notice Withdraw ERC20 tokens from the lockbox + * + * @param _user The user to withdraw to + * @param _amount The amount of tokens to withdraw + */ + + function withdrawTo(address _user, uint256 _amount) external; +} diff --git a/solidity/test/token/HypERC20.t.sol b/solidity/test/token/HypERC20.t.sol index 5be0bd5ed4..82c5359b7d 100644 --- a/solidity/test/token/HypERC20.t.sol +++ b/solidity/test/token/HypERC20.t.sol @@ -28,8 +28,8 @@ import {HypERC20} from "../../contracts/token/HypERC20.sol"; import {HypERC20Collateral} from "../../contracts/token/HypERC20Collateral.sol"; import {IXERC20} from "../../contracts/token/interfaces/IXERC20.sol"; import {IFiatToken} from "../../contracts/token/interfaces/IFiatToken.sol"; -import {HypXERC20Collateral} from "../../contracts/token/extensions/HypXERC20Collateral.sol"; -import {HypFiatTokenCollateral} from "../../contracts/token/extensions/HypFiatTokenCollateral.sol"; +import {HypXERC20} from "../../contracts/token/extensions/HypXERC20.sol"; +import {HypFiatToken} from "../../contracts/token/extensions/HypFiatToken.sol"; import {HypNative} from "../../contracts/token/HypNative.sol"; import {TokenRouter} from "../../contracts/token/libs/TokenRouter.sol"; import {TokenMessage} from "../../contracts/token/libs/TokenMessage.sol"; @@ -394,20 +394,20 @@ contract HypERC20CollateralTest is HypTokenTest { } } -contract HypXERC20CollateralTest is HypTokenTest { +contract HypXERC20Test is HypTokenTest { using TypeCasts for address; - HypXERC20Collateral internal xerc20Collateral; + HypXERC20 internal xerc20Collateral; function setUp() public override { super.setUp(); primaryToken = new XERC20Test(NAME, SYMBOL, TOTAL_SUPPLY, DECIMALS); - localToken = new HypXERC20Collateral( + localToken = new HypXERC20( address(primaryToken), address(localMailbox) ); - xerc20Collateral = HypXERC20Collateral(address(localToken)); + xerc20Collateral = HypXERC20(address(localToken)); xerc20Collateral.enrollRemoteRouter( DESTINATION, @@ -442,22 +442,22 @@ contract HypXERC20CollateralTest is HypTokenTest { } } -contract HypFiatTokenCollateralTest is HypTokenTest { +contract HypFiatTokenTest is HypTokenTest { using TypeCasts for address; - HypFiatTokenCollateral internal fiatTokenCollateral; + HypFiatToken internal fiatToken; function setUp() public override { super.setUp(); primaryToken = new FiatTokenTest(NAME, SYMBOL, TOTAL_SUPPLY, DECIMALS); - localToken = new HypFiatTokenCollateral( + localToken = new HypFiatToken( address(primaryToken), address(localMailbox) ); - fiatTokenCollateral = HypFiatTokenCollateral(address(localToken)); + fiatToken = HypFiatToken(address(localToken)); - fiatTokenCollateral.enrollRemoteRouter( + fiatToken.enrollRemoteRouter( DESTINATION, address(remoteToken).addressToBytes32() ); diff --git a/typescript/cli/src/config/warp.ts b/typescript/cli/src/config/warp.ts index 189d5198cf..d9ca9b4e5d 100644 --- a/typescript/cli/src/config/warp.ts +++ b/typescript/cli/src/config/warp.ts @@ -29,8 +29,10 @@ const TYPE_DESCRIPTIONS: Record = { 'Extends an existing ERC4626 with remote transfer functionality', [TokenType.collateralFiat]: 'Extends an existing FiatToken with remote transfer functionality', - [TokenType.collateralXERC20]: + [TokenType.XERC20]: 'Extends an existing xERC20 with Warp Route functionality', + [TokenType.XERC20Lockbox]: + 'Extends an existing xERC20 Lockbox with Warp Route functionality', // TODO: describe [TokenType.fastSynthetic]: '', [TokenType.syntheticUri]: '', @@ -132,7 +134,8 @@ export async function createWarpRouteDeployConfig({ switch (type) { case TokenType.collateral: - case TokenType.collateralXERC20: + case TokenType.XERC20: + case TokenType.XERC20Lockbox: case TokenType.collateralFiat: case TokenType.collateralUri: case TokenType.fastCollateral: diff --git a/typescript/sdk/src/token/Token.test.ts b/typescript/sdk/src/token/Token.test.ts index 3a1e6414d1..9cd97c9195 100644 --- a/typescript/sdk/src/token/Token.test.ts +++ b/typescript/sdk/src/token/Token.test.ts @@ -47,33 +47,6 @@ const STANDARD_TO_TOKEN: Record = { symbol: 'USDC', name: 'USDC', }, - [TokenStandard.EvmHypXERC20Collateral]: { - chainName: TestChainName.test3, - standard: TokenStandard.EvmHypXERC20Collateral, - addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131', - collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930', - decimals: 18, - symbol: 'USDC', - name: 'USDC', - }, - [TokenStandard.EvmHypFiatCollateral]: { - chainName: TestChainName.test3, - standard: TokenStandard.EvmHypXERC20Collateral, - addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131', - collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930', - decimals: 18, - symbol: 'USDC', - name: 'USDC', - }, - [TokenStandard.EvmHypCollateralVault]: { - chainName: TestChainName.test3, - standard: TokenStandard.EvmHypCollateral, - addressOrDenom: '0x31b5234A896FbC4b3e2F7237592D054716762131', - collateralAddressOrDenom: '0x64544969ed7ebf5f083679233325356ebe738930', - decimals: 18, - symbol: 'USDC', - name: 'USDC', - }, [TokenStandard.EvmHypSynthetic]: { chainName: TestChainName.test2, standard: TokenStandard.EvmHypSynthetic, diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts index 5c0e0af649..7ddc870e57 100644 --- a/typescript/sdk/src/token/Token.ts +++ b/typescript/sdk/src/token/Token.ts @@ -205,12 +205,7 @@ export class Token implements IToken { return new EvmHypNativeAdapter(chainName, multiProvider, { token: addressOrDenom, }); - } else if ( - standard === TokenStandard.EvmHypCollateral || - standard === TokenStandard.EvmHypCollateralVault || - standard === TokenStandard.EvmHypXERC20Collateral || - standard === TokenStandard.EvmHypFiatCollateral - ) { + } else if (standard === TokenStandard.EvmHypCollateral) { return new EvmHypCollateralAdapter(chainName, multiProvider, { token: addressOrDenom, }); diff --git a/typescript/sdk/src/token/TokenStandard.ts b/typescript/sdk/src/token/TokenStandard.ts index d1394d49c9..8bdd0defc6 100644 --- a/typescript/sdk/src/token/TokenStandard.ts +++ b/typescript/sdk/src/token/TokenStandard.ts @@ -14,9 +14,6 @@ export enum TokenStandard { EvmNative = 'EvmNative', EvmHypNative = 'EvmHypNative', EvmHypCollateral = 'EvmHypCollateral', - EvmHypXERC20Collateral = 'EvmHypXERC20Collateral', - EvmHypFiatCollateral = 'EvmHypFiatCollateral', - EvmHypCollateralVault = 'EvmHypCollateralVault', EvmHypSynthetic = 'EvmHypSynthetic', // Sealevel (Solana) @@ -50,10 +47,7 @@ export const TOKEN_STANDARD_TO_PROTOCOL: Record = { EvmNative: ProtocolType.Ethereum, EvmHypNative: ProtocolType.Ethereum, EvmHypCollateral: ProtocolType.Ethereum, - EvmHypCollateralVault: ProtocolType.Ethereum, EvmHypSynthetic: ProtocolType.Ethereum, - EvmHypXERC20Collateral: ProtocolType.Ethereum, - EvmHypFiatCollateral: ProtocolType.Ethereum, // Sealevel (Solana) SealevelSpl: ProtocolType.Sealevel, @@ -96,8 +90,6 @@ export const TOKEN_NFT_STANDARDS = [ export const TOKEN_COLLATERALIZED_STANDARDS = [ TokenStandard.EvmHypCollateral, TokenStandard.EvmHypNative, - TokenStandard.EvmHypXERC20Collateral, - TokenStandard.EvmHypFiatCollateral, TokenStandard.SealevelHypCollateral, TokenStandard.SealevelHypNative, TokenStandard.CwHypCollateral, @@ -107,8 +99,6 @@ export const TOKEN_COLLATERALIZED_STANDARDS = [ export const TOKEN_HYP_STANDARDS = [ TokenStandard.EvmHypNative, TokenStandard.EvmHypCollateral, - TokenStandard.EvmHypXERC20Collateral, - TokenStandard.EvmHypFiatCollateral, TokenStandard.EvmHypSynthetic, TokenStandard.SealevelHypNative, TokenStandard.SealevelHypCollateral, @@ -137,9 +127,10 @@ export const TOKEN_COSMWASM_STANDARDS = [ export const TOKEN_TYPE_TO_STANDARD: Record = { [TokenType.native]: TokenStandard.EvmHypNative, [TokenType.collateral]: TokenStandard.EvmHypCollateral, - [TokenType.collateralFiat]: TokenStandard.EvmHypFiatCollateral, - [TokenType.collateralXERC20]: TokenStandard.EvmHypXERC20Collateral, - [TokenType.collateralVault]: TokenStandard.EvmHypCollateralVault, + [TokenType.collateralFiat]: TokenStandard.EvmHypCollateral, + [TokenType.XERC20]: TokenStandard.EvmHypCollateral, + [TokenType.XERC20Lockbox]: TokenStandard.EvmHypCollateral, + [TokenType.collateralVault]: TokenStandard.EvmHypCollateral, [TokenType.collateralUri]: TokenStandard.EvmHypCollateral, [TokenType.fastCollateral]: TokenStandard.EvmHypCollateral, [TokenType.synthetic]: TokenStandard.EvmHypSynthetic, diff --git a/typescript/sdk/src/token/config.ts b/typescript/sdk/src/token/config.ts index 59a2e597dc..a68b2a5a9c 100644 --- a/typescript/sdk/src/token/config.ts +++ b/typescript/sdk/src/token/config.ts @@ -4,7 +4,8 @@ export enum TokenType { syntheticUri = 'syntheticUri', collateral = 'collateral', collateralVault = 'collateralVault', - collateralXERC20 = 'collateralXERC20', + XERC20 = 'xERC20', + XERC20Lockbox = 'xERC20Lockbox', collateralFiat = 'collateralFiat', fastCollateral = 'fastCollateral', collateralUri = 'collateralUri', diff --git a/typescript/sdk/src/token/contracts.ts b/typescript/sdk/src/token/contracts.ts index 57e45a7bd5..d9982522c2 100644 --- a/typescript/sdk/src/token/contracts.ts +++ b/typescript/sdk/src/token/contracts.ts @@ -8,10 +8,11 @@ import { HypERC721URICollateral__factory, HypERC721URIStorage__factory, HypERC721__factory, - HypFiatTokenCollateral__factory, + HypFiatToken__factory, HypNativeScaled__factory, HypNative__factory, - HypXERC20Collateral__factory, + HypXERC20Lockbox__factory, + HypXERC20__factory, } from '@hyperlane-xyz/core'; import { TokenType } from './config.js'; @@ -21,8 +22,9 @@ export const hypERC20contracts = { [TokenType.fastSynthetic]: 'FastHypERC20', [TokenType.synthetic]: 'HypERC20', [TokenType.collateral]: 'HypERC20Collateral', - [TokenType.collateralFiat]: 'HypFiatTokenCollateral', - [TokenType.collateralXERC20]: 'HypXERC20Collateral', + [TokenType.collateralFiat]: 'HypFiatToken', + [TokenType.XERC20]: 'HypXERC20', + [TokenType.XERC20Lockbox]: 'HypXERC20Lockbox', [TokenType.collateralVault]: 'HypERC20CollateralVaultDeposit', [TokenType.native]: 'HypNative', [TokenType.nativeScaled]: 'HypNativeScaled', @@ -35,8 +37,9 @@ export const hypERC20factories = { [TokenType.synthetic]: new HypERC20__factory(), [TokenType.collateral]: new HypERC20Collateral__factory(), [TokenType.collateralVault]: new HypERC20CollateralVaultDeposit__factory(), - [TokenType.collateralFiat]: new HypFiatTokenCollateral__factory(), - [TokenType.collateralXERC20]: new HypXERC20Collateral__factory(), + [TokenType.collateralFiat]: new HypFiatToken__factory(), + [TokenType.XERC20]: new HypXERC20__factory(), + [TokenType.XERC20Lockbox]: new HypXERC20Lockbox__factory(), [TokenType.native]: new HypNative__factory(), [TokenType.nativeScaled]: new HypNativeScaled__factory(), }; diff --git a/typescript/sdk/src/token/schemas.ts b/typescript/sdk/src/token/schemas.ts index 3ec33bf8f3..8ce070c1e5 100644 --- a/typescript/sdk/src/token/schemas.ts +++ b/typescript/sdk/src/token/schemas.ts @@ -17,11 +17,12 @@ export const TokenMetadataSchema = z.object({ export const CollateralConfigSchema = TokenMetadataSchema.partial().extend({ type: z.enum([ TokenType.collateral, - TokenType.collateralXERC20, + TokenType.collateralVault, + TokenType.XERC20, + TokenType.XERC20Lockbox, TokenType.collateralFiat, - TokenType.collateralUri, TokenType.fastCollateral, - TokenType.collateralVault, + TokenType.collateralUri, ]), token: z .string() From 0cf692e7315cb91059a4c0b39c7cdf76151851cc Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Sun, 26 May 2024 12:44:19 -0400 Subject: [PATCH 21/59] Implement metadata fetching from message (#3702) ### Description - Uses derived hook and ISM config and dispatchTx of message to implement metadata fetching ### Drive-by changes - Change yarn cache key to workaround https://github.com/actions/toolkit/issues/658 - Make `hyperlane message send` use `HyperlaneCore.sendMessage` ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3450 ### Backward compatibility Yes ### Testing E2E testing BaseMetadataBuilder --- .changeset/witty-vans-return.md | 7 + .../infra/scripts/announce-validators.ts | 18 +- .../list-validator-checkpoint-indices.ts | 4 +- typescript/infra/scripts/verify-validators.ts | 8 +- typescript/infra/src/agents/aws/validator.ts | 75 +------ .../infra/src/config/agent/validator.ts | 7 +- typescript/sdk/package.json | 1 + .../{infra/src/agents => sdk/src}/aws/s3.ts | 51 +++-- typescript/sdk/src/aws/validator.ts | 101 +++++++++ .../sdk/src/core/CoreDeployer.hardhat-test.ts | 6 +- typescript/sdk/src/core/HyperlaneCore.ts | 66 +++++- typescript/sdk/src/hook/EvmHookReader.ts | 5 +- typescript/sdk/src/index.ts | 2 + typescript/sdk/src/ism/EvmIsmReader.ts | 32 ++- .../ism/HyperlaneIsmFactory.hardhat-test.ts | 47 +++-- .../sdk/src/ism/metadata/aggregation.test.ts | 24 ++- .../sdk/src/ism/metadata/aggregation.ts | 103 +++++++-- .../src/ism/metadata/builder.hardhat-test.ts | 180 ++++++++++++++++ typescript/sdk/src/ism/metadata/builder.ts | 133 ++++++++++++ .../sdk/src/ism/metadata/multisig.test.ts | 8 +- typescript/sdk/src/ism/metadata/multisig.ts | 198 +++++++++++++++++- typescript/sdk/src/ism/metadata/null.ts | 35 ++++ typescript/sdk/src/ism/metadata/routing.ts | 58 +++++ typescript/sdk/src/ism/types.ts | 13 +- typescript/sdk/src/test/testUtils.ts | 4 - typescript/utils/src/arrays.ts | 6 + typescript/utils/src/index.ts | 14 +- typescript/utils/src/math.ts | 4 + typescript/utils/src/objects.ts | 18 ++ typescript/utils/src/types.ts | 42 ++-- typescript/utils/src/validator.ts | 66 ++++-- yarn.lock | 1 + 32 files changed, 1106 insertions(+), 231 deletions(-) create mode 100644 .changeset/witty-vans-return.md rename typescript/{infra/src/agents => sdk/src}/aws/s3.ts (53%) create mode 100644 typescript/sdk/src/aws/validator.ts create mode 100644 typescript/sdk/src/ism/metadata/builder.hardhat-test.ts create mode 100644 typescript/sdk/src/ism/metadata/builder.ts create mode 100644 typescript/sdk/src/ism/metadata/null.ts create mode 100644 typescript/sdk/src/ism/metadata/routing.ts diff --git a/.changeset/witty-vans-return.md b/.changeset/witty-vans-return.md new file mode 100644 index 0000000000..2ed82ae27d --- /dev/null +++ b/.changeset/witty-vans-return.md @@ -0,0 +1,7 @@ +--- +'@hyperlane-xyz/infra': minor +'@hyperlane-xyz/utils': minor +'@hyperlane-xyz/sdk': minor +--- + +Implement metadata builder fetching from message diff --git a/typescript/infra/scripts/announce-validators.ts b/typescript/infra/scripts/announce-validators.ts index 05250bad83..09748de985 100644 --- a/typescript/infra/scripts/announce-validators.ts +++ b/typescript/infra/scripts/announce-validators.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import { ChainName } from '@hyperlane-xyz/sdk'; import { getChains } from '../config/registry.js'; -import { S3Validator } from '../src/agents/aws/validator.js'; +import { InfraS3Validator } from '../src/agents/aws/validator.js'; import { CheckpointSyncerType } from '../src/config/agent/validator.js'; import { isEthereumProtocolChain } from '../src/utils/utils.js'; @@ -47,7 +47,7 @@ async function main() { chains.push(chain!); if (location.startsWith('s3://')) { - const validator = await S3Validator.fromStorageLocation(location); + const validator = await InfraS3Validator.fromStorageLocation(location); announcements.push({ storageLocation: validator.storageLocation(), announcement: await validator.getAnnouncement(), @@ -87,13 +87,13 @@ async function main() { ) { const contracts = core.getContracts(validatorChain); const localDomain = multiProvider.getDomainId(validatorChain); - const validator = new S3Validator( - validatorBaseConfig.address, - localDomain, - contracts.mailbox.address, - validatorBaseConfig.checkpointSyncer.bucket, - validatorBaseConfig.checkpointSyncer.region, - undefined, + const validator = new InfraS3Validator( + { + localDomain, + address: validatorBaseConfig.address, + mailbox: contracts.mailbox.address, + }, + validatorBaseConfig.checkpointSyncer, ); announcements.push({ storageLocation: validator.storageLocation(), diff --git a/typescript/infra/scripts/list-validator-checkpoint-indices.ts b/typescript/infra/scripts/list-validator-checkpoint-indices.ts index ca539be797..f3cfc6c1cc 100644 --- a/typescript/infra/scripts/list-validator-checkpoint-indices.ts +++ b/typescript/infra/scripts/list-validator-checkpoint-indices.ts @@ -1,6 +1,6 @@ import { concurrentMap } from '@hyperlane-xyz/utils'; -import { S3Validator } from '../src/agents/aws/validator.js'; +import { InfraS3Validator } from '../src/agents/aws/validator.js'; import { getArgs, getValidatorsByChain } from './agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; @@ -26,7 +26,7 @@ async function main() { let identifier = validator; if (storageLocations.length == 1 && storageLocations[0].length == 1) { try { - const s3Validator = await S3Validator.fromStorageLocation( + const s3Validator = await InfraS3Validator.fromStorageLocation( storageLocations[0][0], ); identifier = storageLocations[0][0]; diff --git a/typescript/infra/scripts/verify-validators.ts b/typescript/infra/scripts/verify-validators.ts index 755d568a97..561ef2a5c4 100644 --- a/typescript/infra/scripts/verify-validators.ts +++ b/typescript/infra/scripts/verify-validators.ts @@ -1,6 +1,6 @@ import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; -import { S3Validator } from '../src/agents/aws/validator.js'; +import { InfraS3Validator } from '../src/agents/aws/validator.js'; import { getArgs, getValidatorsByChain } from './agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; @@ -21,20 +21,20 @@ async function main() { if (storageLocations[i].length != 1) { throw new Error('Only support single announcement'); } - return S3Validator.fromStorageLocation(storageLocations[i][0]); + return InfraS3Validator.fromStorageLocation(storageLocations[i][0]); }), ); const controlValidator = validators[0]; await Promise.all( validators.slice(1).map(async (prospectiveValidator) => { const address = prospectiveValidator.address; - const bucket = prospectiveValidator.s3Bucket.bucket; + const bucket = prospectiveValidator.s3Bucket; try { const metrics = await prospectiveValidator.compare( controlValidator, ); console.log( - `${chain} ${bucket} validators against control ${controlValidator.s3Bucket.bucket}`, + `${chain} ${bucket} validators against control ${controlValidator.s3Bucket}`, ); console.table(metrics); } catch (error) { diff --git a/typescript/infra/src/agents/aws/validator.ts b/typescript/infra/src/agents/aws/validator.ts index 2c6b48c607..398190a9c1 100644 --- a/typescript/infra/src/agents/aws/validator.ts +++ b/typescript/infra/src/agents/aws/validator.ts @@ -1,5 +1,5 @@ +import { S3Receipt, S3Validator } from '@hyperlane-xyz/sdk'; import { - BaseValidator, Checkpoint, HexString, S3Checkpoint, @@ -8,8 +8,6 @@ import { isS3CheckpointWithId, } from '@hyperlane-xyz/utils'; -import { S3Receipt, S3Wrapper } from './s3.js'; - export enum CheckpointStatus { EXTRA = '➕', MISSING = '❓', @@ -35,71 +33,22 @@ type S3CheckpointReceipt = S3Receipt; const checkpointWithMessageIdKey = (checkpointIndex: number) => `checkpoint_${checkpointIndex}_with_id.json`; const LATEST_KEY = 'checkpoint_latest_index.json'; -const ANNOUNCEMENT_KEY = 'announcement.json'; -const LOCATION_PREFIX = 's3://'; /** * Extension of BaseValidator that includes AWS S3 utilities. */ -export class S3Validator extends BaseValidator { - s3Bucket: S3Wrapper; - - constructor( - address: string, - localDomain: number, - mailbox: string, - s3Bucket: string, - s3Region: string, - s3Folder: string | undefined, - ) { - super(address, localDomain, mailbox); - this.s3Bucket = new S3Wrapper(s3Bucket, s3Region, s3Folder); - } - +export class InfraS3Validator extends S3Validator { static async fromStorageLocation( storageLocation: string, - ): Promise { - if (storageLocation.startsWith(LOCATION_PREFIX)) { - const suffix = storageLocation.slice(LOCATION_PREFIX.length); - const pieces = suffix.split('/'); - if (pieces.length >= 2) { - const s3Bucket = new S3Wrapper(pieces[0], pieces[1], pieces[2]); - const announcement = await s3Bucket.getS3Obj(ANNOUNCEMENT_KEY); - const address = announcement?.data.value.validator; - const mailbox = announcement?.data.value.mailbox_address; - const localDomain = announcement?.data.value.mailbox_domain; - - return new S3Validator( - address, - localDomain, - mailbox, - pieces[0], - pieces[1], - pieces[2], - ); - } - } - throw new Error(`Unable to parse location ${storageLocation}`); + ): Promise { + const inner = await S3Validator.fromStorageLocation(storageLocation); + return new InfraS3Validator(inner.validatorConfig, inner.s3Config); } - async getAnnouncement(): Promise { - const data = await this.s3Bucket.getS3Obj(ANNOUNCEMENT_KEY); - if (data) { - return data.data; - } - } - - async getLatestCheckpointIndex() { - const latestCheckpointIndex = await this.s3Bucket.getS3Obj( - LATEST_KEY, - ); - - if (!latestCheckpointIndex) return -1; - - return latestCheckpointIndex.data; - } - - async compare(other: S3Validator, count = 5): Promise { + async compare( + other: InfraS3Validator, + count = 5, + ): Promise { const latestCheckpointIndex = await this.s3Bucket.getS3Obj( LATEST_KEY, ); @@ -153,7 +102,7 @@ export class S3Validator extends BaseValidator { actual.data.messageId, ) ) { - const signerAddress = this.recoverAddressFromCheckpoint( + const signerAddress = InfraS3Validator.recoverAddressFromCheckpoint( actual.data.checkpoint, actual.data.signature, actual.data.messageId, @@ -196,10 +145,6 @@ export class S3Validator extends BaseValidator { return checkpointMetrics.slice(-1 * count); } - storageLocation(): string { - return `${LOCATION_PREFIX}/${this.s3Bucket.bucket}/${this.s3Bucket.region}`; - } - private async getCheckpointReceipt( index: number, ): Promise { diff --git a/typescript/infra/src/config/agent/validator.ts b/typescript/infra/src/config/agent/validator.ts index d424c76c39..61fdab217b 100644 --- a/typescript/infra/src/config/agent/validator.ts +++ b/typescript/infra/src/config/agent/validator.ts @@ -4,6 +4,7 @@ import { ValidatorConfig as AgentValidatorConfig, ChainMap, ChainName, + S3Config, } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; @@ -75,11 +76,9 @@ export interface LocalCheckpointSyncerConfig { path: string; } -export interface S3CheckpointSyncerConfig { +export type S3CheckpointSyncerConfig = S3Config & { type: CheckpointSyncerType.S3; - bucket: string; - region: string; -} +}; export class ValidatorConfigHelper extends AgentConfigHelper { readonly #validatorsConfig: ValidatorBaseChainConfigMap; diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index c0fa21462f..342287e223 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -3,6 +3,7 @@ "description": "The official SDK for the Hyperlane Network", "version": "3.12.2", "dependencies": { + "@aws-sdk/client-s3": "^3.74.0", "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", "@hyperlane-xyz/core": "3.12.2", diff --git a/typescript/infra/src/agents/aws/s3.ts b/typescript/sdk/src/aws/s3.ts similarity index 53% rename from typescript/infra/src/agents/aws/s3.ts rename to typescript/sdk/src/aws/s3.ts index 2c12cd00c3..a222617b35 100644 --- a/typescript/infra/src/agents/aws/s3.ts +++ b/typescript/sdk/src/aws/s3.ts @@ -11,38 +11,60 @@ export interface S3Receipt { modified: Date; } +export interface S3Config { + bucket: string; + region: string; + folder?: string; + caching?: boolean; +} + export class S3Wrapper { private readonly client: S3Client; - readonly bucket: string; - readonly region: string; - readonly folder: string | undefined; + + private cache: Record> | undefined; static fromBucketUrl(bucketUrl: string): S3Wrapper { const match = bucketUrl.match(S3_BUCKET_REGEX); if (!match) throw new Error('Could not parse bucket url'); - return new S3Wrapper(match[1], match[2], undefined); + return new S3Wrapper({ + bucket: match[1], + region: match[2], + caching: true, + }); + } + + constructor(readonly config: S3Config) { + this.client = new S3Client(config); + if (config.caching) { + this.cache = {}; + } } - constructor(bucket: string, region: string, folder: string | undefined) { - this.bucket = bucket; - this.region = region; - this.folder = folder; - this.client = new S3Client({ region }); + formatKey(key: string): string { + return this.config.folder ? `${this.config.folder}/${key}` : key; } async getS3Obj(key: string): Promise | undefined> { - const Key = this.folder ? `${this.folder}/${key}` : key; + const Key = this.formatKey(key); + if (this.cache?.[Key]) { + return this.cache![Key]; + } + const command = new GetObjectCommand({ - Bucket: this.bucket, + Bucket: this.config.bucket, Key, }); try { const response = await this.client.send(command); const body: string = await streamToString(response.Body as Readable); - return { + const result = { data: JSON.parse(body), modified: response.LastModified!, }; + if (this.cache) { + this.cache[Key] = result; + } + return result; } catch (e: any) { if (e.message.includes('The specified key does not exist.')) { return; @@ -50,4 +72,9 @@ export class S3Wrapper { throw e; } } + + url(key: string): string { + const Key = this.formatKey(key); + return `https://${this.config.bucket}.${this.config.region}.s3.amazonaws.com/${Key}`; + } } diff --git a/typescript/sdk/src/aws/validator.ts b/typescript/sdk/src/aws/validator.ts new file mode 100644 index 0000000000..a60c05b0ab --- /dev/null +++ b/typescript/sdk/src/aws/validator.ts @@ -0,0 +1,101 @@ +import { + Announcement, + BaseValidator, + S3Announcement, + S3CheckpointWithId, + ValidatorConfig, + isS3CheckpointWithId, +} from '@hyperlane-xyz/utils'; + +import { S3Config, S3Wrapper } from './s3.js'; + +const checkpointWithMessageIdKey = (checkpointIndex: number) => + `checkpoint_${checkpointIndex}_with_id.json`; +const LATEST_KEY = 'checkpoint_latest_index.json'; +const ANNOUNCEMENT_KEY = 'announcement.json'; +const LOCATION_PREFIX = 's3://'; + +/** + * Extension of BaseValidator that includes AWS S3 utilities. + */ +export class S3Validator extends BaseValidator { + public s3Bucket: S3Wrapper; + + constructor( + public validatorConfig: ValidatorConfig, + public s3Config: S3Config, + ) { + super(validatorConfig); + this.s3Bucket = new S3Wrapper(s3Config); + } + + static async fromStorageLocation( + storageLocation: string, + ): Promise { + if (storageLocation.startsWith(LOCATION_PREFIX)) { + const suffix = storageLocation.slice(LOCATION_PREFIX.length); + const pieces = suffix.split('/'); + if (pieces.length >= 2) { + const s3Config = { + bucket: pieces[0], + region: pieces[1], + folder: pieces[2], + caching: true, + }; + const s3Bucket = new S3Wrapper(s3Config); + const announcement = await s3Bucket.getS3Obj( + ANNOUNCEMENT_KEY, + ); + if (!announcement) { + throw new Error('No announcement found'); + } + + const validatorConfig = { + address: announcement.data.value.validator, + localDomain: announcement.data.value.mailbox_domain, + mailbox: announcement.data.value.mailbox_address, + }; + + return new S3Validator(validatorConfig, s3Config); + } + } + throw new Error(`Unable to parse location ${storageLocation}`); + } + + async getAnnouncement(): Promise { + const resp = await this.s3Bucket.getS3Obj(ANNOUNCEMENT_KEY); + if (!resp) { + throw new Error('No announcement found'); + } + + return resp.data.value; + } + + async getCheckpoint(index: number): Promise { + const key = checkpointWithMessageIdKey(index); + const s3Object = await this.s3Bucket.getS3Obj(key); + if (!s3Object) { + return; + } + + if (isS3CheckpointWithId(s3Object.data)) { + return s3Object.data; + } else { + throw new Error('Failed to parse checkpoint'); + } + } + + async getLatestCheckpointIndex(): Promise { + const latestCheckpointIndex = await this.s3Bucket.getS3Obj( + LATEST_KEY, + ); + + if (!latestCheckpointIndex) return -1; + + return latestCheckpointIndex.data; + } + + storageLocation(): string { + return `${LOCATION_PREFIX}/${this.s3Bucket.config.bucket}/${this.s3Bucket.config.region}`; + } +} diff --git a/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts b/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts index 41fd03add6..58bf38f27a 100644 --- a/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts +++ b/typescript/sdk/src/core/CoreDeployer.hardhat-test.ts @@ -9,7 +9,7 @@ import { TestChainName, testChains } from '../consts/testChains.js'; import { HyperlaneContractsMap } from '../contracts/types.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; import { HookConfig } from '../hook/types.js'; -import { DerivedIsmConfigWithAddress } from '../ism/EvmIsmReader.js'; +import { DerivedIsmConfig } from '../ism/EvmIsmReader.js'; import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js'; import { AggregationIsmConfig, IsmType } from '../ism/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; @@ -141,9 +141,9 @@ describe('core', async () => { // Cast because we don't expect the 'string' type const defaultIsmOnchain = - coreConfigOnChain.defaultIsm as DerivedIsmConfigWithAddress; + coreConfigOnChain.defaultIsm as DerivedIsmConfig; const defaultIsmTest = coreConfig[chainName] - .defaultIsm as DerivedIsmConfigWithAddress; + .defaultIsm as DerivedIsmConfig; expect(defaultIsmOnchain.type).to.be.equal(defaultIsmTest.type); }), diff --git a/typescript/sdk/src/core/HyperlaneCore.ts b/typescript/sdk/src/core/HyperlaneCore.ts index f3cb21877c..92f0e30230 100644 --- a/typescript/sdk/src/core/HyperlaneCore.ts +++ b/typescript/sdk/src/core/HyperlaneCore.ts @@ -1,3 +1,4 @@ +import { TransactionReceipt } from '@ethersproject/providers'; import { ethers } from 'ethers'; import type { TransactionReceipt as ViemTxReceipt } from 'viem'; @@ -6,6 +7,7 @@ import { Address, AddressBytes32, ProtocolType, + addressToBytes32, bytes32ToAddress, eqAddress, messageId, @@ -19,10 +21,7 @@ import { HyperlaneApp } from '../app/HyperlaneApp.js'; import { appFromAddressesMapHelper } from '../contracts/contracts.js'; import { HyperlaneAddressesMap } from '../contracts/types.js'; import { OwnableConfig } from '../deploy/types.js'; -import { - DerivedIsmConfigWithAddress, - EvmIsmReader, -} from '../ism/EvmIsmReader.js'; +import { DerivedIsmConfig, EvmIsmReader } from '../ism/EvmIsmReader.js'; import { IsmType, ModuleType, ismTypeToModuleType } from '../ism/types.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { RouterConfig } from '../router/types.js'; @@ -68,11 +67,19 @@ export class HyperlaneCore extends HyperlaneApp { destination: ChainName, recipient: AddressBytes32, body: string, + metadata?: string, + hook?: Address, ): Promise => { const destinationId = this.multiProvider.getDomainId(destination); return this.contractsMap[origin].mailbox[ - 'quoteDispatch(uint32,bytes32,bytes)' - ](destinationId, recipient, body); + 'quoteDispatch(uint32,bytes32,bytes,bytes,address)' + ]( + destinationId, + recipient, + body, + metadata || '0x', + hook || ethers.constants.AddressZero, + ); }; protected getDestination(message: DispatchedMessage): ChainName { @@ -87,7 +94,7 @@ export class HyperlaneCore extends HyperlaneApp { async getRecipientIsmConfig( message: DispatchedMessage, - ): Promise { + ): Promise { const destinationChain = this.getDestination(message); const ismReader = new EvmIsmReader(this.multiProvider, destinationChain); const address = await this.getRecipientIsmAddress(message); @@ -121,6 +128,42 @@ export class HyperlaneCore extends HyperlaneApp { } } + async sendMessage( + origin: ChainName, + destination: ChainName, + recipient: Address, + body: string, + hook?: Address, + metadata?: string, + ): Promise<{ dispatchTx: TransactionReceipt; message: DispatchedMessage }> { + const mailbox = this.getContracts(origin).mailbox; + const destinationDomain = this.multiProvider.getDomainId(destination); + const recipientBytes32 = addressToBytes32(recipient); + const quote = await this.quoteGasPayment( + origin, + destination, + recipientBytes32, + body, + metadata, + hook, + ); + const dispatchTx = await this.multiProvider.handleTx( + origin, + mailbox['dispatch(uint32,bytes32,bytes,bytes,address)']( + destinationDomain, + recipientBytes32, + body, + metadata || '0x', + hook || ethers.constants.AddressZero, + { value: quote }, + ), + ); + return { + dispatchTx, + message: this.getDispatchedMessages(dispatchTx)[0], + }; + } + async relayMessage( message: DispatchedMessage, ): Promise { @@ -212,7 +255,14 @@ export class HyperlaneCore extends HyperlaneApp { getDispatchedMessages( sourceTx: ethers.ContractReceipt | ViemTxReceipt, ): DispatchedMessage[] { - return HyperlaneCore.getDispatchedMessages(sourceTx); + const messages = HyperlaneCore.getDispatchedMessages(sourceTx); + return messages.map(({ parsed, ...other }) => { + const originChain = + this.multiProvider.tryGetChainName(parsed.origin) ?? undefined; + const destinationChain = + this.multiProvider.tryGetChainName(parsed.destination) ?? undefined; + return { parsed: { ...parsed, originChain, destinationChain }, ...other }; + }); } async getDispatchTx( diff --git a/typescript/sdk/src/hook/EvmHookReader.ts b/typescript/sdk/src/hook/EvmHookReader.ts index 32436f4535..c5db5165c1 100644 --- a/typescript/sdk/src/hook/EvmHookReader.ts +++ b/typescript/sdk/src/hook/EvmHookReader.ts @@ -42,6 +42,8 @@ import { RoutingHookConfig, } from './types.js'; +export type DerivedHookConfig = WithAddress; + export interface HookReader { deriveHookConfig(address: Address): Promise>; deriveMerkleTreeConfig( @@ -82,9 +84,10 @@ export class EvmHookReader implements HookReader { this.provider = multiProvider.getProvider(chain); } - async deriveHookConfig(address: Address): Promise> { + async deriveHookConfig(address: Address): Promise { const hook = IPostDispatchHook__factory.connect(address, this.provider); const onchainHookType: OnchainHookType = await hook.hookType(); + this.logger.debug('Deriving HookConfig', { address, onchainHookType }); switch (onchainHookType) { case OnchainHookType.ROUTING: diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index aceb6509ed..79f780df79 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -468,6 +468,8 @@ export { } from './token/schemas.js'; export { isCompliant } from './utils/schemas.js'; export { TokenRouterConfig, WarpRouteDeployConfig } from './token/types.js'; +export { S3Validator } from './aws/validator.js'; +export { S3Config, S3Wrapper, S3Receipt } from './aws/s3.js'; // prettier-ignore // @ts-ignore diff --git a/typescript/sdk/src/ism/EvmIsmReader.ts b/typescript/sdk/src/ism/EvmIsmReader.ts index 64483d4312..b62fd321c7 100644 --- a/typescript/sdk/src/ism/EvmIsmReader.ts +++ b/typescript/sdk/src/ism/EvmIsmReader.ts @@ -28,25 +28,14 @@ import { IsmType, ModuleType, MultisigIsmConfig, - OpStackIsmConfig, - PausableIsmConfig, + NullIsmConfig, RoutingIsmConfig, - TestIsmConfig, - TrustedRelayerIsmConfig, } from './types.js'; -type NullIsmConfig = - | PausableIsmConfig - | TestIsmConfig - | OpStackIsmConfig - | TrustedRelayerIsmConfig; - -export type DerivedIsmConfigWithAddress = WithAddress< - Exclude ->; +export type DerivedIsmConfig = WithAddress>; export interface IsmReader { - deriveIsmConfig(address: Address): Promise; + deriveIsmConfig(address: Address): Promise; deriveRoutingConfig(address: Address): Promise>; deriveAggregationConfig( address: Address, @@ -71,14 +60,13 @@ export class EvmIsmReader implements IsmReader { this.provider = multiProvider.getProvider(chain); } - async deriveIsmConfig( - address: Address, - ): Promise { + async deriveIsmConfig(address: Address): Promise { const ism = IInterchainSecurityModule__factory.connect( address, this.provider, ); const moduleType: ModuleType = await ism.moduleType(); + this.logger.debug('Deriving ISM config', { address, moduleType }); switch (moduleType) { case ModuleType.UNUSED: @@ -116,12 +104,18 @@ export class EvmIsmReader implements IsmReader { const domainIds = await ism.domains(); await concurrentMap(this.concurrency, domainIds, async (domainId) => { - const chainName = this.multiProvider.getChainName(domainId.toNumber()); + const chainName = this.multiProvider.tryGetChainName(domainId.toNumber()); + if (!chainName) { + this.logger.warn( + `Unknown domain ID ${domainId}, skipping domain configuration`, + ); + return; + } const module = await ism.module(domainId); domains[chainName] = await this.deriveIsmConfig(module); }); - // Fallback routing ISM extends from MailboxClient, default routign + // Fallback routing ISM extends from MailboxClient, default routing let ismType = IsmType.FALLBACK_ROUTING; try { await ism.mailbox(); diff --git a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts index 2fe0b8f340..f535277e40 100644 --- a/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts +++ b/typescript/sdk/src/ism/HyperlaneIsmFactory.hardhat-test.ts @@ -3,14 +3,14 @@ import { expect } from 'chai'; import hre from 'hardhat'; import { DomainRoutingIsm, TrustedRelayerIsm } from '@hyperlane-xyz/core'; -import { Address } from '@hyperlane-xyz/utils'; +import { Address, randomElement, randomInt } from '@hyperlane-xyz/utils'; import { TestChainName, testChains } from '../consts/testChains.js'; import { TestCoreApp } from '../core/TestCoreApp.js'; import { TestCoreDeployer } from '../core/TestCoreDeployer.js'; import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js'; import { MultiProvider } from '../providers/MultiProvider.js'; -import { randomAddress, randomInt } from '../test/testUtils.js'; +import { randomAddress } from '../test/testUtils.js'; import { HyperlaneIsmFactory } from './HyperlaneIsmFactory.js'; import { @@ -27,43 +27,58 @@ import { moduleMatchesConfig } from './utils.js'; function randomModuleType(): ModuleType { const choices = [ ModuleType.AGGREGATION, - ModuleType.MERKLE_ROOT_MULTISIG, + ModuleType.MESSAGE_ID_MULTISIG, ModuleType.ROUTING, ModuleType.NULL, ]; - return choices[randomInt(choices.length)]; + return randomElement(choices); } -const randomMultisigIsmConfig = (m: number, n: number): MultisigIsmConfig => { +const randomMultisigIsmConfig = ( + m: number, + n: number, + addresses?: string[], +): MultisigIsmConfig => { const emptyArray = new Array(n).fill(0); - const validators = emptyArray.map(() => randomAddress()); + const validators = emptyArray + .map(() => (addresses ? randomElement(addresses) : randomAddress())) + .sort(); return { - type: IsmType.MERKLE_ROOT_MULTISIG, + type: IsmType.MESSAGE_ID_MULTISIG, validators, threshold: m, }; }; -const randomIsmConfig = (depth = 0, maxDepth = 2): IsmConfig => { +export const randomIsmConfig = ( + maxDepth = 5, + validatorAddresses?: string[], + relayerAddress?: string, +): Exclude => { const moduleType = - depth == maxDepth ? ModuleType.MERKLE_ROOT_MULTISIG : randomModuleType(); - if (moduleType === ModuleType.MERKLE_ROOT_MULTISIG) { - const n = randomInt(5, 1); - return randomMultisigIsmConfig(randomInt(n, 1), n); + maxDepth === 0 ? ModuleType.MESSAGE_ID_MULTISIG : randomModuleType(); + if (moduleType === ModuleType.MESSAGE_ID_MULTISIG) { + const n = randomInt(validatorAddresses?.length ?? 5, 1); + return randomMultisigIsmConfig(randomInt(n, 1), n, validatorAddresses); } else if (moduleType === ModuleType.ROUTING) { const config: RoutingIsmConfig = { type: IsmType.ROUTING, owner: randomAddress(), domains: Object.fromEntries( - testChains.map((c) => [c, randomIsmConfig(depth + 1)]), + testChains.map((c) => [ + c, + randomIsmConfig(maxDepth - 1, validatorAddresses, relayerAddress), + ]), ), }; return config; } else if (moduleType === ModuleType.AGGREGATION) { - const n = randomInt(5, 1); + const n = randomInt(5, 2); const modules = new Array(n) .fill(0) - .map(() => randomIsmConfig(depth + 1)); + .map(() => + randomIsmConfig(maxDepth - 1, validatorAddresses, relayerAddress), + ); const config: AggregationIsmConfig = { type: IsmType.AGGREGATION, threshold: randomInt(n, 1), @@ -73,7 +88,7 @@ const randomIsmConfig = (depth = 0, maxDepth = 2): IsmConfig => { } else if (moduleType === ModuleType.NULL) { const config: TrustedRelayerIsmConfig = { type: IsmType.TRUSTED_RELAYER, - relayer: randomAddress(), + relayer: relayerAddress ?? randomAddress(), }; return config; } else { diff --git a/typescript/sdk/src/ism/metadata/aggregation.test.ts b/typescript/sdk/src/ism/metadata/aggregation.test.ts index 117f059a63..13c06654d2 100644 --- a/typescript/sdk/src/ism/metadata/aggregation.test.ts +++ b/typescript/sdk/src/ism/metadata/aggregation.test.ts @@ -1,21 +1,25 @@ import { expect } from 'chai'; +import { ethers } from 'ethers'; import { existsSync, readFileSync, readdirSync } from 'fs'; +import { IsmType } from '../types.js'; + import { - AggregationIsmMetadata, - AggregationIsmMetadataBuilder, + AggregationMetadata, + AggregationMetadataBuilder, } from './aggregation.js'; import { Fixture } from './types.test.js'; const path = '../../solidity/fixtures/aggregation'; const files = existsSync(path) ? readdirSync(path) : []; -const fixtures: Fixture[] = files +const fixtures: Fixture[] = files .map((f) => JSON.parse(readFileSync(`${path}/${f}`, 'utf8'))) .map((contents) => { const { encoded, ...values } = contents; return { encoded, decoded: { + type: IsmType.AGGREGATION, submoduleMetadata: Object.values(values), }, }; @@ -24,17 +28,21 @@ const fixtures: Fixture[] = files describe('AggregationMetadataBuilder', () => { fixtures.forEach((fixture, i) => { it(`should encode fixture ${i}`, () => { - expect(AggregationIsmMetadataBuilder.encode(fixture.decoded)).to.equal( + expect(AggregationMetadataBuilder.encode(fixture.decoded)).to.equal( fixture.encoded, ); }); it(`should decode fixture ${i}`, () => { + const count = fixture.decoded.submoduleMetadata.length; expect( - AggregationIsmMetadataBuilder.decode( - fixture.encoded, - fixture.decoded.submoduleMetadata.length, - ), + AggregationMetadataBuilder.decode(fixture.encoded, { + ism: { + type: IsmType.AGGREGATION, + modules: new Array(count).fill(ethers.constants.AddressZero), + threshold: count, + }, + } as any), ).to.deep.equal(fixture.decoded); }); }); diff --git a/typescript/sdk/src/ism/metadata/aggregation.ts b/typescript/sdk/src/ism/metadata/aggregation.ts index 8ddae22f65..52f7252199 100644 --- a/typescript/sdk/src/ism/metadata/aggregation.ts +++ b/typescript/sdk/src/ism/metadata/aggregation.ts @@ -1,20 +1,91 @@ -import { fromHexString, toHexString } from '@hyperlane-xyz/utils'; +import { + WithAddress, + assert, + fromHexString, + rootLogger, + timeout, + toHexString, +} from '@hyperlane-xyz/utils'; + +import { DerivedIsmConfig } from '../EvmIsmReader.js'; +import { AggregationIsmConfig, IsmType } from '../types.js'; + +import { + BaseMetadataBuilder, + MetadataBuilder, + MetadataContext, + StructuredMetadata, +} from './builder.js'; // null indicates that metadata is NOT INCLUDED for this submodule // empty or 0x string indicates that metadata is INCLUDED but NULL -export interface AggregationIsmMetadata { - submoduleMetadata: Array; +export interface AggregationMetadata { + type: IsmType.AGGREGATION; + submoduleMetadata: Array; } const RANGE_SIZE = 4; // adapted from rust/agents/relayer/src/msg/metadata/aggregation.rs -export class AggregationIsmMetadataBuilder { +export class AggregationMetadataBuilder implements MetadataBuilder { + protected logger = rootLogger.child({ + module: 'AggregationIsmMetadataBuilder', + }); + + constructor(protected readonly base: BaseMetadataBuilder) {} + + async build( + context: MetadataContext>, + maxDepth = 10, + timeoutMs = maxDepth * 1000, + ): Promise { + this.logger.debug( + { context, maxDepth, timeoutMs }, + 'Building aggregation metadata', + ); + assert(maxDepth > 0, 'Max depth reached'); + const promises = await Promise.allSettled( + context.ism.modules.map((module) => + timeout( + this.base.build( + { + ...context, + ism: module as DerivedIsmConfig, + }, + maxDepth - 1, + ), + timeoutMs, + ), + ), + ); + const metadatas = promises.map((r) => + r.status === 'fulfilled' ? r.value ?? null : null, + ); + const included = metadatas.filter((m) => m !== null).length; + assert( + included >= context.ism.threshold, + `Only built ${included} of ${context.ism.threshold} required modules`, + ); + + // only include the first threshold metadatas + let count = 0; + for (let i = 0; i < metadatas.length; i++) { + if (metadatas[i] === null) continue; + count += 1; + if (count > context.ism.threshold) metadatas[i] = null; + } + + return AggregationMetadataBuilder.encode({ + ...context.ism, + submoduleMetadata: metadatas, + }); + } + static rangeIndex(index: number): number { return index * 2 * RANGE_SIZE; } - static encode(metadata: AggregationIsmMetadata): string { + static encode(metadata: AggregationMetadata): string { const rangeSize = this.rangeIndex(metadata.submoduleMetadata.length); let encoded = Buffer.alloc(rangeSize, 0); @@ -48,13 +119,19 @@ export class AggregationIsmMetadataBuilder { }; } - static decode(metadata: string, count: number): AggregationIsmMetadata { - const submoduleMetadata = []; - for (let i = 0; i < count; i++) { - const range = this.metadataRange(metadata, i); - const submeta = range.start > 0 ? range.encoded : null; - submoduleMetadata.push(submeta); - } - return { submoduleMetadata }; + static decode( + metadata: string, + context: MetadataContext, + ): AggregationMetadata { + const submoduleMetadata = context.ism.modules.map((ism, index) => { + const range = this.metadataRange(metadata, index); + if (range.start == 0) return null; + if (typeof ism === 'string') return range.encoded; + return BaseMetadataBuilder.decode(range.encoded, { + ...context, + ism: ism as DerivedIsmConfig, + }); + }); + return { type: IsmType.AGGREGATION, submoduleMetadata }; } } diff --git a/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts b/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts new file mode 100644 index 0000000000..844a87df5a --- /dev/null +++ b/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts @@ -0,0 +1,180 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers.js'; +import hre from 'hardhat'; +import { before } from 'mocha'; +import sinon from 'sinon'; + +import { MerkleTreeHook, TestRecipient } from '@hyperlane-xyz/core'; +import { + BaseValidator, + Checkpoint, + CheckpointWithId, + Domain, + S3CheckpointWithId, + addressToBytes32, + eqAddress, + objMap, + randomElement, +} from '@hyperlane-xyz/utils'; + +import { testChains } from '../../consts/testChains.js'; +import { serializeContractsMap } from '../../contracts/contracts.js'; +import { HyperlaneCore } from '../../core/HyperlaneCore.js'; +import { TestCoreDeployer } from '../../core/TestCoreDeployer.js'; +import { TestRecipientDeployer } from '../../core/TestRecipientDeployer.js'; +import { HyperlaneProxyFactoryDeployer } from '../../deploy/HyperlaneProxyFactoryDeployer.js'; +import { HyperlaneHookDeployer } from '../../hook/HyperlaneHookDeployer.js'; +import { HookType, MerkleTreeHookConfig } from '../../hook/types.js'; +import { MultiProvider } from '../../providers/MultiProvider.js'; +import { ChainName } from '../../types.js'; +import { EvmIsmReader } from '../EvmIsmReader.js'; +import { randomIsmConfig } from '../HyperlaneIsmFactory.hardhat-test.js'; +import { HyperlaneIsmFactory } from '../HyperlaneIsmFactory.js'; + +import { BaseMetadataBuilder, MetadataContext } from './builder.js'; + +const MAX_ISM_DEPTH = 5; +const MAX_NUM_VALIDATORS = 10; +const NUM_RUNS = 16; + +describe('BaseMetadataBuilder', () => { + let core: HyperlaneCore; + let ismFactory: HyperlaneIsmFactory; + let merkleHooks: Record; + let testRecipients: Record; + let relayer: SignerWithAddress; + let validators: SignerWithAddress[]; + let metadataBuilder: BaseMetadataBuilder; + + before(async () => { + [relayer, ...validators] = await hre.ethers.getSigners(); + const multiProvider = MultiProvider.createTestMultiProvider({ + signer: relayer, + }); + const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider); + ismFactory = new HyperlaneIsmFactory( + await ismFactoryDeployer.deploy(multiProvider.mapKnownChains(() => ({}))), + multiProvider, + ); + + const coreDeployer = new TestCoreDeployer(multiProvider, ismFactory); + const recipientDeployer = new TestRecipientDeployer(multiProvider); + testRecipients = objMap( + await recipientDeployer.deploy( + Object.fromEntries(testChains.map((c) => [c, {}])), + ), + (_, { testRecipient }) => testRecipient, + ); + core = await coreDeployer.deployApp(); + const hookDeployer = new HyperlaneHookDeployer( + multiProvider, + serializeContractsMap(core.contractsMap), + ismFactory, + ); + const hookConfig = objMap( + core.chainMap, + (): MerkleTreeHookConfig => ({ + type: HookType.MERKLE_TREE, + }), + ); + const hookContracts = await hookDeployer.deploy(hookConfig); + merkleHooks = Object.fromEntries( + Object.entries(hookContracts).map(([chain, { merkleTreeHook }]) => [ + core.multiProvider.getDomainId(chain), + merkleTreeHook, + ]), + ); + + metadataBuilder = new BaseMetadataBuilder(core); + + sinon + .stub(metadataBuilder.multisigMetadataBuilder, 'getS3Checkpoints') + .callsFake( + async (multisigAddresses, match): Promise => { + const merkleHook = merkleHooks[match.origin]; + const checkpoint: Checkpoint = { + root: await merkleHook.root(), + merkle_tree_hook_address: addressToBytes32(merkleHook.address), + index: match.index, + mailbox_domain: match.origin, + }; + const checkpointWithId: CheckpointWithId = { + checkpoint, + message_id: match.messageId, + }; + const digest = BaseValidator.messageHash(checkpoint, match.messageId); + const checkpoints: S3CheckpointWithId[] = []; + for (const validator of multisigAddresses) { + const signature = await validators + .find((s) => eqAddress(s.address, validator))! + .signMessage(digest); + checkpoints.push({ value: checkpointWithId, signature }); + } + return checkpoints; + }, + ); + }); + + describe('#build', () => { + let origin: ChainName; + let destination: ChainName; + let context: MetadataContext; + let metadata: string; + + beforeEach(async () => { + origin = randomElement(testChains); + destination = randomElement(testChains.filter((c) => c !== origin)); + const testRecipient = testRecipients[destination]; + + const addresses = validators + .map((s) => s.address) + .slice(0, MAX_NUM_VALIDATORS); + const config = randomIsmConfig(MAX_ISM_DEPTH, addresses, relayer.address); + const deployedIsm = await ismFactory.deploy({ + destination, + config, + mailbox: core.getAddresses(destination).mailbox, + }); + await testRecipient.setInterchainSecurityModule(deployedIsm.address); + + const merkleHookAddress = + merkleHooks[core.multiProvider.getDomainId(origin)].address; + const { dispatchTx, message } = await core.sendMessage( + origin, + destination, + testRecipient.address, + '0xdeadbeef', + merkleHookAddress, + ); + + const derivedIsm = await new EvmIsmReader( + core.multiProvider, + destination, + ).deriveIsmConfig(deployedIsm.address); + + context = { + hook: { + type: HookType.MERKLE_TREE, + address: merkleHookAddress, + }, + ism: derivedIsm, + message, + dispatchTx, + }; + + metadata = await metadataBuilder.build(context, MAX_ISM_DEPTH); + }); + + for (let i = 0; i < NUM_RUNS; i++) { + it(`should build valid metadata for random ism config (${i})`, async () => { + // must call process for trusted relayer to be able to verify + await core + .getContracts(destination) + .mailbox.process(metadata, context.message.message); + }); + + it(`should decode metadata for random ism config (${i})`, async () => { + BaseMetadataBuilder.decode(metadata, context); + }); + } + }); +}); diff --git a/typescript/sdk/src/ism/metadata/builder.ts b/typescript/sdk/src/ism/metadata/builder.ts new file mode 100644 index 0000000000..66e364bbc4 --- /dev/null +++ b/typescript/sdk/src/ism/metadata/builder.ts @@ -0,0 +1,133 @@ +/* eslint-disable no-case-declarations */ +import { TransactionReceipt } from '@ethersproject/providers'; + +import { WithAddress, assert, rootLogger } from '@hyperlane-xyz/utils'; + +import { deepFind } from '../../../../utils/dist/objects.js'; +import { HyperlaneCore } from '../../core/HyperlaneCore.js'; +import { DispatchedMessage } from '../../core/types.js'; +import { DerivedHookConfig } from '../../hook/EvmHookReader.js'; +import { HookType, MerkleTreeHookConfig } from '../../hook/types.js'; +import { MultiProvider } from '../../providers/MultiProvider.js'; +import { DerivedIsmConfig } from '../EvmIsmReader.js'; +import { IsmType } from '../types.js'; + +import { + AggregationMetadata, + AggregationMetadataBuilder, +} from './aggregation.js'; +import { MultisigMetadata, MultisigMetadataBuilder } from './multisig.js'; +import { NullMetadata, NullMetadataBuilder } from './null.js'; +import { RoutingMetadata, RoutingMetadataBuilder } from './routing.js'; + +export type StructuredMetadata = + | NullMetadata + | MultisigMetadata + | AggregationMetadata + | RoutingMetadata; + +export interface MetadataContext< + IsmContext = DerivedIsmConfig, + HookContext = DerivedHookConfig, +> { + message: DispatchedMessage; + dispatchTx: TransactionReceipt; + ism: IsmContext; + hook: HookContext; +} + +export interface MetadataBuilder { + build(context: MetadataContext): Promise; +} + +export class BaseMetadataBuilder implements MetadataBuilder { + public nullMetadataBuilder: NullMetadataBuilder; + public multisigMetadataBuilder: MultisigMetadataBuilder; + public aggregationMetadataBuilder: AggregationMetadataBuilder; + public routingMetadataBuilder: RoutingMetadataBuilder; + + public multiProvider: MultiProvider; + protected logger = rootLogger.child({ module: 'BaseMetadataBuilder' }); + + constructor(core: HyperlaneCore) { + this.multisigMetadataBuilder = new MultisigMetadataBuilder(core); + this.aggregationMetadataBuilder = new AggregationMetadataBuilder(this); + this.routingMetadataBuilder = new RoutingMetadataBuilder(this); + this.nullMetadataBuilder = new NullMetadataBuilder(core.multiProvider); + this.multiProvider = core.multiProvider; + } + + // assumes that all post dispatch hooks are included in dispatchTx logs + async build(context: MetadataContext, maxDepth = 10): Promise { + this.logger.debug( + { context, maxDepth }, + `Building ${context.ism.type} metadata`, + ); + assert(maxDepth > 0, 'Max depth reached'); + + const { ism, hook } = context; + switch (ism.type) { + case IsmType.TRUSTED_RELAYER: + case IsmType.TEST_ISM: + case IsmType.OP_STACK: + case IsmType.PAUSABLE: + return this.nullMetadataBuilder.build({ ...context, ism }); + + case IsmType.MERKLE_ROOT_MULTISIG: + case IsmType.MESSAGE_ID_MULTISIG: + const merkleTreeHook = deepFind( + hook, + (v): v is WithAddress => + v.type === HookType.MERKLE_TREE && !!v.address, + ); + assert(merkleTreeHook, 'Merkle tree hook context not found'); + return this.multisigMetadataBuilder.build({ + ...context, + ism, + hook: merkleTreeHook, + }); + + case IsmType.ROUTING: + return this.routingMetadataBuilder.build( + { + ...context, + ism, + }, + maxDepth, + ); + + case IsmType.AGGREGATION: + return this.aggregationMetadataBuilder.build( + { ...context, ism }, + maxDepth, + ); + + default: + throw new Error(`Unsupported ISM type: ${ism.type}`); + } + } + + static decode( + metadata: string, + context: MetadataContext, + ): StructuredMetadata { + const { ism } = context; + switch (ism.type) { + case IsmType.TRUSTED_RELAYER: + return NullMetadataBuilder.decode(ism); + + case IsmType.MERKLE_ROOT_MULTISIG: + case IsmType.MESSAGE_ID_MULTISIG: + return MultisigMetadataBuilder.decode(metadata, ism.type); + + case IsmType.AGGREGATION: + return AggregationMetadataBuilder.decode(metadata, { ...context, ism }); + + case IsmType.ROUTING: + return RoutingMetadataBuilder.decode(metadata, { ...context, ism }); + + default: + throw new Error(`Unsupported ISM type: ${ism.type}`); + } + } +} diff --git a/typescript/sdk/src/ism/metadata/multisig.test.ts b/typescript/sdk/src/ism/metadata/multisig.test.ts index 93a42e597e..64445f82ab 100644 --- a/typescript/sdk/src/ism/metadata/multisig.test.ts +++ b/typescript/sdk/src/ism/metadata/multisig.test.ts @@ -3,7 +3,7 @@ import { existsSync, readFileSync, readdirSync } from 'fs'; import { SignatureLike } from '@hyperlane-xyz/utils'; -import { ModuleType } from '../types.js'; +import { IsmType, ModuleType } from '../types.js'; import { MultisigMetadata, MultisigMetadataBuilder } from './multisig.js'; import { Fixture } from './types.test.js'; @@ -13,7 +13,7 @@ const files = existsSync(path) ? readdirSync(path) : []; const fixtures: Fixture[] = files .map((f) => JSON.parse(readFileSync(`${path}/${f}`, 'utf8'))) .map((contents) => { - const type = contents.type as MultisigMetadata['type']; + const type = contents.type as ModuleType; const { dummy: _dummy, ...signatureValues } = contents.signatures; const signatures = Object.values(signatureValues); @@ -23,7 +23,7 @@ const fixtures: Fixture[] = files const { dummy: _dummy, ...branchValues } = contents.prefix.proof; const branch = Object.values(branchValues); decoded = { - type, + type: IsmType.MERKLE_ROOT_MULTISIG, proof: { branch, leaf: contents.prefix.id, @@ -38,7 +38,7 @@ const fixtures: Fixture[] = files }; } else { decoded = { - type, + type: IsmType.MESSAGE_ID_MULTISIG, checkpoint: { root: contents.prefix.root, index: contents.prefix.signedIndex, diff --git a/typescript/sdk/src/ism/metadata/multisig.ts b/typescript/sdk/src/ism/metadata/multisig.ts index f27e7114fe..afb6539b05 100644 --- a/typescript/sdk/src/ism/metadata/multisig.ts +++ b/typescript/sdk/src/ism/metadata/multisig.ts @@ -1,39 +1,217 @@ import { joinSignature, splitSignature } from 'ethers/lib/utils.js'; +import { MerkleTreeHook__factory } from '@hyperlane-xyz/core'; import { + Address, Checkpoint, MerkleProof, + S3CheckpointWithId, SignatureLike, + WithAddress, assert, + bytes32ToAddress, chunk, ensure0x, + eqAddress, + eqAddressEvm, fromHexString, + rootLogger, strip0x, toHexString, } from '@hyperlane-xyz/utils'; -import { ModuleType } from '../types.js'; +import { S3Validator } from '../../aws/validator.js'; +import { HyperlaneCore } from '../../core/HyperlaneCore.js'; +import { MerkleTreeHookConfig } from '../../hook/types.js'; +import { ChainName } from '../../types.js'; +import { IsmType, MultisigIsmConfig } from '../types.js'; + +import { MetadataBuilder, MetadataContext } from './builder.js'; interface MessageIdMultisigMetadata { - type: ModuleType.MESSAGE_ID_MULTISIG; + type: IsmType.MESSAGE_ID_MULTISIG; signatures: SignatureLike[]; checkpoint: Omit; } interface MerkleRootMultisigMetadata extends Omit { - type: ModuleType.MERKLE_ROOT_MULTISIG; + type: IsmType.MERKLE_ROOT_MULTISIG; proof: MerkleProof; } +const MerkleTreeInterface = MerkleTreeHook__factory.createInterface(); + const SIGNATURE_LENGTH = 65; export type MultisigMetadata = | MessageIdMultisigMetadata | MerkleRootMultisigMetadata; -export class MultisigMetadataBuilder { - static encodeSimplePrefix(metadata: MessageIdMultisigMetadata): string { +export class MultisigMetadataBuilder implements MetadataBuilder { + protected validatorCache: Record> = {}; + + constructor( + protected readonly core: HyperlaneCore, + protected readonly logger = rootLogger.child({ + module: 'MultisigMetadataBuilder', + }), + ) {} + + protected async s3Validators( + originChain: ChainName, + validators: string[], + ): Promise { + this.validatorCache[originChain] ??= {}; + const toFetch = validators.filter( + (v) => !(v in this.validatorCache[originChain]), + ); + + if (toFetch.length > 0) { + const validatorAnnounce = + this.core.getContracts(originChain).validatorAnnounce; + const storageLocations = + await validatorAnnounce.getAnnouncedStorageLocations(toFetch); + + this.logger.debug({ storageLocations }, 'Fetched storage locations'); + + const s3Validators = await Promise.all( + storageLocations.map((locations) => { + const latestLocation = locations.slice(-1)[0]; + return S3Validator.fromStorageLocation(latestLocation); + }), + ); + + this.logger.debug({ s3Validators }, 'Fetched validators'); + + toFetch.forEach((validator, index) => { + this.validatorCache[originChain][validator] = s3Validators[index]; + }); + } + + return validators.map((v) => this.validatorCache[originChain][v]); + } + + async getS3Checkpoints( + validators: Address[], + match: { + origin: number; + merkleTree: Address; + messageId: string; + index: number; + }, + ): Promise { + this.logger.debug({ match, validators }, 'Fetching checkpoints'); + + const originChain = this.core.multiProvider.getChainName(match.origin); + const s3Validators = await this.s3Validators(originChain, validators); + + const results = await Promise.allSettled( + s3Validators.map((v) => v.getCheckpoint(match.index)), + ); + results + .filter((r) => r.status === 'rejected') + .forEach((r) => { + this.logger.error({ error: r }, 'Failed to fetch checkpoint'); + }); + const checkpoints = results + .filter( + (result): result is PromiseFulfilledResult => + result.status === 'fulfilled' && result.value !== undefined, + ) + .map((result) => result.value); + + this.logger.debug({ checkpoints }, 'Fetched checkpoints'); + + if (checkpoints.length < validators.length) { + this.logger.debug( + { checkpoints, validators, match }, + `Found ${checkpoints.length} checkpoints out of ${validators.length} validators`, + ); + } + + const matchingCheckpoints = checkpoints.filter( + ({ value }) => + eqAddress( + bytes32ToAddress(value.checkpoint.merkle_tree_hook_address), + match.merkleTree, + ) && + value.message_id === match.messageId && + value.checkpoint.index === match.index && + value.checkpoint.mailbox_domain === match.origin, + ); + + if (matchingCheckpoints.length !== checkpoints.length) { + this.logger.warn( + { matchingCheckpoints, checkpoints, match }, + 'Mismatched checkpoints', + ); + } + + return matchingCheckpoints; + } + + async build( + context: MetadataContext< + WithAddress, + WithAddress + >, + ): Promise { + assert( + context.ism.type === IsmType.MESSAGE_ID_MULTISIG, + 'Merkle proofs are not yet supported', + ); + + const merkleTree = context.hook.address; + + const matchingInsertion = context.dispatchTx.logs + .filter((log) => eqAddressEvm(log.address, merkleTree)) + .map((log) => MerkleTreeInterface.parseLog(log)) + .find((event) => event.args.messageId === context.message.id); + + assert( + matchingInsertion, + `No merkle tree insertion of ${context.message.id} to ${merkleTree} found in dispatch tx`, + ); + this.logger.debug({ matchingInsertion }, 'Found matching insertion event'); + + const checkpoints = await this.getS3Checkpoints(context.ism.validators, { + origin: context.message.parsed.origin, + messageId: context.message.id, + merkleTree, + index: matchingInsertion.args.index, + }); + assert( + checkpoints.length >= context.ism.threshold, + `Only ${checkpoints.length} of ${context.ism.threshold} required checkpoints found`, + ); + + this.logger.debug( + { checkpoints }, + `Found ${checkpoints.length} checkpoints for message ${context.message.id}`, + ); + + const signatures = checkpoints + .map((checkpoint) => checkpoint.signature) + .slice(0, context.ism.threshold); + + this.logger.debug( + { signatures, ism: context.ism }, + `Taking ${signatures.length} (threshold) signatures for message ${context.message.id}`, + ); + + const metadata: MessageIdMultisigMetadata = { + type: IsmType.MESSAGE_ID_MULTISIG, + checkpoint: checkpoints[0].value.checkpoint, + signatures, + }; + + return MultisigMetadataBuilder.encode(metadata); + } + + protected static encodeSimplePrefix( + metadata: MessageIdMultisigMetadata, + ): string { const checkpoint = metadata.checkpoint; const buf = Buffer.alloc(68); buf.write(strip0x(checkpoint.merkle_tree_hook_address), 0, 32, 'hex'); @@ -54,7 +232,7 @@ export class MultisigMetadataBuilder { }; return { signatureOffset: 68, - type: ModuleType.MESSAGE_ID_MULTISIG, + type: IsmType.MESSAGE_ID_MULTISIG, checkpoint, }; } @@ -93,7 +271,7 @@ export class MultisigMetadataBuilder { }; return { signatureOffset: 1096, - type: ModuleType.MERKLE_ROOT_MULTISIG, + type: IsmType.MERKLE_ROOT_MULTISIG, checkpoint, proof, }; @@ -101,7 +279,7 @@ export class MultisigMetadataBuilder { static encode(metadata: MultisigMetadata): string { let encoded = - metadata.type === ModuleType.MESSAGE_ID_MULTISIG + metadata.type === IsmType.MESSAGE_ID_MULTISIG ? this.encodeSimplePrefix(metadata) : this.encodeProofPrefix(metadata); @@ -131,10 +309,10 @@ export class MultisigMetadataBuilder { static decode( metadata: string, - type: ModuleType.MERKLE_ROOT_MULTISIG | ModuleType.MESSAGE_ID_MULTISIG, + type: IsmType.MERKLE_ROOT_MULTISIG | IsmType.MESSAGE_ID_MULTISIG, ): MultisigMetadata { const prefix: any = - type === ModuleType.MERKLE_ROOT_MULTISIG + type === IsmType.MERKLE_ROOT_MULTISIG ? this.decodeProofPrefix(metadata) : this.decodeSimplePrefix(metadata); diff --git a/typescript/sdk/src/ism/metadata/null.ts b/typescript/sdk/src/ism/metadata/null.ts new file mode 100644 index 0000000000..ed66277ce4 --- /dev/null +++ b/typescript/sdk/src/ism/metadata/null.ts @@ -0,0 +1,35 @@ +import { WithAddress, assert, eqAddress } from '@hyperlane-xyz/utils'; + +import { MultiProvider } from '../../providers/MultiProvider.js'; +import { IsmType, NullIsmConfig } from '../types.js'; + +import { MetadataBuilder, MetadataContext } from './builder.js'; + +export const NULL_METADATA = '0x'; + +export type NullMetadata = { + type: NullIsmConfig['type']; +}; + +export class NullMetadataBuilder implements MetadataBuilder { + constructor(protected multiProvider: MultiProvider) {} + + async build( + context: MetadataContext>, + ): Promise { + if (context.ism.type === IsmType.TRUSTED_RELAYER) { + const destinationSigner = await this.multiProvider.getSignerAddress( + context.message.parsed.destination, + ); + assert( + eqAddress(destinationSigner, context.ism.relayer), + `Destination signer ${destinationSigner} does not match trusted relayer ${context.ism.relayer}`, + ); + } + return NULL_METADATA; + } + + static decode(ism: NullIsmConfig): NullMetadata { + return { ...ism }; + } +} diff --git a/typescript/sdk/src/ism/metadata/routing.ts b/typescript/sdk/src/ism/metadata/routing.ts new file mode 100644 index 0000000000..a3adb819da --- /dev/null +++ b/typescript/sdk/src/ism/metadata/routing.ts @@ -0,0 +1,58 @@ +import { WithAddress, assert } from '@hyperlane-xyz/utils'; + +import { ChainName } from '../../types.js'; +import { DerivedIsmConfig } from '../EvmIsmReader.js'; +import { IsmType, RoutingIsmConfig } from '../types.js'; + +import { + BaseMetadataBuilder, + MetadataBuilder, + MetadataContext, + StructuredMetadata, +} from './builder.js'; + +export type RoutingMetadata = { + type: IsmType.ROUTING; + origin: ChainName; + metadata: T; +}; + +export class RoutingMetadataBuilder implements MetadataBuilder { + constructor(protected baseMetadataBuilder: BaseMetadataBuilder) {} + + public async build( + context: MetadataContext>, + maxDepth = 10, + ): Promise { + const originChain = this.baseMetadataBuilder.multiProvider.getChainName( + context.message.parsed.origin, + ); + const originContext = { + ...context, + ism: context.ism.domains[originChain] as DerivedIsmConfig, + }; + return this.baseMetadataBuilder.build(originContext, maxDepth - 1); + } + + static decode( + metadata: string, + context: MetadataContext>, + ): RoutingMetadata { + // TODO: this is a naive implementation, we should support domain ID keys + assert(context.message.parsed.originChain, 'originChain is required'); + const ism = context.ism.domains[context.message.parsed.originChain]; + const originMetadata = + typeof ism === 'string' + ? metadata + : BaseMetadataBuilder.decode(metadata, { + ...context, + ism: ism as DerivedIsmConfig, + }); + + return { + type: IsmType.ROUTING, + origin: context.message.parsed.originChain, + metadata: originMetadata, + }; + } +} diff --git a/typescript/sdk/src/ism/types.ts b/typescript/sdk/src/ism/types.ts index 0a08676e9c..89892662d9 100644 --- a/typescript/sdk/src/ism/types.ts +++ b/typescript/sdk/src/ism/types.ts @@ -100,15 +100,18 @@ export type TrustedRelayerIsmConfig = { relayer: Address; }; +export type NullIsmConfig = + | PausableIsmConfig + | TestIsmConfig + | OpStackIsmConfig + | TrustedRelayerIsmConfig; + export type IsmConfig = | Address + | NullIsmConfig | RoutingIsmConfig | MultisigIsmConfig - | AggregationIsmConfig - | OpStackIsmConfig - | TestIsmConfig - | PausableIsmConfig - | TrustedRelayerIsmConfig; + | AggregationIsmConfig; export type DeployedIsmType = { [IsmType.ROUTING]: IRoutingIsm; diff --git a/typescript/sdk/src/test/testUtils.ts b/typescript/sdk/src/test/testUtils.ts index 7dbd38424e..fc498469a1 100644 --- a/typescript/sdk/src/test/testUtils.ts +++ b/typescript/sdk/src/test/testUtils.ts @@ -12,10 +12,6 @@ import { IsmType } from '../ism/types.js'; import { RouterConfig } from '../router/types.js'; import { ChainMap, ChainName } from '../types.js'; -export function randomInt(max: number, min = 0): number { - return Math.floor(Math.random() * (max - min)) + min; -} - export function randomAddress(): Address { return ethers.utils.hexlify(ethers.utils.randomBytes(20)); } diff --git a/typescript/utils/src/arrays.ts b/typescript/utils/src/arrays.ts index 25eb1383e5..9f8c67ed2e 100644 --- a/typescript/utils/src/arrays.ts +++ b/typescript/utils/src/arrays.ts @@ -1,3 +1,5 @@ +import { randomInt } from './math.js'; + interface Sliceable { length: number; slice: (i: number, j: number) => any; @@ -14,3 +16,7 @@ export function chunk(str: T, size: number) { export function exclude(item: T, list: T[]) { return list.filter((i) => i !== item); } + +export function randomElement(list: T[]) { + return list[randomInt(list.length)]; +} diff --git a/typescript/utils/src/index.ts b/typescript/utils/src/index.ts index 08b85a1c87..fdaa204765 100644 --- a/typescript/utils/src/index.ts +++ b/typescript/utils/src/index.ts @@ -46,7 +46,7 @@ export { toWei, tryParseAmount, } from './amount.js'; -export { chunk, exclude } from './arrays.js'; +export { chunk, exclude, randomElement } from './arrays.js'; export { concurrentMap, pollAsync, @@ -88,7 +88,7 @@ export { rootLogger, setRootLogger, } from './logging.js'; -export { mean, median, stdDev, sum } from './math.js'; +export { mean, median, randomInt, stdDev, sum } from './math.js'; export { formatMessage, messageId, parseMessage } from './messages.js'; export { formatLegacyMultisigIsmMetadata, @@ -115,25 +115,26 @@ export { export { difference, setEquality, symmetricDifference } from './sets.js'; export { errorToString, + fromHexString, sanitizeString, streamToString, + toHexString, toTitleCase, trimToLength, - fromHexString, - toHexString, } from './strings.js'; export { isNullish, isNumeric } from './typeof.js'; export { Address, AddressBytes32, Annotated, + Announcement, CallData, ChainCaip2Id, ChainId, Checkpoint, + CheckpointWithId, Domain, HexString, - InterchainSecurityModuleType, MerkleProof, MessageStatus, Numberish, @@ -142,6 +143,7 @@ export { ProtocolSmallestUnit, ProtocolType, ProtocolTypeValue, + S3Announcement, S3Checkpoint, S3CheckpointWithId, SignatureLike, @@ -150,4 +152,4 @@ export { } from './types.js'; export { isHttpsUrl } from './url.js'; export { assert } from './validation.js'; -export { BaseValidator } from './validator.js'; +export { BaseValidator, ValidatorConfig } from './validator.js'; diff --git a/typescript/utils/src/math.ts b/typescript/utils/src/math.ts index d1a3bb910e..ebca6e75ef 100644 --- a/typescript/utils/src/math.ts +++ b/typescript/utils/src/math.ts @@ -19,3 +19,7 @@ export function stdDev(a: number[]): number { const squaredDifferences = a.map((x) => Math.pow(x - xbar, 2)); return Math.sqrt(mean(squaredDifferences)); } + +export function randomInt(max: number, min = 0): number { + return Math.floor(Math.random() * (max - min)) + min; +} diff --git a/typescript/utils/src/objects.ts b/typescript/utils/src/objects.ts index 21e122d867..131ab1f4bc 100644 --- a/typescript/utils/src/objects.ts +++ b/typescript/utils/src/objects.ts @@ -1,6 +1,7 @@ import { stringify as yamlStringify } from 'yaml'; import { ethersBigNumberSerializer } from './logging.js'; +import { assert } from './validation.js'; export function isObject(item: any) { return item && typeof item === 'object' && !Array.isArray(item); @@ -57,6 +58,23 @@ export function objFilter( ) as Record; } +export function deepFind( + obj: I, + func: (v: I) => v is O, + depth = 10, +): O | undefined { + assert(depth > 0, 'deepFind max depth reached'); + if (func(obj)) { + return obj; + } + const entries = isObject(obj) + ? Object.values(obj) + : Array.isArray(obj) + ? obj + : []; + return entries.map((e) => deepFind(e as any, func, depth - 1)).find((v) => v); +} + // promiseObjectAll :: {k: Promise a} -> Promise {k: a} export function promiseObjAll(obj: { [key in K]: Promise; diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index 55e3c46003..122a4f82a4 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -1,3 +1,4 @@ +import type { SignatureLike } from '@ethersproject/bytes'; import type { BigNumber, ethers } from 'ethers'; export enum ProtocolType { @@ -28,17 +29,6 @@ export type WithAddress = T & { address: Address; }; -// copied from node_modules/@ethersproject/bytes/src.ts/index.ts -export type SignatureLike = - | { - r: string; - s?: string; - _vs?: string; - recoveryParam?: number; - v?: number; - } - | ethers.utils.BytesLike; - export type MerkleProof = { branch: ethers.utils.BytesLike[]; leaf: ethers.utils.BytesLike; @@ -46,6 +36,13 @@ export type MerkleProof = { }; /********* HYPERLANE CORE *********/ +export type Announcement = { + mailbox_domain: Domain; + mailbox_address: Address; + validator: Address; + storage_location: string; +}; + export type Checkpoint = { root: string; index: number; // safe because 2 ** 32 leaves < Number.MAX_VALUE @@ -53,14 +50,23 @@ export type Checkpoint = { merkle_tree_hook_address: Address; }; +export type CheckpointWithId = { + checkpoint: Checkpoint; + message_id: HexString; +}; + +export { SignatureLike }; + /** * Shape of a checkpoint in S3 as published by the agent. */ export type S3CheckpointWithId = { - value: { - checkpoint: Checkpoint; - message_id: HexString; - }; + value: CheckpointWithId; + signature: SignatureLike; +}; + +export type S3Announcement = { + value: Announcement; signature: SignatureLike; }; @@ -84,8 +90,10 @@ export type ParsedMessage = { version: number; nonce: number; origin: number; + originChain?: string; sender: string; destination: number; + destinationChain?: string; recipient: string; body: string; }; @@ -99,10 +107,6 @@ export type ParsedLegacyMultisigIsmMetadata = { validators: ethers.utils.BytesLike[]; }; -export enum InterchainSecurityModuleType { - MULTISIG = 3, -} - export type Annotated = T & { annotation?: string; }; diff --git a/typescript/utils/src/validator.ts b/typescript/utils/src/validator.ts index e651fafa6e..99cb9772ee 100644 --- a/typescript/utils/src/validator.ts +++ b/typescript/utils/src/validator.ts @@ -1,36 +1,50 @@ import { ethers } from 'ethers'; +import { eqAddress } from './addresses.js'; import { domainHash } from './domains.js'; import { Address, Checkpoint, - Domain, + CheckpointWithId, HexString, + S3CheckpointWithId, SignatureLike, } from './types.js'; +export interface ValidatorConfig { + address: string; + localDomain: number; + mailbox: string; +} + /** * Utilities for validators to construct and verify checkpoints. */ export class BaseValidator { - constructor( - public readonly address: Address, - public readonly localDomain: Domain, - public readonly mailbox_address: Address, - ) {} + constructor(protected readonly config: ValidatorConfig) {} + + get address() { + return this.config.address; + } announceDomainHash() { - return domainHash(this.localDomain, this.mailbox_address); + return domainHash(this.config.localDomain, this.config.mailbox); } - checkpointDomainHash(merkle_tree_address: Address) { - return domainHash(this.localDomain, merkle_tree_address); + static checkpointDomainHash( + localDomain: number, + merkle_tree_address: Address, + ) { + return domainHash(localDomain, merkle_tree_address); } - message(checkpoint: Checkpoint, messageId: HexString) { + static message(checkpoint: Checkpoint, messageId: HexString) { const types = ['bytes32', 'bytes32', 'uint32', 'bytes32']; const values = [ - this.checkpointDomainHash(checkpoint.merkle_tree_hook_address), + this.checkpointDomainHash( + checkpoint.mailbox_domain, + checkpoint.merkle_tree_hook_address, + ), checkpoint.root, checkpoint.index, messageId, @@ -38,12 +52,12 @@ export class BaseValidator { return ethers.utils.solidityPack(types, values); } - messageHash(checkpoint: Checkpoint, messageId: HexString) { + static messageHash(checkpoint: Checkpoint, messageId: HexString) { const message = this.message(checkpoint, messageId); return ethers.utils.arrayify(ethers.utils.keccak256(message)); } - recoverAddressFromCheckpoint( + static recoverAddressFromCheckpoint( checkpoint: Checkpoint, signature: SignatureLike, messageId: HexString, @@ -52,17 +66,31 @@ export class BaseValidator { return ethers.utils.verifyMessage(msgHash, signature); } + static recoverAddressFromCheckpointWithId( + { checkpoint, message_id }: CheckpointWithId, + signature: SignatureLike, + ): Address { + return BaseValidator.recoverAddressFromCheckpoint( + checkpoint, + signature, + message_id, + ); + } + + static recoverAddress({ value, signature }: S3CheckpointWithId): Address { + return BaseValidator.recoverAddressFromCheckpointWithId(value, signature); + } + matchesSigner( checkpoint: Checkpoint, signature: SignatureLike, messageId: HexString, ) { - return ( - this.recoverAddressFromCheckpoint( - checkpoint, - signature, - messageId, - ).toLowerCase() === this.address.toLowerCase() + const address = BaseValidator.recoverAddressFromCheckpoint( + checkpoint, + signature, + messageId, ); + return eqAddress(address, this.config.address); } } diff --git a/yarn.lock b/yarn.lock index fd3c714eec..f8e0a469ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5891,6 +5891,7 @@ __metadata: version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: + "@aws-sdk/client-s3": "npm:^3.74.0" "@cosmjs/cosmwasm-stargate": "npm:^0.31.3" "@cosmjs/stargate": "npm:^0.31.3" "@hyperlane-xyz/core": "npm:3.12.2" From d0ce9081dd35a7021287e8a14c7565a8b55b977f Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Tue, 28 May 2024 03:19:04 +0530 Subject: [PATCH 22/59] feat: support attesting signing key for Hyperlane AVS (#3847) ### Description - Adding support for attesting signing key and key rotation from here https://github.com/Layr-Labs/eigenlayer-middleware/pull/252 ### Drive-by changes None ### Related issues None ### Backward compatibility Yes ### Testing Tested by Eigenlayer here: https://github.com/Layr-Labs/eigenlayer-middleware/blob/dev/test/unit/ECDSAStakeRegistryUnit.t.sol --- solidity/contracts/avs/ECDSAStakeRegistry.sol | 148 ++++++++++++++---- .../avs/ECDSAStakeRegistryStorage.sol | 6 +- .../IECDSAStakeRegistryEventsAndErrors.sol | 17 +- .../test/avs/HyperlaneServiceManager.t.sol | 17 +- 4 files changed, 150 insertions(+), 38 deletions(-) diff --git a/solidity/contracts/avs/ECDSAStakeRegistry.sol b/solidity/contracts/avs/ECDSAStakeRegistry.sol index 0a4e32a011..6148c3c4e0 100644 --- a/solidity/contracts/avs/ECDSAStakeRegistry.sol +++ b/solidity/contracts/avs/ECDSAStakeRegistry.sol @@ -44,13 +44,14 @@ contract ECDSAStakeRegistry is __ECDSAStakeRegistry_init(_serviceManager, _thresholdWeight, _quorum); } - /// @notice Registers a new operator using a provided signature + /// @notice Registers a new operator using a provided signature and signing key /// @param _operatorSignature Contains the operator's signature, salt, and expiry + /// @param _signingKey The signing key to add to the operator's history function registerOperatorWithSignature( - address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _signingKey ) external { - _registerOperatorWithSig(_operator, _operatorSignature); + _registerOperatorWithSig(msg.sender, _operatorSignature, _signingKey); } /// @notice Deregisters an existing operator @@ -58,6 +59,18 @@ contract ECDSAStakeRegistry is _deregisterOperator(msg.sender); } + /** + * @notice Updates the signing key for an operator + * @dev Only callable by the operator themselves + * @param _newSigningKey The new signing key to set for the operator + */ + function updateOperatorSigningKey(address _newSigningKey) external { + if (!_operatorRegistered[msg.sender]) { + revert OperatorNotRegistered(); + } + _updateOperatorSigningKey(msg.sender, _newSigningKey); + } + /** * @notice Updates the StakeRegistry's view of one or more operators' stakes adding a new entry in their history of stake checkpoints, * @dev Queries stakes from the Eigenlayer core DelegationManager contract @@ -107,18 +120,18 @@ contract ECDSAStakeRegistry is /// @notice Verifies if the provided signature data is valid for the given data hash. /// @param _dataHash The hash of the data that was signed. - /// @param _signatureData Encoded signature data consisting of an array of signers, an array of signatures, and a reference block number. + /// @param _signatureData Encoded signature data consisting of an array of operators, an array of signatures, and a reference block number. /// @return The function selector that indicates the signature is valid according to ERC1271 standard. function isValidSignature( bytes32 _dataHash, bytes memory _signatureData ) external view returns (bytes4) { ( - address[] memory signers, + address[] memory operators, bytes[] memory signatures, uint32 referenceBlock ) = abi.decode(_signatureData, (address[], bytes[], uint32)); - _checkSignatures(_dataHash, signers, signatures, referenceBlock); + _checkSignatures(_dataHash, operators, signatures, referenceBlock); return IERC1271Upgradeable.isValidSignature.selector; } @@ -128,6 +141,37 @@ contract ECDSAStakeRegistry is return _quorum; } + /** + * @notice Retrieves the latest signing key for a given operator. + * @param _operator The address of the operator. + * @return The latest signing key of the operator. + */ + function getLastestOperatorSigningKey( + address _operator + ) external view returns (address) { + return address(uint160(_operatorSigningKeyHistory[_operator].latest())); + } + + /** + * @notice Retrieves the latest signing key for a given operator at a specific block number. + * @param _operator The address of the operator. + * @param _blockNumber The block number to get the operator's signing key. + * @return The signing key of the operator at the given block. + */ + function getOperatorSigningKeyAtBlock( + address _operator, + uint256 _blockNumber + ) external view returns (address) { + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _blockNumber + ) + ) + ); + } + /// @notice Retrieves the last recorded weight for a given operator. /// @param _operator The address of the operator. /// @return uint256 - The latest weight of the operator. @@ -313,9 +357,11 @@ contract ECDSAStakeRegistry is /// @dev registers an operator through a provided signature /// @param _operatorSignature Contains the operator's signature, salt, and expiry + /// @param _signingKey The signing key to add to the operator's history function _registerOperatorWithSig( address _operator, - ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature + ISignatureUtils.SignatureWithSaltAndExpiry memory _operatorSignature, + address _signingKey ) internal virtual { if (_operatorRegistered[_operator]) { revert OperatorAlreadyRegistered(); @@ -324,6 +370,7 @@ contract ECDSAStakeRegistry is _operatorRegistered[_operator] = true; int256 delta = _updateOperatorWeight(_operator); _updateTotalWeight(delta); + _updateOperatorSigningKey(_operator, _signingKey); IServiceManager(_serviceManager).registerOperatorToAVS( _operator, _operatorSignature @@ -331,6 +378,28 @@ contract ECDSAStakeRegistry is emit OperatorRegistered(_operator, _serviceManager); } + /// @dev Internal function to update an operator's signing key + /// @param _operator The address of the operator to update the signing key for + /// @param _newSigningKey The new signing key to set for the operator + function _updateOperatorSigningKey( + address _operator, + address _newSigningKey + ) internal { + address oldSigningKey = address( + uint160(_operatorSigningKeyHistory[_operator].latest()) + ); + if (_newSigningKey == oldSigningKey) { + return; + } + _operatorSigningKeyHistory[_operator].push(uint160(_newSigningKey)); + emit SigningKeyUpdate( + _operator, + block.number, + _newSigningKey, + oldSigningKey + ); + } + /// @notice Updates the weight of an operator and returns the previous and current weights. /// @param _operator The address of the operator to update the weight of. function _updateOperatorWeight( @@ -401,30 +470,33 @@ contract ECDSAStakeRegistry is /** * @notice Common logic to verify a batch of ECDSA signatures against a hash, using either last stake weight or at a specific block. * @param _dataHash The hash of the data the signers endorsed. - * @param _signers A collection of addresses that endorsed the data hash. + * @param _operators A collection of addresses that endorsed the data hash. * @param _signatures A collection of signatures matching the signers. * @param _referenceBlock The block number for evaluating stake weight; use max uint32 for latest weight. */ function _checkSignatures( bytes32 _dataHash, - address[] memory _signers, + address[] memory _operators, bytes[] memory _signatures, uint32 _referenceBlock ) internal view { - uint256 signersLength = _signers.length; - address lastSigner; + uint256 signersLength = _operators.length; + address currentOperator; + address lastOperator; + address signer; uint256 signedWeight; _validateSignaturesLength(signersLength, _signatures.length); for (uint256 i; i < signersLength; i++) { - address currentSigner = _signers[i]; + currentOperator = _operators[i]; + signer = _getOperatorSigningKey(currentOperator, _referenceBlock); - _validateSortedSigners(lastSigner, currentSigner); - _validateSignature(currentSigner, _dataHash, _signatures[i]); + _validateSortedSigners(lastOperator, currentOperator); + _validateSignature(signer, _dataHash, _signatures[i]); - lastSigner = currentSigner; + lastOperator = currentOperator; uint256 operatorWeight = _getOperatorWeight( - currentSigner, + currentOperator, _referenceBlock ); signedWeight += operatorWeight; @@ -474,6 +546,27 @@ contract ECDSAStakeRegistry is } } + /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. + /// @param _operator The operator to query their signing key history for + /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. + /// @return The weight of the operator. + function _getOperatorSigningKey( + address _operator, + uint32 _referenceBlock + ) internal view returns (address) { + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); + } + return + address( + uint160( + _operatorSigningKeyHistory[_operator].getAtBlock( + _referenceBlock + ) + ) + ); + } + /// @notice Retrieves the operator weight for a signer, either at the last checkpoint or a specified block. /// @param _signer The address of the signer whose weight is returned. /// @param _referenceBlock The block number to query the operator's weight at, or the maximum uint32 value for the last checkpoint. @@ -482,11 +575,10 @@ contract ECDSAStakeRegistry is address _signer, uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _operatorWeightHistory[_signer].latest(); - } else { - return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _operatorWeightHistory[_signer].getAtBlock(_referenceBlock); } /// @notice Retrieve the total stake weight at a specific block or the latest if not specified. @@ -496,11 +588,10 @@ contract ECDSAStakeRegistry is function _getTotalWeight( uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _totalWeightHistory.latest(); - } else { - return _totalWeightHistory.getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _totalWeightHistory.getAtBlock(_referenceBlock); } /// @notice Retrieves the threshold stake for a given reference block. @@ -510,11 +601,10 @@ contract ECDSAStakeRegistry is function _getThresholdStake( uint32 _referenceBlock ) internal view returns (uint256) { - if (_referenceBlock == type(uint32).max) { - return _thresholdWeightHistory.latest(); - } else { - return _thresholdWeightHistory.getAtBlock(_referenceBlock); + if (_referenceBlock >= block.number) { + revert InvalidReferenceBlock(); } + return _thresholdWeightHistory.getAtBlock(_referenceBlock); } /// @notice Validates that the cumulative stake of signed messages meets or exceeds the required threshold. diff --git a/solidity/contracts/avs/ECDSAStakeRegistryStorage.sol b/solidity/contracts/avs/ECDSAStakeRegistryStorage.sol index 9e59fd8f63..28a584ca5e 100644 --- a/solidity/contracts/avs/ECDSAStakeRegistryStorage.sol +++ b/solidity/contracts/avs/ECDSAStakeRegistryStorage.sol @@ -30,6 +30,10 @@ abstract contract ECDSAStakeRegistryStorage is /// @notice Defines the duration after which the stake's weight expires. uint256 internal _stakeExpiry; + /// @notice Maps an operator to their signing key history using checkpoints + mapping(address => CheckpointsUpgradeable.History) + internal _operatorSigningKeyHistory; + /// @notice Tracks the total stake history over time using checkpoints CheckpointsUpgradeable.History internal _totalWeightHistory; @@ -51,5 +55,5 @@ abstract contract ECDSAStakeRegistryStorage is // slither-disable-next-line shadowing-state /// @dev Reserves storage slots for future upgrades // solhint-disable-next-line - uint256[42] private __gap; + uint256[40] private __gap; } diff --git a/solidity/contracts/interfaces/avs/vendored/IECDSAStakeRegistryEventsAndErrors.sol b/solidity/contracts/interfaces/avs/vendored/IECDSAStakeRegistryEventsAndErrors.sol index 021d34db40..07f6323da0 100644 --- a/solidity/contracts/interfaces/avs/vendored/IECDSAStakeRegistryEventsAndErrors.sol +++ b/solidity/contracts/interfaces/avs/vendored/IECDSAStakeRegistryEventsAndErrors.sol @@ -12,8 +12,6 @@ struct Quorum { StrategyParams[] strategies; // An array of strategy parameters to define the quorum } -/// part of mock interfaces for vendoring necessary Eigenlayer contracts for the hyperlane AVS -/// @author Layr Labs, Inc. interface IECDSAStakeRegistryEventsAndErrors { /// @notice Emitted when the system registers an operator /// @param _operator The address of the registered operator @@ -61,7 +59,19 @@ interface IECDSAStakeRegistryEventsAndErrors { /// @notice Emits when setting a new threshold weight. event ThresholdWeightUpdated(uint256 _thresholdWeight); + /// @notice Emitted when an operator's signing key is updated + /// @param operator The address of the operator whose signing key was updated + /// @param updateBlock The block number at which the signing key was updated + /// @param newSigningKey The operator's signing key after the update + /// @param oldSigningKey The operator's signing key before the update + event SigningKeyUpdate( + address indexed operator, + uint256 indexed updateBlock, + address indexed newSigningKey, + address oldSigningKey + ); /// @notice Indicates when the lengths of the signers array and signatures array do not match. + error LengthMismatch(); /// @notice Indicates encountering an invalid length for the signers or signatures array. @@ -76,6 +86,9 @@ interface IECDSAStakeRegistryEventsAndErrors { /// @notice Thrown when missing operators in an update error MustUpdateAllOperators(); + /// @notice Reference blocks must be for blocks that have already been confirmed + error InvalidReferenceBlock(); + /// @notice Indicates operator weights were out of sync and the signed weight exceed the total error InvalidSignedWeight(); diff --git a/solidity/test/avs/HyperlaneServiceManager.t.sol b/solidity/test/avs/HyperlaneServiceManager.t.sol index 4ea9ce8f1e..80d49f9749 100644 --- a/solidity/test/avs/HyperlaneServiceManager.t.sol +++ b/solidity/test/avs/HyperlaneServiceManager.t.sol @@ -29,6 +29,7 @@ contract HyperlaneServiceManagerTest is EigenlayerBase { // Operator info uint256 operatorPrivateKey = 0xdeadbeef; address operator; + address avsSigningKey = address(0xc0ffee); bytes32 emptySalt; uint256 maxExpiry = type(uint256).max; @@ -97,9 +98,11 @@ contract HyperlaneServiceManagerTest is EigenlayerBase { emptySalt, maxExpiry ); + + vm.prank(operator); _ecdsaStakeRegistry.registerOperatorWithSignature( - operator, - operatorSignature + operatorSignature, + avsSigningKey ); // assert @@ -122,12 +125,13 @@ contract HyperlaneServiceManagerTest is EigenlayerBase { maxExpiry ); + vm.prank(operator); vm.expectRevert( "EIP1271SignatureUtils.checkSignature_EIP1271: signature not from signer" ); _ecdsaStakeRegistry.registerOperatorWithSignature( - operator, - operatorSignature + operatorSignature, + avsSigningKey ); // assert @@ -409,9 +413,10 @@ contract HyperlaneServiceManagerTest is EigenlayerBase { maxExpiry ); + vm.prank(operator); _ecdsaStakeRegistry.registerOperatorWithSignature( - operator, - operatorSignature + operatorSignature, + avsSigningKey ); } From b440d98be31a5100e8e50e948c8e12d991fcfd3b Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Tue, 28 May 2024 19:20:04 +0530 Subject: [PATCH 23/59] feat: CLI command for (de)registering from AVS (#3790) ### Description - CLI commands for registering/deregistering an operator from the AVS - uses the encrypted local key store as source of operator key - Web3Signer not implemented yet ### Drive-by changes None ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3598 (duplicate) - also fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3758 ### Backward compatibility Yes ### Testing Onchain https://holesky.etherscan.io/tx/0x95d95f6fcf1f80d0911124bd0a85ac848eecedc8ec39b3ceba6e1a30792c5651 and https://holesky.etherscan.io/tx/0xd1d1b56d45c30276596b73e040e3747ca7ef90977182f1912c5b399559d2a503 --------- Co-authored-by: Connor McEwen Co-authored-by: Yorke Rhodes --- .changeset/many-rice-wave.md | 6 +- .changeset/tall-tables-scream.md | 6 + solidity/script/avs/DeployAVS.s.sol | 30 +++- solidity/script/avs/eigenlayer_addresses.json | 2 + typescript/cli/cli.ts | 2 + typescript/cli/src/avs/config.ts | 19 ++ typescript/cli/src/avs/stakeRegistry.ts | 164 ++++++++++++++++++ typescript/cli/src/commands/avs.ts | 84 +++++++++ typescript/cli/src/consts.ts | 1 + typescript/cli/src/utils/files.ts | 9 + 10 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 .changeset/tall-tables-scream.md create mode 100644 typescript/cli/src/avs/config.ts create mode 100644 typescript/cli/src/avs/stakeRegistry.ts create mode 100644 typescript/cli/src/commands/avs.ts diff --git a/.changeset/many-rice-wave.md b/.changeset/many-rice-wave.md index 46b8eabc33..f7db1abdaf 100644 --- a/.changeset/many-rice-wave.md +++ b/.changeset/many-rice-wave.md @@ -1,7 +1,7 @@ --- -"@hyperlane-xyz/cli": patch -"@hyperlane-xyz/helloworld": patch -"@hyperlane-xyz/infra": patch +'@hyperlane-xyz/cli': patch +'@hyperlane-xyz/helloworld': patch +'@hyperlane-xyz/infra': patch --- fix: minor change was breaking in registry export diff --git a/.changeset/tall-tables-scream.md b/.changeset/tall-tables-scream.md new file mode 100644 index 0000000000..c16594638e --- /dev/null +++ b/.changeset/tall-tables-scream.md @@ -0,0 +1,6 @@ +--- +'@hyperlane-xyz/cli': minor +'@hyperlane-xyz/core': minor +--- + +Added support for registering/deregistering from the Hyperlane AVS diff --git a/solidity/script/avs/DeployAVS.s.sol b/solidity/script/avs/DeployAVS.s.sol index f2fce2d1d8..b38762be65 100644 --- a/solidity/script/avs/DeployAVS.s.sol +++ b/solidity/script/avs/DeployAVS.s.sol @@ -12,6 +12,7 @@ import {ProxyAdmin} from "../../contracts/upgrade/ProxyAdmin.sol"; import {TransparentUpgradeableProxy} from "../../contracts/upgrade/TransparentUpgradeableProxy.sol"; import {ECDSAStakeRegistry} from "../../contracts/avs/ECDSAStakeRegistry.sol"; import {Quorum, StrategyParams} from "../../contracts/interfaces/avs/vendored/IECDSAStakeRegistryEventsAndErrors.sol"; +import {ECDSAServiceManagerBase} from "../../contracts/avs/ECDSAServiceManagerBase.sol"; import {HyperlaneServiceManager} from "../../contracts/avs/HyperlaneServiceManager.sol"; import {TestPaymentCoordinator} from "../../contracts/test/avs/TestPaymentCoordinator.sol"; @@ -42,6 +43,11 @@ contract DeployAVS is Script { ); string memory json = vm.readFile(path); + proxyAdmin = ProxyAdmin( + json.readAddress( + string(abi.encodePacked(".", targetEnv, ".proxyAdmin")) + ) + ); avsDirectory = IAVSDirectory( json.readAddress( string(abi.encodePacked(".", targetEnv, ".avsDirectory")) @@ -88,15 +94,14 @@ contract DeployAVS is Script { } } - function run(string memory network) external { + function run(string memory network, string memory metadataUri) external { deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + address deployerAddress = vm.addr(deployerPrivateKey); _loadEigenlayerAddresses(network); vm.startBroadcast(deployerPrivateKey); - proxyAdmin = new ProxyAdmin(); - ECDSAStakeRegistry stakeRegistryImpl = new ECDSAStakeRegistry( delegationManager ); @@ -118,7 +123,7 @@ contract DeployAVS is Script { address(proxyAdmin), abi.encodeWithSelector( HyperlaneServiceManager.initialize.selector, - msg.sender + address(deployerAddress) ) ); @@ -131,7 +136,24 @@ contract DeployAVS is Script { quorum ) ); + + HyperlaneServiceManager hsm = HyperlaneServiceManager( + address(hsmProxy) + ); + require(success, "Failed to initialize ECDSAStakeRegistry"); + require( + ECDSAStakeRegistry(address(stakeRegistryProxy)).owner() == + address(deployerAddress), + "Owner of ECDSAStakeRegistry is not the deployer" + ); + require( + HyperlaneServiceManager(address(hsmProxy)).owner() == + address(deployerAddress), + "Owner of HyperlaneServiceManager is not the deployer" + ); + + hsm.updateAVSMetadataURI(metadataUri); console.log( "ECDSAStakeRegistry Implementation: ", diff --git a/solidity/script/avs/eigenlayer_addresses.json b/solidity/script/avs/eigenlayer_addresses.json index d8890a77be..60e2fceea0 100644 --- a/solidity/script/avs/eigenlayer_addresses.json +++ b/solidity/script/avs/eigenlayer_addresses.json @@ -1,5 +1,6 @@ { "ethereum": { + "proxyAdmin": "0x75EE15Ee1B4A75Fa3e2fDF5DF3253c25599cc659", "delegationManager": "0x39053D51B77DC0d36036Fc1fCc8Cb819df8Ef37A", "avsDirectory": "0x135DDa560e946695d6f155dACaFC6f1F25C1F5AF", "paymentCoordinator": "", @@ -19,6 +20,7 @@ ] }, "holesky": { + "proxyAdmin": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", "delegationManager": "0xA44151489861Fe9e3055d95adC98FbD462B948e7", "avsDirectory": "0x055733000064333CaDDbC92763c58BF0192fFeBf", "paymentCoordinator": "", diff --git a/typescript/cli/cli.ts b/typescript/cli/cli.ts index 39cc3669fe..a8b9127f3d 100644 --- a/typescript/cli/cli.ts +++ b/typescript/cli/cli.ts @@ -5,6 +5,7 @@ import yargs from 'yargs'; import type { LogFormat, LogLevel } from '@hyperlane-xyz/utils'; import './env.js'; +import { avsCommand } from './src/commands/avs.js'; import { chainsCommand } from './src/commands/chains.js'; import { configCommand } from './src/commands/config.js'; import { deployCommand } from './src/commands/deploy.js'; @@ -49,6 +50,7 @@ try { }, contextMiddleware, ]) + .command(avsCommand) .command(chainsCommand) .command(configCommand) .command(deployCommand) diff --git a/typescript/cli/src/avs/config.ts b/typescript/cli/src/avs/config.ts new file mode 100644 index 0000000000..681ed9dee9 --- /dev/null +++ b/typescript/cli/src/avs/config.ts @@ -0,0 +1,19 @@ +import { ChainMap } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +interface AVSContracts { + avsDirectory: Address; + proxyAdmin: Address; + ecdsaStakeRegistry: Address; + hyperlaneServiceManager: Address; +} + +// TODO: move to registry +export const avsAddresses: ChainMap = { + holesky: { + avsDirectory: '0x055733000064333CaDDbC92763c58BF0192fFeBf', + proxyAdmin: '0x33dB966328Ea213b0f76eF96CA368AB37779F065', + ecdsaStakeRegistry: '0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72', + hyperlaneServiceManager: '0xc76E477437065093D353b7d56c81ff54D167B0Ab', + }, +}; diff --git a/typescript/cli/src/avs/stakeRegistry.ts b/typescript/cli/src/avs/stakeRegistry.ts new file mode 100644 index 0000000000..9d23bffaa5 --- /dev/null +++ b/typescript/cli/src/avs/stakeRegistry.ts @@ -0,0 +1,164 @@ +import { password } from '@inquirer/prompts'; +import { BigNumberish, Wallet, utils } from 'ethers'; + +import { + ECDSAStakeRegistry__factory, + TestAVSDirectory__factory, +} from '@hyperlane-xyz/core'; +import { ChainName } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { WriteCommandContext } from '../context/types.js'; +import { log, logBlue } from '../logger.js'; +import { readFileAtPath, resolvePath } from '../utils/files.js'; + +import { avsAddresses } from './config.js'; + +export type SignatureWithSaltAndExpiryStruct = { + signature: utils.BytesLike; + salt: utils.BytesLike; + expiry: BigNumberish; +}; + +export async function registerOperatorWithSignature({ + context, + chain, + operatorKeyPath, + avsSigningKey, +}: { + context: WriteCommandContext; + chain: ChainName; + operatorKeyPath: string; + avsSigningKey: Address; +}) { + const { multiProvider } = context; + + const operatorAsSigner = await readOperatorFromEncryptedJson(operatorKeyPath); + + const provider = multiProvider.getProvider(chain); + const connectedSigner = operatorAsSigner.connect(provider); + + const stakeRegistryAddress = avsAddresses[chain].ecdsaStakeRegistry; + + const ecdsaStakeRegistry = ECDSAStakeRegistry__factory.connect( + stakeRegistryAddress, + connectedSigner, + ); + + const domainId = multiProvider.getDomainId(chain); + const avsDirectoryAddress = avsAddresses[chain].avsDirectory; + const operatorSignature = await getOperatorSignature( + domainId, + avsAddresses[chain].hyperlaneServiceManager, + avsDirectoryAddress, + operatorAsSigner, + connectedSigner, + ); + + // check if the operator is already registered + const operatorStatus = await ecdsaStakeRegistry.operatorRegistered( + operatorAsSigner.address, + ); + if (operatorStatus) { + logBlue( + `Operator ${operatorAsSigner.address} already registered to Hyperlane AVS`, + ); + return; + } + + log( + `Registering operator ${operatorAsSigner.address} attesting ${avsSigningKey} with signature on ${chain}...`, + ); + await multiProvider.handleTx( + chain, + ecdsaStakeRegistry.registerOperatorWithSignature( + operatorSignature, + avsSigningKey, + ), + ); + logBlue(`Operator ${operatorAsSigner.address} registered to Hyperlane AVS`); +} + +export async function deregisterOperator({ + context, + chain, + operatorKeyPath, +}: { + context: WriteCommandContext; + chain: ChainName; + operatorKeyPath: string; +}) { + const { multiProvider } = context; + + const operatorAsSigner = await readOperatorFromEncryptedJson(operatorKeyPath); + + const provider = multiProvider.getProvider(chain); + const connectedSigner = operatorAsSigner.connect(provider); + + const stakeRegistryAddress = avsAddresses[chain].ecdsaStakeRegistry; + + const ecdsaStakeRegistry = ECDSAStakeRegistry__factory.connect( + stakeRegistryAddress, + connectedSigner, + ); + + log(`Deregistering operator ${operatorAsSigner.address} on ${chain}...`); + await multiProvider.handleTx(chain, ecdsaStakeRegistry.deregisterOperator()); + logBlue( + `Operator ${operatorAsSigner.address} deregistered from Hyperlane AVS`, + ); +} + +async function readOperatorFromEncryptedJson( + operatorKeyPath: string, +): Promise { + const encryptedJson = readFileAtPath(resolvePath(operatorKeyPath)); + + const keyFilePassword = await password({ + mask: '*', + message: 'Enter the password for the operator key file: ', + }); + + return await Wallet.fromEncryptedJson(encryptedJson, keyFilePassword); +} + +async function getOperatorSignature( + domain: number, + serviceManager: Address, + avsDirectory: Address, + operator: Wallet, + signer: Wallet, +): Promise { + const avsDirectoryContract = TestAVSDirectory__factory.connect( + avsDirectory, + signer, + ); + + // random salt is ok, because we register the operator right after + const salt = utils.hexZeroPad(utils.randomBytes(32), 32); + // give a expiry timestamp 1 hour from now + const expiry = utils.hexZeroPad( + utils.hexlify(Math.floor(Date.now() / 1000) + 60 * 60), + 32, + ); + + const signingHash = + await avsDirectoryContract.calculateOperatorAVSRegistrationDigestHash( + operator.address, + serviceManager, + salt, + expiry, + ); + + // Eigenlayer's AVSDirectory expects the signature over raw signed hash instead of EIP-191 compatible toEthSignedMessageHash + // see https://github.com/Layr-Labs/eigenlayer-contracts/blob/ef2ea4a7459884f381057aa9bbcd29c7148cfb63/src/contracts/libraries/EIP1271SignatureUtils.sol#L22 + const signature = operator + ._signingKey() + .signDigest(utils.arrayify(signingHash)); + + return { + signature: utils.joinSignature(signature), + salt, + expiry, + }; +} diff --git a/typescript/cli/src/commands/avs.ts b/typescript/cli/src/commands/avs.ts new file mode 100644 index 0000000000..04a51b6b63 --- /dev/null +++ b/typescript/cli/src/commands/avs.ts @@ -0,0 +1,84 @@ +import { CommandModule, Options } from 'yargs'; + +import { ChainName } from '@hyperlane-xyz/sdk'; +import { Address } from '@hyperlane-xyz/utils'; + +import { + deregisterOperator, + registerOperatorWithSignature, +} from '../avs/stakeRegistry.js'; +import { CommandModuleWithWriteContext } from '../context/types.js'; +import { log } from '../logger.js'; + +/** + * Parent command + */ +export const avsCommand: CommandModule = { + command: 'avs', + describe: 'Interact with the Hyperlane AVS', + builder: (yargs) => + yargs + .command(registerCommand) + .command(deregisterCommand) + .version(false) + .demandCommand(), + handler: () => log('Command required'), +}; + +/** + * Registration command + */ +export const registrationOptions: { [k: string]: Options } = { + chain: { + type: 'string', + description: 'Chain to interact with the AVS on', + demandOption: true, + choices: ['holesky', 'ethereum'], + }, + operatorKeyPath: { + type: 'string', + description: 'Path to the operator key file', + demandOption: true, + }, + avsSigningKey: { + type: 'string', + description: 'Address of the AVS signing key', + demandOption: true, + }, +}; + +const registerCommand: CommandModuleWithWriteContext<{ + chain: ChainName; + operatorKeyPath: string; + avsSigningKey: Address; +}> = { + command: 'register', + describe: 'Register operator with the AVS', + builder: registrationOptions, + handler: async ({ context, chain, operatorKeyPath, avsSigningKey }) => { + await registerOperatorWithSignature({ + context, + chain, + operatorKeyPath, + avsSigningKey, + }); + process.exit(0); + }, +}; + +const deregisterCommand: CommandModuleWithWriteContext<{ + chain: ChainName; + operatorKeyPath: string; +}> = { + command: 'deregister', + describe: 'Deregister yourself with the AVS', + builder: registrationOptions, + handler: async ({ context, chain, operatorKeyPath }) => { + await deregisterOperator({ + context, + chain, + operatorKeyPath, + }); + process.exit(0); + }, +}; diff --git a/typescript/cli/src/consts.ts b/typescript/cli/src/consts.ts index 9e05f2fcb6..584ad97b91 100644 --- a/typescript/cli/src/consts.ts +++ b/typescript/cli/src/consts.ts @@ -1,3 +1,4 @@ export const MINIMUM_CORE_DEPLOY_GAS = (1e8).toString(); export const MINIMUM_WARP_DEPLOY_GAS = (1e7).toString(); export const MINIMUM_TEST_SEND_GAS = (3e5).toString(); +export const MINIMUM_AVS_GAS = (3e6).toString(); diff --git a/typescript/cli/src/utils/files.ts b/typescript/cli/src/utils/files.ts index 9734b34885..844eb4b798 100644 --- a/typescript/cli/src/utils/files.ts +++ b/typescript/cli/src/utils/files.ts @@ -1,6 +1,7 @@ import { input } from '@inquirer/prompts'; import select from '@inquirer/select'; import fs from 'fs'; +import os from 'os'; import path from 'path'; import { parse as yamlParse, stringify as yamlStringify } from 'yaml'; @@ -15,6 +16,14 @@ export type ArtifactsFile = { description: string; }; +export function resolvePath(filePath: string): string { + if (filePath.startsWith('~')) { + const homedir = os.homedir(); + return path.join(homedir, filePath.slice(1)); + } + return filePath; +} + export function isFile(filepath: string) { if (!filepath) return false; try { From 806f9842de78da0733e8f91b2c10b01435e86cad Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Tue, 28 May 2024 16:16:24 +0200 Subject: [PATCH 24/59] fix: duplicate word typos (#3854) --- rust/agents/relayer/src/settings/matching_list.rs | 6 +++--- rust/agents/relayer/src/settings/mod.rs | 2 +- rust/agents/scraper/src/settings.rs | 2 +- rust/agents/validator/src/settings.rs | 2 +- rust/hyperlane-base/src/contract_sync/mod.rs | 2 +- rust/hyperlane-base/src/settings/mod.rs | 2 +- rust/hyperlane-base/src/settings/parser/mod.rs | 2 +- typescript/sdk/src/metadata/agentConfig.ts | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/rust/agents/relayer/src/settings/matching_list.rs b/rust/agents/relayer/src/settings/matching_list.rs index da037f19bc..097855f175 100644 --- a/rust/agents/relayer/src/settings/matching_list.rs +++ b/rust/agents/relayer/src/settings/matching_list.rs @@ -1,4 +1,4 @@ -//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape +//! The correct settings shape is defined in the TypeScript SDK metadata. While the exact shape //! and validations it defines are not applied here, we should mirror them. //! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK. @@ -267,13 +267,13 @@ impl<'a> From<&'a HyperlaneMessage> for MatchInfo<'a> { impl MatchingList { /// Check if a message matches any of the rules. - /// - `default`: What to return if the the matching list is empty. + /// - `default`: What to return if the matching list is empty. pub fn msg_matches(&self, msg: &HyperlaneMessage, default: bool) -> bool { self.matches(msg.into(), default) } /// Check if a message matches any of the rules. - /// - `default`: What to return if the the matching list is empty. + /// - `default`: What to return if the matching list is empty. fn matches(&self, info: MatchInfo, default: bool) -> bool { if let Some(rules) = &self.0 { matches_any_rule(rules.iter(), info) diff --git a/rust/agents/relayer/src/settings/mod.rs b/rust/agents/relayer/src/settings/mod.rs index c6934a77c7..2e6b005731 100644 --- a/rust/agents/relayer/src/settings/mod.rs +++ b/rust/agents/relayer/src/settings/mod.rs @@ -1,6 +1,6 @@ //! Relayer configuration //! -//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape +//! The correct settings shape is defined in the TypeScript SDK metadata. While the exact shape //! and validations it defines are not applied here, we should mirror them. //! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK. diff --git a/rust/agents/scraper/src/settings.rs b/rust/agents/scraper/src/settings.rs index b4bdfbb4d5..b6c9eb7e49 100644 --- a/rust/agents/scraper/src/settings.rs +++ b/rust/agents/scraper/src/settings.rs @@ -1,6 +1,6 @@ //! Scraper configuration. //! -//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape +//! The correct settings shape is defined in the TypeScript SDK metadata. While the exact shape //! and validations it defines are not applied here, we should mirror them. //! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK. diff --git a/rust/agents/validator/src/settings.rs b/rust/agents/validator/src/settings.rs index f6870a31c5..70158d2677 100644 --- a/rust/agents/validator/src/settings.rs +++ b/rust/agents/validator/src/settings.rs @@ -1,6 +1,6 @@ //! Validator configuration. //! -//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape +//! The correct settings shape is defined in the TypeScript SDK metadata. While the exact shape //! and validations it defines are not applied here, we should mirror them. //! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK. diff --git a/rust/hyperlane-base/src/contract_sync/mod.rs b/rust/hyperlane-base/src/contract_sync/mod.rs index b97e3e5f4b..85bf36c1c5 100644 --- a/rust/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/mod.rs @@ -82,7 +82,7 @@ where // from the loop (the sleep duration) #[allow(clippy::never_loop)] CursorAction::Query(range) => loop { - debug!(?range, "Looking for for events in index range"); + debug!(?range, "Looking for events in index range"); let logs = match self.indexer.fetch_logs(range.clone()).await { Ok(logs) => logs, diff --git a/rust/hyperlane-base/src/settings/mod.rs b/rust/hyperlane-base/src/settings/mod.rs index e809444328..aa7bee5341 100644 --- a/rust/hyperlane-base/src/settings/mod.rs +++ b/rust/hyperlane-base/src/settings/mod.rs @@ -1,6 +1,6 @@ //! Common settings and configuration for Hyperlane agents //! -//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape +//! The correct settings shape is defined in the TypeScript SDK metadata. While the exact shape //! and validations it defines are not applied here, we should mirror them. //! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK. //! diff --git a/rust/hyperlane-base/src/settings/parser/mod.rs b/rust/hyperlane-base/src/settings/parser/mod.rs index 30430e04ec..3c3a3aa656 100644 --- a/rust/hyperlane-base/src/settings/parser/mod.rs +++ b/rust/hyperlane-base/src/settings/parser/mod.rs @@ -1,6 +1,6 @@ //! This module is responsible for parsing the agent's settings. //! -//! The correct settings shape is defined in the TypeScript SDK metadata. While the the exact shape +//! The correct settings shape is defined in the TypeScript SDK metadata. While the exact shape //! and validations it defines are not applied here, we should mirror them. //! ANY CHANGES HERE NEED TO BE REFLECTED IN THE TYPESCRIPT SDK. diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 3c16651f66..0885a3e4a2 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -106,7 +106,7 @@ const AgentCosmosChainMetadataSchema = z.object({ amount: z .string() .regex(/^(\d*[.])?\d+$/) - .describe('The the gas price, in denom, to pay for each unit of gas'), + .describe('The gas price, in denom, to pay for each unit of gas'), }), contractAddressBytes: z .number() From 8a6654477eccc1316bf2933523c7c3e5a02625c6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 14:46:39 +0000 Subject: [PATCH 25/59] Version Packages (#3842) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @hyperlane-xyz/core@3.13.0 ### Minor Changes - babe816f8: Support xERC20 and xERC20 Lockbox in SDK and CLI - b440d98be: Added support for registering/deregistering from the Hyperlane AVS ### Patch Changes - Updated dependencies [0cf692e73] - @hyperlane-xyz/utils@3.13.0 ## @hyperlane-xyz/cli@3.13.0 ### Minor Changes - b22a0f453: Add hyperlane validator address command to retrieve validator address from AWS - 39ea7cdef: Implement multi collateral warp routes - babe816f8: Support xERC20 and xERC20 Lockbox in SDK and CLI - b440d98be: Added support for registering/deregistering from the Hyperlane AVS ### Patch Changes - b6b26e2bb: fix: minor change was breaking in registry export - Updated dependencies [39ea7cdef] - Updated dependencies [babe816f8] - Updated dependencies [0cf692e73] - @hyperlane-xyz/sdk@3.13.0 - @hyperlane-xyz/utils@3.13.0 ## @hyperlane-xyz/sdk@3.13.0 ### Minor Changes - 39ea7cdef: Implement multi collateral warp routes - babe816f8: Support xERC20 and xERC20 Lockbox in SDK and CLI - 0cf692e73: Implement metadata builder fetching from message ### Patch Changes - Updated dependencies [babe816f8] - Updated dependencies [b440d98be] - Updated dependencies [0cf692e73] - @hyperlane-xyz/core@3.13.0 - @hyperlane-xyz/utils@3.13.0 ## @hyperlane-xyz/utils@3.13.0 ### Minor Changes - 0cf692e73: Implement metadata builder fetching from message ## @hyperlane-xyz/helloworld@3.13.0 ### Patch Changes - b6b26e2bb: fix: minor change was breaking in registry export - Updated dependencies [39ea7cdef] - Updated dependencies [babe816f8] - Updated dependencies [b440d98be] - Updated dependencies [0cf692e73] - @hyperlane-xyz/sdk@3.13.0 - @hyperlane-xyz/core@3.13.0 ## @hyperlane-xyz/infra@3.13.0 ### Minor Changes - 39ea7cdef: Implement multi collateral warp routes - 0cf692e73: Implement metadata builder fetching from message ### Patch Changes - b6b26e2bb: fix: minor change was breaking in registry export - Updated dependencies [b6b26e2bb] - Updated dependencies [39ea7cdef] - Updated dependencies [babe816f8] - Updated dependencies [0cf692e73] - @hyperlane-xyz/helloworld@3.13.0 - @hyperlane-xyz/sdk@3.13.0 - @hyperlane-xyz/utils@3.13.0 ## @hyperlane-xyz/ccip-server@3.13.0 Co-authored-by: github-actions[bot] --- .changeset/many-rice-wave.md | 7 ------- .changeset/neat-ducks-own.md | 5 ----- .changeset/nice-rivers-own.md | 7 ------- .changeset/perfect-seahorses-add.md | 7 ------- .changeset/tall-tables-scream.md | 6 ------ .changeset/witty-vans-return.md | 7 ------- solidity/CHANGELOG.md | 12 ++++++++++++ solidity/package.json | 4 ++-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 18 ++++++++++++++++++ typescript/cli/package.json | 6 +++--- typescript/cli/src/version.ts | 2 +- typescript/helloworld/CHANGELOG.md | 12 ++++++++++++ typescript/helloworld/package.json | 6 +++--- typescript/infra/CHANGELOG.md | 18 ++++++++++++++++++ typescript/infra/package.json | 8 ++++---- typescript/sdk/CHANGELOG.md | 16 ++++++++++++++++ typescript/sdk/package.json | 6 +++--- typescript/utils/CHANGELOG.md | 6 ++++++ typescript/utils/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 22 files changed, 116 insertions(+), 71 deletions(-) delete mode 100644 .changeset/many-rice-wave.md delete mode 100644 .changeset/neat-ducks-own.md delete mode 100644 .changeset/nice-rivers-own.md delete mode 100644 .changeset/perfect-seahorses-add.md delete mode 100644 .changeset/tall-tables-scream.md delete mode 100644 .changeset/witty-vans-return.md diff --git a/.changeset/many-rice-wave.md b/.changeset/many-rice-wave.md deleted file mode 100644 index f7db1abdaf..0000000000 --- a/.changeset/many-rice-wave.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/cli': patch -'@hyperlane-xyz/helloworld': patch -'@hyperlane-xyz/infra': patch ---- - -fix: minor change was breaking in registry export diff --git a/.changeset/neat-ducks-own.md b/.changeset/neat-ducks-own.md deleted file mode 100644 index af030bd5e6..0000000000 --- a/.changeset/neat-ducks-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -Add hyperlane validator address command to retrieve validator address from AWS diff --git a/.changeset/nice-rivers-own.md b/.changeset/nice-rivers-own.md deleted file mode 100644 index 8000edf814..0000000000 --- a/.changeset/nice-rivers-own.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/infra': minor -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor ---- - -Implement multi collateral warp routes diff --git a/.changeset/perfect-seahorses-add.md b/.changeset/perfect-seahorses-add.md deleted file mode 100644 index 372ca45e87..0000000000 --- a/.changeset/perfect-seahorses-add.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/sdk': minor -'@hyperlane-xyz/core': minor ---- - -Support xERC20 and xERC20 Lockbox in SDK and CLI diff --git a/.changeset/tall-tables-scream.md b/.changeset/tall-tables-scream.md deleted file mode 100644 index c16594638e..0000000000 --- a/.changeset/tall-tables-scream.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor -'@hyperlane-xyz/core': minor ---- - -Added support for registering/deregistering from the Hyperlane AVS diff --git a/.changeset/witty-vans-return.md b/.changeset/witty-vans-return.md deleted file mode 100644 index 2ed82ae27d..0000000000 --- a/.changeset/witty-vans-return.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@hyperlane-xyz/infra': minor -'@hyperlane-xyz/utils': minor -'@hyperlane-xyz/sdk': minor ---- - -Implement metadata builder fetching from message diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index e1735ad8e7..f8cee2164e 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,17 @@ # @hyperlane-xyz/core +## 3.13.0 + +### Minor Changes + +- babe816f8: Support xERC20 and xERC20 Lockbox in SDK and CLI +- b440d98be: Added support for registering/deregistering from the Hyperlane AVS + +### Patch Changes + +- Updated dependencies [0cf692e73] + - @hyperlane-xyz/utils@3.13.0 + ## 3.12.0 ### Patch Changes diff --git a/solidity/package.json b/solidity/package.json index 3115dab148..bdbe1f6598 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.12.2", + "version": "3.13.0", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "3.12.2", + "@hyperlane-xyz/utils": "3.13.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index bcee12745f..40e0e5390f 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 3.13.0 + ## 3.12.0 ## 3.11.1 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index f7a9829c83..cc07a17b33 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "3.12.2", + "version": "3.13.0", "description": "CCIP server", "typings": "dist/index.d.ts", "typedocMain": "src/index.ts", diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index 8f42fe354f..c62b58046f 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,23 @@ # @hyperlane-xyz/cli +## 3.13.0 + +### Minor Changes + +- b22a0f453: Add hyperlane validator address command to retrieve validator address from AWS +- 39ea7cdef: Implement multi collateral warp routes +- babe816f8: Support xERC20 and xERC20 Lockbox in SDK and CLI +- b440d98be: Added support for registering/deregistering from the Hyperlane AVS + +### Patch Changes + +- b6b26e2bb: fix: minor change was breaking in registry export +- Updated dependencies [39ea7cdef] +- Updated dependencies [babe816f8] +- Updated dependencies [0cf692e73] + - @hyperlane-xyz/sdk@3.13.0 + - @hyperlane-xyz/utils@3.13.0 + ## 3.12.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 311f505858..645d3a48b8 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/cli", - "version": "3.12.2", + "version": "3.13.0", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.12.2", - "@hyperlane-xyz/utils": "3.12.2", + "@hyperlane-xyz/sdk": "3.13.0", + "@hyperlane-xyz/utils": "3.13.0", "@inquirer/prompts": "^3.0.0", "asn1.js": "^5.4.1", "bignumber.js": "^9.1.1", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index f1447b4559..9fb80ae086 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.12.2'; +export const VERSION = '3.13.0'; diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 979fb82851..f5e8404bec 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,17 @@ # @hyperlane-xyz/helloworld +## 3.13.0 + +### Patch Changes + +- b6b26e2bb: fix: minor change was breaking in registry export +- Updated dependencies [39ea7cdef] +- Updated dependencies [babe816f8] +- Updated dependencies [b440d98be] +- Updated dependencies [0cf692e73] + - @hyperlane-xyz/sdk@3.13.0 + - @hyperlane-xyz/core@3.13.0 + ## 3.12.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 64d3140aab..7d3f62b3d9 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "3.12.2", + "version": "3.13.0", "dependencies": { - "@hyperlane-xyz/core": "3.12.2", + "@hyperlane-xyz/core": "3.13.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.12.2", + "@hyperlane-xyz/sdk": "3.13.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 21b608413b..0696269d8f 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,23 @@ # @hyperlane-xyz/infra +## 3.13.0 + +### Minor Changes + +- 39ea7cdef: Implement multi collateral warp routes +- 0cf692e73: Implement metadata builder fetching from message + +### Patch Changes + +- b6b26e2bb: fix: minor change was breaking in registry export +- Updated dependencies [b6b26e2bb] +- Updated dependencies [39ea7cdef] +- Updated dependencies [babe816f8] +- Updated dependencies [0cf692e73] + - @hyperlane-xyz/helloworld@3.13.0 + - @hyperlane-xyz/sdk@3.13.0 + - @hyperlane-xyz/utils@3.13.0 + ## 3.12.0 ### Patch Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 4f8fcb014d..33f8d61947 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.12.2", + "version": "3.13.0", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -12,10 +12,10 @@ "@ethersproject/experimental": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@hyperlane-xyz/helloworld": "3.12.2", + "@hyperlane-xyz/helloworld": "3.13.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.12.2", - "@hyperlane-xyz/utils": "3.12.2", + "@hyperlane-xyz/sdk": "3.13.0", + "@hyperlane-xyz/utils": "3.13.0", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@solana/web3.js": "^1.78.0", "asn1.js": "5.4.1", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 152e40bd61..b484d11bc1 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,21 @@ # @hyperlane-xyz/sdk +## 3.13.0 + +### Minor Changes + +- 39ea7cdef: Implement multi collateral warp routes +- babe816f8: Support xERC20 and xERC20 Lockbox in SDK and CLI +- 0cf692e73: Implement metadata builder fetching from message + +### Patch Changes + +- Updated dependencies [babe816f8] +- Updated dependencies [b440d98be] +- Updated dependencies [0cf692e73] + - @hyperlane-xyz/core@3.13.0 + - @hyperlane-xyz/utils@3.13.0 + ## 3.12.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 342287e223..ef58f43cda 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "3.12.2", + "version": "3.13.0", "dependencies": { "@aws-sdk/client-s3": "^3.74.0", "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", - "@hyperlane-xyz/core": "3.12.2", - "@hyperlane-xyz/utils": "3.12.2", + "@hyperlane-xyz/core": "3.13.0", + "@hyperlane-xyz/utils": "3.13.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@solana/spl-token": "^0.3.8", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index f729115183..0dce2d1b6b 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,11 @@ # @hyperlane-xyz/utils +## 3.13.0 + +### Minor Changes + +- 0cf692e73: Implement metadata builder fetching from message + ## 3.12.0 ### Minor Changes diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 094b5b053f..260348910d 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.12.2", + "version": "3.13.0", "dependencies": { "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", diff --git a/yarn.lock b/yarn.lock index f8e0a469ec..9fc2aab505 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5691,8 +5691,8 @@ __metadata: "@aws-sdk/client-kms": "npm:^3.577.0" "@aws-sdk/client-s3": "npm:^3.577.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.12.2" - "@hyperlane-xyz/utils": "npm:3.12.2" + "@hyperlane-xyz/sdk": "npm:3.13.0" + "@hyperlane-xyz/utils": "npm:3.13.0" "@inquirer/prompts": "npm:^3.0.0" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -5720,12 +5720,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:3.12.2, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:3.13.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.12.2" + "@hyperlane-xyz/utils": "npm:3.13.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -5773,13 +5773,13 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/helloworld@npm:3.12.2, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:3.13.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.12.2" + "@hyperlane-xyz/core": "npm:3.13.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.12.2" + "@hyperlane-xyz/sdk": "npm:3.13.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -5824,10 +5824,10 @@ __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.12.2" + "@hyperlane-xyz/helloworld": "npm:3.13.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.12.2" - "@hyperlane-xyz/utils": "npm:3.12.2" + "@hyperlane-xyz/sdk": "npm:3.13.0" + "@hyperlane-xyz/utils": "npm:3.13.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -5887,15 +5887,15 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:3.12.2, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:3.13.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: "@aws-sdk/client-s3": "npm:^3.74.0" "@cosmjs/cosmwasm-stargate": "npm:^0.31.3" "@cosmjs/stargate": "npm:^0.31.3" - "@hyperlane-xyz/core": "npm:3.12.2" - "@hyperlane-xyz/utils": "npm:3.12.2" + "@hyperlane-xyz/core": "npm:3.13.0" + "@hyperlane-xyz/utils": "npm:3.13.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -5963,7 +5963,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/utils@npm:3.12.2, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:3.13.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: From db9f733e4f4b82f9fd85d2a1285d95ff22519209 Mon Sep 17 00:00:00 2001 From: Trevor Porter Date: Mon, 3 Jun 2024 15:48:33 +0100 Subject: [PATCH 26/59] fix: use secret RPC urls in infra, apply environment-specific registry overrides (#3875) ### Description A while back, it seems secret RPC URLs stopped being used by monorepo services (see https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3179/files#diff-ba3b7bea2b5c046acd88d25ed382df875b49d77e061f2ef8347ffcc741c0353eR34). This adds them back. Some things to note on the approach: - Sadly, due to the way some of the infra config generation works, we're forced to use synchronous interfaces for getting chain metadata in a bunch of places. This is why `getRegistry` returns a `FileSystemRegistry`, which is a `SynchronousRegistry`. - Applying secrets / overrides requires us to do things async for two reasons -- one is just that fetching from GCP secrets is an async operation, the other is that a `MergedRegistry` is not a `SynchronousRegistry`. The approach I've taken is that there are two ways of getting a registry - using `getRegistry` will give the synchronous version that can continue to be used by our sync config generation, but it won't have any environment-specific overrides (tx overrides or secrets). To get an async registry with environment-specific overrides, the `getRegistry` function on the EnvironmentConfig is used. - Overrides are now more succinct as the merging happens later in the pipeline ### Drive-by changes - Started moving away from using the gcloud CLI under the hood for GCP operations and toward using the GCP secret SDK - Updated the mainnet3 key funder to fix an incident, but will follow up with a fresh deploy once this is mergd ### Related issues - These are the code changes for https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3774, but I'll follow up with fresh deploys for the monorepo services ### Backward compatibility ### Testing --------- Co-authored-by: Connor McEwen Co-authored-by: Yorke Rhodes Co-authored-by: J M Rossy --- rust/config/mainnet_config.json | 49 ++- .../config/environments/mainnet3/chains.ts | 28 +- .../config/environments/mainnet3/funding.ts | 2 +- .../config/environments/mainnet3/index.ts | 48 +-- .../infra/config/environments/test/index.ts | 9 +- .../config/environments/testnet4/chains.ts | 15 +- .../config/environments/testnet4/index.ts | 33 +- typescript/infra/config/registry.ts | 39 ++- typescript/infra/package.json | 1 + typescript/infra/scripts/agent-utils.ts | 58 ++-- .../scripts/agents/update-agent-config.ts | 8 +- typescript/infra/scripts/check-rpc-urls.ts | 10 +- .../funding/fund-keys-from-deployer.ts | 4 +- typescript/infra/scripts/helloworld/kathy.ts | 12 +- .../infra/scripts/print-chain-metadatas.ts | 12 +- typescript/infra/scripts/print-gas-prices.ts | 88 +++-- .../infra/scripts/print-token-prices.ts | 12 +- typescript/infra/src/agents/index.ts | 8 +- typescript/infra/src/config/chain.ts | 81 ++++- typescript/infra/src/config/environment.ts | 8 +- typescript/infra/src/utils/gcloud.ts | 30 +- typescript/infra/src/utils/utils.ts | 4 + typescript/sdk/src/metadata/agentConfig.ts | 10 + yarn.lock | 315 +++++++++++++++++- 24 files changed, 722 insertions(+), 162 deletions(-) diff --git a/rust/config/mainnet_config.json b/rust/config/mainnet_config.json index 25bfbb8954..00ebf00f00 100644 --- a/rust/config/mainnet_config.json +++ b/rust/config/mainnet_config.json @@ -634,13 +634,19 @@ }, "injective": { "bech32Prefix": "inj", + "blockExplorers": [ + ], "blocks": { + "confirmations": 1, + "estimateBlockTime": 1, "reorgPeriod": 10 }, "canonicalAsset": "inj", "chainId": "injective-1", "contractAddressBytes": 20, - "domainId": "6909546", + "displayName": "Injective", + "domainId": 6909546, + "gasCurrencyCoinGeckoId": "injective-protocol", "gasPrice": { "amount": "700000000", "denom": "inj" @@ -658,12 +664,24 @@ "mailbox": "0x0f7fb53961d70687e352aa55cb329ca76edc0c19", "merkleTreeHook": "0x568ad3638447f07def384969f4ea39fae3802962", "name": "injective", + "nativeToken": { + "decimals": 18, + "denom": "inj", + "name": "Injective", + "symbol": "INJ" + }, "protocol": "cosmos", + "restUrls": [ + { + "http": "https://sentry.lcd.injective.network:443" + } + ], "rpcUrls": [ { - "http": "https://injective-rpc.polkachu.com" + "http": "https://sentry.tm.injective.network:443" } ], + "slip44": 118, "validatorAnnounce": "0x1fb225b2fcfbe75e614a1d627de97ff372242eed" }, "mantapacific": { @@ -837,13 +855,25 @@ }, "neutron": { "bech32Prefix": "neutron", + "blockExplorers": [ + { + "apiUrl": "https://www.mintscan.io/neutron", + "family": "other", + "name": "Mintscan", + "url": "https://www.mintscan.io/neutron" + } + ], "blocks": { + "confirmations": 1, + "estimateBlockTime": 3, "reorgPeriod": 1 }, "canonicalAsset": "untrn", "chainId": "neutron-1", "contractAddressBytes": 32, - "domainId": "1853125230", + "displayName": "Neutron", + "domainId": 1853125230, + "gasCurrencyCoinGeckoId": "neutron-3", "gasPrice": { "amount": "0.0053", "denom": "untrn" @@ -858,10 +888,22 @@ "from": 4000000 }, "interchainGasPaymaster": "0x504ee9ac43ec5814e00c7d21869a90ec52becb489636bdf893b7df9d606b5d67", + "isTestnet": false, "mailbox": "0x848426d50eb2104d5c6381ec63757930b1c14659c40db8b8081e516e7c5238fc", "merkleTreeHook": "0xcd30a0001cc1f436c41ef764a712ebabc5a144140e3fd03eafe64a9a24e4e27c", "name": "neutron", + "nativeToken": { + "decimals": 6, + "denom": "untrn", + "name": "Neutron", + "symbol": "NTRN" + }, "protocol": "cosmos", + "restUrls": [ + { + "http": "https://rest-lb.neutron.org" + } + ], "rpcUrls": [ { "http": "https://rpc-kralum.neutron-1.neutron.org" @@ -872,6 +914,7 @@ "prefix": "neutron", "type": "cosmosKey" }, + "slip44": 118, "validatorAnnounce": "0xf3aa0d652226e21ae35cd9035c492ae41725edc9036edf0d6a48701b153b90a0" }, "optimism": { diff --git a/typescript/infra/config/environments/mainnet3/chains.ts b/typescript/infra/config/environments/mainnet3/chains.ts index 0e5d4144ef..c511b72e6e 100644 --- a/typescript/infra/config/environments/mainnet3/chains.ts +++ b/typescript/infra/config/environments/mainnet3/chains.ts @@ -1,30 +1,23 @@ import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; -import { objKeys } from '@hyperlane-xyz/utils'; -import { getChainMetadatas } from '../../../src/config/chain.js'; -import { getChain } from '../../registry.js'; +import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; import { supportedChainNames } from './supportedChainNames.js'; export const environment = 'mainnet3'; -const { - ethereumMetadatas: defaultEthereumMainnetConfigs, - nonEthereumMetadatas: nonEthereumMainnetConfigs, -} = getChainMetadatas(supportedChainNames); +export const ethereumChainNames = supportedChainNames.filter( + isEthereumProtocolChain, +); -export const ethereumMainnetConfigs: ChainMap = { - ...defaultEthereumMainnetConfigs, +export const chainMetadataOverrides: ChainMap> = { bsc: { - ...getChain('bsc'), transactionOverrides: { gasPrice: 3 * 10 ** 9, // 3 gwei }, }, polygon: { - ...getChain('polygon'), blocks: { - ...getChain('polygon').blocks, confirmations: 3, }, transactionOverrides: { @@ -35,9 +28,7 @@ export const ethereumMainnetConfigs: ChainMap = { }, }, ethereum: { - ...getChain('ethereum'), blocks: { - ...getChain('ethereum').blocks, confirmations: 3, }, transactionOverrides: { @@ -46,7 +37,6 @@ export const ethereumMainnetConfigs: ChainMap = { }, }, scroll: { - ...getChain('scroll'), transactionOverrides: { // Scroll doesn't use EIP 1559 and the gas price that's returned is sometimes // too low for the transaction to be included in a reasonable amount of time - @@ -55,17 +45,9 @@ export const ethereumMainnetConfigs: ChainMap = { }, }, moonbeam: { - ...getChain('moonbeam'), transactionOverrides: { maxFeePerGas: 350 * 10 ** 9, // 350 gwei maxPriorityFeePerGas: 50 * 10 ** 9, // 50 gwei }, }, }; - -export const mainnetConfigs: ChainMap = { - ...ethereumMainnetConfigs, - ...nonEthereumMainnetConfigs, -}; - -export const ethereumChainNames = objKeys(ethereumMainnetConfigs); diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index f9f15f0a84..80c0efc970 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.js'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'b22a0f4-20240523-140812', + tag: '7720875-20240531-072251', }, // We're currently using the same deployer/key funder key as mainnet2. // To minimize nonce clobbering we offset the key funder cron diff --git a/typescript/infra/config/environments/mainnet3/index.ts b/typescript/infra/config/environments/mainnet3/index.ts index a8bc14983c..6a765693fd 100644 --- a/typescript/infra/config/environments/mainnet3/index.ts +++ b/typescript/infra/config/environments/mainnet3/index.ts @@ -1,16 +1,21 @@ -import { ChainMetadata, RpcConsensusType } from '@hyperlane-xyz/sdk'; -import { ProtocolType, objFilter } from '@hyperlane-xyz/utils'; +import { IRegistry } from '@hyperlane-xyz/registry'; +import { RpcConsensusType } from '@hyperlane-xyz/sdk'; import { getKeysForRole, + getMultiProtocolProvider, getMultiProviderForRole, } from '../../../scripts/agent-utils.js'; +import { getRegistryForEnvironment } from '../../../src/config/chain.js'; import { EnvironmentConfig } from '../../../src/config/environment.js'; import { Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; import { agents } from './agent.js'; -import { environment as environmentName, mainnetConfigs } from './chains.js'; +import { + chainMetadataOverrides, + environment as environmentName, +} from './chains.js'; import { core } from './core.js'; import { keyFunderConfig } from './funding.js'; import { helloWorld } from './helloworld.js'; @@ -18,34 +23,39 @@ import { igp } from './igp.js'; import { infrastructure } from './infrastructure.js'; import { bridgeAdapterConfigs, relayerConfig } from './liquidityLayer.js'; import { owners } from './owners.js'; +import { supportedChainNames } from './supportedChainNames.js'; + +const getRegistry = async (useSecrets = true): Promise => + getRegistryForEnvironment( + environmentName, + supportedChainNames, + chainMetadataOverrides, + useSecrets, + ); export const environment: EnvironmentConfig = { environment: environmentName, - chainMetadataConfigs: mainnetConfigs, - getMultiProvider: ( + supportedChainNames, + getRegistry, + getMultiProtocolProvider: async () => + getMultiProtocolProvider(await getRegistry()), + getMultiProvider: async ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, - connectionType?: RpcConsensusType, - ) => { - const config = objFilter( - mainnetConfigs, - (_, chainMetadata): chainMetadata is ChainMetadata => - chainMetadata.protocol === ProtocolType.Ethereum, - ); - - return getMultiProviderForRole( - config, + _connectionType?: RpcConsensusType, + useSecrets?: boolean, + ) => + getMultiProviderForRole( environmentName, + await getRegistry(useSecrets), context, role, undefined, - connectionType, - ); - }, + ), getKeys: ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, - ) => getKeysForRole(mainnetConfigs, environmentName, context, role), + ) => getKeysForRole(environmentName, supportedChainNames, context, role), agents, core, igp, diff --git a/typescript/infra/config/environments/test/index.ts b/typescript/infra/config/environments/test/index.ts index c21e411e5b..cdde17fe39 100644 --- a/typescript/infra/config/environments/test/index.ts +++ b/typescript/infra/config/environments/test/index.ts @@ -5,6 +5,7 @@ import { MultiProvider, testChainMetadata } from '@hyperlane-xyz/sdk'; import { EnvironmentConfig } from '../../../src/config/environment.js'; import { agents } from './agent.js'; +import { testChainNames } from './chains.js'; import { core } from './core.js'; import { igp } from './igp.js'; import { infra } from './infra.js'; @@ -12,7 +13,13 @@ import { owners } from './owners.js'; export const environment: EnvironmentConfig = { environment: 'test', - chainMetadataConfigs: testChainMetadata, + supportedChainNames: testChainNames, + getRegistry: () => { + throw new Error('Not implemented'); + }, + getMultiProtocolProvider: () => { + throw new Error('Not implemented'); + }, agents, core, igp, diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 318d67e7b8..caa3c10186 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -1,24 +1,19 @@ import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; -import { objKeys } from '@hyperlane-xyz/utils'; -import { getChainMetadatas } from '../../../src/config/chain.js'; -import { getChain } from '../../registry.js'; +import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; import { supportedChainNames } from './supportedChainNames.js'; export const environment = 'testnet4'; -const { ethereumMetadatas: defaultEthereumMainnetConfigs } = - getChainMetadatas(supportedChainNames); +export const ethereumChainNames = supportedChainNames.filter( + isEthereumProtocolChain, +); -export const testnetConfigs: ChainMap = { - ...defaultEthereumMainnetConfigs, +export const chainMetadataOverrides: ChainMap> = { bsctestnet: { - ...getChain('bsctestnet'), transactionOverrides: { gasPrice: 8 * 10 ** 9, // 8 gwei }, }, }; - -export const ethereumChainNames = objKeys(defaultEthereumMainnetConfigs); diff --git a/typescript/infra/config/environments/testnet4/index.ts b/typescript/infra/config/environments/testnet4/index.ts index 9eaa66e1c7..b93de05c96 100644 --- a/typescript/infra/config/environments/testnet4/index.ts +++ b/typescript/infra/config/environments/testnet4/index.ts @@ -1,15 +1,22 @@ +import { IRegistry } from '@hyperlane-xyz/registry'; import { RpcConsensusType } from '@hyperlane-xyz/sdk'; +import { objMerge } from '@hyperlane-xyz/utils'; import { getKeysForRole, + getMultiProtocolProvider, getMultiProviderForRole, } from '../../../scripts/agent-utils.js'; +import { getRegistryForEnvironment } from '../../../src/config/chain.js'; import { EnvironmentConfig } from '../../../src/config/environment.js'; import { Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; import { agents } from './agent.js'; -import { environment as environmentName, testnetConfigs } from './chains.js'; +import { + chainMetadataOverrides, + environment as environmentName, +} from './chains.js'; import { core } from './core.js'; import { keyFunderConfig } from './funding.js'; import { helloWorld } from './helloworld.js'; @@ -18,27 +25,39 @@ import { infrastructure } from './infrastructure.js'; import { bridgeAdapterConfigs } from './liquidityLayer.js'; import { liquidityLayerRelayerConfig } from './middleware.js'; import { owners } from './owners.js'; +import { supportedChainNames } from './supportedChainNames.js'; + +const getRegistry = async (useSecrets = true): Promise => + getRegistryForEnvironment( + environmentName, + supportedChainNames, + chainMetadataOverrides, + useSecrets, + ); export const environment: EnvironmentConfig = { environment: environmentName, - chainMetadataConfigs: testnetConfigs, - getMultiProvider: ( + supportedChainNames, + getRegistry, + getMultiProtocolProvider: async () => + getMultiProtocolProvider(await getRegistry()), + getMultiProvider: async ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, - connectionType?: RpcConsensusType, + _connectionType?: RpcConsensusType, + useSecrets?: boolean, ) => getMultiProviderForRole( - testnetConfigs, environmentName, + await getRegistry(useSecrets), context, role, undefined, - connectionType, ), getKeys: ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, - ) => getKeysForRole(testnetConfigs, environmentName, context, role), + ) => getKeysForRole(environmentName, supportedChainNames, context, role), agents, core, igp, diff --git a/typescript/infra/config/registry.ts b/typescript/infra/config/registry.ts index a854880146..8c8be46bb8 100644 --- a/typescript/infra/config/registry.ts +++ b/typescript/infra/config/registry.ts @@ -1,7 +1,11 @@ import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; -import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { + ChainAddresses, + MergedRegistry, + PartialRegistry, +} from '@hyperlane-xyz/registry'; import { FileSystemRegistry } from '@hyperlane-xyz/registry/fs'; import { ChainMap, @@ -35,10 +39,18 @@ export function setRegistry(reg: FileSystemRegistry) { registry = reg; } +/** + * Gets a FileSystemRegistry whose contents are found at the environment + * variable `REGISTRY_URI`, or `DEFAULT_REGISTRY_URI` if no env var is specified. + * This registry will not have any environment-specific overrides applied, + * and is useful for synchronous registry operations that do not require + * any overrides. + * @returns A FileSystemRegistry. + */ export function getRegistry(): FileSystemRegistry { if (!registry) { const registryUri = process.env.REGISTRY_URI || DEFAULT_REGISTRY_URI; - rootLogger.info('Using registry URI:', registryUri); + rootLogger.info({ registryUri }, 'Using registry URI'); registry = new FileSystemRegistry({ uri: registryUri, logger: rootLogger.child({ module: 'infra-registry' }), @@ -111,3 +123,26 @@ export function getMainnetAddresses(): ChainMap { export function getTestnetAddresses(): ChainMap { return getEnvAddresses('testnet4'); } + +/** + * Gets a registry, applying the provided overrides. The base registry + * that the overrides are applied to is the registry returned by `getRegistry`. + * @param chainMetadataOverrides Chain metadata overrides. + * @param chainAddressesOverrides Chain address overrides. + * @returns A MergedRegistry merging the registry from `getRegistry` and the overrides. + */ +export function getRegistryWithOverrides( + chainMetadataOverrides: ChainMap> = {}, + chainAddressesOverrides: ChainMap> = {}, +): MergedRegistry { + const baseRegistry = getRegistry(); + + const overrideRegistry = new PartialRegistry({ + chainMetadata: chainMetadataOverrides, + chainAddresses: chainAddressesOverrides, + }); + + return new MergedRegistry({ + registries: [baseRegistry, overrideRegistry], + }); +} diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 33f8d61947..67de934c27 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -12,6 +12,7 @@ "@ethersproject/experimental": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", + "@google-cloud/secret-manager": "^5.5.0", "@hyperlane-xyz/helloworld": "3.13.0", "@hyperlane-xyz/registry": "1.3.0", "@hyperlane-xyz/sdk": "3.13.0", diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index d027d5aad0..3ac0f5ebda 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -1,12 +1,13 @@ import path, { join } from 'path'; import yargs, { Argv } from 'yargs'; -import { ChainAddresses } from '@hyperlane-xyz/registry'; +import { ChainAddresses, IRegistry } from '@hyperlane-xyz/registry'; import { ChainMap, ChainMetadata, ChainName, CoreConfig, + MultiProtocolProvider, MultiProvider, RpcConsensusType, collectValidators, @@ -16,6 +17,7 @@ import { ProtocolType, objFilter, objMap, + objMerge, promiseObjAll, rootLogger, symmetricDifference, @@ -35,7 +37,10 @@ import { getCurrentKubernetesContext } from '../src/agents/index.js'; import { getCloudAgentKey } from '../src/agents/key-utils.js'; import { CloudAgentKey } from '../src/agents/keys.js'; import { RootAgentConfig } from '../src/config/agent/agent.js'; -import { fetchProvider } from '../src/config/chain.js'; +import { + fetchProvider, + getSecretMetadataOverrides, +} from '../src/config/chain.js'; import { AgentEnvironment, DeployEnvironment, @@ -47,6 +52,7 @@ import { assertContext, assertRole, getInfraPath, + inCIMode, readJSONAtPath, writeMergedJSONAtPath, } from '../src/utils/utils.js'; @@ -283,8 +289,8 @@ export function ensureValidatorConfigConsistency(agentConfig: RootAgentConfig) { export function getKeyForRole( environment: DeployEnvironment, context: Contexts, - chain: ChainName, role: Role, + chain?: ChainName, index?: number, ): CloudAgentKey { debugLog(`Getting key for ${role} role`); @@ -292,33 +298,32 @@ export function getKeyForRole( return getCloudAgentKey(agentConfig, role, chain, index); } +export async function getMultiProtocolProvider( + registry: IRegistry, +): Promise { + const chainMetadata = await registry.getMetadata(); + return new MultiProtocolProvider(chainMetadata); +} + export async function getMultiProviderForRole( - txConfigs: ChainMap, environment: DeployEnvironment, + registry: IRegistry, context: Contexts, role: Role, index?: number, - // TODO: rename to consensusType? - connectionType?: RpcConsensusType, ): Promise { + const chainMetadata = await registry.getMetadata(); debugLog(`Getting multiprovider for ${role} role`); - const multiProvider = new MultiProvider(txConfigs); - if (process.env.CI === 'true') { - debugLog('Returning multiprovider with default RPCs in CI'); - // Return the multiProvider with default RPCs + const multiProvider = new MultiProvider(chainMetadata); + if (inCIMode()) { + debugLog('Running in CI, returning multiprovider without secret keys'); return multiProvider; } await promiseObjAll( - objMap(txConfigs, async (chain, _) => { + objMap(chainMetadata, async (chain, _) => { 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); + const key = getKeyForRole(environment, context, role, chain, index); + const signer = await key.getSigner(); multiProvider.setSigner(chain, signer); } }), @@ -330,23 +335,22 @@ export async function getMultiProviderForRole( // Note: this will only work for keystores that allow key's to be extracted. // I.e. GCP will work but AWS HSMs will not. export async function getKeysForRole( - txConfigs: ChainMap, environment: DeployEnvironment, + supportedChainNames: ChainName[], context: Contexts, role: Role, index?: number, ): Promise> { - if (process.env.CI === 'true') { + if (inCIMode()) { debugLog('No keys to return in CI'); return {}; } - const keys = await promiseObjAll( - objMap(txConfigs, async (chain, _) => - getKeyForRole(environment, context, chain, role, index), - ), - ); - return keys; + const keyEntries = supportedChainNames.map((chain) => [ + chain, + getKeyForRole(environment, context, role, chain, index), + ]); + return Object.fromEntries(keyEntries); } export function getEnvironmentDirectory(environment: DeployEnvironment) { diff --git a/typescript/infra/scripts/agents/update-agent-config.ts b/typescript/infra/scripts/agents/update-agent-config.ts index 2ec6431b84..127f4bf167 100644 --- a/typescript/infra/scripts/agents/update-agent-config.ts +++ b/typescript/infra/scripts/agents/update-agent-config.ts @@ -6,7 +6,13 @@ async function main() { const { environment } = await getArgs().argv; const envConfig = getEnvironmentConfig(environment); - let multiProvider = await envConfig.getMultiProvider(); + let multiProvider = await envConfig.getMultiProvider( + undefined, + undefined, + undefined, + // Don't use secrets + false, + ); await writeAgentConfig(multiProvider, environment); } diff --git a/typescript/infra/scripts/check-rpc-urls.ts b/typescript/infra/scripts/check-rpc-urls.ts index 2ab70835b0..95ba7ee4c1 100644 --- a/typescript/infra/scripts/check-rpc-urls.ts +++ b/typescript/infra/scripts/check-rpc-urls.ts @@ -2,24 +2,22 @@ import { ethers } from 'ethers'; import { rootLogger } from '@hyperlane-xyz/utils'; -import { getSecretRpcEndpoint } from '../src/agents/index.js'; +import { getSecretRpcEndpoints } from '../src/agents/index.js'; import { getArgs } from './agent-utils.js'; import { getEnvironmentConfig } from './core-utils.js'; -// TODO remove this script as part of migration to CLI -// It's redundant with metadata-check.ts in the SDK async function main() { const { environment } = await getArgs().argv; - const config = await getEnvironmentConfig(environment); + const config = getEnvironmentConfig(environment); const multiProvider = await config.getMultiProvider(); const chains = multiProvider.getKnownChainNames(); const providers: [string, ethers.providers.JsonRpcProvider][] = []; for (const chain of chains) { rootLogger.debug(`Building providers for ${chain}`); const rpcData = [ - ...(await getSecretRpcEndpoint(environment, chain, false)), - ...(await getSecretRpcEndpoint(environment, chain, true)), + ...(await getSecretRpcEndpoints(environment, chain, false)), + ...(await getSecretRpcEndpoints(environment, chain, true)), ]; for (const url of rpcData) providers.push([chain, new ethers.providers.StaticJsonRpcProvider(url)]); diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index 02b15a978c..000034581e 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -381,7 +381,7 @@ class ContextFunder { [Role.Kathy]: '', }; const roleKeysPerChain: ChainMap> = {}; - const chains = getEnvironmentConfig(environment).chainMetadataConfigs; + const { supportedChainNames } = getEnvironmentConfig(environment); for (const role of rolesToFund) { assertFundableRole(role); // only the relayer and kathy are fundable keys const roleAddress = fetchLocalKeyAddresses(role)[environment][context]; @@ -392,7 +392,7 @@ class ContextFunder { } fundableRoleKeys[role] = roleAddress; - for (const chain of Object.keys(chains)) { + for (const chain of supportedChainNames) { if (!roleKeysPerChain[chain as ChainName]) { roleKeysPerChain[chain as ChainName] = { [Role.Relayer]: [], diff --git a/typescript/infra/scripts/helloworld/kathy.ts b/typescript/infra/scripts/helloworld/kathy.ts index 101bd67796..4e39ab14f1 100644 --- a/typescript/infra/scripts/helloworld/kathy.ts +++ b/typescript/infra/scripts/helloworld/kathy.ts @@ -20,6 +20,7 @@ import { ProtocolType, ensure0x, objMap, + pick, retryAsync, rootLogger, sleep, @@ -28,7 +29,6 @@ import { } from '@hyperlane-xyz/utils'; import { Contexts } from '../../config/contexts.js'; -import { testnetConfigs } from '../../config/environments/testnet4/chains.js'; import { hyperlaneHelloworld, releaseCandidateHelloworld, @@ -166,7 +166,6 @@ async function main(): Promise { logger.debug('Starting up', { environment }); const coreConfig = getEnvironmentConfig(environment); - // const coreConfig = getCoreConfigStub(environment); const { app, core, igp, multiProvider, keys } = await getHelloWorldMultiProtocolApp( @@ -550,7 +549,14 @@ async function updateWalletBalanceMetricFor( } // Get a core config intended for testing Kathy without secret access -export function getCoreConfigStub(environment: DeployEnvironment) { +export async function getCoreConfigStub(environment: DeployEnvironment) { + const environmentConfig = getEnvironmentConfig(environment); + // Don't fetch any secrets. + const registry = await environmentConfig.getRegistry(false); + const testnetConfigs = pick( + await registry.getMetadata(), + environmentConfig.supportedChainNames, + ); const multiProvider = new MultiProvider({ // Desired chains here. Key must have funds on these chains ...testnetConfigs, diff --git a/typescript/infra/scripts/print-chain-metadatas.ts b/typescript/infra/scripts/print-chain-metadatas.ts index 8653ef908c..ff8f771fe0 100644 --- a/typescript/infra/scripts/print-chain-metadatas.ts +++ b/typescript/infra/scripts/print-chain-metadatas.ts @@ -1,3 +1,5 @@ +import { pick } from '@hyperlane-xyz/utils'; + import { getArgs } from './agent-utils.js'; import { getEnvironmentConfig } from './core-utils.js'; @@ -9,7 +11,15 @@ async function main() { const environmentConfig = getEnvironmentConfig(args.environment); - console.log(JSON.stringify(environmentConfig.chainMetadataConfigs, null, 2)); + // Intentionally do not include any secrets in the output + const registry = await environmentConfig.getRegistry(false); + const allMetadata = await registry.getMetadata(); + const environmentMetadata = pick( + allMetadata, + environmentConfig.supportedChainNames, + ); + + console.log(JSON.stringify(environmentMetadata, null, 2)); } main().catch((err) => { diff --git a/typescript/infra/scripts/print-gas-prices.ts b/typescript/infra/scripts/print-gas-prices.ts index e06f134210..61126c3716 100644 --- a/typescript/infra/scripts/print-gas-prices.ts +++ b/typescript/infra/scripts/print-gas-prices.ts @@ -1,47 +1,67 @@ import { ethers } from 'ethers'; -import { MultiProtocolProvider, ProviderType } from '@hyperlane-xyz/sdk'; -import { objMap, promiseObjAll } from '@hyperlane-xyz/utils'; +import { + ChainMap, + MultiProtocolProvider, + ProviderType, +} from '@hyperlane-xyz/sdk'; -import { mainnetConfigs } from '../config/environments/mainnet3/chains.js'; -import { getCosmosChainGasPrice } from '../src/config/gas-oracle.js'; +import { + GasPriceConfig, + getCosmosChainGasPrice, +} from '../src/config/gas-oracle.js'; + +import { getEnvironmentConfig } from './core-utils.js'; async function main() { - const allMetadatas = mainnetConfigs; - - const mpp = new MultiProtocolProvider(allMetadatas); - - const prices = await promiseObjAll( - objMap(allMetadatas, async (chain, metadata) => { - const provider = mpp.getProvider(chain); - switch (provider.type) { - case ProviderType.EthersV5: { - const gasPrice = await provider.provider.getGasPrice(); - return { - amount: ethers.utils.formatUnits(gasPrice, 'gwei'), - decimals: 9, - }; - } - case ProviderType.CosmJsWasm: { - const { amount } = await getCosmosChainGasPrice(chain); - - return { - amount, - decimals: 1, - }; - } - case ProviderType.SolanaWeb3: - // TODO get a reasonable value - return '0.001'; - default: - throw new Error(`Unsupported provider type: ${provider.type}`); - } - }), + const environmentConfig = getEnvironmentConfig('mainnet3'); + + const mpp = await environmentConfig.getMultiProtocolProvider(); + + const prices: ChainMap = Object.fromEntries( + await Promise.all( + environmentConfig.supportedChainNames.map(async (chain) => [ + chain, + await getGasPrice(mpp, chain), + ]), + ), ); console.log(JSON.stringify(prices, null, 2)); } +async function getGasPrice( + mpp: MultiProtocolProvider, + chain: string, +): Promise { + const provider = mpp.getProvider(chain); + switch (provider.type) { + case ProviderType.EthersV5: { + const gasPrice = await provider.provider.getGasPrice(); + return { + amount: ethers.utils.formatUnits(gasPrice, 'gwei'), + decimals: 9, + }; + } + case ProviderType.CosmJsWasm: { + const { amount } = await getCosmosChainGasPrice(chain); + + return { + amount, + decimals: 1, + }; + } + case ProviderType.SolanaWeb3: + // TODO get a reasonable value + return { + amount: '0.001', + decimals: 9, + }; + default: + throw new Error(`Unsupported provider type: ${provider.type}`); + } +} + main() .then() .catch((err) => { diff --git a/typescript/infra/scripts/print-token-prices.ts b/typescript/infra/scripts/print-token-prices.ts index 35c30fb5b7..b125bac957 100644 --- a/typescript/infra/scripts/print-token-prices.ts +++ b/typescript/infra/scripts/print-token-prices.ts @@ -1,11 +1,17 @@ -import { objMap } from '@hyperlane-xyz/utils'; +import { objMap, pick } from '@hyperlane-xyz/utils'; -import { mainnetConfigs } from '../config/environments/mainnet3/chains.js'; +import { getEnvironmentConfig } from './core-utils.js'; const CURRENCY = 'usd'; async function main() { - const metadata = mainnetConfigs; + const environmentConfig = getEnvironmentConfig('mainnet3'); + + const registry = await environmentConfig.getRegistry(); + const metadata = pick( + await registry.getMetadata(), + environmentConfig.supportedChainNames, + ); const ids = objMap( metadata, diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 41eabefae3..66a9be195f 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -300,14 +300,14 @@ export async function getSecretAwsCredentials(agentConfig: AgentContextConfig) { }; } -export async function getSecretRpcEndpoint( +export async function getSecretRpcEndpoints( environment: string, chainName: ChainName, - quorum = false, + multipleEndpoints = false, ): Promise { const secret = await fetchGCPSecret( - `${environment}-rpc-endpoint${quorum ? 's' : ''}-${chainName}`, - quorum, + `${environment}-rpc-endpoint${multipleEndpoints ? 's' : ''}-${chainName}`, + multipleEndpoints, ); if (typeof secret != 'string' && !Array.isArray(secret)) { throw Error(`Expected secret for ${chainName} rpc endpoint`); diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index b64035b2b5..414ca34bd4 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -1,16 +1,21 @@ +import { SecretManagerServiceClient } from '@google-cloud/secret-manager'; import { providers } from 'ethers'; +import { IRegistry } from '@hyperlane-xyz/registry'; import { + ChainMap, ChainMetadata, ChainName, HyperlaneSmartProvider, ProviderRetryOptions, RpcConsensusType, + RpcUrl, } from '@hyperlane-xyz/sdk'; -import { ProtocolType, objFilter } from '@hyperlane-xyz/utils'; +import { ProtocolType, objFilter, objMerge } from '@hyperlane-xyz/utils'; -import { getChain } from '../../config/registry.js'; -import { getSecretRpcEndpoint } from '../agents/index.js'; +import { getChain, getRegistryWithOverrides } from '../../config/registry.js'; +import { getSecretRpcEndpoints } from '../agents/index.js'; +import { inCIMode } from '../utils/utils.js'; import { DeployEnvironment } from './environment.js'; @@ -20,7 +25,6 @@ export const defaultRetry: ProviderRetryOptions = { }; export async function fetchProvider( - environment: DeployEnvironment, chainName: ChainName, connectionType: RpcConsensusType = RpcConsensusType.Single, ): Promise { @@ -29,10 +33,9 @@ export async function fetchProvider( throw Error(`Unsupported chain: ${chainName}`); } const chainId = chainMetadata.chainId; - const single = connectionType === RpcConsensusType.Single; let rpcData = chainMetadata.rpcUrls.map((url) => url.http); if (rpcData.length === 0) { - rpcData = await getSecretRpcEndpoint(environment, chainName, !single); + throw Error(`No RPC URLs found for chain: ${chainName}`); } if (connectionType === RpcConsensusType.Single) { @@ -73,3 +76,69 @@ export function getChainMetadatas(chains: Array) { return { ethereumMetadatas, nonEthereumMetadatas }; } + +/** + * Gets the registry for the given environment, with optional overrides and + * the ability to get overrides from secrets. + * @param deployEnv The deploy environment. + * @param chains The chains to get metadata for. + * @param defaultChainMetadataOverrides The default chain metadata overrides. If + * secret overrides are used, the secret overrides will be merged with these and + * take precedence. + * @param useSecrets Whether to fetch metadata overrides from secrets. + * @returns A registry with overrides for the given environment. + */ +export async function getRegistryForEnvironment( + deployEnv: DeployEnvironment, + chains: ChainName[], + defaultChainMetadataOverrides: ChainMap> = {}, + useSecrets: boolean = true, +): Promise { + let overrides = defaultChainMetadataOverrides; + if (useSecrets && !inCIMode()) { + overrides = objMerge( + overrides, + await getSecretMetadataOverrides(deployEnv, chains), + ); + } + const registry = getRegistryWithOverrides(overrides); + return registry; +} + +/** + * Gets chain metadata overrides from GCP secrets. + * @param deployEnv The deploy environment. + * @param chains The chains to get metadata overrides for. + * @returns A partial chain metadata map with the secret overrides. + */ +export async function getSecretMetadataOverrides( + deployEnv: DeployEnvironment, + chains: string[], +): Promise>> { + const chainMetadataOverrides: ChainMap> = {}; + + const secretRpcUrls = await Promise.all( + chains.map(async (chain) => { + const rpcUrls = await getSecretRpcEndpoints(deployEnv, chain, true); + return { + chain, + rpcUrls, + }; + }), + ); + + for (const { chain, rpcUrls } of secretRpcUrls) { + if (rpcUrls.length === 0) { + throw Error(`No secret RPC URLs found for chain: ${chain}`); + } + // Need explicit casting here because Zod expects a non-empty array. + const metadataRpcUrls = rpcUrls.map((rpcUrl: string) => ({ + http: rpcUrl, + })) as ChainMetadata['rpcUrls']; + chainMetadataOverrides[chain] = { + rpcUrls: metadataRpcUrls, + }; + } + + return chainMetadataOverrides; +} diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index fbb9b74c75..72e30f6d9f 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -1,3 +1,4 @@ +import { IRegistry } from '@hyperlane-xyz/registry'; import { BridgeAdapterConfig, ChainMap, @@ -5,6 +6,7 @@ import { ChainName, CoreConfig, IgpConfig, + MultiProtocolProvider, MultiProvider, OwnableConfig, RpcConsensusType, @@ -39,17 +41,21 @@ export const envNameToAgentEnv: Record = { export type EnvironmentConfig = { environment: DeployEnvironment; - chainMetadataConfigs: ChainMap; + supportedChainNames: ChainName[]; + // Get the registry with or without environment-specific secrets. + getRegistry: (useSecrets?: boolean) => Promise; // Each AgentConfig, keyed by the context agents: Partial>; core: ChainMap; igp: ChainMap; owners: ChainMap; infra: InfrastructureConfig; + getMultiProtocolProvider: () => Promise; getMultiProvider: ( context?: Contexts, role?: Role, connectionType?: RpcConsensusType, + useSecrets?: boolean, ) => Promise; getKeys: ( context?: Contexts, diff --git a/typescript/infra/src/utils/gcloud.ts b/typescript/infra/src/utils/gcloud.ts index 0ce5e21f30..cc4fd2d621 100644 --- a/typescript/infra/src/utils/gcloud.ts +++ b/typescript/infra/src/utils/gcloud.ts @@ -1,3 +1,4 @@ +import { SecretManagerServiceClient } from '@google-cloud/secret-manager'; import fs from 'fs'; import { rootLogger } from '@hyperlane-xyz/utils'; @@ -6,6 +7,8 @@ import { rm, writeFile } from 'fs/promises'; import { execCmd, execCmdAndParseJson } from './utils.js'; +export const GCP_PROJECT_ID = 'abacus-labs-dev'; + interface IamCondition { title: string; expression: string; @@ -32,9 +35,7 @@ export async function fetchGCPSecret( ); output = envVarOverride; } else { - [output] = await execCmd( - `gcloud secrets versions access latest --secret ${secretName}`, - ); + output = await fetchLatestGCPSecret(secretName); } if (parseJson) { @@ -43,6 +44,29 @@ export async function fetchGCPSecret( return output; } +export async function fetchLatestGCPSecret(secretName: string) { + const client = new SecretManagerServiceClient({ + projectId: GCP_PROJECT_ID, + }); + const [secretVersion] = await client.accessSecretVersion({ + name: `projects/${GCP_PROJECT_ID}/secrets/${secretName}/versions/latest`, + }); + const secretData = secretVersion.payload?.data; + if (!secretData) { + throw new Error(`Secret ${secretName} missing payload`); + } + + // Handle both string and Uint8Array + let dataStr: string; + if (typeof secretData === 'string') { + dataStr = secretData; + } else { + dataStr = new TextDecoder().decode(secretData); + } + + return dataStr; +} + // 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()}` diff --git a/typescript/infra/src/utils/utils.ts b/typescript/infra/src/utils/utils.ts index 476fe3913f..d3a8075fff 100644 --- a/typescript/infra/src/utils/utils.ts +++ b/typescript/infra/src/utils/utils.ts @@ -269,3 +269,7 @@ export function isEthereumProtocolChain(chainName: ChainName) { export function getInfraPath() { return join(dirname(fileURLToPath(import.meta.url)), '../../'); } + +export function inCIMode() { + return process.env.CI === 'true'; +} diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index 0885a3e4a2..49f05e883a 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -399,6 +399,16 @@ export function buildAgentConfig( const chainConfigs: ChainMap = {}; for (const chain of [...chains].sort()) { const metadata = multiProvider.tryGetChainMetadata(chain); + if (metadata?.protocol === ProtocolType.Cosmos) { + // Note: the gRPC URL format in the registry lacks a correct http:// or https:// prefix at the moment, + // which is expected by the agents. For now, we intentionally skip this. + delete metadata.grpcUrls; + + // The agents expect gasPrice.amount and gasPrice.denom and ignore the transaction overrides. + // To reduce confusion when looking at the config, we remove the transaction overrides. + delete metadata.transactionOverrides; + } + const chainConfig: AgentChainMetadata = { ...metadata, ...addresses[chain], diff --git a/yarn.lock b/yarn.lock index 9fc2aab505..8b293bfc70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5608,6 +5608,39 @@ __metadata: languageName: node linkType: hard +"@google-cloud/secret-manager@npm:^5.5.0": + version: 5.5.0 + resolution: "@google-cloud/secret-manager@npm:5.5.0" + dependencies: + google-gax: "npm:^4.0.3" + checksum: 487267dab1e260a0da79012194bb61c85f8b02b642330fdec32cac1fe37900f0fd6709ff4928fe631ab227b0d758bd3e59b1e3dc1d0682de566a64ef4fb42bba + languageName: node + linkType: hard + +"@grpc/grpc-js@npm:~1.10.3": + version: 1.10.8 + resolution: "@grpc/grpc-js@npm:1.10.8" + dependencies: + "@grpc/proto-loader": "npm:^0.7.13" + "@js-sdsl/ordered-map": "npm:^4.4.2" + checksum: cb7903e93db38a86bd2ddffb84313de78144454ad988801ede90f0c794d6a5f666a1b24f50e950b50d633b4bacc7416c7cabf4a6791b91c4fa89c001122edba8 + languageName: node + linkType: hard + +"@grpc/proto-loader@npm:^0.7.0, @grpc/proto-loader@npm:^0.7.13": + version: 0.7.13 + resolution: "@grpc/proto-loader@npm:0.7.13" + dependencies: + lodash.camelcase: "npm:^4.3.0" + long: "npm:^5.0.0" + protobufjs: "npm:^7.2.5" + yargs: "npm:^17.7.2" + bin: + proto-loader-gen-types: build/bin/proto-loader-gen-types.js + checksum: 7e2d842c2061cbaf6450c71da0077263be3bab165454d5c8a3e1ae4d3c6d2915f02fd27da63ff01f05e127b1221acd40705273f5d29303901e60514e852992f4 + languageName: node + linkType: hard + "@headlessui/react@npm:^1.7.17": version: 1.7.18 resolution: "@headlessui/react@npm:1.7.18" @@ -5824,6 +5857,7 @@ __metadata: "@ethersproject/experimental": "npm:^5.7.0" "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" + "@google-cloud/secret-manager": "npm:^5.5.0" "@hyperlane-xyz/helloworld": "npm:3.13.0" "@hyperlane-xyz/registry": "npm:1.3.0" "@hyperlane-xyz/sdk": "npm:3.13.0" @@ -6501,6 +6535,13 @@ __metadata: languageName: node linkType: hard +"@js-sdsl/ordered-map@npm:^4.4.2": + version: 4.4.2 + resolution: "@js-sdsl/ordered-map@npm:4.4.2" + checksum: ac64e3f0615ecc015461c9f527f124d2edaa9e68de153c1e270c627e01e83d046522d7e872692fd57a8c514578b539afceff75831c0d8b2a9a7a347fbed35af4 + languageName: node + linkType: hard + "@layerzerolabs/lz-evm-messagelib-v2@npm:^2.0.2": version: 2.0.6 resolution: "@layerzerolabs/lz-evm-messagelib-v2@npm:2.0.6" @@ -9439,6 +9480,13 @@ __metadata: languageName: node linkType: hard +"@types/caseless@npm:*": + version: 0.12.5 + resolution: "@types/caseless@npm:0.12.5" + checksum: f6a3628add76d27005495914c9c3873a93536957edaa5b69c63b46fe10b4649a6fecf16b676c1695f46aab851da47ec6047dcf3570fa8d9b6883492ff6d074e0 + languageName: node + linkType: hard + "@types/chai@npm:*, @types/chai@npm:^4.2.21": version: 4.3.1 resolution: "@types/chai@npm:4.3.1" @@ -9607,7 +9655,7 @@ __metadata: languageName: node linkType: hard -"@types/long@npm:^4.0.1": +"@types/long@npm:^4.0.0, @types/long@npm:^4.0.1": version: 4.0.2 resolution: "@types/long@npm:4.0.2" checksum: 68afa05fb20949d88345876148a76f6ccff5433310e720db51ac5ca21cb8cc6714286dbe04713840ddbd25a8b56b7a23aa87d08472fabf06463a6f2ed4967707 @@ -9814,6 +9862,18 @@ __metadata: languageName: node linkType: hard +"@types/request@npm:^2.48.8": + version: 2.48.12 + resolution: "@types/request@npm:2.48.12" + dependencies: + "@types/caseless": "npm:*" + "@types/node": "npm:*" + "@types/tough-cookie": "npm:*" + form-data: "npm:^2.5.0" + checksum: a7b3f9f14cacc18fe235bb8e57eff1232a04bd3fa3dad29371f24a5d96db2cd295a0c8b6b34ed7efa3efbbcff845febb02c9635cd68c54811c947ea66ae22090 + languageName: node + linkType: hard + "@types/resolve@npm:^0.0.8": version: 0.0.8 resolution: "@types/resolve@npm:0.0.8" @@ -9904,6 +9964,13 @@ __metadata: languageName: node linkType: hard +"@types/tough-cookie@npm:*": + version: 4.0.5 + resolution: "@types/tough-cookie@npm:4.0.5" + checksum: 01fd82efc8202670865928629697b62fe9bf0c0dcbc5b1c115831caeb073a2c0abb871ff393d7df1ae94ea41e256cb87d2a5a91fd03cdb1b0b4384e08d4ee482 + languageName: node + linkType: hard + "@types/trusted-types@npm:^2.0.2": version: 2.0.7 resolution: "@types/trusted-types@npm:2.0.7" @@ -10902,6 +10969,15 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:^7.0.2": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: "npm:^4.3.4" + checksum: c478fec8f79953f118704d007a38f2a185458853f5c45579b9669372bd0e12602e88dc2ad0233077831504f7cd6fcc8251c383375bba5eaaf563b102938bda26 + languageName: node + linkType: hard + "agentkeepalive@npm:^4.2.1": version: 4.2.1 resolution: "agentkeepalive@npm:4.2.1" @@ -11956,6 +12032,13 @@ __metadata: languageName: node linkType: hard +"buffer-equal-constant-time@npm:1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 80bb945f5d782a56f374b292770901065bad21420e34936ecbe949e57724b4a13874f735850dd1cc61f078773c4fb5493a41391e7bda40d1fa388d6bd80daaab + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -13651,6 +13734,18 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^4.0.0": + version: 4.1.3 + resolution: "duplexify@npm:4.1.3" + dependencies: + end-of-stream: "npm:^1.4.1" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + stream-shift: "npm:^1.0.2" + checksum: b44b98ba0ffac3a658b4b1bf877219e996db288c5ae6f3dc55ca9b2cbef7df60c10eabfdd947f3d73a623eb9975a74a66d6d61e6f26bff90155315adb362aa77 + languageName: node + linkType: hard + "duplexify@npm:^4.1.2": version: 4.1.2 resolution: "duplexify@npm:4.1.2" @@ -13680,6 +13775,15 @@ __metadata: languageName: node linkType: hard +"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 878e1aab8a42773320bc04c6de420bee21aebd71810e40b1799880a8a1c4594bcd6adc3d4213a0fb8147d4c3f529d8f9a618d7f59ad5a9a41b142058aceda23f + languageName: node + linkType: hard + "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -14912,7 +15016,7 @@ __metadata: languageName: node linkType: hard -"extend@npm:~3.0.2": +"extend@npm:^3.0.2, extend@npm:~3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" checksum: 59e89e2dc798ec0f54b36d82f32a27d5f6472c53974f61ca098db5d4648430b725387b53449a34df38fd0392045434426b012f302b3cc049a6500ccf82877e4e @@ -15303,7 +15407,7 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^2.2.0": +"form-data@npm:^2.2.0, form-data@npm:^2.5.0": version: 2.5.1 resolution: "form-data@npm:2.5.1" dependencies: @@ -15666,6 +15770,29 @@ __metadata: languageName: node linkType: hard +"gaxios@npm:^6.0.0, gaxios@npm:^6.1.1": + version: 6.6.0 + resolution: "gaxios@npm:6.6.0" + dependencies: + extend: "npm:^3.0.2" + https-proxy-agent: "npm:^7.0.1" + is-stream: "npm:^2.0.0" + node-fetch: "npm:^2.6.9" + uuid: "npm:^9.0.1" + checksum: 9f035590374fd168e7bb3ddda369fc8bd487f16a2308fde18284ccc0f685d0af4ac5e3e38d680a8c6342a9000fbf9d77ce691ee110dbed2feebb659e729c640a + languageName: node + linkType: hard + +"gcp-metadata@npm:^6.1.0": + version: 6.1.0 + resolution: "gcp-metadata@npm:6.1.0" + dependencies: + gaxios: "npm:^6.0.0" + json-bigint: "npm:^1.0.0" + checksum: a0d12a9cb7499fdb9de0fff5406aa220310c1326b80056be8d9b747aae26414f99d14bd795c0ec52ef7d0473eef9d61bb657b8cd3d8186c8a84c4ddbff025fe9 + 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" @@ -16034,6 +16161,40 @@ __metadata: languageName: node linkType: hard +"google-auth-library@npm:^9.3.0": + version: 9.10.0 + resolution: "google-auth-library@npm:9.10.0" + dependencies: + base64-js: "npm:^1.3.0" + ecdsa-sig-formatter: "npm:^1.0.11" + gaxios: "npm:^6.1.1" + gcp-metadata: "npm:^6.1.0" + gtoken: "npm:^7.0.0" + jws: "npm:^4.0.0" + checksum: 10d5863493f9426107b0f6c4df244b1413a2cacff9589076f906924336d894fe8bc4153d4a3756cebec8a58784ff1a3900c621924f75f004908611fa46d3caa6 + languageName: node + linkType: hard + +"google-gax@npm:^4.0.3": + version: 4.3.3 + resolution: "google-gax@npm:4.3.3" + dependencies: + "@grpc/grpc-js": "npm:~1.10.3" + "@grpc/proto-loader": "npm:^0.7.0" + "@types/long": "npm:^4.0.0" + abort-controller: "npm:^3.0.0" + duplexify: "npm:^4.0.0" + google-auth-library: "npm:^9.3.0" + node-fetch: "npm:^2.6.1" + object-hash: "npm:^3.0.0" + proto3-json-serializer: "npm:^2.0.0" + protobufjs: "npm:7.2.6" + retry-request: "npm:^7.0.0" + uuid: "npm:^9.0.1" + checksum: 63335724e741737b90689e43f8ea5804d82b8f4eaa013ba07166bf6119ef7474d06682d580d72f6b708d6c55251204b1f05db615c3cd84abf2f8f295c50882ec + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -16166,6 +16327,16 @@ __metadata: languageName: node linkType: hard +"gtoken@npm:^7.0.0": + version: 7.1.0 + resolution: "gtoken@npm:7.1.0" + dependencies: + gaxios: "npm:^6.0.0" + jws: "npm:^4.0.0" + checksum: 640392261e55c9242137a81a4af8feb053b57061762cedddcbb6a0d62c2314316161808ac2529eea67d06d69fdc56d82361af50f2d840a04a87ea29e124d7382 + languageName: node + linkType: hard + "h3@npm:^1.10.1, h3@npm:^1.8.2": version: 1.10.2 resolution: "h3@npm:1.10.2" @@ -16725,6 +16896,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^7.0.1": + version: 7.0.4 + resolution: "https-proxy-agent@npm:7.0.4" + dependencies: + agent-base: "npm:^7.0.2" + debug: "npm:4" + checksum: 405fe582bba461bfe5c7e2f8d752b384036854488b828ae6df6a587c654299cbb2c50df38c4b6ab303502c3c5e029a793fbaac965d1e86ee0be03faceb554d63 + languageName: node + linkType: hard + "human-id@npm:^1.0.2": version: 1.0.2 resolution: "human-id@npm:1.0.2" @@ -18336,6 +18517,27 @@ __metadata: languageName: node linkType: hard +"jwa@npm:^2.0.0": + version: 2.0.0 + resolution: "jwa@npm:2.0.0" + dependencies: + buffer-equal-constant-time: "npm:1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: ab983f6685d99d13ddfbffef9b1c66309a536362a8412d49ba6e687d834a1240ce39290f30ac7dbe241e0ab6c76fee7ff795776ce534e11d148158c9b7193498 + languageName: node + linkType: hard + +"jws@npm:^4.0.0": + version: 4.0.0 + resolution: "jws@npm:4.0.0" + dependencies: + jwa: "npm:^2.0.0" + safe-buffer: "npm:^5.0.1" + checksum: 1d15f4cdea376c6bd6a81002bd2cb0bf3d51d83da8f0727947b5ba3e10cf366721b8c0d099bf8c1eb99eb036e2c55e5fd5efd378ccff75a2b4e0bd10002348b9 + languageName: node + linkType: hard + "keccak@npm:3.0.1": version: 3.0.1 resolution: "keccak@npm:3.0.1" @@ -18918,6 +19120,13 @@ __metadata: languageName: node linkType: hard +"long@npm:^5.0.0": + version: 5.2.3 + resolution: "long@npm:5.2.3" + checksum: 9167ec6947a825b827c30da169a7384eec6c0c9ec2f0b9c74da2e93d81159bbe39fb09c3f13dae9721d4b807ccfa09797a7dd1012f5d478e3e33ca3c78b608e6 + languageName: node + linkType: hard + "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -20113,7 +20322,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12": +"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.9": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -20422,6 +20631,13 @@ __metadata: languageName: node linkType: hard +"object-hash@npm:^3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: f498d456a20512ba7be500cef4cf7b3c183cc72c65372a549c9a0e6dd78ce26f375e9b1315c07592d3fde8f10d5019986eba35970570d477ed9a2a702514432a + languageName: node + linkType: hard + "object-inspect@npm:^1.12.0, object-inspect@npm:^1.12.2, object-inspect@npm:^1.9.0": version: 1.12.2 resolution: "object-inspect@npm:1.12.2" @@ -21344,6 +21560,35 @@ __metadata: languageName: node linkType: hard +"proto3-json-serializer@npm:^2.0.0": + version: 2.0.1 + resolution: "proto3-json-serializer@npm:2.0.1" + dependencies: + protobufjs: "npm:^7.2.5" + checksum: dc4319c90e2412b9647f13dd1df2a6338ee3a07e2fd693c5ce4d1728c3730d913ebdb6d656f400ae4214a70bf0791ca0bc04d53b2cbdd75394bf0b175898443b + languageName: node + linkType: hard + +"protobufjs@npm:7.2.6": + version: 7.2.6 + resolution: "protobufjs@npm:7.2.6" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.4" + "@protobufjs/eventemitter": "npm:^1.1.0" + "@protobufjs/fetch": "npm:^1.1.0" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/inquire": "npm:^1.1.0" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.0" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.0.0" + checksum: 81ab853d28c71998d056d6b34f83c4bc5be40cb0b416585f99ed618aed833d64b2cf89359bad7474d345302f2b5e236c4519165f8483d7ece7fd5b0d9ac13f8b + languageName: node + linkType: hard + "protobufjs@npm:^6.8.8, protobufjs@npm:~6.11.2, protobufjs@npm:~6.11.3": version: 6.11.4 resolution: "protobufjs@npm:6.11.4" @@ -21368,6 +21613,26 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^7.2.5": + version: 7.3.0 + resolution: "protobufjs@npm:7.3.0" + dependencies: + "@protobufjs/aspromise": "npm:^1.1.2" + "@protobufjs/base64": "npm:^1.1.2" + "@protobufjs/codegen": "npm:^2.0.4" + "@protobufjs/eventemitter": "npm:^1.1.0" + "@protobufjs/fetch": "npm:^1.1.0" + "@protobufjs/float": "npm:^1.0.2" + "@protobufjs/inquire": "npm:^1.1.0" + "@protobufjs/path": "npm:^1.1.2" + "@protobufjs/pool": "npm:^1.1.0" + "@protobufjs/utf8": "npm:^1.1.0" + "@types/node": "npm:>=13.7.0" + long: "npm:^5.0.0" + checksum: aff4aa2a3a2f011accb51e23fcae122acbee35cb761abe51f799675a61ab39ad9a506911f307e0fdb9a1703bed1f522cfbdaafaeefd2b3aaca2ddc18f03029d9 + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -22253,6 +22518,17 @@ __metadata: languageName: node linkType: hard +"retry-request@npm:^7.0.0": + version: 7.0.2 + resolution: "retry-request@npm:7.0.2" + dependencies: + "@types/request": "npm:^2.48.8" + extend: "npm:^3.0.2" + teeny-request: "npm:^9.0.0" + checksum: 8f4c927d41dd575fc460aad7b762fb0a33542097201c3c1a31529ad17fa8af3ac0d2a45bf4a2024d079913e9c2dd431566070fe33321c667ac87ebb400de5917 + languageName: node + linkType: hard + "retry@npm:0.13.1": version: 0.13.1 resolution: "retry@npm:0.13.1" @@ -23390,7 +23666,16 @@ __metadata: languageName: node linkType: hard -"stream-shift@npm:^1.0.0": +"stream-events@npm:^1.0.5": + version: 1.0.5 + resolution: "stream-events@npm:1.0.5" + dependencies: + stubs: "npm:^3.0.0" + checksum: 969ce82e34bfbef5734629cc06f9d7f3705a9ceb8fcd6a526332f9159f1f8bbfdb1a453f3ced0b728083454f7706adbbe8428bceb788a0287ca48ba2642dc3fc + languageName: node + linkType: hard + +"stream-shift@npm:^1.0.0, stream-shift@npm:^1.0.2": version: 1.0.3 resolution: "stream-shift@npm:1.0.3" checksum: a24c0a3f66a8f9024bd1d579a533a53be283b4475d4e6b4b3211b964031447bdf6532dd1f3c2b0ad66752554391b7c62bd7ca4559193381f766534e723d50242 @@ -23690,6 +23975,13 @@ __metadata: languageName: node linkType: hard +"stubs@npm:^3.0.0": + version: 3.0.0 + resolution: "stubs@npm:3.0.0" + checksum: dec7b82186e3743317616235c59bfb53284acc312cb9f4c3e97e2205c67a5c158b0ca89db5927e52351582e90a2672822eeaec9db396e23e56893d2a8676e024 + languageName: node + linkType: hard + "styled-jsx@npm:5.1.1": version: 5.1.1 resolution: "styled-jsx@npm:5.1.1" @@ -23943,6 +24235,19 @@ __metadata: languageName: node linkType: hard +"teeny-request@npm:^9.0.0": + version: 9.0.0 + resolution: "teeny-request@npm:9.0.0" + dependencies: + http-proxy-agent: "npm:^5.0.0" + https-proxy-agent: "npm:^5.0.0" + node-fetch: "npm:^2.6.9" + stream-events: "npm:^1.0.5" + uuid: "npm:^9.0.0" + checksum: 44daabb6c2e239c3daed0218ebdafb50c7141c16d7257a6cfef786dbff56d7853c2c02c97934f7ed57818ce5861ac16c5f52f3a16fa292bd4caf53483d386443 + languageName: node + linkType: hard + "term-size@npm:^2.1.0": version: 2.2.1 resolution: "term-size@npm:2.2.1" From 554f875ebf70661513508b1ab18a31841c4c1d40 Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:41:43 +0530 Subject: [PATCH 27/59] feat: core deployment on Holesky (#3776) ### Description Core deployment on Holesky ### Drive-by changes None ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3597 ### Backward compatibility Yes ### Testing Manual --- rust/config/testnet_config.json | 49 + rust/hyperlane-core/src/chain.rs | 7 +- .../config/environments/testnet4/agent.ts | 5 +- .../testnet4/aw-validators/hyperlane.json | 3 + .../testnet4/core/verification.json | 290 +++ .../config/environments/testnet4/funding.ts | 2 + .../environments/testnet4/gas-oracle.ts | 2 + .../testnet4/ism/verification.json | 1560 +++++++++-------- .../testnet4/supportedChainNames.ts | 1 + .../environments/testnet4/validators.ts | 15 + typescript/sdk/src/consts/multisigIsm.ts | 5 + 11 files changed, 1186 insertions(+), 753 deletions(-) diff --git a/rust/config/testnet_config.json b/rust/config/testnet_config.json index 31a60fecf3..770b181c26 100644 --- a/rust/config/testnet_config.json +++ b/rust/config/testnet_config.json @@ -207,6 +207,55 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a" }, + "holesky": { + "blockExplorers": [ + { + "apiUrl": "https://api-holesky.etherscan.io/api", + "family": "etherscan", + "name": "Etherscan", + "url": "https://holesky.etherscan.io" + } + ], + "blocks": { + "confirmations": 1, + "estimateBlockTime": 13, + "reorgPeriod": 2 + }, + "chainId": 17000, + "domainId": 17000, + "domainRoutingIsmFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", + "fallbackRoutingHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", + "index": { + "from": 1543015 + }, + "interchainGasPaymaster": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "interchainSecurityModule": "0x751f2b684EeBb916dB777767CCb8fd793C8b2956", + "isTestnet": true, + "mailbox": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", + "merkleTreeHook": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", + "name": "holesky", + "nativeToken": { + "decimals": 18, + "name": "Ether", + "symbol": "ETH" + }, + "pausableHook": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", + "protocol": "ethereum", + "protocolFee": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", + "proxyAdmin": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "rpcUrls": [ + { + "http": "https://holesky.drpc.org" + } + ], + "staticAggregationHookFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", + "staticAggregationIsmFactory": "0x54148470292C24345fb828B003461a9444414517", + "staticMerkleRootMultisigIsmFactory": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "staticMessageIdMultisigIsmFactory": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "storageGasOracle": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", + "validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2" + }, "plumetestnet": { "aggregationHook": "0x31dF0EEE7Dc7565665468698a0da221225619a1B", "blockExplorers": [ diff --git a/rust/hyperlane-core/src/chain.rs b/rust/hyperlane-core/src/chain.rs index 667d392add..fe32b86307 100644 --- a/rust/hyperlane-core/src/chain.rs +++ b/rust/hyperlane-core/src/chain.rs @@ -51,6 +51,7 @@ impl<'a> std::fmt::Display for ContractLocator<'a> { pub enum KnownHyperlaneDomain { Ethereum = 1, Sepolia = 11155111, + Holesky = 17000, Polygon = 137, @@ -218,7 +219,7 @@ impl KnownHyperlaneDomain { Moonbeam, Gnosis, MantaPacific, Neutron, Injective, InEvm ], Testnet: [ - Alfajores, MoonbaseAlpha, Sepolia, ScrollSepolia, Chiado, PlumeTestnet, Fuji, BinanceSmartChainTestnet + Alfajores, MoonbaseAlpha, Sepolia, ScrollSepolia, Chiado, PlumeTestnet, Fuji, BinanceSmartChainTestnet, Holesky ], LocalTestChain: [Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, CosmosTest99991], }) @@ -229,7 +230,7 @@ impl KnownHyperlaneDomain { many_to_one!(match self { HyperlaneDomainProtocol::Ethereum: [ - Ethereum, Sepolia, Polygon, Avalanche, Fuji, Arbitrum, + Ethereum, Sepolia, Holesky, Polygon, Avalanche, Fuji, Arbitrum, Optimism, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, Alfajores, Moonbeam, InEvm, MoonbaseAlpha, ScrollSepolia, Chiado, MantaPacific, PlumeTestnet, Test1, Test2, Test3 @@ -246,7 +247,7 @@ impl KnownHyperlaneDomain { many_to_one!(match self { HyperlaneDomainTechnicalStack::ArbitrumNitro: [Arbitrum, PlumeTestnet], HyperlaneDomainTechnicalStack::Other: [ - Ethereum, Sepolia, Polygon, Avalanche, Fuji, Optimism, + Ethereum, Sepolia, Holesky, Polygon, Avalanche, Fuji, Optimism, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, Alfajores, Moonbeam, MoonbaseAlpha, ScrollSepolia, Chiado, MantaPacific, Neutron, Injective, InEvm, Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, CosmosTest99991 diff --git a/typescript/infra/config/environments/testnet4/agent.ts b/typescript/infra/config/environments/testnet4/agent.ts index 62c1628696..4ce2ab88dd 100644 --- a/typescript/infra/config/environments/testnet4/agent.ts +++ b/typescript/infra/config/environments/testnet4/agent.ts @@ -36,6 +36,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = { bsctestnet: true, eclipsetestnet: false, fuji: true, + holesky: true, plumetestnet: true, scrollsepolia: true, sepolia: true, @@ -46,6 +47,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = { bsctestnet: true, eclipsetestnet: false, fuji: true, + holesky: true, plumetestnet: true, scrollsepolia: true, sepolia: true, @@ -57,6 +59,7 @@ export const hyperlaneContextAgentChainConfig: AgentChainConfig = { // Cannot scrape non-EVM chains eclipsetestnet: false, fuji: true, + holesky: true, plumetestnet: true, scrollsepolia: true, sepolia: true, @@ -124,7 +127,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'c9c5d37-20240510-014327', + tag: 'e09a360-20240520-090014', }, chains: validatorChainConfig(Contexts.Hyperlane), }, diff --git a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json index 071d8e5cc0..a78d0e9534 100644 --- a/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json +++ b/typescript/infra/config/environments/testnet4/aw-validators/hyperlane.json @@ -20,6 +20,9 @@ "0x43e915573d9f1383cbf482049e4a012290759e7f" ] }, + "holesky": { + "validators": ["0x7ab28ad88bb45867137ea823af88e2cb02359c03"] + }, "plumetestnet": { "validators": [ "0xe765a214849f3ecdf00793b97d00422f2d408ea6", diff --git a/typescript/infra/config/environments/testnet4/core/verification.json b/typescript/infra/config/environments/testnet4/core/verification.json index 858d975ffe..5abcc05a8e 100644 --- a/typescript/infra/config/environments/testnet4/core/verification.json +++ b/typescript/infra/config/environments/testnet4/core/verification.json @@ -1841,6 +1841,296 @@ "name": "PausableHook" } ], + "holesky": [ + { + "address": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", + "constructorArguments": "", + "isProxy": false, + "name": "ProxyAdmin" + }, + { + "address": "0xB08d78F439e55D02C398519eef61606A5926245F", + "constructorArguments": "0000000000000000000000000000000000000000000000000000000000004268", + "isProxy": false, + "name": "Mailbox" + }, + { + "address": "0x46f7C5D896bbeC89bE1B19e4485e59b4Be49e9Cc", + "constructorArguments": "000000000000000000000000b08d78f439e55d02c398519eef61606a5926245f00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x679Dc08cC3A4acFeea2f7CAFAa37561aE0b41Ce7", + "constructorArguments": "000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false, + "name": "PausableIsm" + }, + { + "address": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000098aae089cad930c64a76dd2247a2ac5773a4b8ce", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x98AAE089CaD930C64a76dD2247a2aC5773a4B8cE", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc", + "isProxy": false, + "name": "MerkleTreeHook" + }, + { + "address": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000098aae089cad930c64a76dd2247a2ac5773a4b8ce", + "isProxy": false, + "name": "FallbackRoutingHook" + }, + { + "address": "0xF7561c34f17A32D5620583A3397C304e7038a7F6", + "constructorArguments": "", + "isProxy": false, + "name": "PausableHook" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x2b2a158B4059C840c7aC67399B153bb567D06303", + "constructorArguments": "", + "isProxy": false, + "name": "StorageGasOracle" + }, + { + "address": "0x5CE550e14B82a9F32A0aaF9eFc4Fce548D8A0B3e", + "constructorArguments": "", + "isProxy": false, + "name": "InterchainGasPaymaster" + }, + { + "address": "0x5CBf4e70448Ed46c2616b04e9ebc72D29FF0cfA9", + "constructorArguments": "0000000000000000000000005ce550e14b82a9f32a0aaf9efc4fce548d8a0b3e00000000000000000000000033db966328ea213b0f76ef96ca368ab37779f06500000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044485cc955000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c00000000000000000000000000000000000000000000000000000000", + "isProxy": true, + "name": "TransparentUpgradeableProxy" + }, + { + "address": "0x6b1bb4ce664Bb4164AEB4d3D2E7DE7450DD8084C", + "constructorArguments": "000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c000000000000000000000000fad1c94469700833717fa8a3017278bc1ca8031c", + "isProxy": false, + "name": "ProtocolFee" + }, + { + "address": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", + "constructorArguments": "00000000000000000000000046f7c5d896bbec89be1b19e4485e59b4be49e9cc", + "isProxy": false, + "name": "ValidatorAnnounce" + } + ], "moonbasealpha": [ { "address": "0xb241991527F1C21adE14F55589E5940aC4852Fa0", diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 42d9c75c94..5bd203227b 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -30,6 +30,8 @@ export const keyFunderConfig: KeyFunderConfig = { bsctestnet: '5', fuji: '5', plumetestnet: '0.2', + holesky: '5', + // Funder boosts itself upto 5x balance on L2 before dispersing funds scrollsepolia: '1', sepolia: '5', }, diff --git a/typescript/infra/config/environments/testnet4/gas-oracle.ts b/typescript/infra/config/environments/testnet4/gas-oracle.ts index 18d94c2e4d..f16da2ed37 100644 --- a/typescript/infra/config/environments/testnet4/gas-oracle.ts +++ b/typescript/infra/config/environments/testnet4/gas-oracle.ts @@ -19,6 +19,7 @@ import { ethereumChainNames } from './chains.js'; const gasPrices: ChainMap = { alfajores: ethers.utils.parseUnits('10', 'gwei'), fuji: ethers.utils.parseUnits('30', 'gwei'), + holesky: ethers.utils.parseUnits('10', 'gwei'), bsctestnet: ethers.utils.parseUnits('15', 'gwei'), sepolia: ethers.utils.parseUnits('5', 'gwei'), scrollsepolia: ethers.utils.parseUnits('0.5', 'gwei'), @@ -48,6 +49,7 @@ const chainTokenRarity: ChainMap = { alfajores: Rarity.Common, fuji: Rarity.Rare, bsctestnet: Rarity.Rare, + holesky: Rarity.Common, sepolia: Rarity.Mythic, scrollsepolia: Rarity.Rare, chiado: Rarity.Common, diff --git a/typescript/infra/config/environments/testnet4/ism/verification.json b/typescript/infra/config/environments/testnet4/ism/verification.json index 10b6f54c11..2b2cd33315 100644 --- a/typescript/infra/config/environments/testnet4/ism/verification.json +++ b/typescript/infra/config/environments/testnet4/ism/verification.json @@ -1,1498 +1,1560 @@ { "alfajores": [ { - "name": "StaticMultisigIsmFactory", "address": "0x9AF85731EDd41E2E50F81Ef8a0A69D2fB836EDf9", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMultisigIsmFactory" }, { - "name": "StaticAggregationIsmFactory", "address": "0xBEd8Fd6d5c6cBd878479C25f4725C7c842a43821", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "DomainRoutingIsmFactory", "address": "0x98F44EA5b9cA6aa02a5B75f31E0621083d9096a2", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", "address": "0x6525Ac4008E38e0E70DaEf59d5f0e1721bd8aA83", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMessageIdMultisigIsmFactory", "address": "0x4C739E01f295B70762C0bA9D86123E1775C2f703", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", "address": "0x9A574458497FCaB5E5673a2610C108725002DbA3", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", "address": "0xa9C7e306C0941896CA1fd528aA59089571D8D67E", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7" + "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsmFactory", "address": "0xC1b8c0e56D6a34940Ee2B86172450B54AFd633A7", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763" + "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsmFactory", "address": "0x4bE8AC22f506B1504C93C3A5b1579C5e7c550D9C", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f" + "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHookFactory", "address": "0x71bB34Ee833467443628CEdFAA188B2387827Cee", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticAggregationHook", - "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e" + "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", + "name": "StaticAggregationHook" }, { - "name": "DomainRoutingIsmFactory", "address": "0x37308d498bc7B0f002cb02Cf8fA01770dC2169c8", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E" + "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7" + "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763" + "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f" + "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e" + "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E" + "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7" + "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763" + "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f" + "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e" + "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E" + "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7" + "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763" + "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f" + "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e" + "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E" + "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7" + "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763" + "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f" + "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e" + "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E" + "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7" + "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763" + "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f" + "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e" + "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E" + "address": "0x94087079Bf22D8e2BCE02e2180e738C3F21dD44E", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", "address": "0xd5ab9FaC601Ed731d5e9c87E5977D2e5fD380666", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", "address": "0xd5ab9FaC601Ed731d5e9c87E5977D2e5fD380666", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", "address": "0xd5ab9FaC601Ed731d5e9c87E5977D2e5fD380666", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", "address": "0x826Ab153e944cE1B251aBD174071DF70d71132D7", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", "address": "0x78ebeCE0fCAa39F0F0C422E87EBd6BcB708aa763", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", "address": "0xd93F514f36d528DCDc3af1C92788f1bf1f480A7f", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", "address": "0x019e0a350D85eF67250dD88CD6477ad5E0c6E31e", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", "address": "0xd5ab9FaC601Ed731d5e9c87E5977D2e5fD380666", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" } ], - "fuji": [ + "bsctestnet": [ { - "name": "StaticMultisigIsmFactory", - "address": "0x094652a8ea2153A03916771a778E7b66839A4F58", + "address": "0xfb6B94750e1307719892fBC21AC7A0C74A467869", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMultisigIsmFactory" }, { - "name": "StaticAggregationIsmFactory", - "address": "0x9fB5D10C07569F2EBdc8ec4432B3a52b6d0ad9A0", + "address": "0xda72972291172B9966Dec7606d45d72e2b9f2470", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "DomainRoutingIsmFactory", - "address": "0xB24C91238eA30D59CF58CEB8dd5e4eaf70544d47", + "address": "0x0CA314006fe0e7EF88ad2Bb69a7421aB2f1C5288", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", + "address": "0x8DA546024850D998Be3b65204c0F0f63C1f3B0A1", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0xA1e6d12a3F5F7e05E4D6cb39E71534F27fE29561", + "address": "0x7Bc0bb71aE0E9bDC0Ac53e932870728D95FA28bF", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0xc25e8cE0960fa2573AFa591484F2cA6e4497C2e5", + "address": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x074D3C4249AFdac44B70d1D220F358Ff895F7a80" + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", + "address": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", + "address": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationIsmFactory", - "address": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", + "address": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8" + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationHookFactory", - "address": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", + "address": "0xea12ECFD1f241da323e93F12b4ed936403990190", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC" + "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71", + "name": "DomaingRoutingIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0xf9271189Cb30AD1F272f1A9EB2272224135B9350", - "constructorArguments": "", - "isProxy": false + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x930c36A0c978B0b86B544859692A1D94c5148204" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F" + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8" + "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71", + "name": "DomaingRoutingIsm" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC" + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x930c36A0c978B0b86B544859692A1D94c5148204" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F" + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8" + "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71", + "name": "DomaingRoutingIsm" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC" + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x930c36A0c978B0b86B544859692A1D94c5148204" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F" + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8" + "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71", + "name": "DomaingRoutingIsm" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC" + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x930c36A0c978B0b86B544859692A1D94c5148204" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F" + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8" + "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71", + "name": "DomaingRoutingIsm" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC" + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x930c36A0c978B0b86B544859692A1D94c5148204" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F" + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8" + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC" + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x930c36A0c978B0b86B544859692A1D94c5148204" + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", + "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticAggregationHook" + } + ], + "fuji": [ + { + "address": "0x094652a8ea2153A03916771a778E7b66839A4F58", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMultisigIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", + "address": "0x9fB5D10C07569F2EBdc8ec4432B3a52b6d0ad9A0", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "address": "0xB24C91238eA30D59CF58CEB8dd5e4eaf70544d47", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "address": "0x76a1aaE73e9D837ceF10Ac5AF5AfDD30d7612f98", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "address": "0xA1e6d12a3F5F7e05E4D6cb39E71534F27fE29561", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticAggregationHook", - "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "address": "0xc25e8cE0960fa2573AFa591484F2cA6e4497C2e5", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", + "address": "0x074D3C4249AFdac44B70d1D220F358Ff895F7a80", + "name": "StaticMerkleRootMultisigIsm" + }, + { + "address": "0x93F50Ac4E5663DAAb03508008d592f6260964f62", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "name": "StaticMerkleRootMultisigIsm" + }, + { + "address": "0x90e1F9918F304645e4F6324E5C0EAc70138C84Ce", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "name": "StaticMessageIdMultisigIsm" + }, + { + "address": "0xF588129ed84F219A1f0f921bE7Aa1B2176516858", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticAggregationIsm", "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "name": "StaticAggregationIsm" + }, + { + "address": "0x99554CC33cBCd6EDDd2f3fc9c7C9194Cb3b5df1E", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticAggregationHook", "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", - "constructorArguments": "", - "isProxy": true + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", + "address": "0xf9271189Cb30AD1F272f1A9EB2272224135B9350", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "DomainRoutingIsmFactory" + }, + { + "address": "0x930c36A0c978B0b86B544859692A1D94c5148204", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", - "constructorArguments": "", - "isProxy": true + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", - "constructorArguments": "", - "isProxy": true + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", - "constructorArguments": "", - "isProxy": true + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", - "constructorArguments": "", - "isProxy": true + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", - "constructorArguments": "", - "isProxy": true - } - ], - "bsctestnet": [ + "address": "0x930c36A0c978B0b86B544859692A1D94c5148204", + "name": "DomaingRoutingIsm" + }, { - "name": "StaticMultisigIsmFactory", - "address": "0xfb6B94750e1307719892fBC21AC7A0C74A467869", - "constructorArguments": "", - "isProxy": false + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsmFactory", - "address": "0xda72972291172B9966Dec7606d45d72e2b9f2470", - "constructorArguments": "", - "isProxy": false + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x0CA314006fe0e7EF88ad2Bb69a7421aB2f1C5288", - "constructorArguments": "", - "isProxy": false + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "name": "StaticAggregationIsm" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x8DA546024850D998Be3b65204c0F0f63C1f3B0A1", - "constructorArguments": "", - "isProxy": false + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "name": "StaticAggregationHook" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x7Bc0bb71aE0E9bDC0Ac53e932870728D95FA28bF", - "constructorArguments": "", - "isProxy": false + "address": "0x930c36A0c978B0b86B544859692A1D94c5148204", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x3E235B90197E1D6b5DB5ad5aD49f2c1ED6406382", - "constructorArguments": "", - "isProxy": false + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c" + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x0D96aF0c01c4bbbadaaF989Eb489c8783F35B763", - "constructorArguments": "", - "isProxy": false + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515" + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsmFactory", - "address": "0x40613dE82d672605Ab051C64079022Bb4F8bDE4f", - "constructorArguments": "", - "isProxy": false + "address": "0x930c36A0c978B0b86B544859692A1D94c5148204", + "name": "DomaingRoutingIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b" + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationHookFactory", - "address": "0xa1145B39F1c7Ef9aA593BC1DB1634b00CC020942", - "constructorArguments": "", - "isProxy": false + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175" + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "name": "StaticAggregationIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0xea12ECFD1f241da323e93F12b4ed936403990190", - "constructorArguments": "", - "isProxy": false + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71" + "address": "0x930c36A0c978B0b86B544859692A1D94c5148204", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c" + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515" + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b" + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175" + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71" + "address": "0x930c36A0c978B0b86B544859692A1D94c5148204", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c" + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515" + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b" + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175" + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71" + "address": "0x683a81E0e1a238dcA7341e04c08d3bba6f0Cb74f", + "constructorArguments": "", + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c" + "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515" + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b" + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175" + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71" + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c" + "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515" + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b" + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175" + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0xf9E264C10E9176c97c22790E0Ece8D98927Bca71" + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "address": "0x0683D0258A162a87E1F25C454BaA4e9E9DE46B0a", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "address": "0xd3262339D39103a4b41379b183b0311B78c9a11F", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "address": "0x6716Ef4e72c74e66eB40198B16b4379a763fCEd8", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "address": "0xBDD7Cfb61D7ae89343b428D3E527BDA9705c4EAC", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "address": "0xD47593C21C2429de56AcA808bb2Bbc236c6311DE", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" + } + ], + "holesky": [ + { + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x9b13f3d75797951f463D61092Bba7947c8cEb25c", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x25069c25466B1F21E0D22f467a5f91bc7b24b515", + "address": "0x54148470292C24345fb828B003461a9444414517", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x25adfb5Ddf378EC77cd5842Ff33B96277E4DD29b", + "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x3938ED4c6b5a7f9947C44e58e36a864F03228175", + "address": "0x589C201a07c26b4725A4A829d772f24423da480B", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationHookFactory" + }, + { + "address": "0xD6B8D6C372e07f67FeAb75403c0Ec88E3cce7Ab7", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" + }, + { + "address": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "constructorArguments": "", - "isProxy": true + "isProxy": false, + "name": "DomainRoutingIsmFactory" + }, + { + "address": "0x8CF9aB5F805072A007dAd0ae56E0099e83B14b61", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" } ], - "sepolia": [ + "moonbasealpha": [ { - "name": "StaticMultisigIsmFactory", - "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", + "address": "0x4266D8Dd66D8Eb3934c8942968d1e54214D072d3", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMultisigIsmFactory" }, { - "name": "StaticAggregationIsmFactory", - "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "address": "0x759c4Eb4575B651a9f0Fb46653dd7B2F32fD7310", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "DomainRoutingIsmFactory", - "address": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c", + "address": "0x561331FafB7f2ABa77E77780178ADdD1A37bdaBD", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0xA9999B4abC373FF2BB95B8725FABC96CA883d811", + "address": "0x0616A79374e81eB1d2275eCe5837aD050f9c53f1", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0xCCC126d96efcc342BF2781A7d224D3AB1F25B19C", + "address": "0x3D696c38Dd958e635f9077e65b64aA9cf7c92627", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x0a71AcC99967829eE305a285750017C4916Ca269", + "address": "0xA59Ba0A8D4ea5A5DC9c8B0101ba7E6eE6C3399A4", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd" + "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", + "address": "0x8f919348F9C4619A196Acb5e377f49E5E2C0B569", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E" + "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsmFactory", - "address": "0xC83e12EF2627ACE445C298e6eC418684918a6002", + "address": "0x0048FaB53526D9a0478f66D660059E3E3611FE3E", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89" + "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHookFactory", - "address": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", + "address": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticAggregationHook", - "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD" + "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", + "name": "StaticAggregationHook" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x3603458990BfEb30f99E61B58427d196814D8ce1", + "address": "0x385C7f179168f5Da92c72E17AE8EF50F3874077f", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3" + "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd" + "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E" + "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89" + "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD" + "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3" + "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd" + "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E" + "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89" + "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD" + "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3" + "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd" + "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E" + "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89" + "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD" + "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3" + "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd" + "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E" + "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89" + "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD" + "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3" + "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", + "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", + "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", + "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", + "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", + "address": "0xE8F752e5C4E1A6a2e3eAfa42d44D601A22d78f2b", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0x9b0CC3BD9030CE269EF3124Bb36Cf954a490688e", + "address": "0x063C2D908EAb532Cd207F309F0fd176ae6b2e1d1", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" } ], - "moonbasealpha": [ + "plumetestnet": [ { - "name": "StaticMultisigIsmFactory", - "address": "0x4266D8Dd66D8Eb3934c8942968d1e54214D072d3", + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticAggregationIsmFactory", - "address": "0x759c4Eb4575B651a9f0Fb46653dd7B2F32fD7310", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x561331FafB7f2ABa77E77780178ADdD1A37bdaBD", + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x0616A79374e81eB1d2275eCe5837aD050f9c53f1", + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x3D696c38Dd958e635f9077e65b64aA9cf7c92627", + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0xA59Ba0A8D4ea5A5DC9c8B0101ba7E6eE6C3399A4", + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186" + "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x8f919348F9C4619A196Acb5e377f49E5E2C0B569", + "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb" + "address": "0x54148470292C24345fb828B003461a9444414517", + "constructorArguments": "", + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticAggregationIsmFactory", - "address": "0x0048FaB53526D9a0478f66D660059E3E3611FE3E", + "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" + } + ], + "scrollsepolia": [ + { + "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e" + "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationHookFactory", - "address": "0x00DFB81Bfc45fa03060b605273147F274ea807E5", + "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticAggregationHook", - "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x385C7f179168f5Da92c72E17AE8EF50F3874077f", + "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186" + "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e" + "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticAggregationHook", - "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9" + "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "constructorArguments": "", + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "name": "StaticAggregationIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "name": "StaticAggregationHook" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "name": "StaticAggregationIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "name": "StaticAggregationHook" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186" + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb" + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9" + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "name": "StaticAggregationIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0xCd76aC44337BbA003bb4Bed2Ac37604148F36bA9" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "name": "StaticAggregationHook" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x97B07A8BCc0d212A4C76679b303A4C6441BB6186", - "constructorArguments": "", - "isProxy": true + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "name": "DomaingRoutingIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xb1Fc72f251A6b6c37CD4Ef8f9287578BCc7CE9Eb", - "constructorArguments": "", - "isProxy": true + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x13b6Cb8d51C7e15e5fFee432CBB1d4614972A87e", - "constructorArguments": "", - "isProxy": true + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0x713496A1477a08230E526a4e9449FCCE4d6523e9", - "constructorArguments": "", - "isProxy": true + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", + "name": "StaticAggregationIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0xE8F752e5C4E1A6a2e3eAfa42d44D601A22d78f2b", - "constructorArguments": "", - "isProxy": false + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "name": "StaticAggregationHook" }, { - "name": "DomaingRoutingIsm", - "address": "0x063C2D908EAb532Cd207F309F0fd176ae6b2e1d1", - "constructorArguments": "", - "isProxy": true - } - ], - "scrollsepolia": [ + "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "name": "DomaingRoutingIsm" + }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x6E7b29CB2A7617405B4d30C6f84bBD51b4Bb4be8", + "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x209e7F9d40954E230008B9bb076a0901d32695e5" + "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", + "constructorArguments": "", + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x275aCcCa81cAD931dC6fB6E49ED233Bc99Bed4A7", + "address": "0x87935eB971eaA9826060261b07a919451dfd0409", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" + "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", + "constructorArguments": "", + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0xeb6f11189197223c656807a83B0DD374f9A6dF44", + "address": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" - }, + "address": "0xea80345322520d37770dbDeD3FE9c53ba93E70D8", + "constructorArguments": "", + "isProxy": true, + "name": "DomaingRoutingIsm" + } + ], + "sepolia": [ { - "name": "StaticAggregationIsmFactory", - "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", + "address": "0xD356C996277eFb7f75Ee8bd61b31cC781A12F54f", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMultisigIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" + "address": "0x01812D60958798695391dacF092BAc4a715B1718", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "StaticAggregationHookFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", + "address": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" + "address": "0xA9999B4abC373FF2BB95B8725FABC96CA883d811", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "DomainRoutingIsmFactory", - "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "address": "0xCCC126d96efcc342BF2781A7d224D3AB1F25B19C", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" + "address": "0x0a71AcC99967829eE305a285750017C4916Ca269", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMerkleRootMultisigIsmFactory" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" + "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" + "address": "0xFEb9585b2f948c1eD74034205a7439261a9d27DD", + "constructorArguments": "", + "isProxy": false, + "name": "StaticMessageIdMultisigIsmFactory" }, { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" + "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" + "address": "0xC83e12EF2627ACE445C298e6eC418684918a6002", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" + "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", + "name": "StaticAggregationIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" + "address": "0x160C28C92cA453570aD7C031972b58d5Dd128F72", + "constructorArguments": "", + "isProxy": false, + "name": "StaticAggregationHookFactory" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" + "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" + "address": "0x3603458990BfEb30f99E61B58427d196814D8ce1", + "constructorArguments": "", + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" + "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3", + "name": "DomaingRoutingIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" + "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" + "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" + "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" + "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" + "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3", + "name": "DomaingRoutingIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" + "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360" + "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391" + "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409" + "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83" + "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3", + "name": "DomaingRoutingIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722" + "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x33999AB153F68D481AAB1B238368Ffd1Fe81F360", - "constructorArguments": "", - "isProxy": true + "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0x3e6F45B03314bD21BcE4201666d483291575E391", - "constructorArguments": "", - "isProxy": true + "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409", - "constructorArguments": "", - "isProxy": true + "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", + "name": "StaticAggregationHook" }, { - "name": "StaticAggregationHook", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", - "constructorArguments": "", - "isProxy": true + "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3", + "name": "DomaingRoutingIsm" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x17866ebE0e503784a9461d3e753dEeD0d3F61153", - "constructorArguments": "", - "isProxy": false + "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "DomaingRoutingIsm", - "address": "0xea80345322520d37770dbDeD3FE9c53ba93E70D8", - "constructorArguments": "", - "isProxy": true - } - ], - "plumetestnet": [ - { - "name": "StaticMerkleRootMultisigIsmFactory", - "address": "0x16B710b86CAd07E6F1C531861a16F5feC29dba37", - "constructorArguments": "", - "isProxy": false + "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticMerkleRootMultisigIsm", - "address": "0x87935eB971eaA9826060261b07a919451dfd0409", - "constructorArguments": "", - "isProxy": true + "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", + "name": "StaticAggregationIsm" }, { - "name": "StaticMessageIdMultisigIsmFactory", - "address": "0x44b764045BfDC68517e10e783E69B376cef196B2", - "constructorArguments": "", - "isProxy": false + "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", + "name": "StaticAggregationHook" }, { - "name": "StaticMessageIdMultisigIsm", - "address": "0xE5cA56294dA5Bd490D5Bc489B177B002ad16AF83", - "constructorArguments": "", - "isProxy": true + "address": "0x67543F48DBe592F1A15FD9aE37b1AbD104c70cf3", + "name": "DomaingRoutingIsm" }, { - "name": "StaticAggregationIsmFactory", - "address": "0xC2E36cd6e32e194EE11f15D9273B64461A4D49A2", + "address": "0xbe94Cd5d0973dB4A6E63Cb9e6b2b2042D99F9Bcd", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticMerkleRootMultisigIsm" }, { - "name": "StaticAggregationIsm", - "address": "0x7fFe8C9c17F46F94D784E148FbadD4bF66477722", + "address": "0x218C5DFb7C2bC1e051FCFebBa8C6D66Dda28617E", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticMessageIdMultisigIsm" }, { - "name": "StaticAggregationHookFactory", - "address": "0x6966b0E55883d49BFB24539356a2f8A673E02039", + "address": "0x3dD1aB6B71EBfEd239869365FE5B8b47E0507d89", "constructorArguments": "", - "isProxy": false + "isProxy": true, + "name": "StaticAggregationIsm" }, { - "name": "StaticAggregationHook", - "address": "0x4863236F3a05A1A1F0850fF8cd09afeBAE82d953", + "address": "0x4BC580FF77Dd1Eb320E96D34FddC97F0F04D3feD", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "StaticAggregationHook" }, { - "name": "DomainRoutingIsmFactory", - "address": "0x54148470292C24345fb828B003461a9444414517", + "address": "0x3F100cBBE5FD5466BdB4B3a15Ac226957e7965Ad", "constructorArguments": "", - "isProxy": false + "isProxy": false, + "name": "DomainRoutingIsmFactory" }, { - "name": "DomaingRoutingIsm", - "address": "0x10c9FF6EEE4BaD29734322467f541C84001422C2", + "address": "0x9b0CC3BD9030CE269EF3124Bb36Cf954a490688e", "constructorArguments": "", - "isProxy": true + "isProxy": true, + "name": "DomaingRoutingIsm" } ] } diff --git a/typescript/infra/config/environments/testnet4/supportedChainNames.ts b/typescript/infra/config/environments/testnet4/supportedChainNames.ts index c65393e550..c966447a1b 100644 --- a/typescript/infra/config/environments/testnet4/supportedChainNames.ts +++ b/typescript/infra/config/environments/testnet4/supportedChainNames.ts @@ -4,6 +4,7 @@ export const supportedChainNames = [ 'alfajores', 'bsctestnet', 'eclipsetestnet', + 'holesky', 'fuji', 'plumetestnet', 'scrollsepolia', diff --git a/typescript/infra/config/environments/testnet4/validators.ts b/typescript/infra/config/environments/testnet4/validators.ts index 6e594ffa73..965f0f38be 100644 --- a/typescript/infra/config/environments/testnet4/validators.ts +++ b/typescript/infra/config/environments/testnet4/validators.ts @@ -70,6 +70,21 @@ export const validatorChainConfig = ( 'bsctestnet', ), }, + holesky: { + interval: 13, + reorgPeriod: getReorgPeriod('holesky'), + validators: validatorsConfig( + { + [Contexts.Hyperlane]: ['0x7ab28ad88bb45867137ea823af88e2cb02359c03'], + [Contexts.ReleaseCandidate]: [ + '0x7ab28ad88bb45867137ea823af88e2cb02359c03', + ], + [Contexts.Neutron]: [], + }, + 'holesky', + ), + }, + scrollsepolia: { interval: 5, reorgPeriod: getReorgPeriod('scrollsepolia'), diff --git a/typescript/sdk/src/consts/multisigIsm.ts b/typescript/sdk/src/consts/multisigIsm.ts index c6f8d8568e..6dd1ba4c97 100644 --- a/typescript/sdk/src/consts/multisigIsm.ts +++ b/typescript/sdk/src/consts/multisigIsm.ts @@ -135,6 +135,11 @@ export const defaultMultisigConfigs: ChainMap = { ], }, + holesky: { + threshold: 1, + validators: ['0x7ab28ad88bb45867137ea823af88e2cb02359c03'], // TODO + }, + inevm: { threshold: 2, validators: [ From efa9025f5ece51efe6f6b08ae39601c2f1bca493 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 5 Jun 2024 10:12:34 +0100 Subject: [PATCH 28/59] chore(infra): Remove RpcConsensusType usage in infra now that SmartProvider is used instead (#3882) ### Description Removed references to RpcConsensusType where TS-based providers are used in infra ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .../config/environments/mainnet3/funding.ts | 3 --- .../environments/mainnet3/helloworld.ts | 4 --- .../config/environments/mainnet3/index.ts | 2 -- .../environments/mainnet3/liquidityLayer.ts | 2 -- .../config/environments/testnet4/funding.ts | 3 --- .../environments/testnet4/helloworld.ts | 4 --- .../config/environments/testnet4/index.ts | 2 -- .../environments/testnet4/middleware.ts | 3 --- .../helloworld-kathy/templates/_helpers.tpl | 4 --- .../helm/key-funder/templates/cron-job.yaml | 4 --- .../templates/circle-relayer-deployment.yaml | 4 --- .../templates/portal-relayer-deployment.yaml | 4 --- typescript/infra/scripts/agent-utils.ts | 1 - .../scripts/agents/update-agent-config.ts | 1 - .../funding/fund-keys-from-deployer.ts | 7 ------ typescript/infra/scripts/helloworld/kathy.ts | 13 ---------- typescript/infra/scripts/helloworld/utils.ts | 5 ---- typescript/infra/src/config/chain.ts | 25 +++++-------------- typescript/infra/src/config/environment.ts | 2 -- typescript/infra/src/config/funding.ts | 3 +-- .../infra/src/config/helloworld/types.ts | 3 +-- typescript/infra/src/config/middleware.ts | 3 --- typescript/infra/src/funding/key-funder.ts | 1 - typescript/infra/src/helloworld/kathy.ts | 1 - .../src/middleware/liquidity-layer-relayer.ts | 1 - 25 files changed, 8 insertions(+), 97 deletions(-) diff --git a/typescript/infra/config/environments/mainnet3/funding.ts b/typescript/infra/config/environments/mainnet3/funding.ts index 80c0efc970..25e01b03d5 100644 --- a/typescript/infra/config/environments/mainnet3/funding.ts +++ b/typescript/infra/config/environments/mainnet3/funding.ts @@ -1,5 +1,3 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; - import { KeyFunderConfig } from '../../../src/config/funding.js'; import { Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; @@ -23,7 +21,6 @@ export const keyFunderConfig: KeyFunderConfig = { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], }, - connectionType: RpcConsensusType.Fallback, // desired balance config desiredBalancePerChain: { arbitrum: '0.5', diff --git a/typescript/infra/config/environments/mainnet3/helloworld.ts b/typescript/infra/config/environments/mainnet3/helloworld.ts index 4f15ba9126..0df01f1d77 100644 --- a/typescript/infra/config/environments/mainnet3/helloworld.ts +++ b/typescript/infra/config/environments/mainnet3/helloworld.ts @@ -1,5 +1,3 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; - import { HelloWorldConfig, HelloWorldKathyRunMode, @@ -26,7 +24,6 @@ export const hyperlane: HelloWorldConfig = { }, messageSendTimeout: 1000 * 60 * 8, // 8 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min - connectionType: RpcConsensusType.Fallback, 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) }, }; @@ -46,7 +43,6 @@ export const releaseCandidate: HelloWorldConfig = { }, messageSendTimeout: 1000 * 60 * 8, // 8 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min - connectionType: RpcConsensusType.Fallback, }, }; diff --git a/typescript/infra/config/environments/mainnet3/index.ts b/typescript/infra/config/environments/mainnet3/index.ts index 6a765693fd..94c76e53aa 100644 --- a/typescript/infra/config/environments/mainnet3/index.ts +++ b/typescript/infra/config/environments/mainnet3/index.ts @@ -1,5 +1,4 @@ import { IRegistry } from '@hyperlane-xyz/registry'; -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; import { getKeysForRole, @@ -42,7 +41,6 @@ export const environment: EnvironmentConfig = { getMultiProvider: async ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, - _connectionType?: RpcConsensusType, useSecrets?: boolean, ) => getMultiProviderForRole( diff --git a/typescript/infra/config/environments/mainnet3/liquidityLayer.ts b/typescript/infra/config/environments/mainnet3/liquidityLayer.ts index ac311f4471..0f3a6d2879 100644 --- a/typescript/infra/config/environments/mainnet3/liquidityLayer.ts +++ b/typescript/infra/config/environments/mainnet3/liquidityLayer.ts @@ -2,7 +2,6 @@ import { BridgeAdapterConfig, BridgeAdapterType, ChainMap, - RpcConsensusType, } from '@hyperlane-xyz/sdk'; import { LiquidityLayerRelayerConfig } from '../../../src/config/middleware.js'; @@ -50,5 +49,4 @@ export const relayerConfig: LiquidityLayerRelayerConfig = { namespace: environment, prometheusPushGateway: 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', - connectionType: RpcConsensusType.Single, }; diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 5bd203227b..0855865483 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -1,5 +1,3 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; - import { KeyFunderConfig } from '../../../src/config/funding.js'; import { Role } from '../../../src/roles.js'; import { Contexts } from '../../contexts.js'; @@ -23,7 +21,6 @@ export const keyFunderConfig: KeyFunderConfig = { [Contexts.Hyperlane]: [Role.Relayer, Role.Kathy], [Contexts.ReleaseCandidate]: [Role.Relayer, Role.Kathy], }, - connectionType: RpcConsensusType.Fallback, // desired balance config desiredBalancePerChain: { alfajores: '5', diff --git a/typescript/infra/config/environments/testnet4/helloworld.ts b/typescript/infra/config/environments/testnet4/helloworld.ts index e6f094c7d9..e8c10ba0e1 100644 --- a/typescript/infra/config/environments/testnet4/helloworld.ts +++ b/typescript/infra/config/environments/testnet4/helloworld.ts @@ -1,5 +1,3 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; - import { HelloWorldConfig, HelloWorldKathyRunMode, @@ -26,7 +24,6 @@ export const hyperlaneHelloworld: HelloWorldConfig = { }, messageSendTimeout: 1000 * 60 * 10, // 10 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min - connectionType: RpcConsensusType.Fallback, }, }; @@ -45,7 +42,6 @@ export const releaseCandidateHelloworld: HelloWorldConfig = { }, messageSendTimeout: 1000 * 60 * 8, // 8 min messageReceiptTimeout: 1000 * 60 * 20, // 20 min - connectionType: RpcConsensusType.Fallback, }, }; diff --git a/typescript/infra/config/environments/testnet4/index.ts b/typescript/infra/config/environments/testnet4/index.ts index b93de05c96..1d1204083d 100644 --- a/typescript/infra/config/environments/testnet4/index.ts +++ b/typescript/infra/config/environments/testnet4/index.ts @@ -1,5 +1,4 @@ import { IRegistry } from '@hyperlane-xyz/registry'; -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; import { objMerge } from '@hyperlane-xyz/utils'; import { @@ -44,7 +43,6 @@ export const environment: EnvironmentConfig = { getMultiProvider: async ( context: Contexts = Contexts.Hyperlane, role: Role = Role.Deployer, - _connectionType?: RpcConsensusType, useSecrets?: boolean, ) => getMultiProviderForRole( diff --git a/typescript/infra/config/environments/testnet4/middleware.ts b/typescript/infra/config/environments/testnet4/middleware.ts index 607b4a3f6b..cf3d2782cb 100644 --- a/typescript/infra/config/environments/testnet4/middleware.ts +++ b/typescript/infra/config/environments/testnet4/middleware.ts @@ -1,5 +1,3 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; - import { LiquidityLayerRelayerConfig } from '../../../src/config/middleware.js'; import { environment } from './chains.js'; @@ -12,5 +10,4 @@ export const liquidityLayerRelayerConfig: LiquidityLayerRelayerConfig = { namespace: environment, prometheusPushGateway: 'http://prometheus-prometheus-pushgateway.monitoring.svc.cluster.local:9091', - connectionType: RpcConsensusType.Single, }; diff --git a/typescript/infra/helm/helloworld-kathy/templates/_helpers.tpl b/typescript/infra/helm/helloworld-kathy/templates/_helpers.tpl index 12b1e0db95..286fd126a6 100644 --- a/typescript/infra/helm/helloworld-kathy/templates/_helpers.tpl +++ b/typescript/infra/helm/helloworld-kathy/templates/_helpers.tpl @@ -91,10 +91,6 @@ The helloworld-kathy container {{- if .Values.hyperlane.cycleOnce }} - --cycle-once {{- end }} -{{- if .Values.hyperlane.connectionType }} - - --connection-type - - {{ .Values.hyperlane.connectionType }} -{{- end }} {{- if .Values.hyperlane.cyclesBetweenEthereumMessages }} - --cycles-between-ethereum-messages - "{{ .Values.hyperlane.cyclesBetweenEthereumMessages }}" diff --git a/typescript/infra/helm/key-funder/templates/cron-job.yaml b/typescript/infra/helm/key-funder/templates/cron-job.yaml index 610ebf8bb2..b891222816 100644 --- a/typescript/infra/helm/key-funder/templates/cron-job.yaml +++ b/typescript/infra/helm/key-funder/templates/cron-job.yaml @@ -29,10 +29,6 @@ spec: - --contexts-and-roles - {{ $context }}={{ join "," $roles }} {{- end }} -{{- if .Values.hyperlane.connectionType }} - - --connection-type - - {{ .Values.hyperlane.connectionType }} -{{- end }} {{- range $chain, $balance := .Values.hyperlane.desiredBalancePerChain }} - --desired-balance-per-chain - {{ $chain }}={{ $balance }} diff --git a/typescript/infra/helm/liquidity-layer-relayers/templates/circle-relayer-deployment.yaml b/typescript/infra/helm/liquidity-layer-relayers/templates/circle-relayer-deployment.yaml index 02dc713787..a41589924f 100644 --- a/typescript/infra/helm/liquidity-layer-relayers/templates/circle-relayer-deployment.yaml +++ b/typescript/infra/helm/liquidity-layer-relayers/templates/circle-relayer-deployment.yaml @@ -21,10 +21,6 @@ spec: - ./typescript/infra/scripts/middleware/circle-relayer.ts - -e - {{ .Values.hyperlane.runEnv }} -{{- if .Values.hyperlane.connectionType }} - - --connection-type - - {{ .Values.hyperlane.connectionType }} -{{- end }} envFrom: - secretRef: name: liquidity-layer-env-var-secret diff --git a/typescript/infra/helm/liquidity-layer-relayers/templates/portal-relayer-deployment.yaml b/typescript/infra/helm/liquidity-layer-relayers/templates/portal-relayer-deployment.yaml index 8f18284244..933210d8d8 100644 --- a/typescript/infra/helm/liquidity-layer-relayers/templates/portal-relayer-deployment.yaml +++ b/typescript/infra/helm/liquidity-layer-relayers/templates/portal-relayer-deployment.yaml @@ -21,10 +21,6 @@ spec: - ./typescript/infra/scripts/middleware/portal-relayer.ts - -e - {{ .Values.hyperlane.runEnv }} -{{- if .Values.hyperlane.connectionType }} - - --connection-type - - {{ .Values.hyperlane.connectionType }} -{{- end }} envFrom: - secretRef: name: liquidity-layer-env-var-secret diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index 3ac0f5ebda..5f814e8274 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -9,7 +9,6 @@ import { CoreConfig, MultiProtocolProvider, MultiProvider, - RpcConsensusType, collectValidators, } from '@hyperlane-xyz/sdk'; import { diff --git a/typescript/infra/scripts/agents/update-agent-config.ts b/typescript/infra/scripts/agents/update-agent-config.ts index 127f4bf167..6b0ed65fdd 100644 --- a/typescript/infra/scripts/agents/update-agent-config.ts +++ b/typescript/infra/scripts/agents/update-agent-config.ts @@ -7,7 +7,6 @@ async function main() { const envConfig = getEnvironmentConfig(environment); let multiProvider = await envConfig.getMultiProvider( - undefined, undefined, undefined, // Don't use secrets diff --git a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts index 000034581e..b949b9a325 100644 --- a/typescript/infra/scripts/funding/fund-keys-from-deployer.ts +++ b/typescript/infra/scripts/funding/fund-keys-from-deployer.ts @@ -9,7 +9,6 @@ import { ChainName, HyperlaneIgp, MultiProvider, - RpcConsensusType, } from '@hyperlane-xyz/sdk'; import { Address, objFilter, objMap, rootLogger } from '@hyperlane-xyz/utils'; @@ -177,11 +176,6 @@ async function main() { ) .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) - .demandOption('connection-type') - .boolean('skip-igp-claim') .describe('skip-igp-claim', 'If true, never claims funds from the IGP') .default('skip-igp-claim', false).argv; @@ -191,7 +185,6 @@ async function main() { const multiProvider = await config.getMultiProvider( Contexts.Hyperlane, // Always fund from the hyperlane context Role.Deployer, // Always fund from the deployer - argv.connectionType, ); let contextFunders: ContextFunder[]; diff --git a/typescript/infra/scripts/helloworld/kathy.ts b/typescript/infra/scripts/helloworld/kathy.ts index 4e39ab14f1..f3cbb1a495 100644 --- a/typescript/infra/scripts/helloworld/kathy.ts +++ b/typescript/infra/scripts/helloworld/kathy.ts @@ -11,7 +11,6 @@ import { MultiProtocolCore, MultiProvider, ProviderType, - RpcConsensusType, TypedTransactionReceipt, resolveOrDeployAccountOwner, } from '@hyperlane-xyz/sdk'; @@ -128,16 +127,6 @@ function getKathyArgs() { chainStrs.map((chainStr: string) => assertChain(chainStr)), ) - .string('connection-type') - .describe('connection-type', 'The provider connection type to use for RPCs') - .default('connection-type', RpcConsensusType.Single) - .choices('connection-type', [ - RpcConsensusType.Single, - RpcConsensusType.Quorum, - RpcConsensusType.Fallback, - ]) - .demandOption('connection-type') - .number('cycles-between-ethereum-messages') .describe( 'cycles-between-ethereum-messages', @@ -156,7 +145,6 @@ async function main(): Promise { fullCycleTime, messageSendTimeout, messageReceiptTimeout, - connectionType, cyclesBetweenEthereumMessages, } = await getKathyArgs(); @@ -173,7 +161,6 @@ async function main(): Promise { context, Role.Kathy, undefined, - connectionType, ); const appChains = app.chains(); diff --git a/typescript/infra/scripts/helloworld/utils.ts b/typescript/infra/scripts/helloworld/utils.ts index 38ab8969f1..03cb408ebd 100644 --- a/typescript/infra/scripts/helloworld/utils.ts +++ b/typescript/infra/scripts/helloworld/utils.ts @@ -8,7 +8,6 @@ import { MultiProtocolCore, MultiProtocolProvider, MultiProvider, - RpcConsensusType, attachContractsMap, attachContractsMapAndGetForeignDeployments, filterChainMapToProtocol, @@ -28,12 +27,10 @@ export async function getHelloWorldApp( context: Contexts, keyRole: Role, keyContext: Contexts = context, - connectionType: RpcConsensusType = RpcConsensusType.Single, ) { const multiProvider: MultiProvider = await coreConfig.getMultiProvider( keyContext, keyRole, - connectionType, ); const helloworldConfig = getHelloWorldConfig(coreConfig, context); @@ -61,12 +58,10 @@ export async function getHelloWorldMultiProtocolApp( context: Contexts, keyRole: Role, keyContext: Contexts = context, - connectionType: RpcConsensusType = RpcConsensusType.Single, ) { const multiProvider: MultiProvider = await coreConfig.getMultiProvider( keyContext, keyRole, - connectionType, ); const envAddresses = getEnvAddresses(coreConfig.environment); diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index 414ca34bd4..2be5bc4eaf 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -8,8 +8,6 @@ import { ChainName, HyperlaneSmartProvider, ProviderRetryOptions, - RpcConsensusType, - RpcUrl, } from '@hyperlane-xyz/sdk'; import { ProtocolType, objFilter, objMerge } from '@hyperlane-xyz/utils'; @@ -26,7 +24,6 @@ export const defaultRetry: ProviderRetryOptions = { export async function fetchProvider( chainName: ChainName, - connectionType: RpcConsensusType = RpcConsensusType.Single, ): Promise { const chainMetadata = getChain(chainName); if (!chainMetadata) { @@ -38,22 +35,12 @@ export async function fetchProvider( throw Error(`No RPC URLs found for chain: ${chainName}`); } - if (connectionType === RpcConsensusType.Single) { - return HyperlaneSmartProvider.fromRpcUrl(chainId, rpcData[0], defaultRetry); - } else if ( - connectionType === RpcConsensusType.Quorum || - connectionType === RpcConsensusType.Fallback - ) { - return new HyperlaneSmartProvider( - chainId, - rpcData.map((url) => ({ http: url })), - undefined, - // disable retry for quorum - connectionType === RpcConsensusType.Fallback ? defaultRetry : undefined, - ); - } else { - throw Error(`Unsupported connectionType: ${connectionType}`); - } + return new HyperlaneSmartProvider( + chainId, + rpcData.map((url) => ({ http: url })), + undefined, + defaultRetry, + ); } export function getChainMetadatas(chains: Array) { diff --git a/typescript/infra/src/config/environment.ts b/typescript/infra/src/config/environment.ts index 72e30f6d9f..5b6fa03506 100644 --- a/typescript/infra/src/config/environment.ts +++ b/typescript/infra/src/config/environment.ts @@ -9,7 +9,6 @@ import { MultiProtocolProvider, MultiProvider, OwnableConfig, - RpcConsensusType, } from '@hyperlane-xyz/sdk'; import { objKeys } from '@hyperlane-xyz/utils'; @@ -54,7 +53,6 @@ export type EnvironmentConfig = { getMultiProvider: ( context?: Contexts, role?: Role, - connectionType?: RpcConsensusType, useSecrets?: boolean, ) => Promise; getKeys: ( diff --git a/typescript/infra/src/config/funding.ts b/typescript/infra/src/config/funding.ts index a55a6524fb..f75d19cfde 100644 --- a/typescript/infra/src/config/funding.ts +++ b/typescript/infra/src/config/funding.ts @@ -1,4 +1,4 @@ -import { ChainMap, RpcConsensusType } from '@hyperlane-xyz/sdk'; +import { ChainMap } from '@hyperlane-xyz/sdk'; import { Contexts } from '../../config/contexts.js'; import { FundableRole, Role } from '../roles.js'; @@ -20,7 +20,6 @@ export interface KeyFunderConfig { contextsAndRolesToFund: ContextAndRolesMap; cyclesBetweenEthereumMessages?: number; prometheusPushGateway: string; - connectionType: RpcConsensusType; desiredBalancePerChain: ChainMap; desiredKathyBalancePerChain: ChainMap; } diff --git a/typescript/infra/src/config/helloworld/types.ts b/typescript/infra/src/config/helloworld/types.ts index 1345894d8e..ba2be07505 100644 --- a/typescript/infra/src/config/helloworld/types.ts +++ b/typescript/infra/src/config/helloworld/types.ts @@ -1,4 +1,4 @@ -import { ChainMap, ChainName, RpcConsensusType } from '@hyperlane-xyz/sdk'; +import { ChainMap, ChainName } from '@hyperlane-xyz/sdk'; import { DockerConfig } from '../agent/agent.js'; @@ -29,7 +29,6 @@ export interface HelloWorldKathyConfig { messageReceiptTimeout: number; // Which type of provider to use - connectionType: RpcConsensusType; // How many cycles to skip between a cycles that send messages to/from Ethereum. Defaults to 0. cyclesBetweenEthereumMessages?: number; } diff --git a/typescript/infra/src/config/middleware.ts b/typescript/infra/src/config/middleware.ts index e6f25f92b3..19fc3d8481 100644 --- a/typescript/infra/src/config/middleware.ts +++ b/typescript/infra/src/config/middleware.ts @@ -1,10 +1,7 @@ -import { RpcConsensusType } from '@hyperlane-xyz/sdk'; - import { DockerConfig } from './agent/agent.js'; export interface LiquidityLayerRelayerConfig { docker: DockerConfig; namespace: string; - connectionType: RpcConsensusType.Single | RpcConsensusType.Quorum; prometheusPushGateway: string; } diff --git a/typescript/infra/src/funding/key-funder.ts b/typescript/infra/src/funding/key-funder.ts index b149b6a0b2..ae29c78426 100644 --- a/typescript/infra/src/funding/key-funder.ts +++ b/typescript/infra/src/funding/key-funder.ts @@ -48,7 +48,6 @@ function getKeyFunderHelmValues( chains: agentConfig.environmentChainNames, contextFundingFrom: keyFunderConfig.contextFundingFrom, contextsAndRolesToFund: keyFunderConfig.contextsAndRolesToFund, - connectionType: keyFunderConfig.connectionType, desiredBalancePerChain: keyFunderConfig.desiredBalancePerChain, desiredKathyBalancePerChain: keyFunderConfig.desiredKathyBalancePerChain, }, diff --git a/typescript/infra/src/helloworld/kathy.ts b/typescript/infra/src/helloworld/kathy.ts index 44651380de..6366049fae 100644 --- a/typescript/infra/src/helloworld/kathy.ts +++ b/typescript/infra/src/helloworld/kathy.ts @@ -85,7 +85,6 @@ function getHelloworldKathyHelmValues( messageReceiptTimeout: kathyConfig.messageReceiptTimeout, cycleOnce, fullCycleTime, - connectionType: kathyConfig.connectionType, cyclesBetweenEthereumMessages: kathyConfig.cyclesBetweenEthereumMessages, }, image: { diff --git a/typescript/infra/src/middleware/liquidity-layer-relayer.ts b/typescript/infra/src/middleware/liquidity-layer-relayer.ts index e334c921e2..4a1c3d7a17 100644 --- a/typescript/infra/src/middleware/liquidity-layer-relayer.ts +++ b/typescript/infra/src/middleware/liquidity-layer-relayer.ts @@ -44,7 +44,6 @@ function getLiquidityLayerRelayerHelmValues( runEnv: agentConfig.runEnv, // Only used for fetching RPC urls as env vars chains: agentConfig.contextChainNames, - connectionType: relayerConfig.connectionType, }, image: { repository: relayerConfig.docker.repo, From d10b035ec2f2b0e95dd6578ff56d1f0efd7e8834 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Wed, 5 Jun 2024 12:59:18 +0100 Subject: [PATCH 29/59] fix: testnet4 holesky config (#3900) ran `yarn tsx ./scripts/agents/update-agent-config.ts -e testnet4` and updated testnet4 config Signed-off-by: Paul Balaji --- rust/config/testnet_config.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/config/testnet_config.json b/rust/config/testnet_config.json index 770b181c26..3680b07c94 100644 --- a/rust/config/testnet_config.json +++ b/rust/config/testnet_config.json @@ -222,6 +222,7 @@ "reorgPeriod": 2 }, "chainId": 17000, + "displayName": "Holesky", "domainId": 17000, "domainRoutingIsmFactory": "0xDDcFEcF17586D08A5740B7D91735fcCE3dfe3eeD", "fallbackRoutingHook": "0x07009DA2249c388aD0f416a235AfE90D784e1aAc", @@ -245,7 +246,7 @@ "proxyAdmin": "0x33dB966328Ea213b0f76eF96CA368AB37779F065", "rpcUrls": [ { - "http": "https://holesky.drpc.org" + "http": "https://ethereum-holesky-rpc.publicnode.com" } ], "staticAggregationHookFactory": "0x589C201a07c26b4725A4A829d772f24423da480B", From 335310355c5fb1cab36b64ede21ee35ddf46211f Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:01:34 +0100 Subject: [PATCH 30/59] feat: relayer dashboard template (#3898) ### Description Relayer metrics template for monitoring & alerting. Looks like so: ![Screenshot 2024-05-29 at 18 08 26](https://github.com/hyperlane-xyz/hyperlane-monorepo/assets/23065004/581fdd7e-3f68-490f-bd6d-bf58ad688ba3) --- .../easy-relayer-dashboard-external.json | 436 ++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 tools/grafana/easy-relayer-dashboard-external.json diff --git a/tools/grafana/easy-relayer-dashboard-external.json b/tools/grafana/easy-relayer-dashboard-external.json new file mode 100644 index 0000000000..49efaaf2f0 --- /dev/null +++ b/tools/grafana/easy-relayer-dashboard-external.json @@ -0,0 +1,436 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 66, + "links": [], + "panels": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "description": "There shouldn't be abrupt changes, especially for a specific pair", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 78, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum by (origin,remote)(round(increase(hyperlane_messages_processed_count[5m])))", + "hide": false, + "interval": "", + "legendFormat": "{{hyperlane_deployment}}: {{origin}}->{{remote}}", + "range": true, + "refId": "A" + } + ], + "title": "Messages Processed", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 1, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "editorMode": "code", + "expr": "sum by (remote, queue_name)(\n hyperlane_submitter_queue_length{queue_name=\"prepare_queue\"}\n)", + "interval": "", + "legendFormat": "{{hyperlane_deployment }} - {{remote}}", + "range": true, + "refId": "A" + } + ], + "title": "Prepare queues (all)", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 18 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by(remote, queue_name) (hyperlane_submitter_queue_length{queue_name=\"submit_queue\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "interval": "", + "legendFormat": "{{remote}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Submit Queues", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 26 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "maxHeight": 600, + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "grafanacloud-prom" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by(remote, queue_name) (avg_over_time(hyperlane_submitter_queue_length{queue_name=\"confirm_queue\"}[20m]))", + "fullMetaSearch": false, + "includeNullMetadata": true, + "interval": "", + "legendFormat": "{{remote}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Confirm Queues", + "type": "timeseries" + } + ], + "refresh": "1m", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-7d", + "to": "now" + }, + "timeRangeUpdatedDuringEditOrView": false, + "timepicker": {}, + "timezone": "browser", + "title": "Easy Dashboard (External Sharing Template)", + "uid": "afdf6ada6uzvgga", + "version": 5, + "weekStart": "" +} \ No newline at end of file From a8a68f6f61c75d7aa100d3d9b87e548fa713d4de Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Wed, 5 Jun 2024 14:08:24 -0400 Subject: [PATCH 31/59] test: XERC20 and XERC20 Lockbox integration tests (#3849) --- .changeset/sweet-pandas-brush.md | 5 + solidity/contracts/test/ERC20Test.sol | 50 ++++++- .../token/extensions/HypXERC20Lockbox.sol | 27 +++- solidity/coverage.sh | 2 +- solidity/foundry.toml | 6 +- solidity/lib/forge-std | 2 +- solidity/script/xerc20/.env.blast | 4 + solidity/script/xerc20/.env.ethereum | 5 + solidity/script/xerc20/ApproveLockbox.s.sol | 50 +++++++ solidity/script/xerc20/GrantLimits.s.sol | 37 +++++ solidity/script/xerc20/ezETH.s.sol | 127 ++++++++++++++++++ solidity/test/AnvilRPC.sol | 107 +++++++++++++++ solidity/test/token/HypERC20.t.sol | 77 ++++++++++- 13 files changed, 485 insertions(+), 14 deletions(-) create mode 100644 .changeset/sweet-pandas-brush.md create mode 100644 solidity/script/xerc20/.env.blast create mode 100644 solidity/script/xerc20/.env.ethereum create mode 100644 solidity/script/xerc20/ApproveLockbox.s.sol create mode 100644 solidity/script/xerc20/GrantLimits.s.sol create mode 100644 solidity/script/xerc20/ezETH.s.sol create mode 100644 solidity/test/AnvilRPC.sol diff --git a/.changeset/sweet-pandas-brush.md b/.changeset/sweet-pandas-brush.md new file mode 100644 index 0000000000..74e05a094d --- /dev/null +++ b/.changeset/sweet-pandas-brush.md @@ -0,0 +1,5 @@ +--- +"@hyperlane-xyz/core": patch +--- + +fix: make XERC20 and XERC20 Lockbox proxy-able diff --git a/solidity/contracts/test/ERC20Test.sol b/solidity/contracts/test/ERC20Test.sol index 8d4580c248..03b3064292 100644 --- a/solidity/contracts/test/ERC20Test.sol +++ b/solidity/contracts/test/ERC20Test.sol @@ -3,6 +3,7 @@ pragma solidity >=0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "../token/interfaces/IXERC20Lockbox.sol"; import "../token/interfaces/IXERC20.sol"; import "../token/interfaces/IFiatToken.sol"; @@ -66,15 +67,50 @@ contract XERC20Test is ERC20Test, IXERC20 { _burn(account, amount); } - function setLimits( - address _bridge, - uint256 _mintingLimit, - uint256 _burningLimit - ) external { - require(false); + function setLimits(address, uint256, uint256) external pure { + assert(false); } - function owner() external returns (address) { + function owner() external pure returns (address) { return address(0x0); } } + +contract XERC20LockboxTest is IXERC20Lockbox { + IXERC20 public immutable XERC20; + IERC20 public immutable ERC20; + + constructor( + string memory name, + string memory symbol, + uint256 totalSupply, + uint8 __decimals + ) { + ERC20Test erc20 = new ERC20Test(name, symbol, totalSupply, __decimals); + erc20.transfer(msg.sender, totalSupply); + ERC20 = erc20; + XERC20 = new XERC20Test(name, symbol, 0, __decimals); + } + + function depositTo(address _user, uint256 _amount) public { + ERC20.transferFrom(msg.sender, address(this), _amount); + XERC20.mint(_user, _amount); + } + + function deposit(uint256 _amount) external { + depositTo(msg.sender, _amount); + } + + function depositNativeTo(address) external payable { + assert(false); + } + + function withdrawTo(address _user, uint256 _amount) public { + XERC20.burn(msg.sender, _amount); + ERC20Test(address(ERC20)).mintTo(_user, _amount); + } + + function withdraw(uint256 _amount) external { + withdrawTo(msg.sender, _amount); + } +} diff --git a/solidity/contracts/token/extensions/HypXERC20Lockbox.sol b/solidity/contracts/token/extensions/HypXERC20Lockbox.sol index f4a8609179..6c95abd3e7 100644 --- a/solidity/contracts/token/extensions/HypXERC20Lockbox.sol +++ b/solidity/contracts/token/extensions/HypXERC20Lockbox.sol @@ -17,18 +17,39 @@ contract HypXERC20Lockbox is HypERC20Collateral { ) HypERC20Collateral(address(IXERC20Lockbox(_lockbox).ERC20()), _mailbox) { lockbox = IXERC20Lockbox(_lockbox); xERC20 = lockbox.XERC20(); + approveLockbox(); + } - // grant infinite approvals to lockbox + /** + * @notice Approve the lockbox to spend the wrapped token and xERC20 + * @dev This function is idempotent and need not be access controlled + */ + function approveLockbox() public { require( - IERC20(wrappedToken).approve(_lockbox, MAX_INT), + IERC20(wrappedToken).approve(address(lockbox), MAX_INT), "erc20 lockbox approve failed" ); require( - xERC20.approve(_lockbox, MAX_INT), + xERC20.approve(address(lockbox), MAX_INT), "xerc20 lockbox approve failed" ); } + /** + * @notice Initialize the contract + * @param _hook The address of the hook contract + * @param _ism The address of the interchain security module + * @param _owner The address of the owner + */ + function initialize( + address _hook, + address _ism, + address _owner + ) public override initializer { + approveLockbox(); + _MailboxClient_initialize(_hook, _ism, _owner); + } + function _transferFromSender( uint256 _amount ) internal override returns (bytes memory) { diff --git a/solidity/coverage.sh b/solidity/coverage.sh index bd9a3e232d..a3f7d463f1 100755 --- a/solidity/coverage.sh +++ b/solidity/coverage.sh @@ -14,7 +14,7 @@ fi lcov --version # exclude FastTokenRouter until https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/2806 -EXCLUDE="*test* *mock* *node_modules* *FastHyp*" +EXCLUDE="*test* *mock* *node_modules* *script* *FastHyp*" lcov \ --rc lcov_branch_coverage=1 \ --remove lcov.info $EXCLUDE \ diff --git a/solidity/foundry.toml b/solidity/foundry.toml index 8180d9b58f..51a9912cbf 100644 --- a/solidity/foundry.toml +++ b/solidity/foundry.toml @@ -14,7 +14,11 @@ fs_permissions = [ { access = "read", path = "./script/avs/"}, { access = "write", path = "./fixtures" } ] -ignored_warnings_from = ['fx-portal'] +ignored_warnings_from = [ + 'lib', + 'test', + 'contracts/test' +] [profile.ci] verbosity = 4 diff --git a/solidity/lib/forge-std b/solidity/lib/forge-std index e8a047e3f4..52715a217d 160000 --- a/solidity/lib/forge-std +++ b/solidity/lib/forge-std @@ -1 +1 @@ -Subproject commit e8a047e3f40f13fa37af6fe14e6e06283d9a060e +Subproject commit 52715a217dc51d0de15877878ab8213f6cbbbab5 diff --git a/solidity/script/xerc20/.env.blast b/solidity/script/xerc20/.env.blast new file mode 100644 index 0000000000..b0db0d8282 --- /dev/null +++ b/solidity/script/xerc20/.env.blast @@ -0,0 +1,4 @@ +export ROUTER_ADDRESS=0xA34ceDf9068C5deE726C67A4e1DCfCc2D6E2A7fD +export ERC20_ADDRESS=0x2416092f143378750bb29b79eD961ab195CcEea5 +export XERC20_ADDRESS=0x2416092f143378750bb29b79eD961ab195CcEea5 +export RPC_URL="https://rpc.blast.io" diff --git a/solidity/script/xerc20/.env.ethereum b/solidity/script/xerc20/.env.ethereum new file mode 100644 index 0000000000..4d6366a8c0 --- /dev/null +++ b/solidity/script/xerc20/.env.ethereum @@ -0,0 +1,5 @@ +export ROUTER_ADDRESS=0x8dfbEA2582F41c8C4Eb25252BbA392fd3c09449A +export ADMIN_ADDRESS=0xa5B0D537CeBE97f087Dc5FE5732d70719caaEc1D +export ERC20_ADDRESS=0xbf5495Efe5DB9ce00f80364C8B423567e58d2110 +export XERC20_ADDRESS=0x2416092f143378750bb29b79eD961ab195CcEea5 +export RPC_URL="https://eth.merkle.io" diff --git a/solidity/script/xerc20/ApproveLockbox.s.sol b/solidity/script/xerc20/ApproveLockbox.s.sol new file mode 100644 index 0000000000..182306eabc --- /dev/null +++ b/solidity/script/xerc20/ApproveLockbox.s.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import "forge-std/Script.sol"; + +import {AnvilRPC} from "test/AnvilRPC.sol"; +import {TypeCasts} from "contracts/libs/TypeCasts.sol"; + +import {ITransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; + +import {ProxyAdmin} from "contracts/upgrade/ProxyAdmin.sol"; + +import {HypXERC20Lockbox} from "contracts/token/extensions/HypXERC20Lockbox.sol"; +import {IXERC20Lockbox} from "contracts/token/interfaces/IXERC20Lockbox.sol"; +import {IXERC20} from "contracts/token/interfaces/IXERC20.sol"; +import {IERC20} from "contracts/token/interfaces/IXERC20.sol"; + +// source .env. +// forge script ApproveLockbox.s.sol --broadcast --rpc-url localhost:XXXX +contract ApproveLockbox is Script { + address router = vm.envAddress("ROUTER_ADDRESS"); + address admin = vm.envAddress("ADMIN_ADDRESS"); + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + + ITransparentUpgradeableProxy proxy = ITransparentUpgradeableProxy(router); + HypXERC20Lockbox old = HypXERC20Lockbox(router); + address lockbox = address(old.lockbox()); + address mailbox = address(old.mailbox()); + ProxyAdmin proxyAdmin = ProxyAdmin(admin); + + function run() external { + assert(proxyAdmin.getProxyAdmin(proxy) == admin); + + vm.startBroadcast(deployerPrivateKey); + HypXERC20Lockbox logic = new HypXERC20Lockbox(lockbox, mailbox); + proxyAdmin.upgradeAndCall( + proxy, + address(logic), + abi.encodeCall(HypXERC20Lockbox.approveLockbox, ()) + ); + vm.stopBroadcast(); + + vm.expectRevert("Initializable: contract is already initialized"); + HypXERC20Lockbox(address(proxy)).initialize( + address(0), + address(0), + mailbox + ); + } +} diff --git a/solidity/script/xerc20/GrantLimits.s.sol b/solidity/script/xerc20/GrantLimits.s.sol new file mode 100644 index 0000000000..e2c79bae6e --- /dev/null +++ b/solidity/script/xerc20/GrantLimits.s.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import "forge-std/Script.sol"; + +import {AnvilRPC} from "test/AnvilRPC.sol"; + +import {IXERC20Lockbox} from "contracts/token/interfaces/IXERC20Lockbox.sol"; +import {IXERC20} from "contracts/token/interfaces/IXERC20.sol"; +import {IERC20} from "contracts/token/interfaces/IXERC20.sol"; + +// source .env. +// anvil --fork-url $RPC_URL --port XXXX +// forge script GrantLimits.s.sol --broadcast --unlocked --rpc-url localhost:XXXX +contract GrantLimits is Script { + address tester = 0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba; + uint256 amount = 1 gwei; + + address router = vm.envAddress("ROUTER_ADDRESS"); + IERC20 erc20 = IERC20(vm.envAddress("ERC20_ADDRESS")); + IXERC20 xerc20 = IXERC20(vm.envAddress("XERC20_ADDRESS")); + + function runFrom(address account) internal { + AnvilRPC.setBalance(account, 1 ether); + AnvilRPC.impersonateAccount(account); + vm.broadcast(account); + } + + function run() external { + address owner = xerc20.owner(); + runFrom(owner); + xerc20.setLimits(router, amount, amount); + + runFrom(address(erc20)); + erc20.transfer(tester, amount); + } +} diff --git a/solidity/script/xerc20/ezETH.s.sol b/solidity/script/xerc20/ezETH.s.sol new file mode 100644 index 0000000000..f6171eb63d --- /dev/null +++ b/solidity/script/xerc20/ezETH.s.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import "forge-std/Script.sol"; + +import {IXERC20Lockbox} from "../../contracts/token/interfaces/IXERC20Lockbox.sol"; +import {IXERC20} from "../../contracts/token/interfaces/IXERC20.sol"; +import {IERC20} from "../../contracts/token/interfaces/IXERC20.sol"; +import {HypXERC20Lockbox} from "../../contracts/token/extensions/HypXERC20Lockbox.sol"; +import {HypERC20Collateral} from "../../contracts/token/HypERC20Collateral.sol"; +import {HypXERC20} from "../../contracts/token/extensions/HypXERC20.sol"; +import {TransparentUpgradeableProxy} from "../../contracts/upgrade/TransparentUpgradeableProxy.sol"; +import {ProxyAdmin} from "../../contracts/upgrade/ProxyAdmin.sol"; + +import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; +import {TokenMessage} from "../../contracts/token/libs/TokenMessage.sol"; + +contract ezETH is Script { + using TypeCasts for address; + + string ETHEREUM_RPC_URL = vm.envString("ETHEREUM_RPC_URL"); + string BLAST_RPC_URL = vm.envString("BLAST_RPC_URL"); + + uint256 ethereumFork; + uint32 ethereumDomainId = 1; + address ethereumMailbox = 0xc005dc82818d67AF737725bD4bf75435d065D239; + address ethereumLockbox = 0xC8140dA31E6bCa19b287cC35531c2212763C2059; + + uint256 blastFork; + uint32 blastDomainId = 81457; + address blastXERC20 = 0x2416092f143378750bb29b79eD961ab195CcEea5; + address blastMailbox = 0x3a867fCfFeC2B790970eeBDC9023E75B0a172aa7; + + uint256 amount = 100; + + function setUp() public { + ethereumFork = vm.createFork(ETHEREUM_RPC_URL); + blastFork = vm.createFork(BLAST_RPC_URL); + } + + function run() external { + address deployer = address(this); + bytes32 recipient = deployer.addressToBytes32(); + bytes memory tokenMessage = TokenMessage.format(recipient, amount, ""); + vm.selectFork(ethereumFork); + HypXERC20Lockbox hypXERC20Lockbox = new HypXERC20Lockbox( + ethereumLockbox, + ethereumMailbox + ); + ProxyAdmin ethAdmin = new ProxyAdmin(); + TransparentUpgradeableProxy ethProxy = new TransparentUpgradeableProxy( + address(hypXERC20Lockbox), + address(ethAdmin), + abi.encodeCall( + HypXERC20Lockbox.initialize, + (address(0), address(0), deployer) + ) + ); + hypXERC20Lockbox = HypXERC20Lockbox(address(ethProxy)); + + vm.selectFork(blastFork); + HypXERC20 hypXERC20 = new HypXERC20(blastXERC20, blastMailbox); + ProxyAdmin blastAdmin = new ProxyAdmin(); + TransparentUpgradeableProxy blastProxy = new TransparentUpgradeableProxy( + address(hypXERC20), + address(blastAdmin), + abi.encodeCall( + HypERC20Collateral.initialize, + (address(0), address(0), deployer) + ) + ); + hypXERC20 = HypXERC20(address(blastProxy)); + hypXERC20.enrollRemoteRouter( + ethereumDomainId, + address(hypXERC20Lockbox).addressToBytes32() + ); + + // grant `amount` mint and burn limit to warp route + vm.prank(IXERC20(blastXERC20).owner()); + IXERC20(blastXERC20).setLimits(address(hypXERC20), amount, amount); + + // test sending `amount` on warp route + vm.prank(0x7BE481D464CAD7ad99500CE8A637599eB8d0FCDB); // ezETH whale + IXERC20(blastXERC20).transfer(address(this), amount); + IXERC20(blastXERC20).approve(address(hypXERC20), amount); + uint256 value = hypXERC20.quoteGasPayment(ethereumDomainId); + hypXERC20.transferRemote{value: value}( + ethereumDomainId, + recipient, + amount + ); + + // test receiving `amount` on warp route + vm.prank(blastMailbox); + hypXERC20.handle( + ethereumDomainId, + address(hypXERC20Lockbox).addressToBytes32(), + tokenMessage + ); + + vm.selectFork(ethereumFork); + hypXERC20Lockbox.enrollRemoteRouter( + blastDomainId, + address(hypXERC20).addressToBytes32() + ); + + // grant `amount` mint and burn limit to warp route + IXERC20 ethereumXERC20 = hypXERC20Lockbox.xERC20(); + vm.prank(ethereumXERC20.owner()); + ethereumXERC20.setLimits(address(hypXERC20Lockbox), amount, amount); + + // test sending `amount` on warp route + IERC20 erc20 = IXERC20Lockbox(ethereumLockbox).ERC20(); + vm.prank(ethereumLockbox); + erc20.transfer(address(this), amount); + erc20.approve(address(hypXERC20Lockbox), amount); + hypXERC20Lockbox.transferRemote(blastDomainId, recipient, amount); + + // test receiving `amount` on warp route + vm.prank(ethereumMailbox); + hypXERC20Lockbox.handle( + blastDomainId, + address(hypXERC20).addressToBytes32(), + tokenMessage + ); + } +} diff --git a/solidity/test/AnvilRPC.sol b/solidity/test/AnvilRPC.sol new file mode 100644 index 0000000000..eb1be413a6 --- /dev/null +++ b/solidity/test/AnvilRPC.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity >=0.8.0; + +import "forge-std/Vm.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; + +// see https://book.getfoundry.sh/reference/anvil/#supported-rpc-methods +library AnvilRPC { + using Strings for address; + using Strings for uint256; + + using AnvilRPC for string; + using AnvilRPC for string[1]; + using AnvilRPC for string[2]; + using AnvilRPC for string[3]; + + Vm private constant vm = + Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + string private constant OPEN_ARRAY = "["; + string private constant CLOSE_ARRAY = "]"; + string private constant COMMA = ","; + string private constant EMPTY_ARRAY = "[]"; + + function escaped( + string memory value + ) internal pure returns (string memory) { + return string.concat(ESCAPED_QUOTE, value, ESCAPED_QUOTE); + } + + function toString( + string[1] memory values + ) internal pure returns (string memory) { + return string.concat(OPEN_ARRAY, values[0], CLOSE_ARRAY); + } + + function toString( + string[2] memory values + ) internal pure returns (string memory) { + return + string.concat(OPEN_ARRAY, values[0], COMMA, values[1], CLOSE_ARRAY); + } + + function toString( + string[3] memory values + ) internal pure returns (string memory) { + return + string.concat( + OPEN_ARRAY, + values[0], + COMMA, + values[1], + COMMA, + values[2], + CLOSE_ARRAY + ); + } + + function impersonateAccount(address account) internal { + vm.rpc( + "anvil_impersonateAccount", + [account.toHexString().escaped()].toString() + ); + } + + function setBalance(address account, uint256 balance) internal { + vm.rpc( + "anvil_setBalance", + [account.toHexString().escaped(), balance.toString()].toString() + ); + } + + function setCode(address account, bytes memory code) internal { + vm.rpc( + "anvil_setCode", + [account.toHexString().escaped(), string(code).escaped()].toString() + ); + } + + function setStorageAt( + address account, + uint256 slot, + uint256 value + ) internal { + vm.rpc( + "anvil_setStorageAt", + [ + account.toHexString().escaped(), + slot.toHexString(), + value.toHexString() + ].toString() + ); + } + + function resetFork(string memory rpcUrl) internal { + string memory obj = string.concat( + // solhint-disable-next-line quotes + '{"forking":{"jsonRpcUrl":', + string(rpcUrl).escaped(), + "}}" + ); + vm.rpc("anvil_reset", [obj].toString()); + } +} + +// here to prevent syntax highlighting from breaking +string constant ESCAPED_QUOTE = '"'; diff --git a/solidity/test/token/HypERC20.t.sol b/solidity/test/token/HypERC20.t.sol index 82c5359b7d..300e59c549 100644 --- a/solidity/test/token/HypERC20.t.sol +++ b/solidity/test/token/HypERC20.t.sol @@ -19,13 +19,14 @@ import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transpa import {Mailbox} from "../../contracts/Mailbox.sol"; import {TypeCasts} from "../../contracts/libs/TypeCasts.sol"; import {TestMailbox} from "../../contracts/test/TestMailbox.sol"; -import {XERC20Test, FiatTokenTest, ERC20Test} from "../../contracts/test/ERC20Test.sol"; +import {XERC20LockboxTest, XERC20Test, FiatTokenTest, ERC20Test} from "../../contracts/test/ERC20Test.sol"; import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol"; import {GasRouter} from "../../contracts/client/GasRouter.sol"; import {HypERC20} from "../../contracts/token/HypERC20.sol"; import {HypERC20Collateral} from "../../contracts/token/HypERC20Collateral.sol"; +import {HypXERC20Lockbox} from "../../contracts/token/extensions/HypXERC20Lockbox.sol"; import {IXERC20} from "../../contracts/token/interfaces/IXERC20.sol"; import {IFiatToken} from "../../contracts/token/interfaces/IFiatToken.sol"; import {HypXERC20} from "../../contracts/token/extensions/HypXERC20.sol"; @@ -442,6 +443,80 @@ contract HypXERC20Test is HypTokenTest { } } +contract HypXERC20LockboxTest is HypTokenTest { + using TypeCasts for address; + HypXERC20Lockbox internal xerc20Lockbox; + + function setUp() public override { + super.setUp(); + + XERC20LockboxTest lockbox = new XERC20LockboxTest( + NAME, + SYMBOL, + TOTAL_SUPPLY, + DECIMALS + ); + primaryToken = ERC20Test(address(lockbox.ERC20())); + + localToken = new HypXERC20Lockbox( + address(lockbox), + address(localMailbox) + ); + xerc20Lockbox = HypXERC20Lockbox(address(localToken)); + + xerc20Lockbox.enrollRemoteRouter( + DESTINATION, + address(remoteToken).addressToBytes32() + ); + + primaryToken.transfer(ALICE, 1000e18); + + _enrollRemoteTokenRouter(); + } + + uint256 constant MAX_INT = 2 ** 256 - 1; + + function testApproval() public { + assertEq( + xerc20Lockbox.xERC20().allowance( + address(localToken), + address(xerc20Lockbox.lockbox()) + ), + MAX_INT + ); + assertEq( + xerc20Lockbox.wrappedToken().allowance( + address(localToken), + address(xerc20Lockbox.lockbox()) + ), + MAX_INT + ); + } + + function testRemoteTransfer() public { + uint256 balanceBefore = localToken.balanceOf(ALICE); + + vm.prank(ALICE); + primaryToken.approve(address(localToken), TRANSFER_AMT); + vm.expectCall( + address(xerc20Lockbox.xERC20()), + abi.encodeCall(IXERC20.burn, (address(localToken), TRANSFER_AMT)) + ); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); + assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + } + + function testHandle() public { + uint256 balanceBefore = localToken.balanceOf(ALICE); + vm.expectCall( + address(xerc20Lockbox.xERC20()), + abi.encodeCall(IXERC20.mint, (address(localToken), TRANSFER_AMT)) + ); + _handleLocalTransfer(TRANSFER_AMT); + assertEq(localToken.balanceOf(ALICE), balanceBefore + TRANSFER_AMT); + } +} + contract HypFiatTokenTest is HypTokenTest { using TypeCasts for address; HypFiatToken internal fiatToken; From cfa20a46fa54e525eba559a4d8f6291ec391e030 Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Wed, 5 Jun 2024 19:32:44 +0100 Subject: [PATCH 32/59] chore(infra): Update docker image tag for kathy and key funder (#3902) ### Description Update docker image tag for kathy and key funder ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- typescript/infra/config/environments/testnet4/funding.ts | 2 +- typescript/infra/config/environments/testnet4/helloworld.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/typescript/infra/config/environments/testnet4/funding.ts b/typescript/infra/config/environments/testnet4/funding.ts index 0855865483..04ec56c98b 100644 --- a/typescript/infra/config/environments/testnet4/funding.ts +++ b/typescript/infra/config/environments/testnet4/funding.ts @@ -7,7 +7,7 @@ import { environment } from './chains.js'; export const keyFunderConfig: KeyFunderConfig = { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'b22a0f4-20240523-140812', + tag: 'efa9025-20240605-091304', }, // We're currently using the same deployer key as testnet2. // To minimize nonce clobbering we offset the key funder cron diff --git a/typescript/infra/config/environments/testnet4/helloworld.ts b/typescript/infra/config/environments/testnet4/helloworld.ts index e8c10ba0e1..dee5ab3f2d 100644 --- a/typescript/infra/config/environments/testnet4/helloworld.ts +++ b/typescript/infra/config/environments/testnet4/helloworld.ts @@ -13,7 +13,7 @@ export const hyperlaneHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'b22a0f4-20240523-140812', + tag: 'efa9025-20240605-091304', }, chainsToSkip: [], runEnv: environment, @@ -32,7 +32,7 @@ export const releaseCandidateHelloworld: HelloWorldConfig = { kathy: { docker: { repo: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: 'b22a0f4-20240523-140812', + tag: 'efa9025-20240605-091304', }, chainsToSkip: [], runEnv: environment, From ba946f1effa034c528536589ccff1be1d0b15382 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noah=20Bayindirli=20=F0=9F=A5=82?= Date: Wed, 5 Jun 2024 16:26:20 -0400 Subject: [PATCH 33/59] fix(config): switch to recommended 'rust-analyzer' from deprecated 'cargo' (#3886) ### Description - switch to recommended `rust-analyzer` since `cargo` is deprecated --- rust/.vscode/extensions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/.vscode/extensions.json b/rust/.vscode/extensions.json index e38df3a9fc..c8e7623ea5 100644 --- a/rust/.vscode/extensions.json +++ b/rust/.vscode/extensions.json @@ -4,7 +4,7 @@ // List of extensions which should be recommended for users of this workspace. "recommendations": [ - "panicbit.cargo", + "rust-lang.rust-analyzer", "tamasfe.even-better-toml", "rust-lang.rust-analyzer", ], From 50d2f29324850aad471d9660d9e3bcf81dd2a811 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Thu, 6 Jun 2024 14:16:39 +0100 Subject: [PATCH 34/59] ci: configure merge queue for `main` (#3913) - configure the rust/test workflows to support merge queues into `main` --------- Signed-off-by: Paul Balaji --- .github/workflows/rust-skipped.yml | 2 ++ .github/workflows/rust.yml | 3 ++- .github/workflows/test.yml | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust-skipped.yml b/.github/workflows/rust-skipped.yml index b6e6c51cd7..2d9fe8a5d0 100644 --- a/.github/workflows/rust-skipped.yml +++ b/.github/workflows/rust-skipped.yml @@ -9,6 +9,8 @@ on: paths-ignore: - 'rust/**' - .github/workflows/rust.yml + # Support for merge queues + merge_group: env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 48caa1f773..a6e270a489 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -6,7 +6,8 @@ on: paths: - 'rust/**' - .github/workflows/rust.yml - + # Support for merge queues + merge_group: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b6bb18d5ff..960b5b770b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,8 @@ on: pull_request: branches: - '*' # run against all branches + # Support for merge queues + merge_group: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -224,7 +226,7 @@ jobs: e2e-matrix: runs-on: larger-runner - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' needs: [yarn-build, checkout-registry] strategy: matrix: @@ -325,7 +327,7 @@ jobs: cli-e2e: runs-on: larger-runner - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' needs: [yarn-build, checkout-registry] strategy: matrix: From 8693ca33a7f760bfed5fb8390d6199e57f82181e Mon Sep 17 00:00:00 2001 From: Avi Atkin <103125634+avious00@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:21:39 -0400 Subject: [PATCH 35/59] chore: create `funding.json` (#3919) ### Description - Add verification for OP retro funding round 4 --------- Co-authored-by: Nam Chu Hoai Co-authored-by: Paul Balaji --- funding.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 funding.json diff --git a/funding.json b/funding.json new file mode 100644 index 0000000000..7eca8c5782 --- /dev/null +++ b/funding.json @@ -0,0 +1,5 @@ +{ + "opRetro": { + "projectId": "0xa47182d330bd0c5c69b1418462f3f742099138f09bff057189cdd19676a6acd1" + } +} From 4261df74bbbf9cfd281ba6139cd811f91e4166ed Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Thu, 6 Jun 2024 15:55:29 +0100 Subject: [PATCH 36/59] chore: restrict for how long the backward syncer can block (#3918) ### Description ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- .../src/contract_sync/cursors/sequence_aware/backward.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 index 3efd04a8d3..135835e12e 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs @@ -9,10 +9,13 @@ use hyperlane_core::{ HyperlaneSequenceAwareIndexerStoreReader, IndexMode, Indexed, LogMeta, SequenceIndexed, }; use itertools::Itertools; +use tokio::time::sleep; use tracing::{debug, instrument, warn}; use super::{LastIndexedSnapshot, TargetSnapshot}; +const MAX_BACKWARD_SYNC_BLOCKING_TIME: Duration = Duration::from_secs(5); + /// A sequence-aware cursor that syncs backward until there are no earlier logs to index. pub(crate) struct BackwardSequenceAwareSyncCursor { /// The max chunk size to query for logs. @@ -68,7 +71,11 @@ impl BackwardSequenceAwareSyncCursor { #[instrument(ret)] pub async fn get_next_range(&mut self) -> Result>> { // Skip any already indexed logs. - self.skip_indexed().await?; + tokio::select! { + res = self.skip_indexed() => res?, + // return early to allow the forward cursor to also make progress + _ = sleep(MAX_BACKWARD_SYNC_BLOCKING_TIME) => { return Ok(None); } + }; // 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. From 942aba8e553ca3d68dc2063f82aa652996b9103e Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:02:25 +0100 Subject: [PATCH 37/59] feat(evm): collaborative indexing through txid sharing (#3833) ### Description - improved indexing reliability by sharing channels between indexing tasks, such that they inform one another when they come across a hyperlane-related transaction - the receiving end will then get the receipt of that tx and filter it for events relevant to it - this adds some flexibility to indexing since we can stop relying on watermark cursor entirely: as long as we have one task using sequence indexing, all others can rely on being informed by their channels about what txs to index - this PR is only for the EVM chains, but can be easily extended ### Drive-by changes - Enforces gas payments for all chains in e2e (previously this only used to be the case for sealevel) - The channel size is configured per type, similarly to the indexing cursor, in `contract_sync/cursors/mod.rs` - Reduces log verbosity is some places, e.g. by manually implementing `Debug` to exclude fields that aren't relevant to debugging (maybe there's even a macro annotation to exclude, but I haven't checked) - Renames `MpmcReceiver` -> `BroadcastReceiver` ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3267 ### Backward compatibility ### Testing e2e tested with the watermark cursor manually disabled --- rust/agents/relayer/src/msg/op_queue.rs | 46 +-- rust/agents/relayer/src/msg/op_submitter.rs | 15 +- rust/agents/relayer/src/relayer.rs | 55 +++- rust/agents/relayer/src/server.rs | 14 +- rust/agents/scraper/src/agent.rs | 46 ++- rust/agents/validator/src/validator.rs | 5 +- .../hyperlane-cosmos/src/interchain_gas.rs | 2 +- rust/chains/hyperlane-cosmos/src/mailbox.rs | 4 +- .../hyperlane-cosmos/src/merkle_tree_hook.rs | 2 +- .../src/contracts/interchain_gas.rs | 34 ++- .../src/contracts/mailbox.rs | 28 +- .../src/contracts/merkle_tree_hook.rs | 31 ++- .../hyperlane-ethereum/src/contracts/mod.rs | 5 +- .../hyperlane-ethereum/src/contracts/utils.rs | 48 ++++ .../hyperlane-fuel/src/interchain_gas.rs | 2 +- rust/chains/hyperlane-fuel/src/mailbox.rs | 4 +- .../hyperlane-sealevel/src/interchain_gas.rs | 2 +- rust/chains/hyperlane-sealevel/src/mailbox.rs | 4 +- .../src/merkle_tree_hook.rs | 4 +- .../src/contract_sync/cursors/mod.rs | 15 + .../src/contract_sync/cursors/rate_limited.rs | 12 +- .../cursors/sequence_aware/backward.rs | 22 +- .../cursors/sequence_aware/forward.rs | 26 +- .../cursors/sequence_aware/mod.rs | 1 + rust/hyperlane-base/src/contract_sync/mod.rs | 261 +++++++++++++----- rust/hyperlane-base/src/settings/base.rs | 4 +- rust/hyperlane-core/src/traits/cursor.rs | 8 +- rust/hyperlane-core/src/traits/indexer.rs | 12 +- rust/hyperlane-core/src/types/channel.rs | 50 ---- rust/hyperlane-core/src/types/mod.rs | 4 - rust/utils/run-locally/src/invariants.rs | 2 - rust/utils/run-locally/src/main.rs | 10 - 32 files changed, 530 insertions(+), 248 deletions(-) create mode 100644 rust/chains/hyperlane-ethereum/src/contracts/utils.rs delete mode 100644 rust/hyperlane-core/src/types/channel.rs diff --git a/rust/agents/relayer/src/msg/op_queue.rs b/rust/agents/relayer/src/msg/op_queue.rs index ef8c2ad2d3..6881d06769 100644 --- a/rust/agents/relayer/src/msg/op_queue.rs +++ b/rust/agents/relayer/src/msg/op_queue.rs @@ -1,10 +1,9 @@ use std::{cmp::Reverse, collections::BinaryHeap, sync::Arc}; use derive_new::new; -use hyperlane_core::MpmcReceiver; use prometheus::{IntGauge, IntGaugeVec}; -use tokio::sync::Mutex; -use tracing::{info, instrument}; +use tokio::sync::{broadcast::Receiver, Mutex}; +use tracing::{debug, info, instrument}; use crate::server::MessageRetryRequest; @@ -18,7 +17,7 @@ pub type QueueOperation = Box; pub struct OpQueue { metrics: IntGaugeVec, queue_metrics_label: String, - retry_rx: MpmcReceiver, + retry_rx: Arc>>, #[new(default)] queue: Arc>>>, } @@ -41,7 +40,7 @@ impl OpQueue { } /// Pop multiple elements at once from the queue and update metrics - #[instrument(skip(self), ret, fields(queue_label=%self.queue_metrics_label), level = "debug")] + #[instrument(skip(self), fields(queue_label=%self.queue_metrics_label), level = "debug")] pub async fn pop_many(&mut self, limit: usize) -> Vec { self.process_retry_requests().await; let mut queue = self.queue.lock().await; @@ -55,6 +54,15 @@ impl OpQueue { break; } } + // This function is called very often by the op_submitter tasks, so only log when there are operations to pop + // to avoid spamming the logs + if !popped.is_empty() { + debug!( + queue_label = %self.queue_metrics_label, + operations = ?popped, + "Popped OpQueue operations" + ); + } popped } @@ -64,7 +72,7 @@ impl OpQueue { // The other consideration is whether to put the channel receiver in the OpQueue or in a dedicated task // that also holds an Arc to the Mutex. For simplicity, we'll put it in the OpQueue for now. let mut message_retry_requests = vec![]; - while let Ok(message_id) = self.retry_rx.receiver.try_recv() { + while let Ok(message_id) = self.retry_rx.lock().await.try_recv() { message_retry_requests.push(message_id); } if message_retry_requests.is_empty() { @@ -103,13 +111,13 @@ mod test { use super::*; use crate::msg::pending_operation::PendingOperationResult; use hyperlane_core::{ - HyperlaneDomain, HyperlaneMessage, KnownHyperlaneDomain, MpmcChannel, TryBatchAs, - TxOutcome, H256, + HyperlaneDomain, HyperlaneMessage, KnownHyperlaneDomain, TryBatchAs, TxOutcome, H256, }; use std::{ collections::VecDeque, time::{Duration, Instant}, }; + use tokio::sync; #[derive(Debug, Clone)] struct MockPendingOperation { @@ -212,13 +220,17 @@ mod test { #[tokio::test] async fn test_multiple_op_queues_message_id() { let (metrics, queue_metrics_label) = dummy_metrics_and_label(); - let mpmc_channel = MpmcChannel::new(100); + let broadcaster = sync::broadcast::Sender::new(100); let mut op_queue_1 = OpQueue::new( metrics.clone(), queue_metrics_label.clone(), - mpmc_channel.receiver(), + Arc::new(Mutex::new(broadcaster.subscribe())), + ); + let mut op_queue_2 = OpQueue::new( + metrics, + queue_metrics_label, + Arc::new(Mutex::new(broadcaster.subscribe())), ); - let mut op_queue_2 = OpQueue::new(metrics, queue_metrics_label, mpmc_channel.receiver()); // Add some operations to the queue with increasing `next_attempt_after` values let destination_domain: HyperlaneDomain = KnownHyperlaneDomain::Injective.into(); @@ -244,11 +256,10 @@ mod test { } // Retry by message ids - let mpmc_tx = mpmc_channel.sender(); - mpmc_tx + broadcaster .send(MessageRetryRequest::MessageId(op_ids[1])) .unwrap(); - mpmc_tx + broadcaster .send(MessageRetryRequest::MessageId(op_ids[2])) .unwrap(); @@ -278,11 +289,11 @@ mod test { #[tokio::test] async fn test_destination_domain() { let (metrics, queue_metrics_label) = dummy_metrics_and_label(); - let mpmc_channel = MpmcChannel::new(100); + let broadcaster = sync::broadcast::Sender::new(100); let mut op_queue = OpQueue::new( metrics.clone(), queue_metrics_label.clone(), - mpmc_channel.receiver(), + Arc::new(Mutex::new(broadcaster.subscribe())), ); // Add some operations to the queue with increasing `next_attempt_after` values @@ -304,8 +315,7 @@ mod test { } // Retry by domain - let mpmc_tx = mpmc_channel.sender(); - mpmc_tx + broadcaster .send(MessageRetryRequest::DestinationDomain( destination_domain_2.id(), )) diff --git a/rust/agents/relayer/src/msg/op_submitter.rs b/rust/agents/relayer/src/msg/op_submitter.rs index dc30911490..66d1a57d38 100644 --- a/rust/agents/relayer/src/msg/op_submitter.rs +++ b/rust/agents/relayer/src/msg/op_submitter.rs @@ -1,10 +1,13 @@ +use std::sync::Arc; use std::time::Duration; use derive_new::new; use futures::future::join_all; use futures_util::future::try_join_all; use prometheus::{IntCounter, IntGaugeVec}; +use tokio::sync::broadcast::Sender; use tokio::sync::mpsc; +use tokio::sync::Mutex; use tokio::task::JoinHandle; use tokio::time::sleep; use tokio_metrics::TaskMonitor; @@ -14,7 +17,7 @@ use tracing::{info, warn}; use hyperlane_base::CoreMetrics; use hyperlane_core::{ BatchItem, ChainCommunicationError, ChainResult, HyperlaneDomain, HyperlaneDomainProtocol, - HyperlaneMessage, MpmcReceiver, TxOutcome, + HyperlaneMessage, TxOutcome, }; use crate::msg::pending_message::CONFIRM_DELAY; @@ -77,7 +80,7 @@ pub struct SerialSubmitter { /// Receiver for new messages to submit. rx: mpsc::UnboundedReceiver, /// Receiver for retry requests. - retry_rx: MpmcReceiver, + retry_tx: Sender, /// Metrics for serial submitter. metrics: SerialSubmitterMetrics, /// Max batch size for submitting messages @@ -101,24 +104,24 @@ impl SerialSubmitter { domain, metrics, rx: rx_prepare, - retry_rx, + retry_tx, max_batch_size, task_monitor, } = self; let prepare_queue = OpQueue::new( metrics.submitter_queue_length.clone(), "prepare_queue".to_string(), - retry_rx.clone(), + Arc::new(Mutex::new(retry_tx.subscribe())), ); let submit_queue = OpQueue::new( metrics.submitter_queue_length.clone(), "submit_queue".to_string(), - retry_rx.clone(), + Arc::new(Mutex::new(retry_tx.subscribe())), ); let confirm_queue = OpQueue::new( metrics.submitter_queue_length.clone(), "confirm_queue".to_string(), - retry_rx, + Arc::new(Mutex::new(retry_tx.subscribe())), ); let tasks = [ diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index 0496e38cac..085e43ee6c 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -13,13 +13,14 @@ use hyperlane_base::{ metrics::{AgentMetrics, MetricsUpdater}, settings::ChainConf, BaseAgent, ChainMetrics, ContractSyncMetrics, ContractSyncer, CoreMetrics, HyperlaneAgentCore, + SyncOptions, }; use hyperlane_core::{ - HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, MpmcChannel, - MpmcReceiver, U256, + HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, H512, U256, }; use tokio::{ sync::{ + broadcast::{Receiver, Sender}, mpsc::{self, UnboundedReceiver, UnboundedSender}, RwLock, }, @@ -134,7 +135,7 @@ impl BaseAgent for Relayer { let contract_sync_metrics = Arc::new(ContractSyncMetrics::new(&core_metrics)); - let message_syncs = settings + let message_syncs: HashMap<_, Arc>> = settings .contract_syncs::( settings.origin_chains.iter(), &core_metrics, @@ -305,8 +306,8 @@ impl BaseAgent for Relayer { } // run server - let mpmc_channel = MpmcChannel::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); - let custom_routes = relayer_server::routes(mpmc_channel.sender()); + let sender = Sender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); + let custom_routes = relayer_server::routes(sender.clone()); let server = self .core @@ -328,7 +329,7 @@ impl BaseAgent for Relayer { self.run_destination_submitter( dest_domain, receive_channel, - mpmc_channel.receiver(), + sender.clone(), // Default to submitting one message at a time if there is no batch config self.core.settings.chains[dest_domain.name()] .connection @@ -352,14 +353,26 @@ impl BaseAgent for Relayer { } for origin in &self.origin_chains { + let maybe_broadcaster = self + .message_syncs + .get(origin) + .and_then(|sync| sync.get_broadcaster()); tasks.push(self.run_message_sync(origin, task_monitor.clone()).await); tasks.push( - self.run_interchain_gas_payment_sync(origin, task_monitor.clone()) - .await, + self.run_interchain_gas_payment_sync( + origin, + maybe_broadcaster.clone().map(|b| b.subscribe()), + task_monitor.clone(), + ) + .await, ); tasks.push( - self.run_merkle_tree_hook_syncs(origin, task_monitor.clone()) - .await, + self.run_merkle_tree_hook_syncs( + origin, + maybe_broadcaster.map(|b| b.subscribe()), + task_monitor.clone(), + ) + .await, ); } @@ -394,7 +407,7 @@ impl Relayer { tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { contract_sync .clone() - .sync("dispatched_messages", cursor) + .sync("dispatched_messages", cursor.into()) .await })) .instrument(info_span!("MessageSync")) @@ -403,6 +416,7 @@ impl Relayer { async fn run_interchain_gas_payment_sync( &self, origin: &HyperlaneDomain, + tx_id_receiver: Option>, task_monitor: TaskMonitor, ) -> Instrumented> { let index_settings = self.as_ref().settings.chains[origin.name()].index_settings(); @@ -413,7 +427,13 @@ impl Relayer { .clone(); let cursor = contract_sync.cursor(index_settings).await; tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { - contract_sync.clone().sync("gas_payments", cursor).await + contract_sync + .clone() + .sync( + "gas_payments", + SyncOptions::new(Some(cursor), tx_id_receiver), + ) + .await })) .instrument(info_span!("IgpSync")) } @@ -421,13 +441,20 @@ impl Relayer { async fn run_merkle_tree_hook_syncs( &self, origin: &HyperlaneDomain, + tx_id_receiver: Option>, task_monitor: TaskMonitor, ) -> 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.cursor(index_settings).await; tokio::spawn(TaskMonitor::instrument(&task_monitor, async move { - contract_sync.clone().sync("merkle_tree_hook", cursor).await + contract_sync + .clone() + .sync( + "merkle_tree_hook", + SyncOptions::new(Some(cursor), tx_id_receiver), + ) + .await })) .instrument(info_span!("MerkleTreeHookSync")) } @@ -498,7 +525,7 @@ impl Relayer { &self, destination: &HyperlaneDomain, receiver: UnboundedReceiver, - retry_receiver_channel: MpmcReceiver, + retry_receiver_channel: Sender, batch_size: u32, task_monitor: TaskMonitor, ) -> Instrumented> { diff --git a/rust/agents/relayer/src/server.rs b/rust/agents/relayer/src/server.rs index 9f6936a222..364181df60 100644 --- a/rust/agents/relayer/src/server.rs +++ b/rust/agents/relayer/src/server.rs @@ -109,12 +109,12 @@ mod tests { use super::*; use axum::http::StatusCode; use ethers::utils::hex::ToHex; - use hyperlane_core::{MpmcChannel, MpmcReceiver}; use std::net::SocketAddr; + use tokio::sync::broadcast::{Receiver, Sender}; - fn setup_test_server() -> (SocketAddr, MpmcReceiver) { - let mpmc_channel = MpmcChannel::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); - let message_retry_api = MessageRetryApi::new(mpmc_channel.sender()); + fn setup_test_server() -> (SocketAddr, Receiver) { + let broadcast_tx = Sender::::new(ENDPOINT_MESSAGES_QUEUE_SIZE); + let message_retry_api = MessageRetryApi::new(broadcast_tx.clone()); let (path, retry_router) = message_retry_api.get_route(); let app = Router::new().nest(path, retry_router); @@ -124,7 +124,7 @@ mod tests { let addr = server.local_addr(); tokio::spawn(server); - (addr, mpmc_channel.receiver()) + (addr, broadcast_tx.subscribe()) } #[tokio::test] @@ -148,7 +148,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); assert_eq!( - rx.receiver.try_recv().unwrap(), + rx.try_recv().unwrap(), MessageRetryRequest::MessageId(message_id) ); } @@ -172,7 +172,7 @@ mod tests { assert_eq!(response.status(), StatusCode::OK); assert_eq!( - rx.receiver.try_recv().unwrap(), + rx.try_recv().unwrap(), MessageRetryRequest::DestinationDomain(destination_domain) ); } diff --git a/rust/agents/scraper/src/agent.rs b/rust/agents/scraper/src/agent.rs index d713432819..f33f005560 100644 --- a/rust/agents/scraper/src/agent.rs +++ b/rust/agents/scraper/src/agent.rs @@ -5,10 +5,13 @@ use derive_more::AsRef; use futures::future::try_join_all; use hyperlane_base::{ metrics::AgentMetrics, settings::IndexSettings, BaseAgent, ChainMetrics, ContractSyncMetrics, - ContractSyncer, CoreMetrics, HyperlaneAgentCore, MetricsUpdater, + ContractSyncer, CoreMetrics, HyperlaneAgentCore, MetricsUpdater, SyncOptions, +}; +use hyperlane_core::{Delivery, HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, H512}; +use tokio::{ + sync::broadcast::{Receiver, Sender}, + task::JoinHandle, }; -use hyperlane_core::{Delivery, HyperlaneDomain, HyperlaneMessage, InterchainGasPayment}; -use tokio::task::JoinHandle; use tracing::{info_span, instrument::Instrumented, trace, Instrument}; use crate::{chain_scraper::HyperlaneSqlDb, db::ScraperDb, settings::ScraperSettings}; @@ -135,16 +138,16 @@ impl Scraper { let domain = scraper.domain.clone(); let mut tasks = Vec::with_capacity(2); - tasks.push( - self.build_message_indexer( + let (message_indexer, maybe_broadcaster) = self + .build_message_indexer( domain.clone(), self.core_metrics.clone(), self.contract_sync_metrics.clone(), db.clone(), index_settings.clone(), ) - .await, - ); + .await; + tasks.push(message_indexer); tasks.push( self.build_delivery_indexer( domain.clone(), @@ -152,6 +155,7 @@ impl Scraper { self.contract_sync_metrics.clone(), db.clone(), index_settings.clone(), + maybe_broadcaster.clone().map(|b| b.subscribe()), ) .await, ); @@ -162,6 +166,7 @@ impl Scraper { self.contract_sync_metrics.clone(), db, index_settings.clone(), + maybe_broadcaster.map(|b| b.subscribe()), ) .await, ); @@ -182,7 +187,7 @@ impl Scraper { contract_sync_metrics: Arc, db: HyperlaneSqlDb, index_settings: IndexSettings, - ) -> Instrumented> { + ) -> (Instrumented>, Option>) { let sync = self .as_ref() .settings @@ -195,9 +200,12 @@ impl Scraper { .await .unwrap(); let cursor = sync.cursor(index_settings.clone()).await; - tokio::spawn(async move { sync.sync("message_dispatch", cursor).await }).instrument( - info_span!("ChainContractSync", chain=%domain.name(), event="message_dispatch"), - ) + let maybe_broadcaser = sync.get_broadcaster(); + let task = tokio::spawn(async move { sync.sync("message_dispatch", cursor.into()).await }) + .instrument( + info_span!("ChainContractSync", chain=%domain.name(), event="message_dispatch"), + ); + (task, maybe_broadcaser) } async fn build_delivery_indexer( @@ -207,6 +215,7 @@ impl Scraper { contract_sync_metrics: Arc, db: HyperlaneSqlDb, index_settings: IndexSettings, + tx_id_receiver: Option>, ) -> Instrumented> { let sync = self .as_ref() @@ -222,8 +231,11 @@ impl Scraper { let label = "message_delivery"; let cursor = sync.cursor(index_settings.clone()).await; - tokio::spawn(async move { sync.sync(label, cursor).await }) - .instrument(info_span!("ChainContractSync", chain=%domain.name(), event=label)) + tokio::spawn(async move { + sync.sync(label, SyncOptions::new(Some(cursor), tx_id_receiver)) + .await + }) + .instrument(info_span!("ChainContractSync", chain=%domain.name(), event=label)) } async fn build_interchain_gas_payment_indexer( @@ -233,6 +245,7 @@ impl Scraper { contract_sync_metrics: Arc, db: HyperlaneSqlDb, index_settings: IndexSettings, + tx_id_receiver: Option>, ) -> Instrumented> { let sync = self .as_ref() @@ -248,7 +261,10 @@ impl Scraper { let label = "gas_payment"; let cursor = sync.cursor(index_settings.clone()).await; - tokio::spawn(async move { sync.sync(label, cursor).await }) - .instrument(info_span!("ChainContractSync", chain=%domain.name(), event=label)) + tokio::spawn(async move { + sync.sync(label, SyncOptions::new(Some(cursor), tx_id_receiver)) + .await + }) + .instrument(info_span!("ChainContractSync", chain=%domain.name(), event=label)) } } diff --git a/rust/agents/validator/src/validator.rs b/rust/agents/validator/src/validator.rs index 043ac9249d..23e96aeb58 100644 --- a/rust/agents/validator/src/validator.rs +++ b/rust/agents/validator/src/validator.rs @@ -210,7 +210,10 @@ impl Validator { let contract_sync = self.merkle_tree_hook_sync.clone(); let cursor = contract_sync.cursor(index_settings).await; tokio::spawn(async move { - contract_sync.clone().sync("merkle_tree_hook", cursor).await; + contract_sync + .clone() + .sync("merkle_tree_hook", cursor.into()) + .await; }) .instrument(info_span!("MerkleTreeHookSyncer")) } diff --git a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs b/rust/chains/hyperlane-cosmos/src/interchain_gas.rs index 4ba2ca87ab..4444a56eaa 100644 --- a/rust/chains/hyperlane-cosmos/src/interchain_gas.rs +++ b/rust/chains/hyperlane-cosmos/src/interchain_gas.rs @@ -202,7 +202,7 @@ impl CosmosInterchainGasPaymasterIndexer { #[async_trait] impl Indexer for CosmosInterchainGasPaymasterIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-cosmos/src/mailbox.rs b/rust/chains/hyperlane-cosmos/src/mailbox.rs index 7f686cb85c..833b92b89f 100644 --- a/rust/chains/hyperlane-cosmos/src/mailbox.rs +++ b/rust/chains/hyperlane-cosmos/src/mailbox.rs @@ -350,7 +350,7 @@ impl CosmosMailboxIndexer { #[async_trait] impl Indexer for CosmosMailboxIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { @@ -397,7 +397,7 @@ impl Indexer for CosmosMailboxIndexer { #[async_trait] impl Indexer for CosmosMailboxIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs index c8e798096c..54acdf80f0 100644 --- a/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-cosmos/src/merkle_tree_hook.rs @@ -283,7 +283,7 @@ impl CosmosMerkleTreeHookIndexer { #[async_trait] impl Indexer for CosmosMerkleTreeHookIndexer { /// Fetch list of logs between `range` of blocks - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs b/rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs index 8ed514c836..76345ec8f4 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs +++ b/rust/chains/hyperlane-ethereum/src/contracts/interchain_gas.rs @@ -10,12 +10,14 @@ use ethers::prelude::Middleware; use hyperlane_core::{ ChainCommunicationError, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, - InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H160, H256, + InterchainGasPaymaster, InterchainGasPayment, LogMeta, SequenceAwareIndexer, H160, H256, H512, }; use tracing::instrument; +use super::utils::fetch_raw_logs_and_log_meta; use crate::interfaces::i_interchain_gas_paymaster::{ - IInterchainGasPaymaster as EthereumInterchainGasPaymasterInternal, IINTERCHAINGASPAYMASTER_ABI, + GasPaymentFilter, IInterchainGasPaymaster as EthereumInterchainGasPaymasterInternal, + IINTERCHAINGASPAYMASTER_ABI, }; use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider}; @@ -86,7 +88,7 @@ where { /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { @@ -124,6 +126,32 @@ where .as_u32() .saturating_sub(self.reorg_period)) } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + let logs = fetch_raw_logs_and_log_meta::( + tx_hash, + self.provider.clone(), + self.contract.address(), + ) + .await? + .into_iter() + .map(|(log, log_meta)| { + ( + Indexed::new(InterchainGasPayment { + message_id: H256::from(log.message_id), + destination: log.destination_domain, + payment: log.payment.into(), + gas_amount: log.gas_amount.into(), + }), + log_meta, + ) + }) + .collect(); + Ok(logs) + } } #[async_trait] diff --git a/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs b/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs index d70c2bfc7d..37933f5f42 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs +++ b/rust/chains/hyperlane-ethereum/src/contracts/mailbox.rs @@ -11,6 +11,7 @@ use ethers::abi::{AbiEncode, Detokenize}; use ethers::prelude::Middleware; use ethers_contract::builders::ContractCall; use futures_util::future::join_all; +use hyperlane_core::H512; use tracing::instrument; use hyperlane_core::{ @@ -25,10 +26,12 @@ use crate::interfaces::arbitrum_node_interface::ArbitrumNodeInterface; use crate::interfaces::i_mailbox::{ IMailbox as EthereumMailboxInternal, ProcessCall, IMAILBOX_ABI, }; +use crate::interfaces::mailbox::DispatchFilter; use crate::tx::{call_with_lag, fill_tx_gas_params, report_tx}; use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider, TransactionOverrides}; use super::multicall::{self, build_multicall}; +use super::utils::fetch_raw_logs_and_log_meta; impl std::fmt::Display for EthereumMailboxInternal where @@ -134,7 +137,7 @@ where /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { @@ -157,6 +160,27 @@ where events.sort_by(|a, b| a.0.inner().nonce.cmp(&b.0.inner().nonce)); Ok(events) } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + let logs = fetch_raw_logs_and_log_meta::( + tx_hash, + self.provider.clone(), + self.contract.address(), + ) + .await? + .into_iter() + .map(|(log, log_meta)| { + ( + HyperlaneMessage::from(log.message.to_vec()).into(), + log_meta, + ) + }) + .collect(); + Ok(logs) + } } #[async_trait] @@ -183,7 +207,7 @@ where /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs b/rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs index a94ceff325..5836838ef1 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-ethereum/src/contracts/merkle_tree_hook.rs @@ -11,13 +11,17 @@ use tracing::instrument; use hyperlane_core::{ ChainCommunicationError, ChainResult, Checkpoint, ContractLocator, HyperlaneChain, HyperlaneContract, HyperlaneDomain, HyperlaneProvider, Indexed, Indexer, LogMeta, - MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, + MerkleTreeHook, MerkleTreeInsertion, SequenceAwareIndexer, H256, H512, }; -use crate::interfaces::merkle_tree_hook::{MerkleTreeHook as MerkleTreeHookContract, Tree}; +use crate::interfaces::merkle_tree_hook::{ + InsertedIntoTreeFilter, MerkleTreeHook as MerkleTreeHookContract, Tree, +}; use crate::tx::call_with_lag; use crate::{BuildableWithProvider, ConnectionConf, EthereumProvider}; +use super::utils::fetch_raw_logs_and_log_meta; + // We don't need the reverse of this impl, so it's ok to disable the clippy lint #[allow(clippy::from_over_into)] impl Into for Tree { @@ -108,7 +112,7 @@ where { /// Note: This call may return duplicates depending on the provider used #[instrument(err, skip(self))] - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { @@ -142,6 +146,27 @@ where .as_u32() .saturating_sub(self.reorg_period)) } + + async fn fetch_logs_by_tx_hash( + &self, + tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + let logs = fetch_raw_logs_and_log_meta::( + tx_hash, + self.provider.clone(), + self.contract.address(), + ) + .await? + .into_iter() + .map(|(log, log_meta)| { + ( + MerkleTreeInsertion::new(log.index, H256::from(log.message_id)).into(), + log_meta, + ) + }) + .collect(); + Ok(logs) + } } #[async_trait] diff --git a/rust/chains/hyperlane-ethereum/src/contracts/mod.rs b/rust/chains/hyperlane-ethereum/src/contracts/mod.rs index 32ad5b953d..1a39fae07a 100644 --- a/rust/chains/hyperlane-ethereum/src/contracts/mod.rs +++ b/rust/chains/hyperlane-ethereum/src/contracts/mod.rs @@ -1,11 +1,8 @@ pub use {interchain_gas::*, mailbox::*, merkle_tree_hook::*, validator_announce::*}; mod interchain_gas; - mod mailbox; - mod merkle_tree_hook; - mod multicall; - +mod utils; mod validator_announce; diff --git a/rust/chains/hyperlane-ethereum/src/contracts/utils.rs b/rust/chains/hyperlane-ethereum/src/contracts/utils.rs new file mode 100644 index 0000000000..bdf3e52f93 --- /dev/null +++ b/rust/chains/hyperlane-ethereum/src/contracts/utils.rs @@ -0,0 +1,48 @@ +use std::sync::Arc; + +use ethers::{ + abi::RawLog, + providers::Middleware, + types::{H160 as EthersH160, H256 as EthersH256}, +}; +use ethers_contract::{ContractError, EthEvent, LogMeta as EthersLogMeta}; +use hyperlane_core::{ChainResult, LogMeta, H512}; +use tracing::warn; + +pub async fn fetch_raw_logs_and_log_meta( + tx_hash: H512, + provider: Arc, + contract_address: EthersH160, +) -> ChainResult> +where + M: Middleware + 'static, +{ + let ethers_tx_hash: EthersH256 = tx_hash.into(); + let receipt = provider + .get_transaction_receipt(ethers_tx_hash) + .await + .map_err(|err| ContractError::::MiddlewareError(err))?; + let Some(receipt) = receipt else { + warn!(%tx_hash, "No receipt found for tx hash"); + return Ok(vec![]); + }; + + let logs: Vec<(T, LogMeta)> = receipt + .logs + .into_iter() + .filter_map(|log| { + // Filter out logs that aren't emitted by this contract + if log.address != contract_address { + return None; + } + let raw_log = RawLog { + topics: log.topics.clone(), + data: log.data.to_vec(), + }; + let log_meta: EthersLogMeta = (&log).into(); + let event_filter = T::decode_log(&raw_log).ok(); + event_filter.map(|log| (log, log_meta.into())) + }) + .collect(); + Ok(logs) +} diff --git a/rust/chains/hyperlane-fuel/src/interchain_gas.rs b/rust/chains/hyperlane-fuel/src/interchain_gas.rs index d969210a60..3385872c35 100644 --- a/rust/chains/hyperlane-fuel/src/interchain_gas.rs +++ b/rust/chains/hyperlane-fuel/src/interchain_gas.rs @@ -35,7 +35,7 @@ pub struct FuelInterchainGasPaymasterIndexer {} #[async_trait] impl Indexer for FuelInterchainGasPaymasterIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-fuel/src/mailbox.rs b/rust/chains/hyperlane-fuel/src/mailbox.rs index 035fe6e6d3..5e8f0cf059 100644 --- a/rust/chains/hyperlane-fuel/src/mailbox.rs +++ b/rust/chains/hyperlane-fuel/src/mailbox.rs @@ -126,7 +126,7 @@ pub struct FuelMailboxIndexer {} #[async_trait] impl Indexer for FuelMailboxIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { @@ -140,7 +140,7 @@ impl Indexer for FuelMailboxIndexer { #[async_trait] impl Indexer for FuelMailboxIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs b/rust/chains/hyperlane-sealevel/src/interchain_gas.rs index 4945833818..beebcb9db4 100644 --- a/rust/chains/hyperlane-sealevel/src/interchain_gas.rs +++ b/rust/chains/hyperlane-sealevel/src/interchain_gas.rs @@ -246,7 +246,7 @@ impl SealevelInterchainGasPaymasterIndexer { #[async_trait] impl Indexer for SealevelInterchainGasPaymasterIndexer { #[instrument(err, skip(self))] - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-sealevel/src/mailbox.rs b/rust/chains/hyperlane-sealevel/src/mailbox.rs index 3fc8393d14..beb4e86c37 100644 --- a/rust/chains/hyperlane-sealevel/src/mailbox.rs +++ b/rust/chains/hyperlane-sealevel/src/mailbox.rs @@ -646,7 +646,7 @@ impl SequenceAwareIndexer for SealevelMailboxIndexer { #[async_trait] impl Indexer for SealevelMailboxIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { @@ -670,7 +670,7 @@ impl Indexer for SealevelMailboxIndexer { #[async_trait] impl Indexer for SealevelMailboxIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, _range: RangeInclusive, ) -> ChainResult, LogMeta)>> { diff --git a/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs b/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs index 9fe48053c8..8c1132addf 100644 --- a/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs +++ b/rust/chains/hyperlane-sealevel/src/merkle_tree_hook.rs @@ -83,11 +83,11 @@ pub struct SealevelMerkleTreeHookIndexer(SealevelMailboxIndexer); #[async_trait] impl Indexer for SealevelMerkleTreeHookIndexer { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>> { - let messages = Indexer::::fetch_logs(&self.0, range).await?; + let messages = Indexer::::fetch_logs_in_range(&self.0, range).await?; let merkle_tree_insertions = messages .into_iter() .map(|(m, meta)| (message_to_merkle_tree_insertion(m.inner()).into(), meta)) diff --git a/rust/hyperlane-base/src/contract_sync/cursors/mod.rs b/rust/hyperlane-base/src/contract_sync/cursors/mod.rs index c7d7274d68..016454d04e 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/mod.rs @@ -13,8 +13,18 @@ pub enum CursorType { RateLimited, } +// H256 * 1M = 32MB per origin chain worst case +// With one such channel per origin chain. +const TX_ID_CHANNEL_CAPACITY: Option = Some(1_000_000); + pub trait Indexable { + /// Returns the configured cursor type of this type for the given domain, (e.g. `SequenceAware` or `RateLimited`) fn indexing_cursor(domain: HyperlaneDomainProtocol) -> CursorType; + /// Indexing tasks may have channels open between them to share information that improves reliability (such as the txid where a message event was indexed). + /// By default this method is None, and it should return a channel capacity if this indexing task is to broadcast anything to other tasks. + fn broadcast_channel_size() -> Option { + None + } } impl Indexable for HyperlaneMessage { @@ -26,6 +36,11 @@ impl Indexable for HyperlaneMessage { HyperlaneDomainProtocol::Cosmos => CursorType::SequenceAware, } } + + // Only broadcast txids from the message indexing task + fn broadcast_channel_size() -> Option { + TX_ID_CHANNEL_CAPACITY + } } impl Indexable for InterchainGasPayment { diff --git a/rust/hyperlane-base/src/contract_sync/cursors/rate_limited.rs b/rust/hyperlane-base/src/contract_sync/cursors/rate_limited.rs index d85b3618f6..242028acb4 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/rate_limited.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/rate_limited.rs @@ -216,6 +216,16 @@ where } } +impl Debug for RateLimitedContractSyncCursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RateLimitedContractSyncCursor") + .field("tip", &self.tip) + .field("last_tip_update", &self.last_tip_update) + .field("sync_state", &self.sync_state) + .finish() + } +} + #[cfg(test)] pub(crate) mod test { use super::*; @@ -234,7 +244,7 @@ pub(crate) mod test { #[async_trait] impl Indexer<()> for Indexer { - async fn fetch_logs(&self, range: RangeInclusive) -> ChainResult , LogMeta)>>; + async fn fetch_logs_in_range(&self, range: RangeInclusive) -> ChainResult , LogMeta)>>; async fn get_finalized_block_number(&self) -> ChainResult; } } 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 index 135835e12e..6a0f66a78d 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/backward.rs @@ -35,6 +35,17 @@ pub(crate) struct BackwardSequenceAwareSyncCursor { index_mode: IndexMode, } +impl Debug for BackwardSequenceAwareSyncCursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BackwardSequenceAwareSyncCursor") + .field("chunk_size", &self.chunk_size) + .field("last_indexed_snapshot", &self.last_indexed_snapshot) + .field("current_indexing_snapshot", &self.current_indexing_snapshot) + .field("index_mode", &self.index_mode) + .finish() + } +} + impl BackwardSequenceAwareSyncCursor { #[instrument( skip(db), @@ -316,17 +327,6 @@ impl BackwardSequenceAwareSyncCursor { } } -impl Debug for BackwardSequenceAwareSyncCursor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("BackwardSequenceAwareSyncCursor") - .field("chunk_size", &self.chunk_size) - .field("current_indexing_snapshot", &self.current_indexing_snapshot) - .field("last_indexed_snapshot", &self.last_indexed_snapshot) - .field("index_mode", &self.index_mode) - .finish() - } -} - #[async_trait] impl ContractSyncCursor for BackwardSequenceAwareSyncCursor 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 index 374b4b797c..7314e2a004 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/forward.rs @@ -41,6 +41,18 @@ pub(crate) struct ForwardSequenceAwareSyncCursor { index_mode: IndexMode, } +impl Debug for ForwardSequenceAwareSyncCursor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ForwardSequenceAwareSyncCursor") + .field("chunk_size", &self.chunk_size) + .field("last_indexed_snapshot", &self.last_indexed_snapshot) + .field("current_indexing_snapshot", &self.current_indexing_snapshot) + .field("target_snapshot", &self.target_snapshot) + .field("index_mode", &self.index_mode) + .finish() + } +} + impl ForwardSequenceAwareSyncCursor { #[instrument( skip(db, latest_sequence_querier), @@ -391,18 +403,6 @@ impl ForwardSequenceAwareSyncCursor { } } -impl Debug for ForwardSequenceAwareSyncCursor { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ForwardSequenceAwareSyncCursor") - .field("chunk_size", &self.chunk_size) - .field("current_indexing_snapshot", &self.current_indexing_snapshot) - .field("last_indexed_snapshot", &self.last_indexed_snapshot) - .field("target_snapshot", &self.target_snapshot) - .field("index_mode", &self.index_mode) - .finish() - } -} - #[async_trait] impl ContractSyncCursor for ForwardSequenceAwareSyncCursor @@ -493,7 +493,7 @@ pub(crate) mod test { where T: Sequenced + Debug, { - async fn fetch_logs( + async fn fetch_logs_in_range( &self, _range: RangeInclusive, ) -> ChainResult, LogMeta)>> { 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 index d3abb4384c..9303438b00 100644 --- a/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/cursors/sequence_aware/mod.rs @@ -62,6 +62,7 @@ pub enum SyncDirection { /// A cursor that prefers to sync forward, but will sync backward if there is nothing to /// sync forward. +#[derive(Debug)] pub(crate) struct ForwardBackwardSequenceAwareSyncCursor { forward: ForwardSequenceAwareSyncCursor, backward: BackwardSequenceAwareSyncCursor, diff --git a/rust/hyperlane-base/src/contract_sync/mod.rs b/rust/hyperlane-base/src/contract_sync/mod.rs index 85bf36c1c5..9c8ba75d6a 100644 --- a/rust/hyperlane-base/src/contract_sync/mod.rs +++ b/rust/hyperlane-base/src/contract_sync/mod.rs @@ -10,9 +10,13 @@ use hyperlane_core::{ HyperlaneSequenceAwareIndexerStore, HyperlaneWatermarkedLogStore, Indexer, SequenceAwareIndexer, }; +use hyperlane_core::{Indexed, LogMeta, H512}; pub use metrics::ContractSyncMetrics; +use prometheus::core::{AtomicI64, AtomicU64, GenericCounter, GenericGauge}; +use tokio::sync::broadcast::error::TryRecvError; +use tokio::sync::broadcast::{Receiver as BroadcastReceiver, Sender as BroadcastSender}; use tokio::time::sleep; -use tracing::{debug, info, warn}; +use tracing::{debug, info, instrument, trace, warn}; use crate::settings::IndexSettings; @@ -27,17 +31,33 @@ 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. -#[derive(Debug, new, Clone)] -pub struct ContractSync, I: Indexer> { +#[derive(Debug)] +pub struct ContractSync, I: Indexer> { domain: HyperlaneDomain, db: D, indexer: I, metrics: ContractSyncMetrics, + broadcast_sender: Option>, _phantom: PhantomData, } +impl, I: Indexer> ContractSync { + /// Create a new ContractSync + pub fn new(domain: HyperlaneDomain, db: D, indexer: I, metrics: ContractSyncMetrics) -> Self { + Self { + domain, + db, + indexer, + metrics, + broadcast_sender: T::broadcast_channel_size().map(BroadcastSender::new), + _phantom: PhantomData, + } + } +} + impl ContractSync where + T: Indexable + Debug + Send + Sync + Clone + Eq + Hash + 'static, D: HyperlaneLogStore, I: Indexer + 'static, { @@ -45,82 +65,161 @@ where pub fn domain(&self) -> &HyperlaneDomain { &self.domain } -} -impl ContractSync -where - T: Debug + Send + Sync + Clone + Eq + Hash + 'static, - D: HyperlaneLogStore, - I: Indexer + 'static, -{ + fn get_broadcaster(&self) -> Option> { + self.broadcast_sender.clone() + } + /// 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>) { + #[instrument(name = "ContractSync", fields(domain=self.domain().name()), skip(self, opts))] + pub async fn sync(&self, label: &'static str, mut opts: SyncOptions) { let chain_name = self.domain.as_ref(); - let indexed_height = self + let indexed_height_metric = self .metrics .indexed_height .with_label_values(&[label, chain_name]); - let stored_logs = self + let stored_logs_metric = self .metrics .stored_events .with_label_values(&[label, chain_name]); loop { - indexed_height.set(cursor.latest_queried_block() as i64); + if let Some(rx) = opts.tx_id_receiver.as_mut() { + self.fetch_logs_from_receiver(rx, &stored_logs_metric).await; + } + if let Some(cursor) = opts.cursor.as_mut() { + self.fetch_logs_with_cursor(cursor, &stored_logs_metric, &indexed_height_metric) + .await; + } + } + } - 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; - } - }; - 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 events in index range"); - - let logs = match self.indexer.fetch_logs(range.clone()).await { + #[instrument(fields(domain=self.domain().name()), skip(self, recv, stored_logs_metric))] + async fn fetch_logs_from_receiver( + &self, + recv: &mut BroadcastReceiver, + stored_logs_metric: &GenericCounter, + ) { + loop { + match recv.try_recv() { + Ok(tx_id) => { + let logs = match self.indexer.fetch_logs_by_tx_hash(tx_id).await { Ok(logs) => logs, Err(err) => { - warn!(?err, "Error fetching logs"); - break SLEEP_DURATION; + warn!(?err, ?tx_id, "Error fetching logs for tx id"); + continue; } }; - let deduped_logs = HashSet::<_>::from_iter(logs); - let logs = Vec::from_iter(deduped_logs); - + let logs = self.dedupe_and_store_logs(logs, stored_logs_metric).await; + let num_logs = logs.len() as u64; info!( - ?range, - num_logs = logs.len(), - estimated_time_to_sync = fmt_sync_time(eta), - "Found log(s) in index range" + num_logs, + ?tx_id, + sequences = ?logs.iter().map(|(log, _)| log.sequence).collect::>(), + "Found log(s) for tx id" ); - // Store deliveries - 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 - if let Err(err) = cursor.update(logs, range).await { - warn!(?err, "Error updating cursor"); + } + Err(TryRecvError::Empty) => { + trace!("No txid received"); + break; + } + Err(err) => { + warn!(?err, "Error receiving txid from channel"); + break; + } + } + } + } + + #[instrument(fields(domain=self.domain().name()), skip(self, stored_logs_metric, indexed_height_metric))] + async fn fetch_logs_with_cursor( + &self, + cursor: &mut Box>, + stored_logs_metric: &GenericCounter, + indexed_height_metric: &GenericGauge, + ) { + indexed_height_metric.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; + return; + } + }; + 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 events in index range"); + + let logs = match self.indexer.fetch_logs_in_range(range.clone()).await { + Ok(logs) => logs, + Err(err) => { + warn!(?err, ?range, "Error fetching logs in range"); break SLEEP_DURATION; - }; - break Default::default(); - }, - CursorAction::Sleep(duration) => duration, - }; - sleep(sleep_duration).await; + } + }; + + let logs = self.dedupe_and_store_logs(logs, stored_logs_metric).await; + let logs_found = logs.len() as u64; + info!( + ?range, + num_logs = logs_found, + estimated_time_to_sync = fmt_sync_time(eta), + sequences = ?logs.iter().map(|(log, _)| log.sequence).collect::>(), + cursor = ?cursor, + "Found log(s) in index range" + ); + + if let Some(tx) = self.broadcast_sender.as_ref() { + logs.iter().for_each(|(_, meta)| { + if let Err(err) = tx.send(meta.transaction_id) { + trace!(?err, "Error sending txid to receiver"); + } + }); + } + + // Update cursor + 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 + } + + async fn dedupe_and_store_logs( + &self, + logs: Vec<(Indexed, LogMeta)>, + stored_logs_metric: &GenericCounter, + ) -> Vec<(Indexed, LogMeta)> { + let deduped_logs = HashSet::<_>::from_iter(logs); + let logs = Vec::from_iter(deduped_logs); + + // Store deliveries + let stored = match self.db.store_logs(&logs).await { + Ok(stored) => stored, + Err(err) => { + warn!(?err, "Error storing logs in db"); + Default::default() + } + }; + if stored > 0 { + debug!( + domain = self.domain.as_ref(), + count = stored, + sequences = ?logs.iter().map(|(log, _)| log.sequence).collect::>(), + "Stored logs in db", + ); } + // Report amount of deliveries stored into db + stored_logs_metric.inc_by(stored as u64); + logs } } @@ -141,16 +240,38 @@ pub trait ContractSyncer: Send + Sync { async fn cursor(&self, index_settings: IndexSettings) -> Box>; /// Syncs events from the indexer using the provided cursor - async fn sync(&self, label: &'static str, cursor: Box>); + async fn sync(&self, label: &'static str, opts: SyncOptions); /// The domain of this syncer fn domain(&self) -> &HyperlaneDomain; + + /// If this syncer is also a broadcaster, return the channel to receive txids + fn get_broadcaster(&self) -> Option>; +} + +#[derive(new)] +/// Options for syncing events +pub struct SyncOptions { + // Keep as optional fields for now to run them simultaneously. + // Might want to refactor into an enum later, where we either index with a cursor or rely on receiving + // txids from a channel to other indexing tasks + cursor: Option>>, + tx_id_receiver: Option>, +} + +impl From>> for SyncOptions { + fn from(cursor: Box>) -> Self { + Self { + cursor: Some(cursor), + tx_id_receiver: None, + } + } } #[async_trait] impl ContractSyncer for WatermarkContractSync where - T: Debug + Send + Sync + Clone + Eq + Hash + 'static, + T: Indexable + Debug + Send + Sync + Clone + Eq + Hash + 'static, { /// Returns a new cursor to be used for syncing events from the indexer based on time async fn cursor(&self, index_settings: IndexSettings) -> Box> { @@ -172,13 +293,17 @@ where ) } - async fn sync(&self, label: &'static str, cursor: Box>) { - ContractSync::sync(self, label, cursor).await; + async fn sync(&self, label: &'static str, opts: SyncOptions) { + ContractSync::sync(self, label, opts).await } fn domain(&self) -> &HyperlaneDomain { ContractSync::domain(self) } + + fn get_broadcaster(&self) -> Option> { + ContractSync::get_broadcaster(self) + } } /// Log store for sequence aware cursors @@ -191,7 +316,7 @@ pub type SequencedDataContractSync = #[async_trait] impl ContractSyncer for SequencedDataContractSync where - T: Send + Sync + Debug + Clone + Eq + Hash + 'static, + T: Indexable + Send + Sync + Debug + Clone + Eq + Hash + 'static, { /// Returns a new cursor to be used for syncing dispatched messages from the indexer async fn cursor(&self, index_settings: IndexSettings) -> Box> { @@ -207,11 +332,15 @@ where ) } - async fn sync(&self, label: &'static str, cursor: Box>) { - ContractSync::sync(self, label, cursor).await; + async fn sync(&self, label: &'static str, opts: SyncOptions) { + ContractSync::sync(self, label, opts).await; } fn domain(&self) -> &HyperlaneDomain { ContractSync::domain(self) } + + fn get_broadcaster(&self) -> Option> { + ContractSync::get_broadcaster(self) + } } diff --git a/rust/hyperlane-base/src/settings/base.rs b/rust/hyperlane-base/src/settings/base.rs index 59b8fa11a0..6757a545ed 100644 --- a/rust/hyperlane-base/src/settings/base.rs +++ b/rust/hyperlane-base/src/settings/base.rs @@ -160,7 +160,7 @@ impl Settings { db: Arc, ) -> eyre::Result>> where - T: Debug, + T: Indexable + Debug, SequenceIndexer: TryFromWithMetrics, D: HyperlaneLogStore + HyperlaneSequenceAwareIndexerStoreReader + 'static, { @@ -184,7 +184,7 @@ impl Settings { db: Arc, ) -> eyre::Result>> where - T: Debug, + T: Indexable + Debug, SequenceIndexer: TryFromWithMetrics, D: HyperlaneLogStore + HyperlaneWatermarkedLogStore + 'static, { diff --git a/rust/hyperlane-core/src/traits/cursor.rs b/rust/hyperlane-core/src/traits/cursor.rs index cfe92b8dc4..b835b94df2 100644 --- a/rust/hyperlane-core/src/traits/cursor.rs +++ b/rust/hyperlane-core/src/traits/cursor.rs @@ -1,4 +1,8 @@ -use std::{fmt, ops::RangeInclusive, time::Duration}; +use std::{ + fmt::{self, Debug}, + ops::RangeInclusive, + time::Duration, +}; use async_trait::async_trait; use auto_impl::auto_impl; @@ -9,7 +13,7 @@ use crate::{Indexed, LogMeta}; /// A cursor governs event indexing for a contract. #[async_trait] #[auto_impl(Box)] -pub trait ContractSyncCursor: Send + Sync + 'static { +pub trait ContractSyncCursor: Debug + Send + Sync + 'static { /// The next block range that should be queried. /// This method should be tolerant to being called multiple times in a row /// without any updates in between. diff --git a/rust/hyperlane-core/src/traits/indexer.rs b/rust/hyperlane-core/src/traits/indexer.rs index 3db7e4f570..1c05360ff5 100644 --- a/rust/hyperlane-core/src/traits/indexer.rs +++ b/rust/hyperlane-core/src/traits/indexer.rs @@ -11,7 +11,7 @@ use async_trait::async_trait; use auto_impl::auto_impl; use serde::Deserialize; -use crate::{ChainResult, Indexed, LogMeta}; +use crate::{ChainResult, Indexed, LogMeta, H512}; /// Indexing mode. #[derive(Copy, Debug, Default, Deserialize, Clone)] @@ -29,13 +29,21 @@ pub enum IndexMode { #[auto_impl(&, Box, Arc,)] pub trait Indexer: Send + Sync + Debug { /// Fetch list of logs between blocks `from` and `to`, inclusive. - async fn fetch_logs( + async fn fetch_logs_in_range( &self, range: RangeInclusive, ) -> ChainResult, LogMeta)>>; /// Get the chain's latest block number that has reached finality async fn get_finalized_block_number(&self) -> ChainResult; + + /// Fetch list of logs emitted in a transaction with the given hash. + async fn fetch_logs_by_tx_hash( + &self, + _tx_hash: H512, + ) -> ChainResult, LogMeta)>> { + Ok(vec![]) + } } /// Interface for indexing data in sequence. diff --git a/rust/hyperlane-core/src/types/channel.rs b/rust/hyperlane-core/src/types/channel.rs deleted file mode 100644 index 2a0bbb8974..0000000000 --- a/rust/hyperlane-core/src/types/channel.rs +++ /dev/null @@ -1,50 +0,0 @@ -use derive_new::new; -use tokio::sync::broadcast::{Receiver, Sender}; - -/// Multi-producer, multi-consumer channel -pub struct MpmcChannel { - sender: Sender, - receiver: MpmcReceiver, -} - -impl MpmcChannel { - /// Creates a new `MpmcChannel` with the specified capacity. - /// - /// # Arguments - /// - /// * `capacity` - The maximum number of messages that can be buffered in the channel. - pub fn new(capacity: usize) -> Self { - let (sender, receiver) = tokio::sync::broadcast::channel(capacity); - Self { - sender: sender.clone(), - receiver: MpmcReceiver::new(sender, receiver), - } - } - - /// Returns a clone of the sender end of the channel. - pub fn sender(&self) -> Sender { - self.sender.clone() - } - - /// Returns a clone of the receiver end of the channel. - pub fn receiver(&self) -> MpmcReceiver { - self.receiver.clone() - } -} - -/// Clonable receiving end of a multi-producer, multi-consumer channel -#[derive(Debug, new)] -pub struct MpmcReceiver { - sender: Sender, - /// The receiving end of the channel. - pub receiver: Receiver, -} - -impl Clone for MpmcReceiver { - fn clone(&self) -> Self { - Self { - sender: self.sender.clone(), - receiver: self.sender.subscribe(), - } - } -} diff --git a/rust/hyperlane-core/src/types/mod.rs b/rust/hyperlane-core/src/types/mod.rs index 59f20630bf..c8b2ad3464 100644 --- a/rust/hyperlane-core/src/types/mod.rs +++ b/rust/hyperlane-core/src/types/mod.rs @@ -8,8 +8,6 @@ pub use self::primitive_types::*; pub use ::primitive_types as ethers_core_types; pub use announcement::*; pub use chain_data::*; -#[cfg(feature = "async")] -pub use channel::*; pub use checkpoint::*; pub use indexing::*; pub use log_metadata::*; @@ -21,8 +19,6 @@ use crate::{Decode, Encode, HyperlaneProtocolError}; mod announcement; mod chain_data; -#[cfg(feature = "async")] -mod channel; mod checkpoint; mod indexing; mod log_metadata; diff --git a/rust/utils/run-locally/src/invariants.rs b/rust/utils/run-locally/src/invariants.rs index 6900210469..15b2584813 100644 --- a/rust/utils/run-locally/src/invariants.rs +++ b/rust/utils/run-locally/src/invariants.rs @@ -1,5 +1,3 @@ -// use std::path::Path; - use std::path::Path; use crate::config::Config; diff --git a/rust/utils/run-locally/src/main.rs b/rust/utils/run-locally/src/main.rs index a287b2bd1f..0e88e685ec 100644 --- a/rust/utils/run-locally/src/main.rs +++ b/rust/utils/run-locally/src/main.rs @@ -200,15 +200,6 @@ fn main() -> ExitCode { r#"[{ "type": "minimum", "payment": "1", - "matchingList": [ - { - "originDomain": ["13375","13376"], - "destinationDomain": ["13375","13376"] - } - ] - }, - { - "type": "none" }]"#, ) .arg( @@ -412,7 +403,6 @@ fn main() -> ExitCode { while !SHUTDOWN.load(Ordering::Relaxed) { if config.ci_mode { // for CI we have to look for the end condition. - // if termination_invariants_met(&config, starting_relayer_balance) if termination_invariants_met( &config, starting_relayer_balance, From 36e9a2e783faf6b78ee352538cba3fe8c21b1b6e Mon Sep 17 00:00:00 2001 From: Mohammed Hussan Date: Fri, 7 Jun 2024 16:27:09 +0100 Subject: [PATCH 38/59] feat(infra): Command to create endpoint secrets; cleaning up endpoints (#3922) ### Description - clean up references to the single rpc secrets which are redundant as the multi rpc secret includes the single one - add get-rpc-urls script to get secret rpc urls for a given environment and chain - add set-rpc-urls script to: 1) create the secret if it does not exist and add the first secret version or 2) add new secret version to an existing secret and disable the previous version of the secret. The script with test all rpc urls to ensure that they are valid example usage: get-rpc-urls `yarn tsx scripts/secret-rpc-urls/get-rpc-urls.ts -e testnet2 -c fuji` set-rpc-urls `yarn tsx scripts/secret-rpc-urls/set-rpc-urls.ts -e testnet2 -c fuji -r https://api.avax-test.network/ext/bc/C/rpc,https://rpc.ankr.com/avalanche_fuji` ### Drive-by changes - replace `withNetwork` util with `withChain` as we rarely use network internally to reference a chain ### Related issues ### Backward compatibility ### Testing - manual --- .../templates/external-secret.yaml | 4 - .../templates/env-var-external-secret.yaml | 4 - .../templates/env-var-external-secret.yaml | 4 - typescript/infra/scripts/agent-utils.ts | 29 ++++- typescript/infra/scripts/check-rpc-urls.ts | 5 +- typescript/infra/scripts/deploy.ts | 10 +- .../scripts/secret-rpc-urls/get-rpc-urls.ts | 26 ++++ .../scripts/secret-rpc-urls/set-rpc-urls.ts | 120 ++++++++++++++++++ typescript/infra/scripts/verify.ts | 8 +- typescript/infra/src/agents/index.ts | 49 +++++-- typescript/infra/src/config/chain.ts | 2 +- typescript/infra/src/utils/gcloud.ts | 117 ++++++++++++++++- 12 files changed, 332 insertions(+), 46 deletions(-) create mode 100644 typescript/infra/scripts/secret-rpc-urls/get-rpc-urls.ts create mode 100644 typescript/infra/scripts/secret-rpc-urls/set-rpc-urls.ts diff --git a/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml b/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml index f0870da0a9..115e249cdf 100644 --- a/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml +++ b/typescript/infra/helm/helloworld-kathy/templates/external-secret.yaml @@ -33,7 +33,6 @@ spec: */}} {{- range .Values.hyperlane.chains }} GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINTS_{{ . | upper }}: {{ printf "'{{ .%s_rpcs | toString }}'" . }} - GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINT_{{ . | upper }}: {{ printf "'{{ .%s_rpc | toString }}'" . }} {{- end }} {{- if .Values.hyperlane.aws }} AWS_ACCESS_KEY_ID: {{ print "'{{ .aws_access_key_id | toString }}'" }} @@ -51,9 +50,6 @@ spec: - secretKey: {{ printf "%s_rpcs" . }} remoteRef: key: {{ printf "%s-rpc-endpoints-%s" $.Values.hyperlane.runEnv . }} - - secretKey: {{ printf "%s_rpc" . }} - remoteRef: - key: {{ printf "%s-rpc-endpoint-%s" $.Values.hyperlane.runEnv . }} {{- end }} {{- if .Values.hyperlane.aws }} - secretKey: aws_access_key_id diff --git a/typescript/infra/helm/key-funder/templates/env-var-external-secret.yaml b/typescript/infra/helm/key-funder/templates/env-var-external-secret.yaml index de6577bfaa..2a95d627e2 100644 --- a/typescript/infra/helm/key-funder/templates/env-var-external-secret.yaml +++ b/typescript/infra/helm/key-funder/templates/env-var-external-secret.yaml @@ -29,7 +29,6 @@ spec: */}} {{- range .Values.hyperlane.chains }} GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINTS_{{ . | upper }}: {{ printf "'{{ .%s_rpcs | toString }}'" . }} - GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINT_{{ . | upper }}: {{ printf "'{{ .%s_rpc | toString }}'" . }} {{- end }} data: - secretKey: deployer_key @@ -43,7 +42,4 @@ spec: - secretKey: {{ printf "%s_rpcs" . }} remoteRef: key: {{ printf "%s-rpc-endpoints-%s" $.Values.hyperlane.runEnv . }} - - secretKey: {{ printf "%s_rpc" . }} - remoteRef: - key: {{ printf "%s-rpc-endpoint-%s" $.Values.hyperlane.runEnv . }} {{- end }} diff --git a/typescript/infra/helm/liquidity-layer-relayers/templates/env-var-external-secret.yaml b/typescript/infra/helm/liquidity-layer-relayers/templates/env-var-external-secret.yaml index a8d44b48cf..62b3117120 100644 --- a/typescript/infra/helm/liquidity-layer-relayers/templates/env-var-external-secret.yaml +++ b/typescript/infra/helm/liquidity-layer-relayers/templates/env-var-external-secret.yaml @@ -29,7 +29,6 @@ spec: */}} {{- range .Values.hyperlane.chains }} GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINTS_{{ . | upper }}: {{ printf "'{{ .%s_rpcs | toString }}'" . }} - GCP_SECRET_OVERRIDE_{{ $.Values.hyperlane.runEnv | upper }}_RPC_ENDPOINT_{{ . | upper }}: {{ printf "'{{ .%s_rpc | toString }}'" . }} {{- end }} data: - secretKey: deployer_key @@ -43,7 +42,4 @@ spec: - secretKey: {{ printf "%s_rpcs" . }} remoteRef: key: {{ printf "%s-rpc-endpoints-%s" $.Values.hyperlane.runEnv . }} - - secretKey: {{ printf "%s_rpc" . }} - remoteRef: - key: {{ printf "%s-rpc-endpoint-%s" $.Values.hyperlane.runEnv . }} {{- end }} diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts index 5f814e8274..313af5d440 100644 --- a/typescript/infra/scripts/agent-utils.ts +++ b/typescript/infra/scripts/agent-utils.ts @@ -101,13 +101,6 @@ export function withModuleAndFork(args: Argv) { .alias('f', 'fork'); } -export function withNetwork(args: Argv) { - return args - .describe('network', 'network to target') - .choices('network', getChains()) - .alias('n', 'network'); -} - export function withContext(args: Argv) { return args .describe('context', 'deploy context') @@ -117,6 +110,17 @@ export function withContext(args: Argv) { .demandOption('context'); } +export function withChainRequired(args: Argv) { + return withChain(args).demandOption('chain'); +} + +export function withChain(args: Argv) { + return args + .describe('chain', 'chain name') + .choices('chain', getChains()) + .alias('c', 'chain'); +} + export function withProtocol(args: Argv) { return args .describe('protocol', 'protocol type') @@ -176,6 +180,17 @@ export function withConcurrentDeploy(args: Argv) { .default('concurrentDeploy', false); } +export function withRpcUrls(args: Argv) { + return args + .describe( + 'rpcUrls', + 'rpc urls in a comma separated list, in order of preference', + ) + .string('rpcUrls') + .demandOption('rpcUrls') + .alias('r', 'rpcUrls'); +} + // not requiring to build coreConfig to get agentConfig export async function getAgentConfigsBasedOnArgs(argv?: { environment: DeployEnvironment; diff --git a/typescript/infra/scripts/check-rpc-urls.ts b/typescript/infra/scripts/check-rpc-urls.ts index 95ba7ee4c1..307e4515cd 100644 --- a/typescript/infra/scripts/check-rpc-urls.ts +++ b/typescript/infra/scripts/check-rpc-urls.ts @@ -15,10 +15,7 @@ async function main() { const providers: [string, ethers.providers.JsonRpcProvider][] = []; for (const chain of chains) { rootLogger.debug(`Building providers for ${chain}`); - const rpcData = [ - ...(await getSecretRpcEndpoints(environment, chain, false)), - ...(await getSecretRpcEndpoints(environment, chain, true)), - ]; + const rpcData = await getSecretRpcEndpoints(environment, chain); for (const url of rpcData) providers.push([chain, new ethers.providers.StaticJsonRpcProvider(url)]); } diff --git a/typescript/infra/scripts/deploy.ts b/typescript/infra/scripts/deploy.ts index 733bfd819e..fc67190510 100644 --- a/typescript/infra/scripts/deploy.ts +++ b/typescript/infra/scripts/deploy.ts @@ -42,10 +42,10 @@ import { getArgs, getModuleDirectory, withBuildArtifactPath, + withChain, withConcurrentDeploy, withContext, withModuleAndFork, - withNetwork, } from './agent-utils.js'; import { getEnvironmentConfig, getHyperlaneCore } from './core-utils.js'; @@ -55,12 +55,12 @@ async function main() { module, fork, environment, - network, + chain, buildArtifactPath, concurrentDeploy, } = await withContext( withConcurrentDeploy( - withNetwork(withModuleAndFork(withBuildArtifactPath(getArgs()))), + withChain(withModuleAndFork(withBuildArtifactPath(getArgs()))), ), ).argv; const envConfig = getEnvironmentConfig(environment); @@ -233,7 +233,7 @@ async function main() { // prompt for confirmation in production environments if (environment !== 'test' && !fork) { - const confirmConfig = network ? config[network] : config; + const confirmConfig = chain ? config[chain] : config; console.log(JSON.stringify(confirmConfig, null, 2)); const { value: confirmed } = await prompts({ type: 'confirm', @@ -250,7 +250,7 @@ async function main() { config, deployer, cache, - network ?? fork, + chain ?? fork, agentConfig, ); } diff --git a/typescript/infra/scripts/secret-rpc-urls/get-rpc-urls.ts b/typescript/infra/scripts/secret-rpc-urls/get-rpc-urls.ts new file mode 100644 index 0000000000..0f2c9fd5f4 --- /dev/null +++ b/typescript/infra/scripts/secret-rpc-urls/get-rpc-urls.ts @@ -0,0 +1,26 @@ +import { + getSecretRpcEndpoints, + secretRpcEndpointsExist, +} from '../../src/agents/index.js'; +import { getArgs, withChainRequired } from '../agent-utils.js'; + +async function main() { + const { environment, chain } = await withChainRequired(getArgs()).argv; + const secretExists = await secretRpcEndpointsExist(environment, chain); + if (!secretExists) { + console.log( + `No secret rpc urls found for ${chain} in ${environment} environment`, + ); + process.exit(0); + } + + const secrets = await getSecretRpcEndpoints(environment, chain); + console.log(secrets); +} + +main() + .then() + .catch((e) => { + console.error(e); + process.exit(1); + }); diff --git a/typescript/infra/scripts/secret-rpc-urls/set-rpc-urls.ts b/typescript/infra/scripts/secret-rpc-urls/set-rpc-urls.ts new file mode 100644 index 0000000000..78881a01ea --- /dev/null +++ b/typescript/infra/scripts/secret-rpc-urls/set-rpc-urls.ts @@ -0,0 +1,120 @@ +import { confirm } from '@inquirer/prompts'; +import { ethers } from 'ethers'; + +import { + getSecretRpcEndpoints, + getSecretRpcEndpointsLatestVersionName, + secretRpcEndpointsExist, + setSecretRpcEndpoints, +} from '../../src/agents/index.js'; +import { disableGCPSecretVersion } from '../../src/utils/gcloud.js'; +import { isEthereumProtocolChain } from '../../src/utils/utils.js'; +import { getArgs, withChainRequired, withRpcUrls } from '../agent-utils.js'; + +async function testProviders(rpcUrlsArray: string[]): Promise { + let providersSucceeded = true; + for (const url of rpcUrlsArray) { + const provider = new ethers.providers.StaticJsonRpcProvider(url); + try { + const blockNumber = await provider.getBlockNumber(); + console.log(`Valid provider for ${url} with block number ${blockNumber}`); + } catch (e) { + console.error(`Provider failed: ${url}`); + providersSucceeded = false; + } + } + + return providersSucceeded; +} + +async function main() { + const { environment, chain, rpcUrls } = await withRpcUrls( + withChainRequired(getArgs()), + ).argv; + + const rpcUrlsArray = rpcUrls + .split(/,\s*/) + .filter(Boolean) // filter out empty strings + .map((url) => url.trim()); + + if (!rpcUrlsArray.length) { + console.error('No rpc urls provided, Exiting.'); + process.exit(1); + } + + const secretPayload = JSON.stringify(rpcUrlsArray); + + const secretExists = await secretRpcEndpointsExist(environment, chain); + if (!secretExists) { + console.log( + `No secret rpc urls found for ${chain} in ${environment} environment\n`, + ); + } else { + const currentSecrets = await getSecretRpcEndpoints(environment, chain); + console.log( + `Current secrets found for ${chain} in ${environment} environment:\n${JSON.stringify( + currentSecrets, + null, + 2, + )}\n`, + ); + } + + const confirmedSet = await confirm({ + message: `Are you sure you want to set the following RPC URLs for ${chain} in ${environment}?\n${secretPayload}\n`, + }); + + if (!confirmedSet) { + console.log('Exiting without setting secret.'); + process.exit(0); + } + + if (isEthereumProtocolChain(chain)) { + console.log('\nTesting providers...'); + const testPassed = await testProviders(rpcUrlsArray); + if (!testPassed) { + console.error('At least one provider failed. Exiting.'); + process.exit(1); + } + + const confirmedProviders = await confirm({ + message: `All providers passed. Do you want to continue setting the secret?\n`, + }); + + if (!confirmedProviders) { + console.log('Exiting without setting secret.'); + process.exit(0); + } + } else { + console.log( + 'Skipping provider testing as chain is not an Ethereum protocol chain.', + ); + } + + let latestVersionName; + if (secretExists) { + latestVersionName = await getSecretRpcEndpointsLatestVersionName( + environment, + chain, + ); + } + console.log(`Setting secret...`); + await setSecretRpcEndpoints(environment, chain, secretPayload); + console.log(`Added secret version!`); + + if (latestVersionName) { + try { + await disableGCPSecretVersion(latestVersionName); + console.log(`Disabled previous version of the secret!`); + } catch (e) { + console.log(`Could not disable previous version of the secret`); + } + } +} + +main() + .then() + .catch((e) => { + console.error(e); + process.exit(1); + }); diff --git a/typescript/infra/scripts/verify.ts b/typescript/infra/scripts/verify.ts index 9c15c6597b..0e1f782d4f 100644 --- a/typescript/infra/scripts/verify.ts +++ b/typescript/infra/scripts/verify.ts @@ -12,12 +12,12 @@ import { } from '../src/deployment/verify.js'; import { readJSONAtPath } from '../src/utils/utils.js'; -import { getArgs, withBuildArtifactPath, withNetwork } from './agent-utils.js'; +import { getArgs, withBuildArtifactPath, withChain } from './agent-utils.js'; import { getEnvironmentConfig } from './core-utils.js'; async function main() { - const { environment, buildArtifactPath, verificationArtifactPath, network } = - await withNetwork(withBuildArtifactPath(getArgs())) + const { environment, buildArtifactPath, verificationArtifactPath, chain } = + await withChain(withBuildArtifactPath(getArgs())) .string('verificationArtifactPath') .describe( 'verificationArtifactPath', @@ -54,7 +54,7 @@ async function main() { // verify all the things const failedResults = ( - await verifier.verify(network ? [network] : undefined) + await verifier.verify(chain ? [chain] : undefined) ).filter((result) => result.status === 'rejected'); // only log the failed verifications to console diff --git a/typescript/infra/src/agents/index.ts b/typescript/infra/src/agents/index.ts index 66a9be195f..c959bca650 100644 --- a/typescript/infra/src/agents/index.ts +++ b/typescript/infra/src/agents/index.ts @@ -17,7 +17,12 @@ import { ScraperConfigHelper } from '../config/agent/scraper.js'; import { ValidatorConfigHelper } from '../config/agent/validator.js'; import { DeployEnvironment } from '../config/environment.js'; import { AgentRole, Role } from '../roles.js'; -import { fetchGCPSecret } from '../utils/gcloud.js'; +import { + fetchGCPSecret, + gcpSecretExistsUsingClient, + getGcpSecretLatestVersionName, + setGCPSecretUsingClient, +} from '../utils/gcloud.js'; import { HelmCommand, buildHelmChartDependencies, @@ -287,6 +292,13 @@ export class ValidatorHelmManager extends MultichainAgentHelmManager { } } +export function getSecretName( + environment: string, + chainName: ChainName, +): string { + return `${environment}-rpc-endpoints-${chainName}`; +} + export async function getSecretAwsCredentials(agentConfig: AgentContextConfig) { return { accessKeyId: await fetchGCPSecret( @@ -303,17 +315,11 @@ export async function getSecretAwsCredentials(agentConfig: AgentContextConfig) { export async function getSecretRpcEndpoints( environment: string, chainName: ChainName, - multipleEndpoints = false, ): Promise { - const secret = await fetchGCPSecret( - `${environment}-rpc-endpoint${multipleEndpoints ? 's' : ''}-${chainName}`, - multipleEndpoints, - ); - if (typeof secret != 'string' && !Array.isArray(secret)) { - throw Error(`Expected secret for ${chainName} rpc endpoint`); - } + const secret = await fetchGCPSecret(getSecretName(environment, chainName)); + if (!Array.isArray(secret)) { - return [secret.trimEnd()]; + throw Error(`Expected secret for ${chainName} rpc endpoint`); } return secret.map((i) => { @@ -323,6 +329,29 @@ export async function getSecretRpcEndpoints( }); } +export async function getSecretRpcEndpointsLatestVersionName( + environment: string, + chainName: ChainName, +) { + return getGcpSecretLatestVersionName(getSecretName(environment, chainName)); +} + +export async function secretRpcEndpointsExist( + environment: string, + chainName: ChainName, +): Promise { + return gcpSecretExistsUsingClient(getSecretName(environment, chainName)); +} + +export async function setSecretRpcEndpoints( + environment: string, + chainName: ChainName, + endpoints: string, +) { + const secretName = getSecretName(environment, chainName); + await setGCPSecretUsingClient(secretName, endpoints); +} + export async function getSecretDeployerKey( environment: DeployEnvironment, context: Contexts, diff --git a/typescript/infra/src/config/chain.ts b/typescript/infra/src/config/chain.ts index 2be5bc4eaf..51584fc373 100644 --- a/typescript/infra/src/config/chain.ts +++ b/typescript/infra/src/config/chain.ts @@ -106,7 +106,7 @@ export async function getSecretMetadataOverrides( const secretRpcUrls = await Promise.all( chains.map(async (chain) => { - const rpcUrls = await getSecretRpcEndpoints(deployEnv, chain, true); + const rpcUrls = await getSecretRpcEndpoints(deployEnv, chain); return { chain, rpcUrls, diff --git a/typescript/infra/src/utils/gcloud.ts b/typescript/infra/src/utils/gcloud.ts index cc4fd2d621..56d913b7c3 100644 --- a/typescript/infra/src/utils/gcloud.ts +++ b/typescript/infra/src/utils/gcloud.ts @@ -45,9 +45,7 @@ export async function fetchGCPSecret( } export async function fetchLatestGCPSecret(secretName: string) { - const client = new SecretManagerServiceClient({ - projectId: GCP_PROJECT_ID, - }); + const client = await getSecretManagerServiceClient(); const [secretVersion] = await client.accessSecretVersion({ name: `projects/${GCP_PROJECT_ID}/secrets/${secretName}/versions/latest`, }); @@ -84,6 +82,12 @@ function tryGCPSecretFromEnvVariable(gcpSecretName: string) { return process.env[overrideEnvVarName]; } +/** + * Checks if a secret exists in GCP using the gcloud CLI. + * @deprecated Use gcpSecretExistsUsingClient instead. + * @param secretName The name of the secret to check. + * @returns A boolean indicating whether the secret exists. + */ export async function gcpSecretExists(secretName: string) { const fullName = `projects/${await getCurrentProjectNumber()}/secrets/${secretName}`; debugLog(`Checking if GCP secret exists for ${fullName}`); @@ -95,6 +99,55 @@ export async function gcpSecretExists(secretName: string) { return matches.length > 0; } +/** + * Uses the SecretManagerServiceClient to check if a secret exists. + * @param secretName The name of the secret to check. + * @returns A boolean indicating whether the secret exists. + */ +export async function gcpSecretExistsUsingClient( + secretName: string, + client?: SecretManagerServiceClient, +): Promise { + if (!client) { + client = await getSecretManagerServiceClient(); + } + + try { + const fullSecretName = `projects/${await getCurrentProjectNumber()}/secrets/${secretName}`; + const [secrets] = await client.listSecrets({ + parent: `projects/${GCP_PROJECT_ID}`, + filter: `name=${fullSecretName}`, + }); + + return secrets.length > 0; + } catch (e) { + debugLog(`Error checking if secret exists: ${e}`); + throw e; + } +} + +export async function getGcpSecretLatestVersionName(secretName: string) { + const client = await getSecretManagerServiceClient(); + const [version] = await client.getSecretVersion({ + name: `projects/${GCP_PROJECT_ID}/secrets/${secretName}/versions/latest`, + }); + + return version?.name; +} + +export async function getSecretManagerServiceClient() { + return new SecretManagerServiceClient({ + projectId: GCP_PROJECT_ID, + }); +} + +/** + * Sets a GCP secret using the gcloud CLI. Create secret if it doesn't exist and add a new version or update the existing one. + * @deprecated Use setGCPSecretUsingClient instead. + * @param secretName The name of the secret to set. + * @param secret The secret to set. + * @param labels The labels to set on the secret. + */ export async function setGCPSecret( secretName: string, secret: string, @@ -121,6 +174,64 @@ export async function setGCPSecret( await rm(fileName); } +/** + * Sets a GCP secret using the SecretManagerServiceClient. Create secret if it doesn't exist and add a new version or update the existing one. + * @param secretName The name of the secret to set. + * @param secret The secret to set. + */ +export async function setGCPSecretUsingClient( + secretName: string, + secret: string, + labels?: Record, +) { + const client = await getSecretManagerServiceClient(); + + const exists = await gcpSecretExistsUsingClient(secretName, client); + if (!exists) { + // Create the secret + await client.createSecret({ + parent: `projects/${GCP_PROJECT_ID}`, + secretId: secretName, + secret: { + name: secretName, + replication: { + automatic: {}, + }, + labels, + }, + }); + debugLog(`Created new GCP secret for ${secretName}`); + } + await addGCPSecretVersion(secretName, secret, client); +} + +export async function addGCPSecretVersion( + secretName: string, + secret: string, + client?: SecretManagerServiceClient, +) { + if (!client) { + client = await getSecretManagerServiceClient(); + } + + const [version] = await client.addSecretVersion({ + parent: `projects/${GCP_PROJECT_ID}/secrets/${secretName}`, + payload: { + data: Buffer.from(secret, 'utf8'), + }, + }); + debugLog(`Added secret version ${version?.name}`); +} + +export async function disableGCPSecretVersion(secretName: string) { + const client = await getSecretManagerServiceClient(); + + const [version] = await client.disableSecretVersion({ + name: secretName, + }); + debugLog(`Disabled secret version ${version?.name}`); +} + // Returns the email of the service account export async function createServiceAccountIfNotExists( serviceAccountName: string, From 826b4ae57fe8642651b9a58ed0344ab1111b1d31 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 7 Jun 2024 19:03:13 +0100 Subject: [PATCH 39/59] feat: batching igp (#3870) ### Description Deducts IGP cost from senders of messages within a batch, based on the total gas used by the batch and the estimates for submitting messages individually. The formula is `gas_used_by_operation = gas_used_by_tx * (operation_estimated_gas / total_estimated_cost)` Note that the individual estimate have a buffer value added to them (currently 75k), which will slightly skew proportions, though by a negligible amount. For example, given these estimates in a batch: 150k, 160k, 180k, if we add 80k to each, we go from e.g. 0.312 for the first message to 0.319 -> 2.25% error which isn't so bad. The error can be larger if operations within a batch have very different estimates, but realistically within a 5% range based on back-of-the-napkin calculations. ### Drive-by changes - The batching feature introduced an bug whereby gas expenditure would only be deducted in the `confirm` step. Now this is done in the `submit` step to account for txs that revert - Sealevel e2e can now be disabled for faster iteration! I've not done the cleanest job, but even the fact that we have this will reduce the time to run e2e locally by more than half. To use it, set `SEALEVEL_ENABLED=false` when running `run-locally` - e2e uses a new utility called `get_matching_lines`, that can be used to count (and in the future _parse_) logs, to reconstruct the state of the relayer and have more expressive correctness checks. This is used to make sure that gas is deducted for all messages, including those in batches. ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3709 ### Backward compatibility Yes ### Testing E2E, using `get_matching_lines` --- rust/Cargo.lock | 2 + rust/agents/relayer/Cargo.toml | 2 +- rust/agents/relayer/src/lib.rs | 10 ++ rust/agents/relayer/src/main.rs | 10 +- .../agents/relayer/src/msg/gas_payment/mod.rs | 9 + rust/agents/relayer/src/msg/mod.rs | 3 +- rust/agents/relayer/src/msg/op_queue.rs | 21 ++- rust/agents/relayer/src/msg/op_submitter.rs | 13 +- .../agents/relayer/src/msg/pending_message.rs | 68 +++++-- rust/agents/relayer/src/msg/processor.rs | 4 +- rust/agents/relayer/src/relayer.rs | 4 +- rust/agents/relayer/src/server.rs | 4 +- .../src/db/rocks/hyperlane_db.rs | 6 +- rust/hyperlane-core/Cargo.toml | 2 +- rust/hyperlane-core/src/traits/mod.rs | 2 + .../src/traits}/pending_operation.rs | 56 +++++- .../src/types/primitive_types.rs | 27 ++- rust/utils/run-locally/Cargo.toml | 2 + rust/utils/run-locally/src/config.rs | 4 + rust/utils/run-locally/src/cosmos/cli.rs | 2 +- rust/utils/run-locally/src/cosmos/mod.rs | 4 +- rust/utils/run-locally/src/ethereum/mod.rs | 2 +- rust/utils/run-locally/src/invariants.rs | 39 +++- rust/utils/run-locally/src/main.rs | 169 ++++++++++++------ rust/utils/run-locally/src/program.rs | 55 ++++-- rust/utils/run-locally/src/solana.rs | 2 +- rust/utils/run-locally/src/utils.rs | 18 ++ 27 files changed, 405 insertions(+), 135 deletions(-) create mode 100644 rust/agents/relayer/src/lib.rs rename rust/{agents/relayer/src/msg => hyperlane-core/src/traits}/pending_operation.rs (75%) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index f29c2c80ea..f1c4f79b2d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -7275,7 +7275,9 @@ dependencies = [ "macro_rules_attribute", "maplit", "nix 0.26.4", + "once_cell", "regex", + "relayer", "ripemd", "serde", "serde_json", diff --git a/rust/agents/relayer/Cargo.toml b/rust/agents/relayer/Cargo.toml index 2df8f54d9a..cf35092f7a 100644 --- a/rust/agents/relayer/Cargo.toml +++ b/rust/agents/relayer/Cargo.toml @@ -38,7 +38,7 @@ tracing-futures.workspace = true tracing.workspace = true hyperlane-core = { path = "../../hyperlane-core", features = ["agent", "async"] } -hyperlane-base = { path = "../../hyperlane-base" } +hyperlane-base = { path = "../../hyperlane-base", features = ["test-utils"] } hyperlane-ethereum = { path = "../../chains/hyperlane-ethereum" } [dev-dependencies] diff --git a/rust/agents/relayer/src/lib.rs b/rust/agents/relayer/src/lib.rs new file mode 100644 index 0000000000..62b896d628 --- /dev/null +++ b/rust/agents/relayer/src/lib.rs @@ -0,0 +1,10 @@ +mod merkle_tree; +mod msg; +mod processor; +mod prover; +mod relayer; +mod server; +mod settings; + +pub use msg::GAS_EXPENDITURE_LOG_MESSAGE; +pub use relayer::*; diff --git a/rust/agents/relayer/src/main.rs b/rust/agents/relayer/src/main.rs index 1223702f8b..7d085f5293 100644 --- a/rust/agents/relayer/src/main.rs +++ b/rust/agents/relayer/src/main.rs @@ -11,15 +11,7 @@ use eyre::Result; use hyperlane_base::agent_main; -use crate::relayer::Relayer; - -mod merkle_tree; -mod msg; -mod processor; -mod prover; -mod relayer; -mod server; -mod settings; +use relayer::Relayer; #[tokio::main(flavor = "multi_thread", worker_threads = 20)] async fn main() -> Result<()> { diff --git a/rust/agents/relayer/src/msg/gas_payment/mod.rs b/rust/agents/relayer/src/msg/gas_payment/mod.rs index cd9dd61c06..a072103915 100644 --- a/rust/agents/relayer/src/msg/gas_payment/mod.rs +++ b/rust/agents/relayer/src/msg/gas_payment/mod.rs @@ -19,6 +19,8 @@ use crate::{ mod policies; +pub const GAS_EXPENDITURE_LOG_MESSAGE: &str = "Recording gas expenditure for message"; + #[async_trait] pub trait GasPaymentPolicy: Debug + Send + Sync { /// Returns Some(gas_limit) if the policy has approved the transaction or @@ -132,6 +134,13 @@ impl GasPaymentEnforcer { } pub fn record_tx_outcome(&self, message: &HyperlaneMessage, outcome: TxOutcome) -> Result<()> { + // This log is required in E2E, hence the use of a `const` + debug!( + msg=%message, + ?outcome, + "{}", + GAS_EXPENDITURE_LOG_MESSAGE, + ); self.db.process_gas_expenditure(InterchainGasExpenditure { message_id: message.id(), gas_used: outcome.gas_used, diff --git a/rust/agents/relayer/src/msg/mod.rs b/rust/agents/relayer/src/msg/mod.rs index 60c2ce0c56..dd7bac22bb 100644 --- a/rust/agents/relayer/src/msg/mod.rs +++ b/rust/agents/relayer/src/msg/mod.rs @@ -30,5 +30,6 @@ pub(crate) mod metadata; pub(crate) mod op_queue; pub(crate) mod op_submitter; pub(crate) mod pending_message; -pub(crate) mod pending_operation; pub(crate) mod processor; + +pub use gas_payment::GAS_EXPENDITURE_LOG_MESSAGE; diff --git a/rust/agents/relayer/src/msg/op_queue.rs b/rust/agents/relayer/src/msg/op_queue.rs index 6881d06769..0072085547 100644 --- a/rust/agents/relayer/src/msg/op_queue.rs +++ b/rust/agents/relayer/src/msg/op_queue.rs @@ -1,16 +1,13 @@ use std::{cmp::Reverse, collections::BinaryHeap, sync::Arc}; use derive_new::new; +use hyperlane_core::{PendingOperation, QueueOperation}; use prometheus::{IntGauge, IntGaugeVec}; use tokio::sync::{broadcast::Receiver, Mutex}; use tracing::{debug, info, instrument}; use crate::server::MessageRetryRequest; -use super::pending_operation::PendingOperation; - -pub type QueueOperation = Box; - /// Queue of generic operations that can be submitted to a destination chain. /// Includes logic for maintaining queue metrics by the destination and `app_context` of an operation #[derive(Debug, Clone, new)] @@ -109,9 +106,9 @@ impl OpQueue { #[cfg(test)] mod test { use super::*; - use crate::msg::pending_operation::PendingOperationResult; use hyperlane_core::{ - HyperlaneDomain, HyperlaneMessage, KnownHyperlaneDomain, TryBatchAs, TxOutcome, H256, + HyperlaneDomain, HyperlaneMessage, KnownHyperlaneDomain, PendingOperationResult, + TryBatchAs, TxOutcome, H256, U256, }; use std::{ collections::VecDeque, @@ -182,6 +179,10 @@ mod test { todo!() } + fn get_tx_cost_estimate(&self) -> Option { + todo!() + } + /// This will be called after the operation has been submitted and is /// responsible for checking if the operation has reached a point at /// which we consider it safe from reorgs. @@ -189,6 +190,14 @@ mod test { todo!() } + fn set_operation_outcome( + &mut self, + _submission_outcome: TxOutcome, + _submission_estimated_cost: U256, + ) { + todo!() + } + fn next_attempt_after(&self) -> Option { Some( Instant::now() diff --git a/rust/agents/relayer/src/msg/op_submitter.rs b/rust/agents/relayer/src/msg/op_submitter.rs index 66d1a57d38..20eb936163 100644 --- a/rust/agents/relayer/src/msg/op_submitter.rs +++ b/rust/agents/relayer/src/msg/op_submitter.rs @@ -4,6 +4,7 @@ use std::time::Duration; use derive_new::new; use futures::future::join_all; use futures_util::future::try_join_all; +use hyperlane_core::total_estimated_cost; use prometheus::{IntCounter, IntGaugeVec}; use tokio::sync::broadcast::Sender; use tokio::sync::mpsc; @@ -17,14 +18,13 @@ use tracing::{info, warn}; use hyperlane_base::CoreMetrics; use hyperlane_core::{ BatchItem, ChainCommunicationError, ChainResult, HyperlaneDomain, HyperlaneDomainProtocol, - HyperlaneMessage, TxOutcome, + HyperlaneMessage, PendingOperationResult, QueueOperation, TxOutcome, }; use crate::msg::pending_message::CONFIRM_DELAY; use crate::server::MessageRetryRequest; -use super::op_queue::{OpQueue, QueueOperation}; -use super::pending_operation::*; +use super::op_queue::OpQueue; /// SerialSubmitter accepts operations over a channel. It is responsible for /// executing the right strategy to deliver those messages to the destination @@ -428,11 +428,10 @@ impl OperationBatch { async fn submit(self, confirm_queue: &mut OpQueue, metrics: &SerialSubmitterMetrics) { match self.try_submit_as_batch(metrics).await { Ok(outcome) => { - // TODO: use the `tx_outcome` with the total gas expenditure - // We'll need to proportionally set `used_gas` based on the tx_outcome, so it can be updated in the confirm step - // which means we need to add a `set_transaction_outcome` fn to `PendingOperation` info!(outcome=?outcome, batch_size=self.operations.len(), batch=?self.operations, "Submitted transaction batch"); + let total_estimated_cost = total_estimated_cost(&self.operations); for mut op in self.operations { + op.set_operation_outcome(outcome.clone(), total_estimated_cost); op.set_next_attempt_after(CONFIRM_DELAY); confirm_queue.push(op).await; } @@ -462,8 +461,6 @@ impl OperationBatch { return Err(ChainCommunicationError::BatchIsEmpty); }; - // We use the estimated gas limit from the prior call to - // `process_estimate_costs` to avoid a second gas estimation. let outcome = first_item.mailbox.process_batch(&batch).await?; metrics.ops_submitted.inc_by(self.operations.len() as u64); Ok(outcome) diff --git a/rust/agents/relayer/src/msg/pending_message.rs b/rust/agents/relayer/src/msg/pending_message.rs index b2f8369d05..a0c373adce 100644 --- a/rust/agents/relayer/src/msg/pending_message.rs +++ b/rust/agents/relayer/src/msg/pending_message.rs @@ -9,8 +9,9 @@ use derive_new::new; use eyre::Result; use hyperlane_base::{db::HyperlaneRocksDB, CoreMetrics}; use hyperlane_core::{ - BatchItem, ChainCommunicationError, ChainResult, HyperlaneChain, HyperlaneDomain, - HyperlaneMessage, Mailbox, MessageSubmissionData, TryBatchAs, TxOutcome, H256, U256, + gas_used_by_operation, make_op_try, BatchItem, ChainCommunicationError, ChainResult, + HyperlaneChain, HyperlaneDomain, HyperlaneMessage, Mailbox, MessageSubmissionData, + PendingOperation, PendingOperationResult, TryBatchAs, TxOutcome, H256, U256, }; use prometheus::{IntCounter, IntGauge}; use tracing::{debug, error, info, instrument, trace, warn}; @@ -18,7 +19,6 @@ use tracing::{debug, error, info, instrument, trace, warn}; use super::{ gas_payment::GasPaymentEnforcer, metadata::{BaseMetadataBuilder, MessageMetadataBuilder, MetadataBuilder}, - pending_operation::*, }; pub const CONFIRM_DELAY: Duration = if cfg!(any(test, feature = "test-utils")) { @@ -259,7 +259,7 @@ impl PendingOperation for PendingMessage { let state = self .submission_data - .take() + .clone() .expect("Pending message must be prepared before it can be submitted"); // We use the estimated gas limit from the prior call to @@ -271,7 +271,7 @@ impl PendingOperation for PendingMessage { .await; match tx_outcome { Ok(outcome) => { - self.set_submission_outcome(outcome); + self.set_operation_outcome(outcome, state.gas_limit); } Err(e) => { error!(error=?e, "Error when processing message"); @@ -283,6 +283,10 @@ impl PendingOperation for PendingMessage { self.submission_outcome = Some(outcome); } + fn get_tx_cost_estimate(&self) -> Option { + self.submission_data.as_ref().map(|d| d.gas_limit) + } + async fn confirm(&mut self) -> PendingOperationResult { make_op_try!(|| { // Provider error; just try again later @@ -313,15 +317,6 @@ impl PendingOperation for PendingMessage { ); PendingOperationResult::Success } else { - if let Some(outcome) = &self.submission_outcome { - if let Err(e) = self - .ctx - .origin_gas_payment_enforcer - .record_tx_outcome(&self.message, outcome.clone()) - { - error!(error=?e, "Error when recording tx outcome"); - } - } warn!( tx_outcome=?self.submission_outcome, message_id=?self.message.id(), @@ -331,6 +326,50 @@ impl PendingOperation for PendingMessage { } } + fn set_operation_outcome( + &mut self, + submission_outcome: TxOutcome, + submission_estimated_cost: U256, + ) { + let Some(operation_estimate) = self.get_tx_cost_estimate() else { + warn!("Cannot set operation outcome without a cost estimate set previously"); + return; + }; + // calculate the gas used by the operation + let gas_used_by_operation = match gas_used_by_operation( + &submission_outcome, + submission_estimated_cost, + operation_estimate, + ) { + Ok(gas_used_by_operation) => gas_used_by_operation, + Err(e) => { + warn!(error = %e, "Error when calculating gas used by operation, falling back to charging the full cost of the tx. Are gas estimates enabled for this chain?"); + submission_outcome.gas_used + } + }; + let operation_outcome = TxOutcome { + gas_used: gas_used_by_operation, + ..submission_outcome + }; + // record it in the db, to subtract from the sender's igp allowance + if let Err(e) = self + .ctx + .origin_gas_payment_enforcer + .record_tx_outcome(&self.message, operation_outcome.clone()) + { + error!(error=?e, "Error when recording tx outcome"); + } + // set the outcome in `Self` as well, for later logging + self.set_submission_outcome(operation_outcome); + debug!( + actual_gas_for_message = ?gas_used_by_operation, + message_gas_estimate = ?operation_estimate, + submission_gas_estimate = ?submission_estimated_cost, + message = ?self.message, + "Gas used by message submission" + ); + } + fn next_attempt_after(&self) -> Option { self.next_attempt_after } @@ -343,7 +382,6 @@ impl PendingOperation for PendingMessage { self.reset_attempts(); } - #[cfg(test)] fn set_retries(&mut self, retries: u32) { self.set_retries(retries); } diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/agents/relayer/src/msg/processor.rs index 166ee6561f..6545d480ad 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/agents/relayer/src/msg/processor.rs @@ -13,12 +13,12 @@ use hyperlane_base::{ db::{HyperlaneRocksDB, ProcessMessage}, CoreMetrics, }; -use hyperlane_core::{HyperlaneDomain, HyperlaneMessage}; +use hyperlane_core::{HyperlaneDomain, HyperlaneMessage, QueueOperation}; use prometheus::IntGauge; use tokio::sync::mpsc::UnboundedSender; use tracing::{debug, instrument, trace}; -use super::{metadata::AppContextClassifier, op_queue::QueueOperation, pending_message::*}; +use super::{metadata::AppContextClassifier, pending_message::*}; use crate::{processor::ProcessorExt, settings::matching_list::MatchingList}; /// Finds unprocessed messages from an origin and submits then through a channel diff --git a/rust/agents/relayer/src/relayer.rs b/rust/agents/relayer/src/relayer.rs index 085e43ee6c..4206c0584a 100644 --- a/rust/agents/relayer/src/relayer.rs +++ b/rust/agents/relayer/src/relayer.rs @@ -16,7 +16,8 @@ use hyperlane_base::{ SyncOptions, }; use hyperlane_core::{ - HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, H512, U256, + HyperlaneDomain, HyperlaneMessage, InterchainGasPayment, MerkleTreeInsertion, QueueOperation, + H512, U256, }; use tokio::{ sync::{ @@ -34,7 +35,6 @@ use crate::{ msg::{ gas_payment::GasPaymentEnforcer, metadata::{BaseMetadataBuilder, IsmAwareAppContextClassifier}, - op_queue::QueueOperation, op_submitter::{SerialSubmitter, SerialSubmitterMetrics}, pending_message::{MessageContext, MessageSubmissionMetrics}, processor::{MessageProcessor, MessageProcessorMetrics}, diff --git a/rust/agents/relayer/src/server.rs b/rust/agents/relayer/src/server.rs index 364181df60..264ef03800 100644 --- a/rust/agents/relayer/src/server.rs +++ b/rust/agents/relayer/src/server.rs @@ -3,13 +3,11 @@ use axum::{ routing, Router, }; use derive_new::new; -use hyperlane_core::{ChainCommunicationError, H256}; +use hyperlane_core::{ChainCommunicationError, QueueOperation, H256}; use serde::Deserialize; use std::str::FromStr; use tokio::sync::broadcast::Sender; -use crate::msg::op_queue::QueueOperation; - const MESSAGE_RETRY_API_BASE: &str = "/message_retry"; pub const ENDPOINT_MESSAGES_QUEUE_SIZE: usize = 1_000; diff --git a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs index 3d164ce269..b4323613ad 100644 --- a/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs +++ b/rust/hyperlane-base/src/db/rocks/hyperlane_db.rs @@ -242,10 +242,10 @@ impl HyperlaneRocksDB { &self, event: InterchainGasExpenditure, ) -> DbResult<()> { - let existing_payment = self.retrieve_gas_expenditure_by_message_id(event.message_id)?; - let total = existing_payment + event; + let existing_expenditure = self.retrieve_gas_expenditure_by_message_id(event.message_id)?; + let total = existing_expenditure + event; - debug!(?event, new_total_gas_payment=?total, "Storing gas payment"); + debug!(?event, new_total_gas_expenditure=?total, "Storing gas expenditure"); self.store_interchain_gas_expenditure_data_by_message_id( &total.message_id, &InterchainGasExpenditureData { diff --git a/rust/hyperlane-core/Cargo.toml b/rust/hyperlane-core/Cargo.toml index 5f34bc2091..21ee23235f 100644 --- a/rust/hyperlane-core/Cargo.toml +++ b/rust/hyperlane-core/Cargo.toml @@ -49,7 +49,7 @@ uint.workspace = true tokio = { workspace = true, features = ["rt", "time"] } [features] -default = [] +default = ["strum"] float = [] test-utils = ["dep:config"] agent = ["ethers", "strum"] diff --git a/rust/hyperlane-core/src/traits/mod.rs b/rust/hyperlane-core/src/traits/mod.rs index e85b04f4a6..b168a18920 100644 --- a/rust/hyperlane-core/src/traits/mod.rs +++ b/rust/hyperlane-core/src/traits/mod.rs @@ -10,6 +10,7 @@ pub use interchain_security_module::*; pub use mailbox::*; pub use merkle_tree_hook::*; pub use multisig_ism::*; +pub use pending_operation::*; pub use provider::*; pub use routing_ism::*; pub use signing::*; @@ -29,6 +30,7 @@ mod interchain_security_module; mod mailbox; mod merkle_tree_hook; mod multisig_ism; +mod pending_operation; mod provider; mod routing_ism; mod signing; diff --git a/rust/agents/relayer/src/msg/pending_operation.rs b/rust/hyperlane-core/src/traits/pending_operation.rs similarity index 75% rename from rust/agents/relayer/src/msg/pending_operation.rs rename to rust/hyperlane-core/src/traits/pending_operation.rs index 206e062e2a..c6d494467e 100644 --- a/rust/agents/relayer/src/msg/pending_operation.rs +++ b/rust/hyperlane-core/src/traits/pending_operation.rs @@ -4,10 +4,16 @@ use std::{ time::{Duration, Instant}, }; +use crate::{ + ChainResult, FixedPointNumber, HyperlaneDomain, HyperlaneMessage, TryBatchAs, TxOutcome, H256, + U256, +}; use async_trait::async_trait; -use hyperlane_core::{HyperlaneDomain, HyperlaneMessage, TryBatchAs, TxOutcome, H256}; +use num::CheckedDiv; +use tracing::warn; -use super::op_queue::QueueOperation; +/// Boxed operation that can be stored in an operation queue +pub type QueueOperation = Box; /// A pending operation that will be run by the submitter and cause a /// transaction to be sent. @@ -67,11 +73,21 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { /// Set the outcome of the `submit` call fn set_submission_outcome(&mut self, outcome: TxOutcome); + /// Get the estimated the cost of the `submit` call + fn get_tx_cost_estimate(&self) -> Option; + /// This will be called after the operation has been submitted and is /// responsible for checking if the operation has reached a point at /// which we consider it safe from reorgs. async fn confirm(&mut self) -> PendingOperationResult; + /// Record the outcome of the operation + fn set_operation_outcome( + &mut self, + submission_outcome: TxOutcome, + submission_estimated_cost: U256, + ); + /// Get the earliest instant at which this should next be attempted. /// /// This is only used for sorting, the functions are responsible for @@ -85,11 +101,41 @@ pub trait PendingOperation: Send + Sync + Debug + TryBatchAs { /// retried immediately. fn reset_attempts(&mut self); - #[cfg(test)] /// Set the number of times this operation has been retried. + #[cfg(any(test, feature = "test-utils"))] fn set_retries(&mut self, retries: u32); } +/// Utility fn to calculate the total estimated cost of an operation batch +pub fn total_estimated_cost(ops: &[Box]) -> U256 { + ops.iter() + .fold(U256::zero(), |acc, op| match op.get_tx_cost_estimate() { + Some(cost_estimate) => acc.saturating_add(cost_estimate), + None => { + warn!(operation=?op, "No cost estimate available for operation, defaulting to 0"); + acc + } + }) +} + +/// Calculate the gas used by an operation (either in a batch or single-submission), by looking at the total cost of the tx, +/// and the estimated cost of the operation compared to the sum of the estimates of all operations in the batch. +/// When using this for single-submission rather than a batch, +/// the `tx_estimated_cost` should be the same as the `tx_estimated_cost` +pub fn gas_used_by_operation( + tx_outcome: &TxOutcome, + tx_estimated_cost: U256, + operation_estimated_cost: U256, +) -> ChainResult { + let gas_used_by_tx = FixedPointNumber::try_from(tx_outcome.gas_used)?; + let operation_gas_estimate = FixedPointNumber::try_from(operation_estimated_cost)?; + let tx_gas_estimate = FixedPointNumber::try_from(tx_estimated_cost)?; + let gas_used_by_operation = (gas_used_by_tx * operation_gas_estimate) + .checked_div(&tx_gas_estimate) + .ok_or(eyre::eyre!("Division by zero"))?; + gas_used_by_operation.try_into() +} + impl Display for QueueOperation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -138,6 +184,7 @@ impl Ord for QueueOperation { } } +/// Possible outcomes of performing an action on a pending operation (such as `prepare`, `submit` or `confirm`). #[derive(Debug)] pub enum PendingOperationResult { /// Promote to the next step @@ -153,6 +200,7 @@ pub enum PendingOperationResult { } /// create a `op_try!` macro for the `on_retry` handler. +#[macro_export] macro_rules! make_op_try { ($on_retry:expr) => { /// Handle a result and either return early with retry or a critical failure on @@ -181,5 +229,3 @@ macro_rules! make_op_try { } }; } - -pub(super) use make_op_try; diff --git a/rust/hyperlane-core/src/types/primitive_types.rs b/rust/hyperlane-core/src/types/primitive_types.rs index 2a3c53d403..c5636b3b9e 100644 --- a/rust/hyperlane-core/src/types/primitive_types.rs +++ b/rust/hyperlane-core/src/types/primitive_types.rs @@ -3,11 +3,15 @@ #![allow(clippy::assign_op_pattern)] #![allow(clippy::reversed_empty_ranges)] -use std::{ops::Mul, str::FromStr}; +use std::{ + ops::{Div, Mul}, + str::FromStr, +}; use bigdecimal::{BigDecimal, RoundingMode}; use borsh::{BorshDeserialize, BorshSerialize}; use fixed_hash::impl_fixed_hash_conversions; +use num::CheckedDiv; use num_traits::Zero; use uint::construct_uint; @@ -421,6 +425,27 @@ where } } +impl Div for FixedPointNumber +where + T: Into, +{ + type Output = FixedPointNumber; + + fn div(self, rhs: T) -> Self::Output { + let rhs = rhs.into(); + Self(self.0 / rhs.0) + } +} + +impl CheckedDiv for FixedPointNumber { + fn checked_div(&self, v: &Self) -> Option { + if v.0.is_zero() { + return None; + } + Some(Self(self.0.clone() / v.0.clone())) + } +} + impl FromStr for FixedPointNumber { type Err = ChainCommunicationError; diff --git a/rust/utils/run-locally/Cargo.toml b/rust/utils/run-locally/Cargo.toml index 45c07d030d..99b0e41c9b 100644 --- a/rust/utils/run-locally/Cargo.toml +++ b/rust/utils/run-locally/Cargo.toml @@ -28,11 +28,13 @@ ethers-contract.workspace = true tokio.workspace = true maplit.workspace = true nix = { workspace = true, features = ["signal"], default-features = false } +once_cell.workspace = true tempfile.workspace = true ureq = { workspace = true, default-features = false } which.workspace = true macro_rules_attribute.workspace = true regex.workspace = true +relayer = { path = "../../agents/relayer"} hyperlane-cosmwasm-interface.workspace = true cosmwasm-schema.workspace = true diff --git a/rust/utils/run-locally/src/config.rs b/rust/utils/run-locally/src/config.rs index 7e1358dfd3..476a10725d 100644 --- a/rust/utils/run-locally/src/config.rs +++ b/rust/utils/run-locally/src/config.rs @@ -6,6 +6,7 @@ pub struct Config { pub ci_mode: bool, pub ci_mode_timeout: u64, pub kathy_messages: u64, + pub sealevel_enabled: bool, // TODO: Include count of sealevel messages in a field separate from `kathy_messages`? } @@ -26,6 +27,9 @@ impl Config { .map(|r| r.parse::().unwrap()); r.unwrap_or(16) }, + sealevel_enabled: env::var("SEALEVEL_ENABLED") + .map(|k| k.parse::().unwrap()) + .unwrap_or(true), }) } } diff --git a/rust/utils/run-locally/src/cosmos/cli.rs b/rust/utils/run-locally/src/cosmos/cli.rs index 4258f149c2..934a3758ab 100644 --- a/rust/utils/run-locally/src/cosmos/cli.rs +++ b/rust/utils/run-locally/src/cosmos/cli.rs @@ -152,7 +152,7 @@ impl OsmosisCLI { .arg("grpc.address", &endpoint.grpc_addr) // default is 0.0.0.0:9090 .arg("rpc.pprof_laddr", pprof_addr) // default is localhost:6060 .arg("log_level", "panic") - .spawn("COSMOS"); + .spawn("COSMOS", None); endpoint.wait_for_node(); diff --git a/rust/utils/run-locally/src/cosmos/mod.rs b/rust/utils/run-locally/src/cosmos/mod.rs index 1a3f1e7cdd..48cc117e2f 100644 --- a/rust/utils/run-locally/src/cosmos/mod.rs +++ b/rust/utils/run-locally/src/cosmos/mod.rs @@ -271,7 +271,7 @@ fn launch_cosmos_validator( .hyp_env("SIGNER_SIGNER_TYPE", "hexKey") .hyp_env("SIGNER_KEY", agent_config.signer.key) .hyp_env("TRACING_LEVEL", if debug { "debug" } else { "info" }) - .spawn("VAL"); + .spawn("VAL", None); validator } @@ -299,7 +299,7 @@ fn launch_cosmos_relayer( .hyp_env("TRACING_LEVEL", if debug { "debug" } else { "info" }) .hyp_env("GASPAYMENTENFORCEMENT", "[{\"type\": \"none\"}]") .hyp_env("METRICSPORT", metrics.to_string()) - .spawn("RLY"); + .spawn("RLY", None); relayer } diff --git a/rust/utils/run-locally/src/ethereum/mod.rs b/rust/utils/run-locally/src/ethereum/mod.rs index bebe063484..acdd3057d3 100644 --- a/rust/utils/run-locally/src/ethereum/mod.rs +++ b/rust/utils/run-locally/src/ethereum/mod.rs @@ -36,7 +36,7 @@ pub fn start_anvil(config: Arc) -> AgentHandles { } log!("Launching anvil..."); let anvil_args = Program::new("anvil").flag("silent").filter_logs(|_| false); // for now do not keep any of the anvil logs - let anvil = anvil_args.spawn("ETH"); + let anvil = anvil_args.spawn("ETH", None); sleep(Duration::from_secs(10)); diff --git a/rust/utils/run-locally/src/invariants.rs b/rust/utils/run-locally/src/invariants.rs index 15b2584813..2191f2ac8f 100644 --- a/rust/utils/run-locally/src/invariants.rs +++ b/rust/utils/run-locally/src/invariants.rs @@ -1,12 +1,15 @@ +use std::fs::File; use std::path::Path; use crate::config::Config; use crate::metrics::agent_balance_sum; +use crate::utils::get_matching_lines; use maplit::hashmap; +use relayer::GAS_EXPENDITURE_LOG_MESSAGE; use crate::logging::log; use crate::solana::solana_termination_invariants_met; -use crate::{fetch_metric, ZERO_MERKLE_INSERTION_KATHY_MESSAGES}; +use crate::{fetch_metric, AGENT_LOGGING_DIR, ZERO_MERKLE_INSERTION_KATHY_MESSAGES}; // This number should be even, so the messages can be split into two equal halves // sent before and after the relayer spins up, to avoid rounding errors. @@ -17,11 +20,16 @@ pub const SOL_MESSAGES_EXPECTED: u32 = 20; pub fn termination_invariants_met( config: &Config, starting_relayer_balance: f64, - solana_cli_tools_path: &Path, - solana_config_path: &Path, + solana_cli_tools_path: Option<&Path>, + solana_config_path: Option<&Path>, ) -> eyre::Result { let eth_messages_expected = (config.kathy_messages / 2) as u32 * 2; - let total_messages_expected = eth_messages_expected + SOL_MESSAGES_EXPECTED; + let sol_messages_expected = if config.sealevel_enabled { + SOL_MESSAGES_EXPECTED + } else { + 0 + }; + let total_messages_expected = eth_messages_expected + sol_messages_expected; let lengths = fetch_metric("9092", "hyperlane_submitter_queue_length", &hashmap! {})?; assert!(!lengths.is_empty(), "Could not find queue length metric"); @@ -53,6 +61,19 @@ pub fn termination_invariants_met( .iter() .sum::(); + let log_file_path = AGENT_LOGGING_DIR.join("RLY-output.log"); + let relayer_logfile = File::open(log_file_path)?; + let gas_expenditure_log_count = + get_matching_lines(&relayer_logfile, GAS_EXPENDITURE_LOG_MESSAGE) + .unwrap() + .len(); + + // Zero insertion messages don't reach `submit` stage where gas is spent, so we only expect these logs for the other messages. + assert_eq!( + gas_expenditure_log_count as u32, total_messages_expected, + "Didn't record gas payment for all delivered messages" + ); + let gas_payment_sealevel_events_count = fetch_metric( "9092", "hyperlane_contract_sync_stored_events", @@ -74,9 +95,13 @@ pub fn termination_invariants_met( return Ok(false); } - if !solana_termination_invariants_met(solana_cli_tools_path, solana_config_path) { - log!("Solana termination invariants not met"); - return Ok(false); + if let Some((solana_cli_tools_path, solana_config_path)) = + solana_cli_tools_path.zip(solana_config_path) + { + if !solana_termination_invariants_met(solana_cli_tools_path, solana_config_path) { + log!("Solana termination invariants not met"); + return Ok(false); + } } let dispatched_messages_scraped = fetch_metric( diff --git a/rust/utils/run-locally/src/main.rs b/rust/utils/run-locally/src/main.rs index 0e88e685ec..1bf2990758 100644 --- a/rust/utils/run-locally/src/main.rs +++ b/rust/utils/run-locally/src/main.rs @@ -11,12 +11,17 @@ //! the end conditions are met, the test is a failure. Defaults to 10 min. //! - `E2E_KATHY_MESSAGES`: Number of kathy messages to dispatch. Defaults to 16 if CI mode is enabled. //! else false. +//! - `SEALEVEL_ENABLED`: true/false, enables sealevel testing. Defaults to true. use std::{ - fs, + collections::HashMap, + fs::{self, File}, path::Path, process::{Child, ExitCode}, - sync::atomic::{AtomicBool, Ordering}, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, Mutex, + }, thread::sleep, time::{Duration, Instant}, }; @@ -24,6 +29,7 @@ use std::{ use ethers_contract::MULTICALL_ADDRESS; use logging::log; pub use metrics::fetch_metric; +use once_cell::sync::Lazy; use program::Program; use tempfile::tempdir; @@ -46,6 +52,12 @@ mod program; mod solana; mod utils; +pub static AGENT_LOGGING_DIR: Lazy<&Path> = Lazy::new(|| { + let dir = Path::new("/tmp/test_logs"); + fs::create_dir_all(dir).unwrap(); + dir +}); + /// These private keys are from hardhat/anvil's testing accounts. const RELAYER_KEYS: &[&str] = &[ // test1 @@ -61,17 +73,18 @@ const RELAYER_KEYS: &[&str] = &[ ]; /// These private keys are from hardhat/anvil's testing accounts. /// These must be consistent with the ISM config for the test. -const VALIDATOR_KEYS: &[&str] = &[ +const ETH_VALIDATOR_KEYS: &[&str] = &[ // eth "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a", "0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba", "0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e", +]; + +const SEALEVEL_VALIDATOR_KEYS: &[&str] = &[ // sealevel "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", ]; -const VALIDATOR_ORIGIN_CHAINS: &[&str] = &["test1", "test2", "test3", "sealeveltest1"]; - const AGENT_BIN_PATH: &str = "target/debug"; const INFRA_PATH: &str = "../typescript/infra"; const MONOREPO_ROOT_PATH: &str = "../"; @@ -87,14 +100,15 @@ static SHUTDOWN: AtomicBool = AtomicBool::new(false); /// cleanup purposes at this time. #[derive(Default)] struct State { - agents: Vec<(String, Child)>, + #[allow(clippy::type_complexity)] + agents: HashMap>>)>, watchers: Vec>>, data: Vec>, } impl State { fn push_agent(&mut self, handles: AgentHandles) { - self.agents.push((handles.0, handles.1)); + self.agents.insert(handles.0, (handles.1, handles.5)); self.watchers.push(handles.2); self.watchers.push(handles.3); self.data.push(handles.4); @@ -105,9 +119,7 @@ impl Drop for State { fn drop(&mut self) { SHUTDOWN.store(true, Ordering::Relaxed); log!("Signaling children to stop..."); - // stop children in reverse order - self.agents.reverse(); - for (name, mut agent) in self.agents.drain(..) { + for (name, (mut agent, _)) in self.agents.drain() { log!("Stopping child {}", name); stop_child(&mut agent); } @@ -122,6 +134,7 @@ impl Drop for State { drop(data) } fs::remove_dir_all(SOLANA_CHECKPOINT_LOCATION).unwrap_or_default(); + fs::remove_dir_all::<&Path>(AGENT_LOGGING_DIR.as_ref()).unwrap_or_default(); } } @@ -133,20 +146,27 @@ fn main() -> ExitCode { }) .unwrap(); - assert_eq!(VALIDATOR_ORIGIN_CHAINS.len(), VALIDATOR_KEYS.len()); - const VALIDATOR_COUNT: usize = VALIDATOR_KEYS.len(); - let config = Config::load(); - - let solana_checkpoint_path = Path::new(SOLANA_CHECKPOINT_LOCATION); - fs::remove_dir_all(solana_checkpoint_path).unwrap_or_default(); - let checkpoints_dirs: Vec = (0..VALIDATOR_COUNT - 1) + let mut validator_origin_chains = ["test1", "test2", "test3"].to_vec(); + let mut validator_keys = ETH_VALIDATOR_KEYS.to_vec(); + let mut validator_count: usize = validator_keys.len(); + let mut checkpoints_dirs: Vec = (0..validator_count) .map(|_| Box::new(tempdir().unwrap()) as DynPath) - .chain([Box::new(solana_checkpoint_path) as DynPath]) .collect(); + if config.sealevel_enabled { + validator_origin_chains.push("sealeveltest1"); + let mut sealevel_keys = SEALEVEL_VALIDATOR_KEYS.to_vec(); + validator_keys.append(&mut sealevel_keys); + let solana_checkpoint_path = Path::new(SOLANA_CHECKPOINT_LOCATION); + fs::remove_dir_all(solana_checkpoint_path).unwrap_or_default(); + checkpoints_dirs.push(Box::new(solana_checkpoint_path) as DynPath); + validator_count += 1; + } + assert_eq!(validator_origin_chains.len(), validator_keys.len()); + let rocks_db_dir = tempdir().unwrap(); let relayer_db = concat_path(&rocks_db_dir, "relayer"); - let validator_dbs = (0..VALIDATOR_COUNT) + let validator_dbs = (0..validator_count) .map(|i| concat_path(&rocks_db_dir, format!("validator{i}"))) .collect::>(); @@ -207,11 +227,15 @@ fn main() -> ExitCode { "http://127.0.0.1:8545,http://127.0.0.1:8545,http://127.0.0.1:8545", ) // default is used for TEST3 - .arg("defaultSigner.key", RELAYER_KEYS[2]) - .arg( + .arg("defaultSigner.key", RELAYER_KEYS[2]); + let relayer_env = if config.sealevel_enabled { + relayer_env.arg( "relayChains", "test1,test2,test3,sealeveltest1,sealeveltest2", - ); + ) + } else { + relayer_env.arg("relayChains", "test1,test2,test3") + }; let base_validator_env = common_agent_env .clone() @@ -233,14 +257,14 @@ fn main() -> ExitCode { .hyp_env("INTERVAL", "5") .hyp_env("CHECKPOINTSYNCER_TYPE", "localStorage"); - let validator_envs = (0..VALIDATOR_COUNT) + let validator_envs = (0..validator_count) .map(|i| { base_validator_env .clone() .hyp_env("METRICSPORT", (9094 + i).to_string()) .hyp_env("DB", validator_dbs[i].to_str().unwrap()) - .hyp_env("ORIGINCHAINNAME", VALIDATOR_ORIGIN_CHAINS[i]) - .hyp_env("VALIDATOR_KEY", VALIDATOR_KEYS[i]) + .hyp_env("ORIGINCHAINNAME", validator_origin_chains[i]) + .hyp_env("VALIDATOR_KEY", validator_keys[i]) .hyp_env( "CHECKPOINTSYNCER_PATH", (*checkpoints_dirs[i]).as_ref().to_str().unwrap(), @@ -274,7 +298,7 @@ fn main() -> ExitCode { .join(", ") ); log!("Relayer DB in {}", relayer_db.display()); - (0..VALIDATOR_COUNT).for_each(|i| { + (0..validator_count).for_each(|i| { log!("Validator {} DB in {}", i + 1, validator_dbs[i].display()); }); @@ -282,9 +306,14 @@ fn main() -> ExitCode { // Ready to run... // - let (solana_path, solana_path_tempdir) = install_solana_cli_tools().join(); - state.data.push(Box::new(solana_path_tempdir)); - let solana_program_builder = build_solana_programs(solana_path.clone()); + let solana_paths = if config.sealevel_enabled { + let (solana_path, solana_path_tempdir) = install_solana_cli_tools().join(); + state.data.push(Box::new(solana_path_tempdir)); + let solana_program_builder = build_solana_programs(solana_path.clone()); + Some((solana_program_builder.join(), solana_path)) + } else { + None + }; // this task takes a long time in the CI so run it in parallel log!("Building rust..."); @@ -294,15 +323,18 @@ fn main() -> ExitCode { .arg("bin", "relayer") .arg("bin", "validator") .arg("bin", "scraper") - .arg("bin", "init-db") - .arg("bin", "hyperlane-sealevel-client") + .arg("bin", "init-db"); + let build_rust = if config.sealevel_enabled { + build_rust.arg("bin", "hyperlane-sealevel-client") + } else { + build_rust + }; + let build_rust = build_rust .filter_logs(|l| !l.contains("workspace-inheritance")) .run(); let start_anvil = start_anvil(config.clone()); - let solana_program_path = solana_program_builder.join(); - log!("Running postgres db..."); let postgres = Program::new("docker") .cmd("run") @@ -311,24 +343,31 @@ fn main() -> ExitCode { .arg("env", "POSTGRES_PASSWORD=47221c18c610") .arg("publish", "5432:5432") .cmd("postgres:14") - .spawn("SQL"); + .spawn("SQL", None); state.push_agent(postgres); build_rust.join(); let solana_ledger_dir = tempdir().unwrap(); - let start_solana_validator = start_solana_test_validator( - solana_path.clone(), - solana_program_path, - solana_ledger_dir.as_ref().to_path_buf(), - ); + let solana_config_path = if let Some((solana_program_path, solana_path)) = solana_paths.clone() + { + let start_solana_validator = start_solana_test_validator( + solana_path.clone(), + solana_program_path, + solana_ledger_dir.as_ref().to_path_buf(), + ); + + let (solana_config_path, solana_validator) = start_solana_validator.join(); + state.push_agent(solana_validator); + Some(solana_config_path) + } else { + None + }; - let (solana_config_path, solana_validator) = start_solana_validator.join(); - state.push_agent(solana_validator); state.push_agent(start_anvil.join()); // spawn 1st validator before any messages have been sent to test empty mailbox - state.push_agent(validator_envs.first().unwrap().clone().spawn("VL1")); + state.push_agent(validator_envs.first().unwrap().clone().spawn("VL1", None)); sleep(Duration::from_secs(5)); @@ -336,7 +375,7 @@ fn main() -> ExitCode { Program::new(concat_path(AGENT_BIN_PATH, "init-db")) .run() .join(); - state.push_agent(scraper_env.spawn("SCR")); + state.push_agent(scraper_env.spawn("SCR", None)); // Send half the kathy messages before starting the rest of the agents let kathy_env_single_insertion = Program::new("yarn") @@ -369,22 +408,35 @@ fn main() -> ExitCode { .arg("required-hook", "merkleTreeHook"); kathy_env_double_insertion.clone().run().join(); - // Send some sealevel messages before spinning up the agents, to test the backward indexing cursor - for _i in 0..(SOL_MESSAGES_EXPECTED / 2) { - initiate_solana_hyperlane_transfer(solana_path.clone(), solana_config_path.clone()).join(); + if let Some((solana_config_path, (_, solana_path))) = + solana_config_path.clone().zip(solana_paths.clone()) + { + // Send some sealevel messages before spinning up the agents, to test the backward indexing cursor + for _i in 0..(SOL_MESSAGES_EXPECTED / 2) { + initiate_solana_hyperlane_transfer(solana_path.clone(), solana_config_path.clone()) + .join(); + } } // spawn the rest of the validators for (i, validator_env) in validator_envs.into_iter().enumerate().skip(1) { - let validator = validator_env.spawn(make_static(format!("VL{}", 1 + i))); + let validator = validator_env.spawn( + make_static(format!("VL{}", 1 + i)), + Some(AGENT_LOGGING_DIR.as_ref()), + ); state.push_agent(validator); } - state.push_agent(relayer_env.spawn("RLY")); + state.push_agent(relayer_env.spawn("RLY", Some(&AGENT_LOGGING_DIR))); - // Send some sealevel messages after spinning up the relayer, to test the forward indexing cursor - for _i in 0..(SOL_MESSAGES_EXPECTED / 2) { - initiate_solana_hyperlane_transfer(solana_path.clone(), solana_config_path.clone()).join(); + if let Some((solana_config_path, (_, solana_path))) = + solana_config_path.clone().zip(solana_paths.clone()) + { + // Send some sealevel messages before spinning up the agents, to test the backward indexing cursor + for _i in 0..(SOL_MESSAGES_EXPECTED / 2) { + initiate_solana_hyperlane_transfer(solana_path.clone(), solana_config_path.clone()) + .join(); + } } log!("Setup complete! Agents running in background..."); @@ -393,7 +445,11 @@ fn main() -> ExitCode { // Send half the kathy messages after the relayer comes up kathy_env_double_insertion.clone().run().join(); kathy_env_zero_insertion.clone().run().join(); - state.push_agent(kathy_env_single_insertion.flag("mineforever").spawn("KTY")); + state.push_agent( + kathy_env_single_insertion + .flag("mineforever") + .spawn("KTY", None), + ); let loop_start = Instant::now(); // give things a chance to fully start. @@ -406,8 +462,11 @@ fn main() -> ExitCode { if termination_invariants_met( &config, starting_relayer_balance, - &solana_path, - &solana_config_path, + solana_paths + .clone() + .map(|(_, solana_path)| solana_path) + .as_deref(), + solana_config_path.as_deref(), ) .unwrap_or(false) { @@ -422,7 +481,7 @@ fn main() -> ExitCode { } // verify long-running tasks are still running - for (name, child) in state.agents.iter_mut() { + for (name, (child, _)) in state.agents.iter_mut() { if let Some(status) = child.try_wait().unwrap() { if !status.success() { log!( diff --git a/rust/utils/run-locally/src/program.rs b/rust/utils/run-locally/src/program.rs index 5c2768ae1c..3775ef8e99 100644 --- a/rust/utils/run-locally/src/program.rs +++ b/rust/utils/run-locally/src/program.rs @@ -2,14 +2,14 @@ use std::{ collections::BTreeMap, ffi::OsStr, fmt::{Debug, Display, Formatter}, - io::{BufRead, BufReader, Read}, + fs::{File, OpenOptions}, + io::{BufRead, BufReader, Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, sync::{ atomic::{AtomicBool, Ordering}, - mpsc, - mpsc::Sender, - Arc, + mpsc::{self, Sender}, + Arc, Mutex, }, thread::{sleep, spawn}, time::Duration, @@ -240,8 +240,18 @@ impl Program { }) } - pub fn spawn(self, log_prefix: &'static str) -> AgentHandles { + pub fn spawn(self, log_prefix: &'static str, logs_dir: Option<&Path>) -> AgentHandles { let mut command = self.create_command(); + let log_file = logs_dir.map(|logs_dir| { + let log_file_name = format!("{}-output.log", log_prefix); + let log_file_path = logs_dir.join(log_file_name); + let log_file = OpenOptions::new() + .append(true) + .create(true) + .open(log_file_path) + .expect("Failed to create a log file"); + Arc::new(Mutex::new(log_file)) + }); command.stdout(Stdio::piped()).stderr(Stdio::piped()); log!("Spawning {}...", &self); @@ -250,17 +260,35 @@ impl Program { .unwrap_or_else(|e| panic!("Failed to start {:?} with error: {e}", &self)); let child_stdout = child.stdout.take().unwrap(); let filter = self.get_filter(); - let stdout = - spawn(move || prefix_log(child_stdout, log_prefix, &RUN_LOG_WATCHERS, filter, None)); + let cloned_log_file = log_file.clone(); + let stdout = spawn(move || { + prefix_log( + child_stdout, + log_prefix, + &RUN_LOG_WATCHERS, + filter, + cloned_log_file, + None, + ) + }); let child_stderr = child.stderr.take().unwrap(); - let stderr = - spawn(move || prefix_log(child_stderr, log_prefix, &RUN_LOG_WATCHERS, filter, None)); + let stderr = spawn(move || { + prefix_log( + child_stderr, + log_prefix, + &RUN_LOG_WATCHERS, + filter, + None, + None, + ) + }); ( log_prefix.to_owned(), child, Box::new(SimpleTaskHandle(stdout)), Box::new(SimpleTaskHandle(stderr)), self.get_memory(), + log_file.clone(), ) } @@ -281,13 +309,13 @@ impl Program { let stdout = child.stdout.take().unwrap(); let name = self.get_bin_name(); let running = running.clone(); - spawn(move || prefix_log(stdout, &name, &running, filter, stdout_ch_tx)) + spawn(move || prefix_log(stdout, &name, &running, filter, None, stdout_ch_tx)) }; let stderr = { let stderr = child.stderr.take().unwrap(); let name = self.get_bin_name(); let running = running.clone(); - spawn(move || prefix_log(stderr, &name, &running, filter, None)) + spawn(move || prefix_log(stderr, &name, &running, filter, None, None)) }; let status = loop { @@ -321,6 +349,7 @@ fn prefix_log( prefix: &str, run_log_watcher: &AtomicBool, filter: Option, + file: Option>>, channel: Option>, ) { let mut reader = BufReader::new(output).lines(); @@ -340,6 +369,10 @@ fn prefix_log( } } println!("<{prefix}> {line}"); + if let Some(file) = &file { + let mut writer = file.lock().expect("Failed to acquire lock for log file"); + writeln!(writer, "{}", line).unwrap_or(()); + } if let Some(channel) = &channel { // ignore send errors channel.send(line).unwrap_or(()); diff --git a/rust/utils/run-locally/src/solana.rs b/rust/utils/run-locally/src/solana.rs index bf5b7d4176..9b0fe41e4a 100644 --- a/rust/utils/run-locally/src/solana.rs +++ b/rust/utils/run-locally/src/solana.rs @@ -202,7 +202,7 @@ pub fn start_solana_test_validator( concat_path(&solana_programs_path, lib).to_str().unwrap(), ); } - let validator = args.spawn("SOL"); + let validator = args.spawn("SOL", None); sleep(Duration::from_secs(5)); log!("Deploying the hyperlane programs to solana"); diff --git a/rust/utils/run-locally/src/utils.rs b/rust/utils/run-locally/src/utils.rs index 206b4bc699..5319701742 100644 --- a/rust/utils/run-locally/src/utils.rs +++ b/rust/utils/run-locally/src/utils.rs @@ -1,5 +1,8 @@ +use std::fs::File; +use std::io::{self, BufRead}; use std::path::{Path, PathBuf}; use std::process::Child; +use std::sync::{Arc, Mutex}; use std::thread::JoinHandle; use nix::libc::pid_t; @@ -54,6 +57,8 @@ pub type AgentHandles = ( Box>, // data to drop once program exits Box, + // file with stdout logs + Option>>, ); pub type LogFilter = fn(&str) -> bool; @@ -112,3 +117,16 @@ pub fn stop_child(child: &mut Child) { } }; } + +pub fn get_matching_lines(file: &File, search_string: &str) -> io::Result> { + let reader = io::BufReader::new(file); + + // Read lines and collect those that contain the search string + let matching_lines: Vec = reader + .lines() + .map_while(Result::ok) + .filter(|line| line.contains(search_string)) + .collect(); + + Ok(matching_lines) +} From 939fa814d9c64c337d7fa903dd2e9b095484be1a Mon Sep 17 00:00:00 2001 From: Connor McEwen Date: Fri, 7 Jun 2024 15:15:07 -0400 Subject: [PATCH 40/59] chore: add release steps to readme (#3928) ### Description Add some release/prerelease directions to readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 04551feee7..7ff7af9321 100644 --- a/README.md +++ b/README.md @@ -103,3 +103,9 @@ See [`rust/README.md`](rust/README.md) - Create a summary of change highlights - Create a "breaking changes" section with any changes required - Deploy agents with the new image tag (if it makes sense to) + +### Releasing packages to NPM + +We use [changesets](https://github.com/changesets/changesets) to release to NPM. You can use the `release` script in `package.json` to publish. + +For an alpha or beta version, follow the directions [here](https://github.com/changesets/changesets/blob/main/docs/prereleases.md). From 65e4b05f77c75a99f756aeff8eef0e5d666f92a1 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Mon, 10 Jun 2024 13:35:30 +0100 Subject: [PATCH 41/59] chore: fix mainnet3 agent config (#3931) chore: fix mainnet3 agent config follow-on from https://github.com/hyperlane-xyz/hyperlane-registry/pull/49 Signed-off-by: Paul Balaji --- rust/config/mainnet_config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/config/mainnet_config.json b/rust/config/mainnet_config.json index 00ebf00f00..c4c494f463 100644 --- a/rust/config/mainnet_config.json +++ b/rust/config/mainnet_config.json @@ -1041,8 +1041,8 @@ "name": "polygon", "nativeToken": { "decimals": 18, - "name": "Ether", - "symbol": "ETH" + "name": "Matic", + "symbol": "MATIC" }, "pausableHook": "0x748040afB89B8FdBb992799808215419d36A0930", "pausableIsm": "0x6741e91fFDC31c7786E3684427c628dad06299B0", From 47831d5211466688f287e0ba7d97a1f81516e862 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Mon, 10 Jun 2024 15:02:20 +0100 Subject: [PATCH 42/59] ci: ignore `*.md` changes when triggering rust/test workflows (#3930) - skips unnecessary tests for PRs on just markdown changes e.g. README Signed-off-by: Paul Balaji --- .github/workflows/rust-skipped.yml | 1 + .github/workflows/rust.yml | 1 + .github/workflows/test.yml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/.github/workflows/rust-skipped.yml b/.github/workflows/rust-skipped.yml index 2d9fe8a5d0..fd35820b5a 100644 --- a/.github/workflows/rust-skipped.yml +++ b/.github/workflows/rust-skipped.yml @@ -9,6 +9,7 @@ on: paths-ignore: - 'rust/**' - .github/workflows/rust.yml + - '*.md' # Support for merge queues merge_group: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a6e270a489..e91bed49ed 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -6,6 +6,7 @@ on: paths: - 'rust/**' - .github/workflows/rust.yml + - '!*.md' # Support for merge queues merge_group: # Allows you to run this workflow manually from the Actions tab diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 960b5b770b..664383cc22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,8 @@ on: pull_request: branches: - '*' # run against all branches + paths-ignore: + - '*.md' # Support for merge queues merge_group: # Allows you to run this workflow manually from the Actions tab From 41e739b5133a30acc41f36bdea446253a9563a2d Mon Sep 17 00:00:00 2001 From: vamsisai <39260099+vamsi4845@users.noreply.github.com> Date: Mon, 10 Jun 2024 20:14:08 +0530 Subject: [PATCH 43/59] chore: fix typos in code_of_conduct.md (#3890) This PR addresses minor typographical errors and readability improvements in the Code of Conduct file. Specifically: - Corrected "critism" to "criticism". - Corrected "tenant" to "tenet". - Corrected "discrimnate" to "discriminate. These changes are intended to enhance the professionalism and clarity of the document, ensuring it accurately reflects the project's standards. --- CODE_OF_CONDUCT.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index a88580d401..b7e03e6758 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -9,10 +9,10 @@ This CoC applies to all members of the Hyperlane Network's community including, **Code** 1. Never harass or bully anyone. Not verbally, not physically, not sexually. Harassment will not be tolerated. -2. Never discrimnate on the basis of personal characteristics or group membership. +2. Never discriminate on the basis of personal characteristics or group membership. 3. Treat your fellow contributors with respect, fairness, and professionalism, especially in situations of high pressure. -4. Seek, offer, and accept objective critism of yours and others work, strive to acknowledge the contributions of others. -5. Be transparent and honest about your qualifications and any potential conflicts of interest. Transparency is a key tenant of the Hyperlane project and we expect it from all contributors. +4. Seek, offer, and accept objective criticism of yours and others work, strive to acknowledge the contributions of others. +5. Be transparent and honest about your qualifications and any potential conflicts of interest. Transparency is a key tenet of the Hyperlane project and we expect it from all contributors. 6. Bring an open and curious mind, the Hyperlane project is designed to enable developers to express their curiosity, experiment, and build things we couldn't have imagined ourselves. 7. Stay on track - Do your best to avoid off-topic discussion and make sure you are posting to the correct channel and repositories. Distractions are costly and it is far too easy for work to go off track. 8. Step down properly - Think of your fellow contributors when you step down from the project. Contributors of open-source projects come and go. It is crucial that when you leave the project or reduce your contribution significantly you do so in a way that minimizes disruption and keeps continuity in mind. Concretely this means telling your fellow contributors you are leaving and taking the proper steps to enable a smooth transition for other contributors to pick up where you left off. From 42a6dbc3e6e504a67cdd8607c8c2f9a86d3b035d Mon Sep 17 00:00:00 2001 From: overallteach <167623066+overallteach@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:03:41 +0800 Subject: [PATCH 44/59] chore: remove repetitive words (#3644) ### Description remove repetitive words ### Drive-by changes ### Related issues ### Backward compatibility ### Testing Signed-off-by: overallteach --- rust/utils/backtrace-oneline/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/utils/backtrace-oneline/src/lib.rs b/rust/utils/backtrace-oneline/src/lib.rs index 0c69ee374b..61261f11d4 100644 --- a/rust/utils/backtrace-oneline/src/lib.rs +++ b/rust/utils/backtrace-oneline/src/lib.rs @@ -118,7 +118,7 @@ impl BacktraceFrameFmt<'_, '_, '_> { symbol.name(), // TODO: this isn't great that we don't end up printing anything // with non-utf8 filenames. Thankfully almost everything is utf8 so - // this shouldn't be too too bad. + // this shouldn't be too bad. symbol .filename() .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))), From cb7ae33cded2e41f0263a3d3f422c4ba5d55e942 Mon Sep 17 00:00:00 2001 From: Sergey Kaunov Date: Tue, 11 Jun 2024 05:07:50 +0300 Subject: [PATCH 45/59] chore: Update Solidity README.md (#3545) Pls, review I got that right. ### Description Corrects a **semantic** typo. ### Drive-by changes relative paths to the libs are corrected ### Related issues none ### Backward compatibility Yes ### Testing None --- solidity/contracts/token/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solidity/contracts/token/README.md b/solidity/contracts/token/README.md index 3d8c900820..99edbd11d3 100644 --- a/solidity/contracts/token/README.md +++ b/solidity/contracts/token/README.md @@ -6,7 +6,7 @@ For instructions on deploying Warp Routes, see [the deployment documentation](ht ## Warp Route Architecture -A Warp Route is a collection of [`TokenRouter`](./contracts/libs/TokenRouter.sol) contracts deployed across a set of Hyperlane chains. These contracts leverage the `Router` pattern to implement access control and routing logic for remote token transfers. These contracts send and receive [`Messages`](./contracts/libs/Message.sol) which encode payloads containing a transfer `amount` and `recipient` address. +A Warp Route is a collection of [`TokenRouter`](./libs/TokenRouter.sol) contracts deployed across a set of Hyperlane chains. These contracts leverage the `Router` pattern to implement access control and routing logic for remote token transfers. These contracts send and receive [`Messages`](./libs/TokenMessage.sol) which encode payloads containing a transfer `amount` and `recipient` address. ```mermaid %%{ init: { @@ -39,7 +39,7 @@ graph LR Mailbox_G[(Mailbox)] end - HYP_E -. "router" .- HYP_P -. "router" .- HYP_G + HYP_E -. "TokenMessage" .- HYP_P -. "TokenMessage" .- HYP_G ``` From b0d26b66e49c1d7f4702d4098f9476e267df3f91 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Tue, 11 Jun 2024 10:20:25 +0100 Subject: [PATCH 46/59] fix: test filtering for `*.md` changes (#3936) fix: test filtering for `*.md` changes - fix the rust-skipped flow - add a test-skipped flow to still go green on the required status checks --------- Signed-off-by: Paul Balaji --- .github/workflows/rust-skipped.yml | 3 - .github/workflows/test-skipped.yml | 109 +++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/test-skipped.yml diff --git a/.github/workflows/rust-skipped.yml b/.github/workflows/rust-skipped.yml index fd35820b5a..a854837a08 100644 --- a/.github/workflows/rust-skipped.yml +++ b/.github/workflows/rust-skipped.yml @@ -9,7 +9,6 @@ on: paths-ignore: - 'rust/**' - .github/workflows/rust.yml - - '*.md' # Support for merge queues merge_group: @@ -19,12 +18,10 @@ env: jobs: test-rs: runs-on: ubuntu-latest - steps: - run: 'echo "No test required" ' lint-rs: runs-on: ubuntu-latest - steps: - run: 'echo "No lint required" ' diff --git a/.github/workflows/test-skipped.yml b/.github/workflows/test-skipped.yml new file mode 100644 index 0000000000..3cff777846 --- /dev/null +++ b/.github/workflows/test-skipped.yml @@ -0,0 +1,109 @@ +name: test + +on: + push: + branches: [main] + pull_request: + branches: + - '*' + paths: + - '*.md' + - '!**/*' + merge_group: + +concurrency: + group: e2e-${{ github.ref }} + cancel-in-progress: ${{ github.ref_name != 'main' }} + +jobs: + yarn-install: + runs-on: ubuntu-latest + steps: + - name: Instant pass + run: echo "yarn-install job passed" + + yarn-build: + runs-on: ubuntu-latest + steps: + - name: Instant pass + run: echo "yarn-build job passed" + + checkout-registry: + runs-on: ubuntu-latest + steps: + - name: Instant pass + run: echo "checkout-registry job passed" + + lint-prettier: + runs-on: ubuntu-latest + steps: + - name: Instant pass + run: echo "lint-prettier job passed" + + yarn-test: + runs-on: ubuntu-latest + steps: + - name: Instant pass + run: echo "yarn-test job passed" + + agent-configs: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + environment: [mainnet3, testnet4] + steps: + - name: Instant pass + run: echo "agent-configs job passed" + + e2e-matrix: + runs-on: ubuntu-latest + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' + strategy: + matrix: + e2e-type: [cosmwasm, non-cosmwasm] + steps: + - name: Instant pass + run: echo "e2e-matrix job passed" + + e2e: + runs-on: ubuntu-latest + if: always() + steps: + - name: Instant pass + run: echo "e2e job passed" + + cli-e2e: + runs-on: ubuntu-latest + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.base_ref == 'main') || github.event_name == 'merge_group' + strategy: + matrix: + include: + - test-type: preset_hook_enabled + - test-type: configure_hook_enabled + - test-type: pi_with_core_chain + steps: + - name: Instant pass + run: echo "cli-e2e job passed" + + env-test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + environment: [mainnet3] + chain: [ethereum, arbitrum, optimism, inevm, viction] + module: [core, igp] + include: + - environment: testnet4 + chain: sepolia + module: core + steps: + - name: Instant pass + run: echo "env-test job passed" + + coverage: + runs-on: ubuntu-latest + steps: + - name: Instant pass + run: echo "coverage job passed" From 9f19cdf469237773ccd42bcbd6bd3c58c56e644f Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Tue, 11 Jun 2024 13:26:29 +0100 Subject: [PATCH 47/59] ci: update action versions (#3927) - update to latest versions of actions - notably docker tasks get a couple of major bumps - rename the foundry action to its current name --------- Signed-off-by: Paul Balaji --- .github/workflows/agent-release-artifacts.yml | 4 +- .github/workflows/monorepo-docker.yml | 10 +-- .github/workflows/release.yml | 4 +- .github/workflows/rust-docker.yml | 10 +-- .github/workflows/rust.yml | 4 +- .github/workflows/static-analysis.yml | 10 +-- .github/workflows/storage-analysis.yml | 8 +- .github/workflows/test.yml | 82 +++++++++---------- 8 files changed, 66 insertions(+), 66 deletions(-) diff --git a/.github/workflows/agent-release-artifacts.yml b/.github/workflows/agent-release-artifacts.yml index 2827120989..f548228175 100644 --- a/.github/workflows/agent-release-artifacts.yml +++ b/.github/workflows/agent-release-artifacts.yml @@ -43,7 +43,7 @@ jobs: runs-on: ${{ matrix.OS }} steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: ubuntu setup if: ${{ matrix.OS == 'larger-runner' }} run: | @@ -74,7 +74,7 @@ jobs: run: chmod ug+x,-w relayer scraper validator working-directory: rust/target/${{ matrix.TARGET }}/release - name: upload binaries - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.TARGET }}-${{ needs.prepare.outputs.tag_sha }}-${{ needs.prepare.outputs.tag_date }} path: | diff --git a/.github/workflows/monorepo-docker.yml b/.github/workflows/monorepo-docker.yml index b9d90db2b2..629e08acbb 100644 --- a/.github/workflows/monorepo-docker.yml +++ b/.github/workflows/monorepo-docker.yml @@ -36,7 +36,7 @@ jobs: if: needs.check-env.outputs.gcloud-service-key == 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive @@ -48,7 +48,7 @@ jobs: echo "TAG_SHA=$(echo '${{ github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT - name: Docker meta id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: # list of Docker images to use as base name for tags images: | @@ -59,15 +59,15 @@ jobs: type=ref,event=pr type=raw,value=${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to GCR - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: gcr.io username: _json_key password: ${{ secrets.GCLOUD_SERVICE_KEY }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: ./ file: ./Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6fc7eba09a..dfedb241a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,14 +19,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # check out full history fetch-depth: 0 submodules: recursive - name: Setup Node.js 18.x - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: 18.x diff --git a/.github/workflows/rust-docker.yml b/.github/workflows/rust-docker.yml index 1463d11a13..0da80ff794 100644 --- a/.github/workflows/rust-docker.yml +++ b/.github/workflows/rust-docker.yml @@ -33,7 +33,7 @@ jobs: if: needs.check-env.outputs.gcloud-service-key == 'true' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Generate tag data @@ -43,7 +43,7 @@ jobs: echo "TAG_SHA=$(echo '${{ github.sha }}' | cut -b 1-7)" >> $GITHUB_OUTPUT - name: Docker meta id: meta - uses: docker/metadata-action@v3 + uses: docker/metadata-action@v5 with: # list of Docker images to use as base name for tags images: | @@ -54,15 +54,15 @@ jobs: type=ref,event=pr type=raw,value=${{ steps.taggen.outputs.TAG_SHA }}-${{ steps.taggen.outputs.TAG_DATE }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to GCR - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: gcr.io username: _json_key password: ${{ secrets.GCLOUD_SERVICE_KEY }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: ./rust file: ./rust/Dockerfile diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e91bed49ed..4f52f82bb9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -29,7 +29,7 @@ jobs: runs-on: larger-runner steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - uses: actions-rs/toolchain@v1 @@ -57,7 +57,7 @@ jobs: runs-on: larger-runner steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - uses: actions-rs/toolchain@v1 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 881d8f9bda..a3eba2c886 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -18,13 +18,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -35,13 +35,13 @@ jobs: run: yarn install - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 - name: forge-build run: cd solidity && forge build --build-info - name: Static analysis - uses: crytic/slither-action@v0.3.0 + uses: crytic/slither-action@v0.4.0 id: slither with: target: 'solidity/' @@ -51,6 +51,6 @@ jobs: ignore-compile: true - name: Upload SARIF file - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: ${{ steps.slither.outputs.sarif }} diff --git a/.github/workflows/storage-analysis.yml b/.github/workflows/storage-analysis.yml index f50a17b4d2..70e77f0ddc 100644 --- a/.github/workflows/storage-analysis.yml +++ b/.github/workflows/storage-analysis.yml @@ -14,17 +14,17 @@ jobs: steps: # Checkout the PR branch - name: Checkout PR branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -35,7 +35,7 @@ jobs: run: yarn install - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 # Run the command on PR branch - name: Run command on PR branch diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 664383cc22..b83bc9c35d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,17 +30,17 @@ jobs: yarn-install: runs-on: ubuntu-latest steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -61,14 +61,14 @@ jobs: runs-on: ubuntu-latest needs: [yarn-install] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive fetch-depth: 0 - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -76,7 +76,7 @@ jobs: key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -89,7 +89,7 @@ jobs: checkout-registry: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: repository: hyperlane-xyz/hyperlane-registry ref: main @@ -105,7 +105,7 @@ jobs: - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV - name: registry-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.REGISTRY_URI_ABSOLUTE }} @@ -115,14 +115,14 @@ jobs: runs-on: ubuntu-latest needs: [yarn-install] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} # check out full history fetch-depth: 0 - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -145,17 +145,17 @@ jobs: runs-on: ubuntu-latest needs: [yarn-build, checkout-registry] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive fetch-depth: 0 - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -167,7 +167,7 @@ jobs: - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV - name: registry-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.REGISTRY_URI_ABSOLUTE }} @@ -184,13 +184,13 @@ jobs: matrix: environment: [mainnet3, testnet4] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -198,7 +198,7 @@ jobs: key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -210,7 +210,7 @@ jobs: - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV - name: registry-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.REGISTRY_URI_ABSOLUTE }} @@ -234,17 +234,17 @@ jobs: matrix: e2e-type: [cosmwasm, non-cosmwasm] steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 - name: setup rust uses: actions-rs/toolchain@v1 @@ -267,7 +267,7 @@ jobs: make-default: true - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -275,7 +275,7 @@ jobs: key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -287,14 +287,14 @@ jobs: - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV - name: registry-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.REGISTRY_URI_ABSOLUTE }} key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} - name: cargo-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -338,17 +338,17 @@ jobs: - test-type: configure_hook_enabled - test-type: pi_with_core_chain steps: - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 18 - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} submodules: recursive - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 - name: setup rust uses: actions-rs/toolchain@v1 @@ -371,7 +371,7 @@ jobs: make-default: true - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -379,7 +379,7 @@ jobs: key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -391,14 +391,14 @@ jobs: - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV - name: registry-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.REGISTRY_URI_ABSOLUTE }} key: hyperlane-registry-${{ github.event.pull_request.head.sha || github.sha }} - name: cargo-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo @@ -422,15 +422,15 @@ jobs: module: core steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -442,7 +442,7 @@ jobs: - run: echo "REGISTRY_URI_ABSOLUTE=$(realpath $REGISTRY_URI)" >> $GITHUB_ENV - name: registry-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ${{ env.REGISTRY_URI_ABSOLUTE }} @@ -456,13 +456,13 @@ jobs: needs: [yarn-test] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 - name: yarn-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | **/node_modules @@ -470,7 +470,7 @@ jobs: key: ${{ runner.os }}-yarn-cache-${{ hashFiles('./yarn.lock') }} - name: build-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./* @@ -478,7 +478,7 @@ jobs: key: ${{ github.event.pull_request.head.sha || github.sha }} - name: foundry-install - uses: onbjerg/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@v1 - name: Run tests with coverage run: yarn coverage @@ -486,6 +486,6 @@ jobs: NODE_OPTIONS: --max_old_space_size=4096 - name: Upload coverage reports to Codecov with GitHub Action - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} From 5a31e7b5d39000af78d5fe206ef1e9c3627a2652 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:51:56 +0100 Subject: [PATCH 48/59] chore: debug slow relayer startup (#3932) ### Description Opened this PR to debug slow relayer startup, but it actually looks like that's already been fixed by hook indexing. The requirement for fast startup is that the processor sees new messages and then one of the following happens: - (1) if the message is meant for an unknown domain, it drops it ([this log](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/6d132b02a3e181c929aee7d452ca765cc039ea4e/rust/agents/relayer/src/msg/processor.rs#L269)), otherwise - it forwards the message to the submitter, which then: - (2) adds it straight to the confirm queue because it's already been submitted ([here](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/6d132b02a3e181c929aee7d452ca765cc039ea4e/rust/agents/relayer/src/msg/pending_message.rs#L159)) - (3) successfully builds metadata ([here](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/6d132b02a3e181c929aee7d452ca765cc039ea4e/rust/agents/relayer/src/msg/op_submitter.rs#L231)) - (4) in some cases building metadata fails because the ism doesn't exist The relayer logs either of the four cases (in most cases (2), because we're running a second relayer for now). To be even more confident in fast startup, I ran a single relayer and restarted it. New messages were successfully processed according to one of the four cases above, and the prep queues didn't increase ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/agents/relayer/src/msg/op_submitter.rs | 1 + rust/agents/relayer/src/msg/processor.rs | 12 ++++++++++-- .../infra/config/environments/mainnet3/agent.ts | 6 +++--- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/rust/agents/relayer/src/msg/op_submitter.rs b/rust/agents/relayer/src/msg/op_submitter.rs index 20eb936163..84731aa634 100644 --- a/rust/agents/relayer/src/msg/op_submitter.rs +++ b/rust/agents/relayer/src/msg/op_submitter.rs @@ -244,6 +244,7 @@ async fn prepare_task( metrics.ops_dropped.inc(); } PendingOperationResult::Confirm => { + debug!(?op, "Pushing operation to confirm queue"); confirm_queue.push(op).await; } } diff --git a/rust/agents/relayer/src/msg/processor.rs b/rust/agents/relayer/src/msg/processor.rs index 6545d480ad..1c81c30174 100644 --- a/rust/agents/relayer/src/msg/processor.rs +++ b/rust/agents/relayer/src/msg/processor.rs @@ -138,7 +138,10 @@ impl DirectionalNonceIterator { #[instrument] fn iterate(&mut self) { match self.direction { - NonceDirection::High => self.nonce = self.nonce.map(|n| n.saturating_add(1)), + NonceDirection::High => { + self.nonce = self.nonce.map(|n| n.saturating_add(1)); + debug!(?self, "Iterating high nonce"); + } NonceDirection::Low => { if let Some(nonce) = self.nonce { // once the message with nonce zero is processed, we should stop going backwards @@ -155,6 +158,7 @@ impl DirectionalNonceIterator { if let Some(message) = self.indexed_message_with_nonce()? { Self::update_max_nonce_gauge(&message, metrics); if !self.is_message_processed()? { + debug!(?message, iterator=?self, "Found processable message"); return Ok(MessageStatus::Processable(message)); } else { return Ok(MessageStatus::Processed); @@ -235,7 +239,11 @@ impl ProcessorExt for MessageProcessor { // nonce. // Scan until we find next nonce without delivery confirmation. if let Some(msg) = self.try_get_unprocessed_message().await? { - debug!(?msg, "Processor working on message"); + debug!( + ?msg, + cursor = ?self.nonce_iterator, + "Processor working on message" + ); let destination = msg.destination; // Skip if not whitelisted. diff --git a/typescript/infra/config/environments/mainnet3/agent.ts b/typescript/infra/config/environments/mainnet3/agent.ts index 971e305305..7d24fcc203 100644 --- a/typescript/infra/config/environments/mainnet3/agent.ts +++ b/typescript/infra/config/environments/mainnet3/agent.ts @@ -209,7 +209,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd6bb976-20240520-164138', + tag: '939fa81-20240607-194607', }, gasPaymentEnforcement: gasPaymentEnforcement, metricAppContexts, @@ -226,7 +226,7 @@ const hyperlane: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'd6bb976-20240520-164138', + tag: '939fa81-20240607-194607', }, }, }; @@ -240,7 +240,7 @@ const releaseCandidate: RootAgentConfig = { rpcConsensusType: RpcConsensusType.Fallback, docker: { repo, - tag: 'c9c5d37-20240510-014327', + tag: '939fa81-20240607-194607', }, // We're temporarily (ab)using the RC relayer as a way to increase // message throughput. From 29f368513faa73f041be4b00906c086c70384fa5 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Tue, 11 Jun 2024 15:16:53 +0100 Subject: [PATCH 49/59] ci: temporarily skip a flaky test (#3940) temporarily skipping this test that occasionally flakes until we can fix it for good --------- Signed-off-by: Paul Balaji --- typescript/sdk/src/ism/metadata/builder.hardhat-test.ts | 3 ++- .../middleware/liquidity-layer/liquidity-layer.hardhat-test.ts | 1 + typescript/sdk/src/middleware/query/queries.hardhat-test.ts | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts b/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts index 844a87df5a..09f90012c1 100644 --- a/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts +++ b/typescript/sdk/src/ism/metadata/builder.hardhat-test.ts @@ -114,7 +114,8 @@ describe('BaseMetadataBuilder', () => { ); }); - describe('#build', () => { + // eslint-disable-next-line jest/no-disabled-tests + describe.skip('#build', () => { let origin: ChainName; let destination: ChainName; let context: MetadataContext; diff --git a/typescript/sdk/src/middleware/liquidity-layer/liquidity-layer.hardhat-test.ts b/typescript/sdk/src/middleware/liquidity-layer/liquidity-layer.hardhat-test.ts index 51732aaff3..cf7bdde70c 100644 --- a/typescript/sdk/src/middleware/liquidity-layer/liquidity-layer.hardhat-test.ts +++ b/typescript/sdk/src/middleware/liquidity-layer/liquidity-layer.hardhat-test.ts @@ -33,6 +33,7 @@ import { PortalAdapterConfig, } from './LiquidityLayerRouterDeployer.js'; +// eslint-disable-next-line jest/no-disabled-tests describe.skip('LiquidityLayerRouter', async () => { const localChain = TestChainName.test1; const remoteChain = TestChainName.test2; diff --git a/typescript/sdk/src/middleware/query/queries.hardhat-test.ts b/typescript/sdk/src/middleware/query/queries.hardhat-test.ts index 00ab9b855a..1a507f77c0 100644 --- a/typescript/sdk/src/middleware/query/queries.hardhat-test.ts +++ b/typescript/sdk/src/middleware/query/queries.hardhat-test.ts @@ -24,6 +24,7 @@ import { InterchainQueryChecker } from './InterchainQueryChecker.js'; import { InterchainQueryDeployer } from './InterchainQueryDeployer.js'; import { InterchainQueryFactories } from './contracts.js'; +// eslint-disable-next-line jest/no-disabled-tests describe.skip('InterchainQueryRouter', async () => { const localChain = TestChainName.test1; const remoteChain = TestChainName.test2; From f4bbfcf08a4d93d681eb99a9f32c5c9b84e5f41e Mon Sep 17 00:00:00 2001 From: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> Date: Thu, 13 Jun 2024 07:37:44 +0530 Subject: [PATCH 50/59] feat: AVS mainnet deployment (#3945) ### Description - Added all the strategies used by EigenDA on mainnet here:https://github.com/Layr-Labs/eigenlayer-contracts?tab=readme-ov-file#deployments - Deployed AVS contracts to mainnet - Added contract address to CLI (reminder: strategies are just LST tokens supported for restaking. These are not relevant for us but brings us to parity with most other AVSs). ### Drive-by changes - `avsSigningKey` renamed to `avsSigningKeyAddress` as `avsSigningKey` may imply the private key and confuse operators. ### Related issues ### Backward compatibility ### Testing --- .changeset/strong-colts-hide.md | 5 +++ solidity/script/avs/eigenlayer_addresses.json | 40 +++++++++++++++++++ typescript/cli/src/avs/config.ts | 6 +++ typescript/cli/src/avs/stakeRegistry.ts | 8 ++-- typescript/cli/src/commands/avs.ts | 13 ++++-- 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 .changeset/strong-colts-hide.md diff --git a/.changeset/strong-colts-hide.md b/.changeset/strong-colts-hide.md new file mode 100644 index 0000000000..98413f21c5 --- /dev/null +++ b/.changeset/strong-colts-hide.md @@ -0,0 +1,5 @@ +--- +'@hyperlane-xyz/cli': minor +--- + +AVS deployment on mainnet diff --git a/solidity/script/avs/eigenlayer_addresses.json b/solidity/script/avs/eigenlayer_addresses.json index 60e2fceea0..1c20dae290 100644 --- a/solidity/script/avs/eigenlayer_addresses.json +++ b/solidity/script/avs/eigenlayer_addresses.json @@ -5,14 +5,54 @@ "avsDirectory": "0x135DDa560e946695d6f155dACaFC6f1F25C1F5AF", "paymentCoordinator": "", "strategies": [ + { + "name": "swETH", + "strategy": "0x0Fe4F44beE93503346A3Ac9EE5A26b130a5796d6" + }, + { + "name": "oETH", + "strategy": "0x13760F50a9d7377e4F20CB8CF9e4c26586c658ff" + }, + { + "name": "rETH", + "strategy": "0x1BeE69b7dFFfA4E2d53C2a2Df135C388AD25dCD2" + }, + { + "name": "mETH", + "strategy": "0x298aFB19A105D59E74658C4C334Ff360BadE6dd2" + }, { "name": "cbETH", "strategy": "0x54945180dB7943c0ed0FEE7EdaB2Bd24620256bc" }, + { + "name": "osETH", + "strategy": "0x57ba429517c3473B6d34CA9aCd56c0e735b94c02" + }, + { + "name": "wBETH", + "strategy": "0x7CA911E83dabf90C90dD3De5411a10F1A6112184" + }, + { + "name": "sfrxETH", + "strategy": "0x8CA7A5d6f3acd3A7A8bC468a8CD0FB14B6BD28b6" + }, { "name": "stETH", "strategy": "0x93c4b944D05dfe6df7645A86cd2206016c51564D" }, + { + "name": "ETHx", + "strategy": "0x9d7eD45EE2E8FC5482fa2428f15C971e6369011d" + }, + { + "name": "ankrETH", + "strategy": "0xa4C637e0F704745D182e4D38cAb7E7485321d059" + }, + { + "name": "lsETH", + "strategy": "0xAe60d8180437b5C34bB956822ac2710972584473" + }, { "name": "Beacon Chain ETH", "strategy": "0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0" diff --git a/typescript/cli/src/avs/config.ts b/typescript/cli/src/avs/config.ts index 681ed9dee9..79715a6765 100644 --- a/typescript/cli/src/avs/config.ts +++ b/typescript/cli/src/avs/config.ts @@ -16,4 +16,10 @@ export const avsAddresses: ChainMap = { ecdsaStakeRegistry: '0xFfa913705484C9BAea32Ffe9945BeA099A1DFF72', hyperlaneServiceManager: '0xc76E477437065093D353b7d56c81ff54D167B0Ab', }, + ethereum: { + avsDirectory: '0x135dda560e946695d6f155dacafc6f1f25c1f5af', + proxyAdmin: '0x75EE15Ee1B4A75Fa3e2fDF5DF3253c25599cc659', + ecdsaStakeRegistry: '0x272CF0BB70D3B4f79414E0823B426d2EaFd48910', + hyperlaneServiceManager: '0xe8E59c6C8B56F2c178f63BCFC4ce5e5e2359c8fc', + }, }; diff --git a/typescript/cli/src/avs/stakeRegistry.ts b/typescript/cli/src/avs/stakeRegistry.ts index 9d23bffaa5..d1bdd0716d 100644 --- a/typescript/cli/src/avs/stakeRegistry.ts +++ b/typescript/cli/src/avs/stakeRegistry.ts @@ -24,12 +24,12 @@ export async function registerOperatorWithSignature({ context, chain, operatorKeyPath, - avsSigningKey, + avsSigningKeyAddress, }: { context: WriteCommandContext; chain: ChainName; operatorKeyPath: string; - avsSigningKey: Address; + avsSigningKeyAddress: Address; }) { const { multiProvider } = context; @@ -67,13 +67,13 @@ export async function registerOperatorWithSignature({ } log( - `Registering operator ${operatorAsSigner.address} attesting ${avsSigningKey} with signature on ${chain}...`, + `Registering operator ${operatorAsSigner.address} attesting ${avsSigningKeyAddress} with signature on ${chain}...`, ); await multiProvider.handleTx( chain, ecdsaStakeRegistry.registerOperatorWithSignature( operatorSignature, - avsSigningKey, + avsSigningKeyAddress, ), ); logBlue(`Operator ${operatorAsSigner.address} registered to Hyperlane AVS`); diff --git a/typescript/cli/src/commands/avs.ts b/typescript/cli/src/commands/avs.ts index 04a51b6b63..ce238df621 100644 --- a/typescript/cli/src/commands/avs.ts +++ b/typescript/cli/src/commands/avs.ts @@ -40,7 +40,7 @@ export const registrationOptions: { [k: string]: Options } = { description: 'Path to the operator key file', demandOption: true, }, - avsSigningKey: { + avsSigningKeyAddress: { type: 'string', description: 'Address of the AVS signing key', demandOption: true, @@ -50,17 +50,22 @@ export const registrationOptions: { [k: string]: Options } = { const registerCommand: CommandModuleWithWriteContext<{ chain: ChainName; operatorKeyPath: string; - avsSigningKey: Address; + avsSigningKeyAddress: Address; }> = { command: 'register', describe: 'Register operator with the AVS', builder: registrationOptions, - handler: async ({ context, chain, operatorKeyPath, avsSigningKey }) => { + handler: async ({ + context, + chain, + operatorKeyPath, + avsSigningKeyAddress, + }) => { await registerOperatorWithSignature({ context, chain, operatorKeyPath, - avsSigningKey, + avsSigningKeyAddress, }); process.exit(0); }, From cb9c2aa5711d5838bf853f4e08eb969536576ea4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 05:34:40 +0000 Subject: [PATCH 51/59] Version Packages (#3910) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @hyperlane-xyz/cli@3.14.0 ### Minor Changes - f4bbfcf08: AVS deployment on mainnet ### Patch Changes - @hyperlane-xyz/sdk@3.14.0 - @hyperlane-xyz/utils@3.14.0 ## @hyperlane-xyz/core@3.14.0 ### Patch Changes - a8a68f6f6: fix: make XERC20 and XERC20 Lockbox proxy-able - @hyperlane-xyz/utils@3.14.0 ## @hyperlane-xyz/helloworld@3.14.0 ### Patch Changes - Updated dependencies [a8a68f6f6] - @hyperlane-xyz/core@3.14.0 - @hyperlane-xyz/sdk@3.14.0 ## @hyperlane-xyz/sdk@3.14.0 ### Patch Changes - Updated dependencies [a8a68f6f6] - @hyperlane-xyz/core@3.14.0 - @hyperlane-xyz/utils@3.14.0 ## @hyperlane-xyz/utils@3.14.0 ## @hyperlane-xyz/infra@3.14.0 ### Patch Changes - @hyperlane-xyz/helloworld@3.14.0 - @hyperlane-xyz/sdk@3.14.0 - @hyperlane-xyz/utils@3.14.0 ## @hyperlane-xyz/ccip-server@3.14.0 Co-authored-by: github-actions[bot] --- .changeset/strong-colts-hide.md | 5 ----- .changeset/sweet-pandas-brush.md | 5 ----- solidity/CHANGELOG.md | 7 +++++++ solidity/package.json | 4 ++-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 11 +++++++++++ typescript/cli/package.json | 6 +++--- typescript/cli/src/version.ts | 2 +- typescript/helloworld/CHANGELOG.md | 8 ++++++++ typescript/helloworld/package.json | 6 +++--- typescript/infra/CHANGELOG.md | 8 ++++++++ typescript/infra/package.json | 8 ++++---- typescript/sdk/CHANGELOG.md | 8 ++++++++ typescript/sdk/package.json | 6 +++--- typescript/utils/CHANGELOG.md | 2 ++ typescript/utils/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 18 files changed, 78 insertions(+), 42 deletions(-) delete mode 100644 .changeset/strong-colts-hide.md delete mode 100644 .changeset/sweet-pandas-brush.md diff --git a/.changeset/strong-colts-hide.md b/.changeset/strong-colts-hide.md deleted file mode 100644 index 98413f21c5..0000000000 --- a/.changeset/strong-colts-hide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@hyperlane-xyz/cli': minor ---- - -AVS deployment on mainnet diff --git a/.changeset/sweet-pandas-brush.md b/.changeset/sweet-pandas-brush.md deleted file mode 100644 index 74e05a094d..0000000000 --- a/.changeset/sweet-pandas-brush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@hyperlane-xyz/core": patch ---- - -fix: make XERC20 and XERC20 Lockbox proxy-able diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index f8cee2164e..d972340c6f 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,12 @@ # @hyperlane-xyz/core +## 3.14.0 + +### Patch Changes + +- a8a68f6f6: fix: make XERC20 and XERC20 Lockbox proxy-able + - @hyperlane-xyz/utils@3.14.0 + ## 3.13.0 ### Minor Changes diff --git a/solidity/package.json b/solidity/package.json index bdbe1f6598..809fc0b32f 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.13.0", + "version": "3.14.0", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "3.13.0", + "@hyperlane-xyz/utils": "3.14.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index 40e0e5390f..abe728f76a 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 3.14.0 + ## 3.13.0 ## 3.12.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index cc07a17b33..a18b7eeeb2 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "3.13.0", + "version": "3.14.0", "description": "CCIP server", "typings": "dist/index.d.ts", "typedocMain": "src/index.ts", diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index c62b58046f..08968e90db 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @hyperlane-xyz/cli +## 3.14.0 + +### Minor Changes + +- f4bbfcf08: AVS deployment on mainnet + +### Patch Changes + +- @hyperlane-xyz/sdk@3.14.0 +- @hyperlane-xyz/utils@3.14.0 + ## 3.13.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 645d3a48b8..855091d80b 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/cli", - "version": "3.13.0", + "version": "3.14.0", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.13.0", - "@hyperlane-xyz/utils": "3.13.0", + "@hyperlane-xyz/sdk": "3.14.0", + "@hyperlane-xyz/utils": "3.14.0", "@inquirer/prompts": "^3.0.0", "asn1.js": "^5.4.1", "bignumber.js": "^9.1.1", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 9fb80ae086..69e26984e9 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.13.0'; +export const VERSION = '3.14.0'; diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index f5e8404bec..577113c0de 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/helloworld +## 3.14.0 + +### Patch Changes + +- Updated dependencies [a8a68f6f6] + - @hyperlane-xyz/core@3.14.0 + - @hyperlane-xyz/sdk@3.14.0 + ## 3.13.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 7d3f62b3d9..c7aba6272d 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "3.13.0", + "version": "3.14.0", "dependencies": { - "@hyperlane-xyz/core": "3.13.0", + "@hyperlane-xyz/core": "3.14.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.13.0", + "@hyperlane-xyz/sdk": "3.14.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index 0696269d8f..e1da9e3b31 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/infra +## 3.14.0 + +### Patch Changes + +- @hyperlane-xyz/helloworld@3.14.0 +- @hyperlane-xyz/sdk@3.14.0 +- @hyperlane-xyz/utils@3.14.0 + ## 3.13.0 ### Minor Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 67de934c27..75a58f80bc 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.13.0", + "version": "3.14.0", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "3.13.0", + "@hyperlane-xyz/helloworld": "3.14.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.13.0", - "@hyperlane-xyz/utils": "3.13.0", + "@hyperlane-xyz/sdk": "3.14.0", + "@hyperlane-xyz/utils": "3.14.0", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@solana/web3.js": "^1.78.0", "asn1.js": "5.4.1", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index b484d11bc1..245e84426f 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/sdk +## 3.14.0 + +### Patch Changes + +- Updated dependencies [a8a68f6f6] + - @hyperlane-xyz/core@3.14.0 + - @hyperlane-xyz/utils@3.14.0 + ## 3.13.0 ### Minor Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index ef58f43cda..05880d466b 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "3.13.0", + "version": "3.14.0", "dependencies": { "@aws-sdk/client-s3": "^3.74.0", "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", - "@hyperlane-xyz/core": "3.13.0", - "@hyperlane-xyz/utils": "3.13.0", + "@hyperlane-xyz/core": "3.14.0", + "@hyperlane-xyz/utils": "3.14.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@solana/spl-token": "^0.3.8", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index 0dce2d1b6b..e0ab748ce7 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/utils +## 3.14.0 + ## 3.13.0 ### Minor Changes diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 260348910d..e085760d0c 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.13.0", + "version": "3.14.0", "dependencies": { "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", diff --git a/yarn.lock b/yarn.lock index 8b293bfc70..00c79e108d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5724,8 +5724,8 @@ __metadata: "@aws-sdk/client-kms": "npm:^3.577.0" "@aws-sdk/client-s3": "npm:^3.577.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.13.0" - "@hyperlane-xyz/utils": "npm:3.13.0" + "@hyperlane-xyz/sdk": "npm:3.14.0" + "@hyperlane-xyz/utils": "npm:3.14.0" "@inquirer/prompts": "npm:^3.0.0" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -5753,12 +5753,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:3.13.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:3.14.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.13.0" + "@hyperlane-xyz/utils": "npm:3.14.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -5806,13 +5806,13 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/helloworld@npm:3.13.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:3.14.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.13.0" + "@hyperlane-xyz/core": "npm:3.14.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.13.0" + "@hyperlane-xyz/sdk": "npm:3.14.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -5858,10 +5858,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:3.13.0" + "@hyperlane-xyz/helloworld": "npm:3.14.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.13.0" - "@hyperlane-xyz/utils": "npm:3.13.0" + "@hyperlane-xyz/sdk": "npm:3.14.0" + "@hyperlane-xyz/utils": "npm:3.14.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -5921,15 +5921,15 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:3.13.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:3.14.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: "@aws-sdk/client-s3": "npm:^3.74.0" "@cosmjs/cosmwasm-stargate": "npm:^0.31.3" "@cosmjs/stargate": "npm:^0.31.3" - "@hyperlane-xyz/core": "npm:3.13.0" - "@hyperlane-xyz/utils": "npm:3.13.0" + "@hyperlane-xyz/core": "npm:3.14.0" + "@hyperlane-xyz/utils": "npm:3.14.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -5997,7 +5997,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/utils@npm:3.13.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:3.14.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: From f4ad66a1a1d6dc09cd04144362bbbb44fa0092d3 Mon Sep 17 00:00:00 2001 From: Paul Balaji Date: Thu, 13 Jun 2024 14:49:03 +0100 Subject: [PATCH 52/59] ci: fix slither (#3956) - ci: try fix slither - drive-by: fix a bunch of the warnings from running `yarn` --------- Signed-off-by: Paul Balaji --- package.json | 2 +- solidity/package.json | 3 + typescript/cli/package.json | 2 + typescript/helloworld/package.json | 1 + yarn.lock | 199 +++++++---------------------- 5 files changed, 55 insertions(+), 152 deletions(-) diff --git a/package.json b/package.json index 3da986a519..36ea71a2ac 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@trivago/prettier-plugin-sort-imports": "^4.2.1", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", - "eslint": "^9.0.0", + "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.2.0", "husky": "^8.0.0", diff --git a/solidity/package.json b/solidity/package.json index 809fc0b32f..7a7c60fb92 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -15,7 +15,9 @@ "@nomiclabs/hardhat-ethers": "^2.2.3", "@nomiclabs/hardhat-waffle": "^2.0.6", "@typechain/ethers-v5": "^11.1.2", + "@typechain/ethers-v6": "^0.5.1", "@typechain/hardhat": "^9.1.0", + "@types/node": "^18.14.5", "chai": "^4.3.6", "ethereum-waffle": "^4.0.10", "ethers": "^5.7.2", @@ -26,6 +28,7 @@ "prettier-plugin-solidity": "^1.1.3", "solhint": "^4.5.4", "solhint-plugin-prettier": "^0.0.5", + "solidity-bytes-utils": "^0.8.0", "solidity-coverage": "^0.8.3", "ts-generator": "^0.1.1", "ts-node": "^10.8.0", diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 855091d80b..0a9e7b804c 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -21,6 +21,8 @@ "zod": "^3.21.2" }, "devDependencies": { + "@ethersproject/abi": "*", + "@ethersproject/providers": "*", "@types/mocha": "^10.0.1", "@types/node": "^18.14.5", "@types/yargs": "^17.0.24", diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index c7aba6272d..96c5ef9f81 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -14,6 +14,7 @@ "@nomiclabs/hardhat-waffle": "^2.0.6", "@trivago/prettier-plugin-sort-imports": "^4.2.1", "@typechain/ethers-v5": "^11.1.2", + "@typechain/ethers-v6": "^0.5.1", "@typechain/hardhat": "^9.1.0", "@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/parser": "^7.4.0", diff --git a/yarn.lock b/yarn.lock index 00c79e108d..cef359be81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4520,23 +4520,6 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.0.2": - version: 3.0.2 - resolution: "@eslint/eslintrc@npm:3.0.2" - dependencies: - ajv: "npm:^6.12.4" - debug: "npm:^4.3.2" - espree: "npm:^10.0.1" - globals: "npm:^14.0.0" - ignore: "npm:^5.2.0" - import-fresh: "npm:^3.2.1" - js-yaml: "npm:^4.1.0" - minimatch: "npm:^3.1.2" - strip-json-comments: "npm:^3.1.1" - checksum: 04e3d7de2b16fd59ba8985ecd6922eb488e630f94e4433858567a8a6c99b478bb7b47854b166b830b44905759547d0a03654eb1265952c812d5d1d70e3e4ccf9 - languageName: node - linkType: hard - "@eslint/js@npm:8.57.0": version: 8.57.0 resolution: "@eslint/js@npm:8.57.0" @@ -4544,13 +4527,6 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.0.0": - version: 9.0.0 - resolution: "@eslint/js@npm:9.0.0" - checksum: b14b20af72410ef53e3e77e7d83cc1d6e6554b0092ceb9f969d25d765f4d775b4be32b0cd99bbfd6ce72eb2e4fb6b39b42a159b31909fbe1b3a5e88d75211687 - languageName: node - linkType: hard - "@eth-optimism/contracts-bedrock@npm:0.16.2": version: 0.16.2 resolution: "@eth-optimism/contracts-bedrock@npm:0.16.2" @@ -4862,7 +4838,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.0.9, @ethersproject/abi@npm:^5.4.0, @ethersproject/abi@npm:^5.7.0": +"@ethersproject/abi@npm:*, @ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.0.9, @ethersproject/abi@npm:^5.4.0, @ethersproject/abi@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/abi@npm:5.7.0" dependencies: @@ -5272,7 +5248,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/providers@npm:5.7.2, @ethersproject/providers@npm:^5.4.4, @ethersproject/providers@npm:^5.7.0, @ethersproject/providers@npm:^5.7.1, @ethersproject/providers@npm:^5.7.2": +"@ethersproject/providers@npm:*, @ethersproject/providers@npm:5.7.2, @ethersproject/providers@npm:^5.4.4, @ethersproject/providers@npm:^5.7.0, @ethersproject/providers@npm:^5.7.1, @ethersproject/providers@npm:^5.7.2": version: 5.7.2 resolution: "@ethersproject/providers@npm:5.7.2" dependencies: @@ -5665,17 +5641,6 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.12.3": - version: 0.12.3 - resolution: "@humanwhocodes/config-array@npm:0.12.3" - dependencies: - "@humanwhocodes/object-schema": "npm:^2.0.3" - debug: "npm:^4.3.1" - minimatch: "npm:^3.0.5" - checksum: b05f528c110aa1657d95d213e4ad2662f4161e838806af01a4d3f3b6ee3878d9b6f87d1b10704917f5c2f116757cb5c818480c32c4c4c6f84fe775a170b5f758 - languageName: node - linkType: hard - "@humanwhocodes/module-importer@npm:^1.0.1": version: 1.0.1 resolution: "@humanwhocodes/module-importer@npm:1.0.1" @@ -5690,13 +5655,6 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.3": - version: 2.0.3 - resolution: "@humanwhocodes/object-schema@npm:2.0.3" - checksum: 05bb99ed06c16408a45a833f03a732f59bf6184795d4efadd33238ff8699190a8c871ad1121241bb6501589a9598dc83bf25b99dcbcf41e155cdf36e35e937a3 - 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" @@ -5723,6 +5681,8 @@ __metadata: dependencies: "@aws-sdk/client-kms": "npm:^3.577.0" "@aws-sdk/client-s3": "npm:^3.577.0" + "@ethersproject/abi": "npm:*" + "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:1.3.0" "@hyperlane-xyz/sdk": "npm:3.14.0" "@hyperlane-xyz/utils": "npm:3.14.0" @@ -5766,7 +5726,9 @@ __metadata: "@openzeppelin/contracts": "npm:^4.9.3" "@openzeppelin/contracts-upgradeable": "npm:^v4.9.3" "@typechain/ethers-v5": "npm:^11.1.2" + "@typechain/ethers-v6": "npm:^0.5.1" "@typechain/hardhat": "npm:^9.1.0" + "@types/node": "npm:^18.14.5" chai: "npm:^4.3.6" ethereum-waffle: "npm:^4.0.10" ethers: "npm:^5.7.2" @@ -5778,6 +5740,7 @@ __metadata: prettier-plugin-solidity: "npm:^1.1.3" solhint: "npm:^4.5.4" solhint-plugin-prettier: "npm:^0.0.5" + solidity-bytes-utils: "npm:^0.8.0" solidity-coverage: "npm:^0.8.3" ts-generator: "npm:^0.1.1" ts-node: "npm:^10.8.0" @@ -5818,6 +5781,7 @@ __metadata: "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" "@trivago/prettier-plugin-sort-imports": "npm:^4.2.1" "@typechain/ethers-v5": "npm:^11.1.2" + "@typechain/ethers-v6": "npm:^0.5.1" "@typechain/hardhat": "npm:^9.1.0" "@typescript-eslint/eslint-plugin": "npm:^7.4.0" "@typescript-eslint/parser": "npm:^7.4.0" @@ -5901,7 +5865,7 @@ __metadata: "@trivago/prettier-plugin-sort-imports": "npm:^4.2.1" "@typescript-eslint/eslint-plugin": "npm:^7.4.0" "@typescript-eslint/parser": "npm:^7.4.0" - eslint: "npm:^9.0.0" + eslint: "npm:^8.57.0" eslint-config-prettier: "npm:^9.1.0" eslint-plugin-jest: "npm:^28.2.0" husky: "npm:^8.0.0" @@ -9379,6 +9343,20 @@ __metadata: languageName: node linkType: hard +"@typechain/ethers-v6@npm:^0.5.1": + version: 0.5.1 + resolution: "@typechain/ethers-v6@npm:0.5.1" + dependencies: + lodash: "npm:^4.17.15" + ts-essentials: "npm:^7.0.1" + peerDependencies: + ethers: 6.x + typechain: ^8.3.2 + typescript: ">=4.7.0" + checksum: 51dd8be3548fe3c061d2a5372beb9214e767e2b69f10c12424b699bba7ff409a13c4bdff2e513ef49046b51153db56489752205541be8fb1775f3b9ad884b85b + languageName: node + linkType: hard + "@typechain/hardhat@npm:^9.1.0": version: 9.1.0 resolution: "@typechain/hardhat@npm:9.1.0" @@ -13727,6 +13705,13 @@ __metadata: languageName: node linkType: hard +"ds-test@github:dapphub/ds-test": + version: 1.0.0 + resolution: "ds-test@https://github.com/dapphub/ds-test.git#commit=e282159d5170298eb2455a6c05280ab5a73a4ef0" + checksum: a63cada107d8f2775934bc580f04cb6f6509f843cb41cbc3a617e77b2e628a86d7fd858f964e7e2d6f41c3797c0e16ec2d87a6cb4c6187c5b6c2bc969ccae4b3 + languageName: node + linkType: hard + "duplexer3@npm:^0.1.4": version: 0.1.4 resolution: "duplexer3@npm:0.1.4" @@ -14296,16 +14281,6 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^8.0.1": - version: 8.0.1 - resolution: "eslint-scope@npm:8.0.1" - dependencies: - esrecurse: "npm:^4.3.0" - estraverse: "npm:^5.2.0" - checksum: 458513863d3c79005b599f40250437bddba923f18549058ea45820a8d3d4bbc67fe292751d522a0cab69dd01fe211ffde5c1a5fc867e86f2d28727b1d61610da - languageName: node - linkType: hard - "eslint-visitor-keys@npm:^3.3.0": version: 3.3.0 resolution: "eslint-visitor-keys@npm:3.3.0" @@ -14327,13 +14302,6 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.0.0": - version: 4.0.0 - resolution: "eslint-visitor-keys@npm:4.0.0" - checksum: c7617166e6291a15ce2982b5c4b9cdfb6409f5c14562712d12e2584480cdf18609694b21d7dad35b02df0fa2cd037505048ded54d2f405c64f600949564eb334 - languageName: node - linkType: hard - "eslint@npm:^8.57.0": version: 8.57.0 resolution: "eslint@npm:8.57.0" @@ -14382,61 +14350,6 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^9.0.0": - version: 9.0.0 - resolution: "eslint@npm:9.0.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@eslint-community/regexpp": "npm:^4.6.1" - "@eslint/eslintrc": "npm:^3.0.2" - "@eslint/js": "npm:9.0.0" - "@humanwhocodes/config-array": "npm:^0.12.3" - "@humanwhocodes/module-importer": "npm:^1.0.1" - "@nodelib/fs.walk": "npm:^1.2.8" - ajv: "npm:^6.12.4" - chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.2" - debug: "npm:^4.3.2" - escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.0.1" - eslint-visitor-keys: "npm:^4.0.0" - espree: "npm:^10.0.1" - esquery: "npm:^1.4.2" - esutils: "npm:^2.0.2" - fast-deep-equal: "npm:^3.1.3" - file-entry-cache: "npm:^8.0.0" - find-up: "npm:^5.0.0" - glob-parent: "npm:^6.0.2" - graphemer: "npm:^1.4.0" - ignore: "npm:^5.2.0" - imurmurhash: "npm:^0.1.4" - is-glob: "npm:^4.0.0" - is-path-inside: "npm:^3.0.3" - json-stable-stringify-without-jsonify: "npm:^1.0.1" - levn: "npm:^0.4.1" - lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" - natural-compare: "npm:^1.4.0" - optionator: "npm:^0.9.3" - strip-ansi: "npm:^6.0.1" - text-table: "npm:^0.2.0" - bin: - eslint: bin/eslint.js - checksum: 5cf03e14eb114f95bc4e553c8ae2da65ec09d519779beb08e326d98518bce647ce9c8bf3467bcea4cab35a2657cc3a8e945717e784afa4b1bdb9d1ecd9173ba0 - languageName: node - linkType: hard - -"espree@npm:^10.0.1": - version: 10.0.1 - resolution: "espree@npm:10.0.1" - dependencies: - acorn: "npm:^8.11.3" - acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^4.0.0" - checksum: 557d6cfb4894b1489effcaed8702682086033f8a2449568933bc59493734733d750f2a87907ba575844d3933340aea2d84288f5e67020c6152f6fd18a86497b2 - languageName: node - linkType: hard - "espree@npm:^9.6.0, espree@npm:^9.6.1": version: 9.6.1 resolution: "espree@npm:9.6.1" @@ -15201,15 +15114,6 @@ __metadata: languageName: node linkType: hard -"file-entry-cache@npm:^8.0.0": - version: 8.0.0 - resolution: "file-entry-cache@npm:8.0.0" - dependencies: - flat-cache: "npm:^4.0.0" - checksum: afe55c4de4e0d226a23c1eae62a7219aafb390859122608a89fa4df6addf55c7fd3f1a2da6f5b41e7cdff496e4cf28bbd215d53eab5c817afa96d2b40c81bfb0 - languageName: node - linkType: hard - "file-uri-to-path@npm:1.0.0": version: 1.0.0 resolution: "file-uri-to-path@npm:1.0.0" @@ -15315,16 +15219,6 @@ __metadata: languageName: node linkType: hard -"flat-cache@npm:^4.0.0": - version: 4.0.1 - resolution: "flat-cache@npm:4.0.1" - dependencies: - flatted: "npm:^3.2.9" - keyv: "npm:^4.5.4" - checksum: 58ce851d9045fffc7871ce2bd718bc485ad7e777bf748c054904b87c351ff1080c2c11da00788d78738bfb51b71e4d5ea12d13b98eb36e3358851ffe495b62dc - languageName: node - linkType: hard - "flat@npm:^5.0.2": version: 5.0.2 resolution: "flat@npm:5.0.2" @@ -15341,13 +15235,6 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.2.9": - version: 3.3.1 - resolution: "flatted@npm:3.3.1" - checksum: 7b8376061d5be6e0d3658bbab8bde587647f68797cf6bfeae9dea0e5137d9f27547ab92aaff3512dd9d1299086a6d61be98e9d48a56d17531b634f77faadbc49 - languageName: node - linkType: hard - "fmix@npm:^0.1.0": version: 0.1.0 resolution: "fmix@npm:0.1.0" @@ -15393,6 +15280,13 @@ __metadata: languageName: node linkType: hard +"forge-std@npm:^1.1.2": + version: 1.1.2 + resolution: "forge-std@npm:1.1.2" + checksum: 78fa45e7df8076d4e8a3d8494736931082e1faa02495593b0330c09464a053d2ff1d48c2d1db004c15d763ba4547ecfb46b701f79655a46ca638033913e729a1 + languageName: node + linkType: hard + "form-data-encoder@npm:1.7.1": version: 1.7.1 resolution: "form-data-encoder@npm:1.7.1" @@ -16115,13 +16009,6 @@ __metadata: languageName: node linkType: hard -"globals@npm:^14.0.0": - version: 14.0.0 - resolution: "globals@npm:14.0.0" - checksum: 03939c8af95c6df5014b137cac83aa909090c3a3985caef06ee9a5a669790877af8698ab38007e4c0186873adc14c0b13764acc754b16a754c216cc56aa5f021 - languageName: node - linkType: hard - "globalthis@npm:^1.0.1, globalthis@npm:^1.0.3": version: 1.0.3 resolution: "globalthis@npm:1.0.3" @@ -18582,7 +18469,7 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.5.3, keyv@npm:^4.5.4": +"keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" dependencies: @@ -23309,6 +23196,16 @@ __metadata: languageName: node linkType: hard +"solidity-bytes-utils@npm:^0.8.0": + version: 0.8.2 + resolution: "solidity-bytes-utils@npm:0.8.2" + dependencies: + ds-test: "github:dapphub/ds-test" + forge-std: "npm:^1.1.2" + checksum: 72238183c3cea06867244e359d47d6355d9d8c72d50ed7a3b2e87c6ba3bf760cc7c7bfef089c04ce60f8c6c4f6f213e49a4c009f27902465e660c7b30fa5ab57 + languageName: node + linkType: hard + "solidity-comments-darwin-arm64@npm:0.0.2": version: 0.0.2 resolution: "solidity-comments-darwin-arm64@npm:0.0.2" From 12840964b98910f00c909b7e943b3904dd6476f9 Mon Sep 17 00:00:00 2001 From: Daniel Savu <23065004+daniel-savu@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:48:55 +0100 Subject: [PATCH 53/59] chore: add all new chains to `KnownHyperlaneDomain` struct (#3960) ### Description So validators can be run with `--originChainName `, like in the docs [here](https://docs.hyperlane.xyz/docs/operate/validators/run-validators#running-the-binary) ### Drive-by changes ### Related issues ### Backward compatibility ### Testing --- rust/hyperlane-core/src/chain.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/rust/hyperlane-core/src/chain.rs b/rust/hyperlane-core/src/chain.rs index fe32b86307..03f4dc44da 100644 --- a/rust/hyperlane-core/src/chain.rs +++ b/rust/hyperlane-core/src/chain.rs @@ -83,6 +83,18 @@ pub enum KnownHyperlaneDomain { Injective = 6909546, InEvm = 2525, + Ancient8 = 888888888, + + Blast = 81457, + + Mode = 34443, + + Redstone = 690, + + Viction = 88, + + Zetachain = 7000, + PlumeTestnet = 161221135, // -- Local test chains -- @@ -216,7 +228,8 @@ impl KnownHyperlaneDomain { many_to_one!(match self { Mainnet: [ Ethereum, Avalanche, Arbitrum, Polygon, Optimism, BinanceSmartChain, Celo, - Moonbeam, Gnosis, MantaPacific, Neutron, Injective, InEvm + Moonbeam, Gnosis, MantaPacific, Neutron, Injective, InEvm, Ancient8, Blast, + Mode, Redstone, Viction, Zetachain ], Testnet: [ Alfajores, MoonbaseAlpha, Sepolia, ScrollSepolia, Chiado, PlumeTestnet, Fuji, BinanceSmartChainTestnet, Holesky @@ -232,8 +245,9 @@ impl KnownHyperlaneDomain { HyperlaneDomainProtocol::Ethereum: [ Ethereum, Sepolia, Holesky, Polygon, Avalanche, Fuji, Arbitrum, Optimism, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, - Alfajores, Moonbeam, InEvm, MoonbaseAlpha, ScrollSepolia, - Chiado, MantaPacific, PlumeTestnet, Test1, Test2, Test3 + Alfajores, Moonbeam, InEvm, Ancient8, Blast, Mode, Redstone, Viction, + Zetachain, MoonbaseAlpha, ScrollSepolia, Chiado, MantaPacific, PlumeTestnet, + Test1, Test2, Test3 ], HyperlaneDomainProtocol::Fuel: [FuelTest1], HyperlaneDomainProtocol::Sealevel: [SealevelTest1, SealevelTest2], @@ -249,7 +263,8 @@ impl KnownHyperlaneDomain { HyperlaneDomainTechnicalStack::Other: [ Ethereum, Sepolia, Holesky, Polygon, Avalanche, Fuji, Optimism, BinanceSmartChain, BinanceSmartChainTestnet, Celo, Gnosis, Alfajores, Moonbeam, MoonbaseAlpha, - ScrollSepolia, Chiado, MantaPacific, Neutron, Injective, InEvm, + ScrollSepolia, Chiado, MantaPacific, Neutron, Injective, InEvm, Ancient8, Blast, Mode, Redstone, + Viction, Zetachain, Test1, Test2, Test3, FuelTest1, SealevelTest1, SealevelTest2, CosmosTest99990, CosmosTest99991 ], }) From 51bfff683e903a217efc15ae566c6e713c428b42 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 14 Jun 2024 19:30:33 +0200 Subject: [PATCH 54/59] feat(sdk): XERC20 token adapter (#3911) ### Description `HypXERC20Adapter` that allows checking for mint and burn limits put in place on XERC20 contracts. ### Drive-by changes - Couple methods added to the `XERC20` interface - Corrects `HypXERC20Lockbox` and `HypXERC20` config during CLI deployments ### Related issues - Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3851 ### Backward compatibility Yes ### Testing CLI testing. --- .changeset/selfish-days-glow.md | 8 ++ solidity/contracts/test/ERC20Test.sol | 12 +++ .../contracts/token/interfaces/IXERC20.sol | 18 ++++ typescript/cli/src/deploy/warp.ts | 33 ++++++- typescript/sdk/src/token/Token.test.ts | 16 ++++ typescript/sdk/src/token/Token.ts | 10 ++ typescript/sdk/src/token/TokenStandard.ts | 12 ++- .../sdk/src/token/adapters/EvmTokenAdapter.ts | 92 +++++++++++++++++++ .../sdk/src/token/adapters/ITokenAdapter.ts | 5 + typescript/sdk/src/warp/WarpCore.test.ts | 4 +- typescript/sdk/src/warp/WarpCore.ts | 71 +++++++++++++- 11 files changed, 272 insertions(+), 9 deletions(-) create mode 100644 .changeset/selfish-days-glow.md diff --git a/.changeset/selfish-days-glow.md b/.changeset/selfish-days-glow.md new file mode 100644 index 0000000000..32d0e52742 --- /dev/null +++ b/.changeset/selfish-days-glow.md @@ -0,0 +1,8 @@ +--- +'@hyperlane-xyz/sdk': minor +'@hyperlane-xyz/core': minor +'@hyperlane-xyz/cli': minor +--- + +Mint/burn limit checking for xERC20 bridging +Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments diff --git a/solidity/contracts/test/ERC20Test.sol b/solidity/contracts/test/ERC20Test.sol index 03b3064292..b9e43a7ac9 100644 --- a/solidity/contracts/test/ERC20Test.sol +++ b/solidity/contracts/test/ERC20Test.sol @@ -74,6 +74,18 @@ contract XERC20Test is ERC20Test, IXERC20 { function owner() external pure returns (address) { return address(0x0); } + + function burningCurrentLimitOf( + address _bridge + ) external view returns (uint256) { + return type(uint256).max; + } + + function mintingCurrentLimitOf( + address _bridge + ) external view returns (uint256) { + return type(uint256).max; + } } contract XERC20LockboxTest is IXERC20Lockbox { diff --git a/solidity/contracts/token/interfaces/IXERC20.sol b/solidity/contracts/token/interfaces/IXERC20.sol index 2c9bad49a5..61b03e46be 100644 --- a/solidity/contracts/token/interfaces/IXERC20.sol +++ b/solidity/contracts/token/interfaces/IXERC20.sol @@ -36,4 +36,22 @@ interface IXERC20 is IERC20 { ) external; function owner() external returns (address); + + /** + * @notice Returns the current limit of a bridge + * @param _bridge the bridge we are viewing the limits of + * @return _limit The limit the bridge has + */ + function burningCurrentLimitOf( + address _bridge + ) external view returns (uint256 _limit); + + /** + * @notice Returns the current limit of a bridge + * @param _bridge the bridge we are viewing the limits of + * @return _limit The limit the bridge has + */ + function mintingCurrentLimitOf( + address _bridge + ) external view returns (uint256 _limit); } diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 0968f0d994..61e34921bd 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -1,5 +1,9 @@ import { confirm } from '@inquirer/prompts'; +import { + HypXERC20Lockbox__factory, + HypXERC20__factory, +} from '@hyperlane-xyz/core'; import { HypERC20Deployer, HypERC721Deployer, @@ -161,8 +165,33 @@ async function getWarpCoreConfig( throw new Error('Missing decimals on token metadata'); } - const collateralAddressOrDenom = - config.type === TokenType.collateral ? config.token : undefined; + const collateralAddressOrDenom = await (async () => { + if (config.type === TokenType.XERC20Lockbox) { + const provider = context.multiProvider.tryGetProvider(chainName); + if (!provider) { + throw new Error(`Unable to pull provider for ${chainName}`); + } + + const xERC20 = await HypXERC20Lockbox__factory.connect( + config.token, + provider, + ).xERC20(); + const wrappedToken = await HypXERC20__factory.connect( + xERC20, + provider, + ).wrappedToken(); + return wrappedToken; + } + + if ( + config.type === TokenType.collateral || + config.type === TokenType.XERC20 + ) { + return config.token; + } + + return undefined; + })(); warpCoreConfig.tokens.push({ chainName, standard: TOKEN_TYPE_TO_STANDARD[config.type], diff --git a/typescript/sdk/src/token/Token.test.ts b/typescript/sdk/src/token/Token.test.ts index 9cd97c9195..7545389038 100644 --- a/typescript/sdk/src/token/Token.test.ts +++ b/typescript/sdk/src/token/Token.test.ts @@ -55,6 +55,22 @@ const STANDARD_TO_TOKEN: Record = { symbol: 'USDC', name: 'USDC', }, + [TokenStandard.EvmHypXERC20]: { + chainName: TestChainName.test2, + standard: TokenStandard.EvmHypXERC20, + addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147', + decimals: 6, + symbol: 'USDC', + name: 'USDC', + }, + [TokenStandard.EvmHypXERC20Lockbox]: { + chainName: TestChainName.test2, + standard: TokenStandard.EvmHypXERC20Lockbox, + addressOrDenom: '0x8358D8291e3bEDb04804975eEa0fe9fe0fAfB147', + decimals: 6, + symbol: 'USDC', + name: 'USDC', + }, // Sealevel [TokenStandard.SealevelSpl]: { diff --git a/typescript/sdk/src/token/Token.ts b/typescript/sdk/src/token/Token.ts index 7ddc870e57..1a4747b7b7 100644 --- a/typescript/sdk/src/token/Token.ts +++ b/typescript/sdk/src/token/Token.ts @@ -41,6 +41,8 @@ import { EvmHypCollateralAdapter, EvmHypNativeAdapter, EvmHypSyntheticAdapter, + EvmHypXERC20Adapter, + EvmHypXERC20LockboxAdapter, EvmNativeTokenAdapter, EvmTokenAdapter, } from './adapters/EvmTokenAdapter.js'; @@ -213,6 +215,14 @@ export class Token implements IToken { return new EvmHypSyntheticAdapter(chainName, multiProvider, { token: addressOrDenom, }); + } else if (standard === TokenStandard.EvmHypXERC20) { + return new EvmHypXERC20Adapter(chainName, multiProvider, { + token: addressOrDenom, + }); + } else if (standard === TokenStandard.EvmHypXERC20Lockbox) { + return new EvmHypXERC20LockboxAdapter(chainName, multiProvider, { + token: addressOrDenom, + }); } else if (standard === TokenStandard.SealevelHypNative) { return new SealevelHypNativeAdapter( chainName, diff --git a/typescript/sdk/src/token/TokenStandard.ts b/typescript/sdk/src/token/TokenStandard.ts index 8bdd0defc6..ba03d4d509 100644 --- a/typescript/sdk/src/token/TokenStandard.ts +++ b/typescript/sdk/src/token/TokenStandard.ts @@ -15,6 +15,8 @@ export enum TokenStandard { EvmHypNative = 'EvmHypNative', EvmHypCollateral = 'EvmHypCollateral', EvmHypSynthetic = 'EvmHypSynthetic', + EvmHypXERC20 = 'EvmHypXERC20', + EvmHypXERC20Lockbox = 'EvmHypXERC20Lockbox', // Sealevel (Solana) SealevelSpl = 'SealevelSpl', @@ -48,6 +50,8 @@ export const TOKEN_STANDARD_TO_PROTOCOL: Record = { EvmHypNative: ProtocolType.Ethereum, EvmHypCollateral: ProtocolType.Ethereum, EvmHypSynthetic: ProtocolType.Ethereum, + EvmHypXERC20: ProtocolType.Ethereum, + EvmHypXERC20Lockbox: ProtocolType.Ethereum, // Sealevel (Solana) SealevelSpl: ProtocolType.Sealevel, @@ -90,6 +94,8 @@ export const TOKEN_NFT_STANDARDS = [ export const TOKEN_COLLATERALIZED_STANDARDS = [ TokenStandard.EvmHypCollateral, TokenStandard.EvmHypNative, + TokenStandard.EvmHypXERC20, + TokenStandard.EvmHypXERC20Lockbox, TokenStandard.SealevelHypCollateral, TokenStandard.SealevelHypNative, TokenStandard.CwHypCollateral, @@ -100,6 +106,8 @@ export const TOKEN_HYP_STANDARDS = [ TokenStandard.EvmHypNative, TokenStandard.EvmHypCollateral, TokenStandard.EvmHypSynthetic, + TokenStandard.EvmHypXERC20, + TokenStandard.EvmHypXERC20Lockbox, TokenStandard.SealevelHypNative, TokenStandard.SealevelHypCollateral, TokenStandard.SealevelHypSynthetic, @@ -128,8 +136,8 @@ export const TOKEN_TYPE_TO_STANDARD: Record = { [TokenType.native]: TokenStandard.EvmHypNative, [TokenType.collateral]: TokenStandard.EvmHypCollateral, [TokenType.collateralFiat]: TokenStandard.EvmHypCollateral, - [TokenType.XERC20]: TokenStandard.EvmHypCollateral, - [TokenType.XERC20Lockbox]: TokenStandard.EvmHypCollateral, + [TokenType.XERC20]: TokenStandard.EvmHypXERC20, + [TokenType.XERC20Lockbox]: TokenStandard.EvmHypXERC20Lockbox, [TokenType.collateralVault]: TokenStandard.EvmHypCollateral, [TokenType.collateralUri]: TokenStandard.EvmHypCollateral, [TokenType.fastCollateral]: TokenStandard.EvmHypCollateral, diff --git a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts index 3fc25ade31..4d549b865e 100644 --- a/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/EvmTokenAdapter.ts @@ -7,6 +7,11 @@ import { HypERC20Collateral, HypERC20Collateral__factory, HypERC20__factory, + HypXERC20, + HypXERC20Lockbox, + HypXERC20Lockbox__factory, + HypXERC20__factory, + IXERC20__factory, } from '@hyperlane-xyz/core'; import { Address, @@ -25,6 +30,7 @@ import { TokenMetadata } from '../types.js'; import { IHypTokenAdapter, + IHypXERC20Adapter, ITokenAdapter, InterchainGasQuote, TransferParams, @@ -279,6 +285,92 @@ export class EvmHypCollateralAdapter } } +// Interacts with HypXERC20Lockbox contracts +export class EvmHypXERC20LockboxAdapter + extends EvmHypCollateralAdapter + implements IHypXERC20Adapter +{ + hypXERC20Lockbox: HypXERC20Lockbox; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address }, + ) { + super(chainName, multiProvider, addresses); + + this.hypXERC20Lockbox = HypXERC20Lockbox__factory.connect( + addresses.token, + this.getProvider(), + ); + } + + async getMintLimit() { + const xERC20 = await this.hypXERC20Lockbox.xERC20(); + + const limit = await IXERC20__factory.connect( + xERC20, + this.getProvider(), + ).mintingCurrentLimitOf(this.contract.address); + + return BigInt(limit.toString()); + } + + async getBurnLimit() { + const xERC20 = await this.hypXERC20Lockbox.xERC20(); + + const limit = await IXERC20__factory.connect( + xERC20, + this.getProvider(), + ).mintingCurrentLimitOf(this.contract.address); + + return BigInt(limit.toString()); + } +} + +// Interacts with HypXERC20 contracts +export class EvmHypXERC20Adapter + extends EvmHypCollateralAdapter + implements IHypXERC20Adapter +{ + hypXERC20: HypXERC20; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address }, + ) { + super(chainName, multiProvider, addresses); + + this.hypXERC20 = HypXERC20__factory.connect( + addresses.token, + this.getProvider(), + ); + } + + async getMintLimit() { + const xERC20 = await this.hypXERC20.wrappedToken(); + + const limit = await IXERC20__factory.connect( + xERC20, + this.getProvider(), + ).mintingCurrentLimitOf(this.contract.address); + + return BigInt(limit.toString()); + } + + async getBurnLimit() { + const xERC20 = await this.hypXERC20.wrappedToken(); + + const limit = await IXERC20__factory.connect( + xERC20, + this.getProvider(), + ).burningCurrentLimitOf(this.contract.address); + + return BigInt(limit.toString()); + } +} + // Interacts HypNative contracts export class EvmHypNativeAdapter extends EvmHypCollateralAdapter diff --git a/typescript/sdk/src/token/adapters/ITokenAdapter.ts b/typescript/sdk/src/token/adapters/ITokenAdapter.ts index 67bd1a0f4f..f0a8032d55 100644 --- a/typescript/sdk/src/token/adapters/ITokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/ITokenAdapter.ts @@ -40,3 +40,8 @@ export interface IHypTokenAdapter extends ITokenAdapter { quoteTransferRemoteGas(destination: Domain): Promise; populateTransferRemoteTx(p: TransferRemoteParams): Promise; } + +export interface IHypXERC20Adapter extends IHypTokenAdapter { + getMintLimit(): Promise; + getBurnLimit(): Promise; +} diff --git a/typescript/sdk/src/warp/WarpCore.test.ts b/typescript/sdk/src/warp/WarpCore.test.ts index e24ebd5b77..b10eb3fdc1 100644 --- a/typescript/sdk/src/warp/WarpCore.test.ts +++ b/typescript/sdk/src/warp/WarpCore.test.ts @@ -222,7 +222,7 @@ describe('WarpCore', () => { const invalidAmount = await warpCore.validateTransfer({ originTokenAmount: evmHypNative.amount(-10), - destination: test1.name, + destination: test2.name, recipient: MOCK_ADDRESS, sender: MOCK_ADDRESS, }); @@ -230,7 +230,7 @@ describe('WarpCore', () => { const insufficientBalance = await warpCore.validateTransfer({ originTokenAmount: evmHypNative.amount(BIG_TRANSFER_AMOUNT), - destination: test1.name, + destination: test2.name, recipient: MOCK_ADDRESS, sender: MOCK_ADDRESS, }); diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index 9c9da1ca60..dc9988a034 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -24,8 +24,10 @@ import { parseTokenConnectionId } from '../token/TokenConnection.js'; import { TOKEN_COLLATERALIZED_STANDARDS, TOKEN_STANDARD_TO_PROVIDER_TYPE, + TokenStandard, } from '../token/TokenStandard.js'; import { EVM_TRANSFER_REMOTE_GAS_ESTIMATE } from '../token/adapters/EvmTokenAdapter.js'; +import { IHypXERC20Adapter } from '../token/adapters/ITokenAdapter.js'; import { ChainName, ChainNameOrId } from '../types.js'; import { @@ -432,10 +434,22 @@ export class WarpCore { return true; } + let destinationBalance: bigint; + const adapter = destinationToken.getAdapter(this.multiProvider); - const destinationBalance = await adapter.getBalance( - destinationToken.addressOrDenom, - ); + if ( + destinationToken.standard === TokenStandard.EvmHypXERC20 || + destinationToken.standard === TokenStandard.EvmHypXERC20Lockbox + ) { + destinationBalance = await ( + adapter as IHypXERC20Adapter + ).getMintLimit(); + } else { + destinationBalance = await adapter.getBalance( + destinationToken.addressOrDenom, + ); + } + const destinationBalanceInOriginDecimals = convertDecimals( destinationToken.decimals, originToken.decimals, @@ -504,6 +518,17 @@ export class WarpCore { const amountError = this.validateAmount(originTokenAmount); if (amountError) return amountError; + const destinationCollateralError = await this.validateDestinationCollateral( + originTokenAmount, + destination, + ); + if (destinationCollateralError) return destinationCollateralError; + + const originCollateralError = await this.validateOriginCollateral( + originTokenAmount, + ); + if (originCollateralError) return originCollateralError; + const balancesError = await this.validateTokenBalances( originTokenAmount, destination, @@ -592,6 +617,7 @@ export class WarpCore { senderPubKey?: HexString, ): Promise | null> { const { token, amount } = originTokenAmount; + const { amount: senderBalance } = await token.getBalance( this.multiProvider, sender, @@ -637,6 +663,45 @@ export class WarpCore { return null; } + /** + * Ensure the sender has sufficient balances for transfer and interchain gas + */ + protected async validateDestinationCollateral( + originTokenAmount: TokenAmount, + destination: ChainNameOrId, + ): Promise | null> { + const valid = await this.isDestinationCollateralSufficient({ + originTokenAmount, + destination, + }); + if (!valid) return { amount: 'Insufficient collateral on destination' }; + + return null; + } + + /** + * Ensure the sender has sufficient balances for transfer and interchain gas + */ + protected async validateOriginCollateral( + originTokenAmount: TokenAmount, + ): Promise | null> { + const adapter = originTokenAmount.token.getAdapter(this.multiProvider); + + if ( + originTokenAmount.token.standard === TokenStandard.EvmHypXERC20 || + originTokenAmount.token.standard === TokenStandard.EvmHypXERC20Lockbox + ) { + const burnLimit = await ( + adapter as IHypXERC20Adapter + ).getBurnLimit(); + if (burnLimit < BigInt(originTokenAmount.amount)) { + return { amount: 'Insufficient burn limit on origin' }; + } + } + + return null; + } + /** * Search through token list to find token with matching chain and address */ From 27580329ebea8c97a9542b7523b45c73d760ee51 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Jun 2024 22:13:58 +0000 Subject: [PATCH 55/59] Version Packages (#3967) This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @hyperlane-xyz/core@3.15.0 ### Minor Changes - 51bfff683: Mint/burn limit checking for xERC20 bridging Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments ### Patch Changes - @hyperlane-xyz/utils@3.15.0 ## @hyperlane-xyz/cli@3.15.0 ### Minor Changes - 51bfff683: Mint/burn limit checking for xERC20 bridging Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments ### Patch Changes - Updated dependencies [51bfff683] - @hyperlane-xyz/sdk@3.15.0 - @hyperlane-xyz/utils@3.15.0 ## @hyperlane-xyz/sdk@3.15.0 ### Minor Changes - 51bfff683: Mint/burn limit checking for xERC20 bridging Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments ### Patch Changes - Updated dependencies [51bfff683] - @hyperlane-xyz/core@3.15.0 - @hyperlane-xyz/utils@3.15.0 ## @hyperlane-xyz/helloworld@3.15.0 ### Patch Changes - Updated dependencies [51bfff683] - @hyperlane-xyz/sdk@3.15.0 - @hyperlane-xyz/core@3.15.0 ## @hyperlane-xyz/utils@3.15.0 ## @hyperlane-xyz/infra@3.15.0 ### Patch Changes - Updated dependencies [51bfff683] - @hyperlane-xyz/sdk@3.15.0 - @hyperlane-xyz/helloworld@3.15.0 - @hyperlane-xyz/utils@3.15.0 ## @hyperlane-xyz/ccip-server@3.15.0 Co-authored-by: github-actions[bot] --- .changeset/selfish-days-glow.md | 8 -------- solidity/CHANGELOG.md | 11 +++++++++++ solidity/package.json | 4 ++-- typescript/ccip-server/CHANGELOG.md | 2 ++ typescript/ccip-server/package.json | 2 +- typescript/cli/CHANGELOG.md | 13 +++++++++++++ typescript/cli/package.json | 6 +++--- typescript/cli/src/version.ts | 2 +- typescript/helloworld/CHANGELOG.md | 8 ++++++++ typescript/helloworld/package.json | 6 +++--- typescript/infra/CHANGELOG.md | 9 +++++++++ typescript/infra/package.json | 8 ++++---- typescript/sdk/CHANGELOG.md | 13 +++++++++++++ typescript/sdk/package.json | 6 +++--- typescript/utils/CHANGELOG.md | 2 ++ typescript/utils/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 17 files changed, 90 insertions(+), 40 deletions(-) delete mode 100644 .changeset/selfish-days-glow.md diff --git a/.changeset/selfish-days-glow.md b/.changeset/selfish-days-glow.md deleted file mode 100644 index 32d0e52742..0000000000 --- a/.changeset/selfish-days-glow.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@hyperlane-xyz/sdk': minor -'@hyperlane-xyz/core': minor -'@hyperlane-xyz/cli': minor ---- - -Mint/burn limit checking for xERC20 bridging -Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments diff --git a/solidity/CHANGELOG.md b/solidity/CHANGELOG.md index d972340c6f..0763cef17c 100644 --- a/solidity/CHANGELOG.md +++ b/solidity/CHANGELOG.md @@ -1,5 +1,16 @@ # @hyperlane-xyz/core +## 3.15.0 + +### Minor Changes + +- 51bfff683: Mint/burn limit checking for xERC20 bridging + Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments + +### Patch Changes + +- @hyperlane-xyz/utils@3.15.0 + ## 3.14.0 ### Patch Changes diff --git a/solidity/package.json b/solidity/package.json index 7a7c60fb92..0d2921a584 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.14.0", + "version": "3.15.0", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "3.14.0", + "@hyperlane-xyz/utils": "3.15.0", "@layerzerolabs/lz-evm-oapp-v2": "2.0.2", "@openzeppelin/contracts": "^4.9.3", "@openzeppelin/contracts-upgradeable": "^v4.9.3", diff --git a/typescript/ccip-server/CHANGELOG.md b/typescript/ccip-server/CHANGELOG.md index abe728f76a..ee27ea4460 100644 --- a/typescript/ccip-server/CHANGELOG.md +++ b/typescript/ccip-server/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/ccip-server +## 3.15.0 + ## 3.14.0 ## 3.13.0 diff --git a/typescript/ccip-server/package.json b/typescript/ccip-server/package.json index a18b7eeeb2..c5e859ac2f 100644 --- a/typescript/ccip-server/package.json +++ b/typescript/ccip-server/package.json @@ -1,6 +1,6 @@ { "name": "@hyperlane-xyz/ccip-server", - "version": "3.14.0", + "version": "3.15.0", "description": "CCIP server", "typings": "dist/index.d.ts", "typedocMain": "src/index.ts", diff --git a/typescript/cli/CHANGELOG.md b/typescript/cli/CHANGELOG.md index 08968e90db..be0e07baa3 100644 --- a/typescript/cli/CHANGELOG.md +++ b/typescript/cli/CHANGELOG.md @@ -1,5 +1,18 @@ # @hyperlane-xyz/cli +## 3.15.0 + +### Minor Changes + +- 51bfff683: Mint/burn limit checking for xERC20 bridging + Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments + +### Patch Changes + +- Updated dependencies [51bfff683] + - @hyperlane-xyz/sdk@3.15.0 + - @hyperlane-xyz/utils@3.15.0 + ## 3.14.0 ### Minor Changes diff --git a/typescript/cli/package.json b/typescript/cli/package.json index 0a9e7b804c..aaa8fe0e8b 100644 --- a/typescript/cli/package.json +++ b/typescript/cli/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/cli", - "version": "3.14.0", + "version": "3.15.0", "description": "A command-line utility for common Hyperlane operations", "dependencies": { "@aws-sdk/client-kms": "^3.577.0", "@aws-sdk/client-s3": "^3.577.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.14.0", - "@hyperlane-xyz/utils": "3.14.0", + "@hyperlane-xyz/sdk": "3.15.0", + "@hyperlane-xyz/utils": "3.15.0", "@inquirer/prompts": "^3.0.0", "asn1.js": "^5.4.1", "bignumber.js": "^9.1.1", diff --git a/typescript/cli/src/version.ts b/typescript/cli/src/version.ts index 69e26984e9..eb5b8185d0 100644 --- a/typescript/cli/src/version.ts +++ b/typescript/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.14.0'; +export const VERSION = '3.15.0'; diff --git a/typescript/helloworld/CHANGELOG.md b/typescript/helloworld/CHANGELOG.md index 577113c0de..1ea1e9cc39 100644 --- a/typescript/helloworld/CHANGELOG.md +++ b/typescript/helloworld/CHANGELOG.md @@ -1,5 +1,13 @@ # @hyperlane-xyz/helloworld +## 3.15.0 + +### Patch Changes + +- Updated dependencies [51bfff683] + - @hyperlane-xyz/sdk@3.15.0 + - @hyperlane-xyz/core@3.15.0 + ## 3.14.0 ### Patch Changes diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 96c5ef9f81..22138b9bcb 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,11 +1,11 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "3.14.0", + "version": "3.15.0", "dependencies": { - "@hyperlane-xyz/core": "3.14.0", + "@hyperlane-xyz/core": "3.15.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.14.0", + "@hyperlane-xyz/sdk": "3.15.0", "@openzeppelin/contracts-upgradeable": "^4.9.3", "ethers": "^5.7.2" }, diff --git a/typescript/infra/CHANGELOG.md b/typescript/infra/CHANGELOG.md index e1da9e3b31..ed68044f8f 100644 --- a/typescript/infra/CHANGELOG.md +++ b/typescript/infra/CHANGELOG.md @@ -1,5 +1,14 @@ # @hyperlane-xyz/infra +## 3.15.0 + +### Patch Changes + +- Updated dependencies [51bfff683] + - @hyperlane-xyz/sdk@3.15.0 + - @hyperlane-xyz/helloworld@3.15.0 + - @hyperlane-xyz/utils@3.15.0 + ## 3.14.0 ### Patch Changes diff --git a/typescript/infra/package.json b/typescript/infra/package.json index 75a58f80bc..daede08e4b 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.14.0", + "version": "3.15.0", "dependencies": { "@arbitrum/sdk": "^3.0.0", "@aws-sdk/client-iam": "^3.74.0", @@ -13,10 +13,10 @@ "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@google-cloud/secret-manager": "^5.5.0", - "@hyperlane-xyz/helloworld": "3.14.0", + "@hyperlane-xyz/helloworld": "3.15.0", "@hyperlane-xyz/registry": "1.3.0", - "@hyperlane-xyz/sdk": "3.14.0", - "@hyperlane-xyz/utils": "3.14.0", + "@hyperlane-xyz/sdk": "3.15.0", + "@hyperlane-xyz/utils": "3.15.0", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@solana/web3.js": "^1.78.0", "asn1.js": "5.4.1", diff --git a/typescript/sdk/CHANGELOG.md b/typescript/sdk/CHANGELOG.md index 245e84426f..3810b575db 100644 --- a/typescript/sdk/CHANGELOG.md +++ b/typescript/sdk/CHANGELOG.md @@ -1,5 +1,18 @@ # @hyperlane-xyz/sdk +## 3.15.0 + +### Minor Changes + +- 51bfff683: Mint/burn limit checking for xERC20 bridging + Corrects CLI output for HypXERC20 and HypXERC20Lockbox deployments + +### Patch Changes + +- Updated dependencies [51bfff683] + - @hyperlane-xyz/core@3.15.0 + - @hyperlane-xyz/utils@3.15.0 + ## 3.14.0 ### Patch Changes diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 05880d466b..0b74f6a0c3 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,13 +1,13 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "3.14.0", + "version": "3.15.0", "dependencies": { "@aws-sdk/client-s3": "^3.74.0", "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", - "@hyperlane-xyz/core": "3.14.0", - "@hyperlane-xyz/utils": "3.14.0", + "@hyperlane-xyz/core": "3.15.0", + "@hyperlane-xyz/utils": "3.15.0", "@safe-global/api-kit": "1.3.0", "@safe-global/protocol-kit": "1.3.0", "@solana/spl-token": "^0.3.8", diff --git a/typescript/utils/CHANGELOG.md b/typescript/utils/CHANGELOG.md index e0ab748ce7..a796ccac6b 100644 --- a/typescript/utils/CHANGELOG.md +++ b/typescript/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # @hyperlane-xyz/utils +## 3.15.0 + ## 3.14.0 ## 3.13.0 diff --git a/typescript/utils/package.json b/typescript/utils/package.json index e085760d0c..c7f4b7ec08 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.14.0", + "version": "3.15.0", "dependencies": { "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", diff --git a/yarn.lock b/yarn.lock index cef359be81..577194fa9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5684,8 +5684,8 @@ __metadata: "@ethersproject/abi": "npm:*" "@ethersproject/providers": "npm:*" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.14.0" - "@hyperlane-xyz/utils": "npm:3.14.0" + "@hyperlane-xyz/sdk": "npm:3.15.0" + "@hyperlane-xyz/utils": "npm:3.15.0" "@inquirer/prompts": "npm:^3.0.0" "@types/mocha": "npm:^10.0.1" "@types/node": "npm:^18.14.5" @@ -5713,12 +5713,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/core@npm:3.14.0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@npm:3.15.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.14.0" + "@hyperlane-xyz/utils": "npm:3.15.0" "@layerzerolabs/lz-evm-oapp-v2": "npm:2.0.2" "@layerzerolabs/solidity-examples": "npm:^1.1.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" @@ -5769,13 +5769,13 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/helloworld@npm:3.14.0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@npm:3.15.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.14.0" + "@hyperlane-xyz/core": "npm:3.15.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.14.0" + "@hyperlane-xyz/sdk": "npm:3.15.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@openzeppelin/contracts-upgradeable": "npm:^4.9.3" @@ -5822,10 +5822,10 @@ __metadata: "@ethersproject/hardware-wallets": "npm:^5.7.0" "@ethersproject/providers": "npm:^5.7.2" "@google-cloud/secret-manager": "npm:^5.5.0" - "@hyperlane-xyz/helloworld": "npm:3.14.0" + "@hyperlane-xyz/helloworld": "npm:3.15.0" "@hyperlane-xyz/registry": "npm:1.3.0" - "@hyperlane-xyz/sdk": "npm:3.14.0" - "@hyperlane-xyz/utils": "npm:3.14.0" + "@hyperlane-xyz/sdk": "npm:3.15.0" + "@hyperlane-xyz/utils": "npm:3.15.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-etherscan": "npm:^3.0.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" @@ -5885,15 +5885,15 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/sdk@npm:3.14.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@npm:3.15.0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: "@aws-sdk/client-s3": "npm:^3.74.0" "@cosmjs/cosmwasm-stargate": "npm:^0.31.3" "@cosmjs/stargate": "npm:^0.31.3" - "@hyperlane-xyz/core": "npm:3.14.0" - "@hyperlane-xyz/utils": "npm:3.14.0" + "@hyperlane-xyz/core": "npm:3.15.0" + "@hyperlane-xyz/utils": "npm:3.15.0" "@nomiclabs/hardhat-ethers": "npm:^2.2.3" "@nomiclabs/hardhat-waffle": "npm:^2.0.6" "@safe-global/api-kit": "npm:1.3.0" @@ -5961,7 +5961,7 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/utils@npm:3.14.0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@npm:3.15.0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: From 6620fe6367cc8cbc50d2edab4b057ccec1da41ba Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Mon, 17 Jun 2024 12:29:19 -0400 Subject: [PATCH 56/59] fix: eliminate branching from `TokenRouter` (#3878) ### Description Fixes bug in `TokenRouter` which dispatches messages to token recipient rather than address in `Router` table when hook overrides are provided. ### Drive-by changes Simplifies inheritance tree of `TokenRouter` > `GasRouter` > `Router` > `MailboxClient` ### Backward compatibility Yes ### Testing Unit Tests --- .changeset/mean-impalas-leave.md | 6 ++ solidity/contracts/client/GasRouter.sol | 38 ++++++++- solidity/contracts/client/MailboxClient.sol | 81 ------------------- solidity/contracts/client/Router.sol | 65 ++++++++++++--- solidity/contracts/test/TestGasRouter.sol | 2 +- solidity/contracts/token/HypNative.sol | 16 +--- .../token/extensions/HypNativeScaled.sol | 12 ++- .../contracts/token/libs/FastTokenRouter.sol | 6 +- solidity/contracts/token/libs/TokenRouter.sol | 71 ++++++++-------- solidity/test/InterchainAccountRouter.t.sol | 7 +- solidity/test/token/HypERC20.t.sol | 33 ++++---- .../helloworld/contracts/HelloWorld.sol | 6 -- 12 files changed, 160 insertions(+), 183 deletions(-) create mode 100644 .changeset/mean-impalas-leave.md diff --git a/.changeset/mean-impalas-leave.md b/.changeset/mean-impalas-leave.md new file mode 100644 index 0000000000..caa85890f4 --- /dev/null +++ b/.changeset/mean-impalas-leave.md @@ -0,0 +1,6 @@ +--- +"@hyperlane-xyz/core": patch +"@hyperlane-xyz/helloworld": patch +--- + +fix: `TokenRouter.transferRemote` with hook overrides diff --git a/solidity/contracts/client/GasRouter.sol b/solidity/contracts/client/GasRouter.sol index 6f4b248e55..c5f9efd1c1 100644 --- a/solidity/contracts/client/GasRouter.sol +++ b/solidity/contracts/client/GasRouter.sol @@ -43,13 +43,13 @@ abstract contract GasRouter is Router { */ function quoteGasPayment( uint32 _destinationDomain - ) external view returns (uint256 _gasPayment) { - return _quoteDispatch(_destinationDomain, ""); + ) external view returns (uint256) { + return _GasRouter_quoteDispatch(_destinationDomain, "", address(hook)); } - function _metadata( + function _GasRouter_hookMetadata( uint32 _destination - ) internal view virtual override returns (bytes memory) { + ) internal view returns (bytes memory) { return StandardHookMetadata.overrideGasLimit(destinationGas[_destination]); } @@ -57,4 +57,34 @@ abstract contract GasRouter is Router { function _setDestinationGas(uint32 domain, uint256 gas) internal { destinationGas[domain] = gas; } + + function _GasRouter_dispatch( + uint32 _destination, + uint256 _value, + bytes memory _messageBody, + address _hook + ) internal returns (bytes32) { + return + _Router_dispatch( + _destination, + _value, + _messageBody, + _GasRouter_hookMetadata(_destination), + _hook + ); + } + + function _GasRouter_quoteDispatch( + uint32 _destination, + bytes memory _messageBody, + address _hook + ) internal view returns (uint256) { + return + _Router_quoteDispatch( + _destination, + _messageBody, + _GasRouter_hookMetadata(_destination), + _hook + ); + } } diff --git a/solidity/contracts/client/MailboxClient.sol b/solidity/contracts/client/MailboxClient.sol index bf6b79c343..3e0b751e6b 100644 --- a/solidity/contracts/client/MailboxClient.sol +++ b/solidity/contracts/client/MailboxClient.sol @@ -95,85 +95,4 @@ abstract contract MailboxClient is OwnableUpgradeable { function _isDelivered(bytes32 id) internal view returns (bool) { return mailbox.delivered(id); } - - function _metadata( - uint32 /*_destinationDomain*/ - ) internal view virtual returns (bytes memory) { - return ""; - } - - function _dispatch( - uint32 _destinationDomain, - bytes32 _recipient, - bytes memory _messageBody - ) internal virtual returns (bytes32) { - return - _dispatch(_destinationDomain, _recipient, msg.value, _messageBody); - } - - function _dispatch( - uint32 _destinationDomain, - bytes32 _recipient, - uint256 _value, - bytes memory _messageBody - ) internal virtual returns (bytes32) { - return - mailbox.dispatch{value: _value}( - _destinationDomain, - _recipient, - _messageBody, - _metadata(_destinationDomain), - hook - ); - } - - function _dispatch( - uint32 _destinationDomain, - bytes32 _recipient, - uint256 _value, - bytes memory _messageBody, - bytes memory _hookMetadata, - IPostDispatchHook _hook - ) internal virtual returns (bytes32) { - return - mailbox.dispatch{value: _value}( - _destinationDomain, - _recipient, - _messageBody, - _hookMetadata, - _hook - ); - } - - function _quoteDispatch( - uint32 _destinationDomain, - bytes32 _recipient, - bytes memory _messageBody - ) internal view virtual returns (uint256) { - return - mailbox.quoteDispatch( - _destinationDomain, - _recipient, - _messageBody, - _metadata(_destinationDomain), - hook - ); - } - - function _quoteDispatch( - uint32 _destinationDomain, - bytes32 _recipient, - bytes memory _messageBody, - bytes calldata _hookMetadata, - IPostDispatchHook _hook - ) internal view virtual returns (uint256) { - return - mailbox.quoteDispatch( - _destinationDomain, - _recipient, - _messageBody, - _hookMetadata, - _hook - ); - } } diff --git a/solidity/contracts/client/Router.sol b/solidity/contracts/client/Router.sol index ef14912f90..52f3fe5372 100644 --- a/solidity/contracts/client/Router.sol +++ b/solidity/contracts/client/Router.sol @@ -167,28 +167,73 @@ abstract contract Router is MailboxClient, IMessageRecipient { ); } - function _dispatch( + function _Router_dispatch( uint32 _destinationDomain, - bytes memory _messageBody - ) internal virtual returns (bytes32) { - return _dispatch(_destinationDomain, msg.value, _messageBody); + uint256 _value, + bytes memory _messageBody, + bytes memory _hookMetadata, + address _hook + ) internal returns (bytes32) { + bytes32 _router = _mustHaveRemoteRouter(_destinationDomain); + return + mailbox.dispatch{value: _value}( + _destinationDomain, + _router, + _messageBody, + _hookMetadata, + IPostDispatchHook(_hook) + ); } + /** + * DEPRECATED: Use `_Router_dispatch` instead + * @dev For backward compatibility with v2 client contracts + */ function _dispatch( uint32 _destinationDomain, - uint256 _value, bytes memory _messageBody - ) internal virtual returns (bytes32) { + ) internal returns (bytes32) { + return + _Router_dispatch( + _destinationDomain, + msg.value, + _messageBody, + "", + address(hook) + ); + } + + function _Router_quoteDispatch( + uint32 _destinationDomain, + bytes memory _messageBody, + bytes memory _hookMetadata, + address _hook + ) internal view returns (uint256) { bytes32 _router = _mustHaveRemoteRouter(_destinationDomain); return - super._dispatch(_destinationDomain, _router, _value, _messageBody); + mailbox.quoteDispatch( + _destinationDomain, + _router, + _messageBody, + _hookMetadata, + IPostDispatchHook(_hook) + ); } + /** + * DEPRECATED: Use `_Router_quoteDispatch` instead + * @dev For backward compatibility with v2 client contracts + */ function _quoteDispatch( uint32 _destinationDomain, bytes memory _messageBody - ) internal view virtual returns (uint256) { - bytes32 _router = _mustHaveRemoteRouter(_destinationDomain); - return super._quoteDispatch(_destinationDomain, _router, _messageBody); + ) internal view returns (uint256) { + return + _Router_quoteDispatch( + _destinationDomain, + _messageBody, + "", + address(hook) + ); } } diff --git a/solidity/contracts/test/TestGasRouter.sol b/solidity/contracts/test/TestGasRouter.sol index 3c8f6ecbf0..b74bd6ac6b 100644 --- a/solidity/contracts/test/TestGasRouter.sol +++ b/solidity/contracts/test/TestGasRouter.sol @@ -7,7 +7,7 @@ contract TestGasRouter is GasRouter { constructor(address _mailbox) GasRouter(_mailbox) {} function dispatch(uint32 _destination, bytes memory _msg) external payable { - _dispatch(_destination, _msg); + _GasRouter_dispatch(_destination, msg.value, _msg, address(hook)); } function _handle(uint32, bytes32, bytes calldata) internal pure override {} diff --git a/solidity/contracts/token/HypNative.sol b/solidity/contracts/token/HypNative.sol index 5afafdd96b..5b620d00a6 100644 --- a/solidity/contracts/token/HypNative.sol +++ b/solidity/contracts/token/HypNative.sol @@ -36,24 +36,16 @@ contract HypNative is TokenRouter { /** * @inheritdoc TokenRouter - * @dev uses (`msg.value` - `_amount`) as interchain gas payment and `msg.sender` as refund address. + * @dev uses (`msg.value` - `_amount`) as hook payment and `msg.sender` as refund address. */ function transferRemote( uint32 _destination, bytes32 _recipient, uint256 _amount - ) public payable virtual override returns (bytes32 messageId) { + ) external payable virtual override returns (bytes32 messageId) { require(msg.value >= _amount, "Native: amount exceeds msg.value"); - uint256 gasPayment = msg.value - _amount; - return - _transferRemote( - _destination, - _recipient, - _amount, - gasPayment, - bytes(""), - address(0) - ); + uint256 _hookPayment = msg.value - _amount; + return _transferRemote(_destination, _recipient, _amount, _hookPayment); } function balanceOf( diff --git a/solidity/contracts/token/extensions/HypNativeScaled.sol b/solidity/contracts/token/extensions/HypNativeScaled.sol index 625fa7e0ec..88ccaa4682 100644 --- a/solidity/contracts/token/extensions/HypNativeScaled.sol +++ b/solidity/contracts/token/extensions/HypNativeScaled.sol @@ -25,18 +25,16 @@ contract HypNativeScaled is HypNative { uint32 _destination, bytes32 _recipient, uint256 _amount - ) public payable override returns (bytes32 messageId) { + ) external payable override returns (bytes32 messageId) { require(msg.value >= _amount, "Native: amount exceeds msg.value"); - uint256 gasPayment = msg.value - _amount; - uint256 scaledAmount = _amount / scale; + uint256 _hookPayment = msg.value - _amount; + uint256 _scaledAmount = _amount / scale; return _transferRemote( _destination, _recipient, - scaledAmount, - gasPayment, - bytes(""), - address(0) + _scaledAmount, + _hookPayment ); } diff --git a/solidity/contracts/token/libs/FastTokenRouter.sol b/solidity/contracts/token/libs/FastTokenRouter.sol index b5fdaaee31..8e4418a89e 100644 --- a/solidity/contracts/token/libs/FastTokenRouter.sol +++ b/solidity/contracts/token/libs/FastTokenRouter.sol @@ -109,9 +109,11 @@ abstract contract FastTokenRouter is TokenRouter { _fastTransferId ); - messageId = _dispatch( + messageId = _GasRouter_dispatch( _destination, - TokenMessage.format(_recipient, _amountOrId, metadata) + msg.value, + TokenMessage.format(_recipient, _amountOrId, metadata), + address(hook) ); emit SentTransferRemote(_destination, _recipient, _amountOrId); } diff --git a/solidity/contracts/token/libs/TokenRouter.sol b/solidity/contracts/token/libs/TokenRouter.sol index c5d0bf6b13..96ffcf8385 100644 --- a/solidity/contracts/token/libs/TokenRouter.sol +++ b/solidity/contracts/token/libs/TokenRouter.sol @@ -57,14 +57,7 @@ abstract contract TokenRouter is GasRouter { uint256 _amountOrId ) external payable virtual returns (bytes32 messageId) { return - _transferRemote( - _destination, - _recipient, - _amountOrId, - msg.value, - bytes(""), - address(0) - ); + _transferRemote(_destination, _recipient, _amountOrId, msg.value); } /** @@ -97,45 +90,45 @@ abstract contract TokenRouter is GasRouter { ); } - /** - * @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain. - * @dev Delegates transfer logic to `_transferFromSender` implementation. - * @dev The metadata is the token metadata, and is DIFFERENT than the hook metadata. - * @dev Emits `SentTransferRemote` event on the origin chain. - * @param _destination The identifier of the destination chain. - * @param _recipient The address of the recipient on the destination chain. - * @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient. - * @param _gasPayment The amount of native token to pay for interchain gas. - * @param _hookMetadata The metadata passed into the hook - * @param _hook The post dispatch hook to be called by the Mailbox - * @return messageId The identifier of the dispatched message. - */ function _transferRemote( uint32 _destination, bytes32 _recipient, uint256 _amountOrId, - uint256 _gasPayment, - bytes memory _hookMetadata, - address _hook + uint256 _value ) internal returns (bytes32 messageId) { - bytes memory metadata = _transferFromSender(_amountOrId); - - if (address(_hook) == address(0)) { - messageId = _dispatch( - _destination, - _gasPayment, - TokenMessage.format(_recipient, _amountOrId, metadata) - ); - } else { - messageId = _dispatch( + return + _transferRemote( _destination, _recipient, - _gasPayment, - TokenMessage.format(_recipient, _amountOrId, metadata), - _hookMetadata, - IPostDispatchHook(_hook) + _amountOrId, + _value, + _GasRouter_hookMetadata(_destination), + address(hook) ); - } + } + + function _transferRemote( + uint32 _destination, + bytes32 _recipient, + uint256 _amountOrId, + uint256 _value, + bytes memory _hookMetadata, + address _hook + ) internal virtual returns (bytes32 messageId) { + bytes memory _tokenMetadata = _transferFromSender(_amountOrId); + bytes memory _tokenMessage = TokenMessage.format( + _recipient, + _amountOrId, + _tokenMetadata + ); + + messageId = _Router_dispatch( + _destination, + _value, + _tokenMessage, + _hookMetadata, + _hook + ); emit SentTransferRemote(_destination, _recipient, _amountOrId); } diff --git a/solidity/test/InterchainAccountRouter.t.sol b/solidity/test/InterchainAccountRouter.t.sol index d966857065..b610532c1b 100644 --- a/solidity/test/InterchainAccountRouter.t.sol +++ b/solidity/test/InterchainAccountRouter.t.sol @@ -479,6 +479,7 @@ contract InterchainAccountRouterTest is Test { uint64 payment, bytes32 data ) public { + CallLib.Call[] memory calls = getCalls(data); vm.assume(payment < gasLimit * igp.gasPrice()); // arrange bytes memory metadata = StandardHookMetadata.formatMetadata( @@ -495,11 +496,7 @@ contract InterchainAccountRouterTest is Test { // act vm.expectRevert("IGP: insufficient interchain gas payment"); - originRouter.callRemote{value: payment}( - destination, - getCalls(data), - metadata - ); + originRouter.callRemote{value: payment}(destination, calls, metadata); } function testFuzz_callRemoteWithOverrides_default(bytes32 data) public { diff --git a/solidity/test/token/HypERC20.t.sol b/solidity/test/token/HypERC20.t.sol index 300e59c549..9191d32254 100644 --- a/solidity/test/token/HypERC20.t.sol +++ b/solidity/test/token/HypERC20.t.sol @@ -23,6 +23,7 @@ import {XERC20LockboxTest, XERC20Test, FiatTokenTest, ERC20Test} from "../../con import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol"; import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol"; import {GasRouter} from "../../contracts/client/GasRouter.sol"; +import {IPostDispatchHook} from "../../contracts/interfaces/hooks/IPostDispatchHook.sol"; import {HypERC20} from "../../contracts/token/HypERC20.sol"; import {HypERC20Collateral} from "../../contracts/token/HypERC20Collateral.sol"; @@ -198,38 +199,38 @@ abstract contract HypTokenTest is Test { function _performRemoteTransferWithHook( uint256 _msgValue, - uint256 _amount + uint256 _amount, + address _hook, + bytes memory _hookMetadata ) internal returns (bytes32 messageId) { vm.prank(ALICE); messageId = localToken.transferRemote{value: _msgValue}( DESTINATION, BOB.addressToBytes32(), _amount, - bytes(""), - address(noopHook) + _hookMetadata, + address(_hook) ); _processTransfers(BOB, _amount); assertEq(remoteToken.balanceOf(BOB), _amount); } - function testTransfer_withHookSpecified() public { + function testTransfer_withHookSpecified( + uint256 fee, + bytes calldata metadata + ) public { + TestPostDispatchHook hook = new TestPostDispatchHook(); + hook.setFee(fee); + vm.prank(ALICE); primaryToken.approve(address(localToken), TRANSFER_AMT); bytes32 messageId = _performRemoteTransferWithHook( REQUIRED_VALUE, - TRANSFER_AMT + TRANSFER_AMT, + address(hook), + metadata ); - assertTrue(noopHook.messageDispatched(messageId)); - /// @dev Using this test would be ideal, but vm.expectCall with nested functions more than 1 level deep is broken - /// In other words, the call graph of Route.transferRemote() -> Mailbox.dispatch() -> Hook.postDispatch() does not work with expectCall - // vm.expectCall( - // address(noopHook), - // abi.encodeCall( - // IPostDispatchHook.postDispatch, - // (bytes(""), outboundMessage) - // ) - // ); - /// @dev Also, using expectedCall with Mailbox.dispatch() won't work either because overloaded function selection is broken, see https://github.com/ethereum/solidity/issues/13815 + assertTrue(hook.messageDispatched(messageId)); } function testBenchmark_overheadGasUsage() public virtual { diff --git a/typescript/helloworld/contracts/HelloWorld.sol b/typescript/helloworld/contracts/HelloWorld.sol index b3bdb16fdc..fc81207c50 100644 --- a/typescript/helloworld/contracts/HelloWorld.sol +++ b/typescript/helloworld/contracts/HelloWorld.sol @@ -83,12 +83,6 @@ contract HelloWorld is Router { } // ============ Internal functions ============ - function _metadata( - uint32 /*_destinationDomain*/ - ) internal view override returns (bytes memory) { - return StandardHookMetadata.overrideGasLimit(HANDLE_GAS_AMOUNT); - } - /** * @notice Handles a message from a remote router. * @dev Only called for messages sent from a remote router, as enforced by Router.sol. From 921e449b4180677044b852ac395a1955328c8098 Mon Sep 17 00:00:00 2001 From: Nam Chu Hoai Date: Mon, 17 Jun 2024 13:00:22 -0400 Subject: [PATCH 57/59] feat: support priorityFee fetching from RPC and some better logging (#3972) ### Description `main` equivalent for https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3971 to get it in for the latest warp UI deployment --- .changeset/sixty-ducks-brush.md | 6 ++++ typescript/cli/src/deploy/warp.ts | 2 +- .../HyperlaneEtherscanProvider.ts | 1 + .../SmartProvider/HyperlaneJsonRpcProvider.ts | 7 ++++ .../SmartProvider/ProviderMethods.ts | 1 + .../providers/SmartProvider/SmartProvider.ts | 36 ++++++++++++++++++- typescript/sdk/src/token/deploy.ts | 15 +++++--- 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 .changeset/sixty-ducks-brush.md diff --git a/.changeset/sixty-ducks-brush.md b/.changeset/sixty-ducks-brush.md new file mode 100644 index 0000000000..ac4e4366cd --- /dev/null +++ b/.changeset/sixty-ducks-brush.md @@ -0,0 +1,6 @@ +--- +"@hyperlane-xyz/cli": patch +"@hyperlane-xyz/sdk": patch +--- + +Support priorityFee fetching from RPC and some better logging diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 61e34921bd..45919d2af2 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -125,7 +125,7 @@ async function executeDeploy(params: DeployParams) { const deployedContracts = await deployer.deploy(config); - logGreen('✅ Hyp token deployments complete'); + logGreen('✅ Warp contract deployments complete'); const warpCoreConfig = await getWarpCoreConfig(params, deployedContracts); if (!isDryRun) { diff --git a/typescript/sdk/src/providers/SmartProvider/HyperlaneEtherscanProvider.ts b/typescript/sdk/src/providers/SmartProvider/HyperlaneEtherscanProvider.ts index 908c7939ae..56fc50f57f 100644 --- a/typescript/sdk/src/providers/SmartProvider/HyperlaneEtherscanProvider.ts +++ b/typescript/sdk/src/providers/SmartProvider/HyperlaneEtherscanProvider.ts @@ -24,6 +24,7 @@ export class HyperlaneEtherscanProvider ProviderMethod.Call, ProviderMethod.EstimateGas, ProviderMethod.SendTransaction, + ProviderMethod.MaxPriorityFeePerGas, ]); constructor( diff --git a/typescript/sdk/src/providers/SmartProvider/HyperlaneJsonRpcProvider.ts b/typescript/sdk/src/providers/SmartProvider/HyperlaneJsonRpcProvider.ts index 8b434a29c4..b6695bcb29 100644 --- a/typescript/sdk/src/providers/SmartProvider/HyperlaneJsonRpcProvider.ts +++ b/typescript/sdk/src/providers/SmartProvider/HyperlaneJsonRpcProvider.ts @@ -32,6 +32,13 @@ export class HyperlaneJsonRpcProvider super(rpcConfig.connection ?? rpcConfig.http, network); } + prepareRequest(method: string, params: any): [string, any[]] { + if (method === ProviderMethod.MaxPriorityFeePerGas) { + return ['eth_maxPriorityFeePerGas', []]; + } + return super.prepareRequest(method, params); + } + async perform(method: string, params: any, reqId?: number): Promise { if (this.options?.debug) this.logger.debug( diff --git a/typescript/sdk/src/providers/SmartProvider/ProviderMethods.ts b/typescript/sdk/src/providers/SmartProvider/ProviderMethods.ts index 2a36c65990..6b74177861 100644 --- a/typescript/sdk/src/providers/SmartProvider/ProviderMethods.ts +++ b/typescript/sdk/src/providers/SmartProvider/ProviderMethods.ts @@ -16,6 +16,7 @@ export enum ProviderMethod { GetTransactionReceipt = 'getTransactionReceipt', GetLogs = 'getLogs', SendTransaction = 'sendTransaction', + MaxPriorityFeePerGas = 'maxPriorityFeePerGas', } export const AllProviderMethods = Object.values(ProviderMethod); diff --git a/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts b/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts index a3bb1f05b7..a5daf9ea0e 100644 --- a/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts +++ b/typescript/sdk/src/providers/SmartProvider/SmartProvider.ts @@ -1,4 +1,4 @@ -import { providers } from 'ethers'; +import { BigNumber, providers, utils } from 'ethers'; import { Logger } from 'pino'; import { @@ -97,6 +97,40 @@ export class HyperlaneSmartProvider this.supportedMethods = [...supportedMethods.values()]; } + async getPriorityFee() { + try { + return BigNumber.from(await this.perform('maxPriorityFeePerGas', {})); + } catch (error) { + return BigNumber.from('1500000000'); + } + } + + async getFeeData(): Promise { + // override hardcoded getFeedata + // Copied from https://github.com/ethers-io/ethers.js/blob/v5/packages/abstract-provider/src.ts/index.ts#L235 which SmartProvider inherits this logic from + const { block, gasPrice } = await utils.resolveProperties({ + block: this.getBlock('latest'), + gasPrice: this.getGasPrice().catch(() => { + return null; + }), + }); + + let lastBaseFeePerGas: BigNumber | null = null, + maxFeePerGas: BigNumber | null = null, + maxPriorityFeePerGas: BigNumber | null = null; + + if (block?.baseFeePerGas) { + // We may want to compute this more accurately in the future, + // using the formula "check if the base fee is correct". + // See: https://eips.ethereum.org/EIPS/eip-1559 + lastBaseFeePerGas = block.baseFeePerGas; + maxPriorityFeePerGas = await this.getPriorityFee(); + maxFeePerGas = block.baseFeePerGas.mul(2).add(maxPriorityFeePerGas); + } + + return { lastBaseFeePerGas, maxFeePerGas, maxPriorityFeePerGas, gasPrice }; + } + static fromChainMetadata( chainMetadata: ChainMetadataWithRpcConnectionInfo, options?: SmartProviderOptions, diff --git a/typescript/sdk/src/token/deploy.ts b/typescript/sdk/src/token/deploy.ts index ad865c4f02..2ad13c2f61 100644 --- a/typescript/sdk/src/token/deploy.ts +++ b/typescript/sdk/src/token/deploy.ts @@ -135,10 +135,17 @@ abstract class TokenDeployer< } async deploy(configMap: WarpRouteDeployConfig) { - const tokenMetadata = await TokenDeployer.deriveTokenMetadata( - this.multiProvider, - configMap, - ); + let tokenMetadata: TokenMetadata | undefined; + try { + tokenMetadata = await TokenDeployer.deriveTokenMetadata( + this.multiProvider, + configMap, + ); + } catch (err) { + this.logger.error('Failed to derive token metadata', err, configMap); + throw err; + } + const resolvedConfigMap = objMap(configMap, (_, config) => ({ ...tokenMetadata, gas: gasOverhead(config.type), From 1579ca221c2741764207d1d2fa9f8383ac633d55 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Mon, 17 Jun 2024 13:01:15 -0400 Subject: [PATCH 58/59] fix: disable initializers in xERC20 adapters (#3979) ### Description - Follow OZ best practice from https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract ### Backward compatibility Yes ### Testing Unit tests --- solidity/contracts/token/extensions/HypXERC20.sol | 4 +++- solidity/contracts/token/extensions/HypXERC20Lockbox.sol | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/solidity/contracts/token/extensions/HypXERC20.sol b/solidity/contracts/token/extensions/HypXERC20.sol index 9f50b4537f..84ab7a8791 100644 --- a/solidity/contracts/token/extensions/HypXERC20.sol +++ b/solidity/contracts/token/extensions/HypXERC20.sol @@ -8,7 +8,9 @@ contract HypXERC20 is HypERC20Collateral { constructor( address _xerc20, address _mailbox - ) HypERC20Collateral(_xerc20, _mailbox) {} + ) HypERC20Collateral(_xerc20, _mailbox) { + _disableInitializers(); + } function _transferFromSender( uint256 _amountOrId diff --git a/solidity/contracts/token/extensions/HypXERC20Lockbox.sol b/solidity/contracts/token/extensions/HypXERC20Lockbox.sol index 6c95abd3e7..f2c26e7846 100644 --- a/solidity/contracts/token/extensions/HypXERC20Lockbox.sol +++ b/solidity/contracts/token/extensions/HypXERC20Lockbox.sol @@ -18,6 +18,7 @@ contract HypXERC20Lockbox is HypERC20Collateral { lockbox = IXERC20Lockbox(_lockbox); xERC20 = lockbox.XERC20(); approveLockbox(); + _disableInitializers(); } /** From acaa22cd90c3856f529cd0d0efb62068ab143f6b Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 17 Jun 2024 19:55:31 +0200 Subject: [PATCH 59/59] fix: Do not consider xERC20 a collateral standard 2 (#3977) Amending https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/3973 to ensure output of the CLI doesn't incorrectly add `collateralAddressOrDenom` to xERC20 configs. --------- Co-authored-by: nambrot --- .changeset/olive-geckos-behave.md | 5 +++++ typescript/cli/src/deploy/warp.ts | 5 +---- typescript/sdk/src/token/TokenStandard.ts | 7 +++++-- typescript/sdk/src/warp/WarpCore.ts | 6 +++++- 4 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 .changeset/olive-geckos-behave.md diff --git a/.changeset/olive-geckos-behave.md b/.changeset/olive-geckos-behave.md new file mode 100644 index 0000000000..b199df2cb7 --- /dev/null +++ b/.changeset/olive-geckos-behave.md @@ -0,0 +1,5 @@ +--- +"@hyperlane-xyz/sdk": patch +--- + +Do not consider xERC20 a collateral standard to fix fungibility checking logic while maintaining mint limit checking diff --git a/typescript/cli/src/deploy/warp.ts b/typescript/cli/src/deploy/warp.ts index 45919d2af2..dfcd7a2c22 100644 --- a/typescript/cli/src/deploy/warp.ts +++ b/typescript/cli/src/deploy/warp.ts @@ -183,10 +183,7 @@ async function getWarpCoreConfig( return wrappedToken; } - if ( - config.type === TokenType.collateral || - config.type === TokenType.XERC20 - ) { + if (config.type === TokenType.collateral) { return config.token; } diff --git a/typescript/sdk/src/token/TokenStandard.ts b/typescript/sdk/src/token/TokenStandard.ts index ba03d4d509..d8346be16c 100644 --- a/typescript/sdk/src/token/TokenStandard.ts +++ b/typescript/sdk/src/token/TokenStandard.ts @@ -94,14 +94,17 @@ export const TOKEN_NFT_STANDARDS = [ export const TOKEN_COLLATERALIZED_STANDARDS = [ TokenStandard.EvmHypCollateral, TokenStandard.EvmHypNative, - TokenStandard.EvmHypXERC20, - TokenStandard.EvmHypXERC20Lockbox, TokenStandard.SealevelHypCollateral, TokenStandard.SealevelHypNative, TokenStandard.CwHypCollateral, TokenStandard.CwHypNative, ]; +export const MINT_LIMITED_STANDARDS = [ + TokenStandard.EvmHypXERC20, + TokenStandard.EvmHypXERC20Lockbox, +]; + export const TOKEN_HYP_STANDARDS = [ TokenStandard.EvmHypNative, TokenStandard.EvmHypCollateral, diff --git a/typescript/sdk/src/warp/WarpCore.ts b/typescript/sdk/src/warp/WarpCore.ts index dc9988a034..f68201d73f 100644 --- a/typescript/sdk/src/warp/WarpCore.ts +++ b/typescript/sdk/src/warp/WarpCore.ts @@ -22,6 +22,7 @@ import { Token } from '../token/Token.js'; import { TokenAmount } from '../token/TokenAmount.js'; import { parseTokenConnectionId } from '../token/TokenConnection.js'; import { + MINT_LIMITED_STANDARDS, TOKEN_COLLATERALIZED_STANDARDS, TOKEN_STANDARD_TO_PROVIDER_TYPE, TokenStandard, @@ -427,7 +428,10 @@ export class WarpCore { originToken.getConnectionForChain(destinationName)?.token; assert(destinationToken, `No connection found for ${destinationName}`); - if (!TOKEN_COLLATERALIZED_STANDARDS.includes(destinationToken.standard)) { + if ( + !TOKEN_COLLATERALIZED_STANDARDS.includes(destinationToken.standard) && + !MINT_LIMITED_STANDARDS.includes(destinationToken.standard) + ) { this.logger.debug( `${destinationToken.symbol} is not collateralized, skipping`, );