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 f847f584b..000000000 --- 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 fe5b1c953..000000000 --- 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 b1523f71d..000000000 --- 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 df8957059..000000000 --- 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 821e105c0..000000000 --- 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 37d54bb10..000000000 --- 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 bb3548828..e1735ad8e 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 96077c433..4c496eb59 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 561ec8845..bcee12745 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 1fe18f6bc..f2ea58a20 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 118f93943..8f42fe354 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 b5ce7a6f1..8ee9535f0 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 68e87af9a..e253aa91f 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 fec151087..979fb8285 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 edd483b2f..376a7656e 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 4c249b07d..21b608413 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 a000f25e1..e78e76a7f 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 f87c40305..152e40bd6 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 d543d5856..4627de39f 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 2f6ec6c57..f72911518 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 02c8cd29f..c6db537ec 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 ef6de3e36..4710b1bab 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 4c496eb59..3115dab14 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 f2ea58a20..f7a9829c8 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 8ee9535f0..4d5ddf2db 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 e253aa91f..f1447b455 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 376a7656e..f577cf8d8 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 e78e76a7f..428569c33 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 4627de39f..c0fa21462 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 c6db537ec..094b5b053 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 4710b1bab..2adc97823 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 000000000..f530f652f --- /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 64cf0fd66..a8f12f503 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 1ffdf19fc..36c15751c 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 e4aec1e77..ac311f447 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 56dbd116e..6880dd220 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 33d26d2fe..eb747804c 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 e68cee52f..607b4a3f6 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 e7dd84b78..9aa3618f9 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 ced36006a..679dbfc2d 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 2d2d770c6..04551feee 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 0322d7629..ba33c80eb 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 d800b4a1c..041d45fa6 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 20fe12cda..8493414cd 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 4c704aa12..d662581f0 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 cb6c3818c..117f059a6 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 1afca2bdb..93a42e597 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 000000000..bff29e6e1 --- /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 d8b69f4ea..dd039b7d9 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 ba33c80eb..5909e10bc 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 f931c0fa7..7a5c88260 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 000000000..bff29e6e1 --- /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 59b1c74d6..5c8095208 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 4350baeff..dc3091149 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 3aae0d308..3e3b5aa61 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 56dbe3eb8..c38449cae 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 581620f2f..0496e38ca 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 587a56b8d..234813573 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 cc113cacf..d71343281 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 98a5fe6f8..e9e66eb30 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 8d2898009..043ac9249 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 97d84b622..9f401b399 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 5f6b504e9..153526d58 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 b0640f36e..00d9cb4c5 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 4a4727d03..62228a44b 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 9aa3618f9..25bfbb895 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 2b4d53e57..971e30530 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 550918b8d..a51936232 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 112dc4a77..31f01e7aa 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 29f33e936..0e5d4144e 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 b49623540..aa032b48a 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 a8f12f503..9b5fa1523 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 7d34ef96f..8fd3ca2aa 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 fbbaaf72d..cd76f9f2b 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 3b2d89d7a..bf1239efc 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 03a7d1655..5a0042c8e 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 eeb0b6bcf..dac9798f6 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 45d7a469a..444bc70f2 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 7226bdf0b..d027d5aad 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 c761c59c1..733bfd819 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 6ca80b39d..1994e91ce 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 7812dec3a..4a418def7 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 31eb1dc38..a2449ec43 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 d384e073d..c6f8d8568 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 dc36a05b9..9edcfe5f8 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 46f4db67e..0598d4807 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 f6baa1c16..e43646396 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 3e521b094..a426e079b 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 62228a44b..b6bb18d5f 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 72a0fc1f1..b9d90db2b 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 4a01616b8..763e1186f 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 000000000..af030bd5e --- /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 0ced1ef0f..39cc3669f 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 4d5ddf2db..47d0a1b11 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 aa2b7e036..3774b43cb 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 000000000..973c0cd25 --- /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 9a3e74ccd..51ab1ce35 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 000000000..d816fcb1f --- /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 2adc97823..009df5a75 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 000000000..8000edf81 --- /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 b498db4b9..bf30e78ee 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 232205dc0..c2655cab3 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 19f1a1fbd..189d5198c 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 a2f411d2e..f1082b301 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 a4a92c465..eb07f189a 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 e48e71237..102592bbc 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 dcd3aa6fd..23cd5ba52 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 517668a59..be49a13da 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 470f0a20d..3c0e7477d 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 a878cfc24..43fe0beda 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 a2449ec43..cfe2243a8 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 07f94c0ce..e5852d1f3 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 9edcfe5f8..6a06fa88f 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 0598d4807..d9fefa0e1 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 7d965f0bc..99b106652 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 084979d0e..5c74845a4 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 a426e079b..d1877c930 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 fd338351e..aceb6509e 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 db4410903..7605382c2 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 f07df3f25..c449e0e42 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 fd0e91d2c..5b95b13d1 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 8c7d0a52e..2c929123a 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 f8a560804..335a421e2 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 e186c4e1c..6c6ffa031 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 f1c7ab448..1d4202e05 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 0699fe5c4..d62285705 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 9ee5fba38..e1b476520 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 de0bfd2ee..ea85d05aa 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 febbd57a9..3fc25ade3 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 f5dfd906a..67bd1a0f4 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 a924bc92c..5cae1811f 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 9ea8141fa..0eeb41dbc 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 9b0e366f8..48b38841f 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 e8d57a4a7..59a2e597d 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 50ef36dbb..57e45a7bd 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 25ed9e50b..49323191d 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 f4f4e93b9..ad865c4f0 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 394b4c9fb..65f780cd0 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 2890261a5..3ec33bf8f 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 0d0a70800..8cbead331 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 000000000..2babea6c0 --- /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 6a5ccb733..ab8ed10d1 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 9b5fa1523..f9f15f0a8 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 c4d1920ca..4f15ba912 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 6880dd220..42d9c75c9 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 96903af85..e6f094c7d 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 5ee9aba1c..02b15a978 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 dd039b7d9..f29c2c80e 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 5c8095208..2df8f54d9 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 3e3b5aa61..166ee6561 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 fd6a6b280..d70c2bfc7 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 e217d4bb4..3efd04a8d 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 aef515b2b..374b4b797 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 da61a26ce..3d164ce26 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 000000000..46b8eabc3 --- /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 47d0a1b11..311f50585 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 b153854df..fba159a69 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 f3da40e48..8f8bf2043 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 eb07f189a..0968f0d99 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 fe6737cf8..000000000 --- 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 f577cf8d8..64d3140aa 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 91abea01c..a85488014 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 428569c33..4f8fcb014 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 009df5a75..fd3c714ee 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 000000000..372ca45e8 --- /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 a4cc08a3b..177b098d2 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 9a4af1ecb..a1c868d03 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 87f38420e..8d4580c24 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 ab043e641..c31351abc 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 f58b526ba..9f50b4537 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 000000000..f4a860917 --- /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 3f63c477a..2c9bad49a 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 000000000..ba01f7bc4 --- /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 5be0bd5ed..82c5359b7 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 189d5198c..d9ca9b4e5 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 3a1e6414d..9cd97c919 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 5c0e0af64..7ddc870e5 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 d1394d49c..8bdd0defc 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 59a2e597d..a68b2a5a9 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 57e45a7bd..d9982522c 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 3ec33bf8f..8ce070c1e 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 000000000..2ed82ae27 --- /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 05250bad8..09748de98 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 ca539be79..f3cfc6c1c 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 755d568a9..561ef2a5c 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 2c6b48c60..398190a9c 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 d424c76c3..61fdab217 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 c0fa21462..342287e22 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 2c12cd00c..a222617b3 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 000000000..a60c05b0a --- /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 41fd03add..58bf38f27 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 f3cb21877..92f0e3023 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 32436f453..c5db5165c 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 aceb6509e..79f780df7 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 64483d431..b62fd321c 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 2fe0b8f34..f535277e4 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 117f059a6..13c06654d 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 8ddae22f6..52f725219 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 000000000..844a87df5 --- /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 000000000..66e364bbc --- /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 93a42e597..64445f82a 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 f27e7114f..afb6539b0 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 000000000..ed66277ce --- /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 000000000..a3adb819d --- /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 0a08676e9..89892662d 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 7dbd38424..fc498469a 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 25eb1383e..9f8c67ed2 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 08b85a1c8..fdaa20476 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 d1a3bb910..ebca6e75e 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 21e122d86..131ab1f4b 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 55e3c4600..122a4f82a 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 e651fafa6..99cb9772e 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 fd3c714ee..f8e0a469e 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 0a4e32a01..6148c3c4e 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 9e59fd8f6..28a584ca5 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 021d34db4..07f6323da 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 4ea9ce8f1..80d49f974 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 46b8eabc3..f7db1abda 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 000000000..c16594638 --- /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 f2fce2d1d..b38762be6 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 d8890a77b..60e2fceea 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 39cc3669f..a8b9127f3 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 000000000..681ed9dee --- /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 000000000..9d23bffaa --- /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 000000000..04a51b6b6 --- /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 9e05f2fcb..584ad97b9 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 9734b3488..844eb4b79 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 da037f19b..097855f17 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 c6934a77c..2e6b00573 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 b4bdfbb4d..b6c9eb7e4 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 f6870a31c..70158d267 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 b97e3e5f4..85bf36c1c 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 e80944432..aa7bee534 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 30430e04e..3c3a3aa65 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 3c16651f6..0885a3e4a 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 f7db1abda..000000000 --- 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 af030bd5e..000000000 --- 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 8000edf81..000000000 --- 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 372ca45e8..000000000 --- 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 c16594638..000000000 --- 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 2ed82ae27..000000000 --- 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 e1735ad8e..f8cee2164 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 3115dab14..bdbe1f659 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 bcee12745..40e0e5390 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 f7a9829c8..cc07a17b3 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 8f42fe354..c62b58046 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 311f50585..645d3a48b 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 f1447b455..9fb80ae08 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 979fb8285..f5e8404be 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 64d3140aa..7d3f62b3d 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 21b608413..0696269d8 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 4f8fcb014..33f8d6194 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 152e40bd6..b484d11bc 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 342287e22..ef58f43cd 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 f72911518..0dce2d1b6 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 094b5b053..260348910 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 f8e0a469e..9fc2aab50 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 25bfbb895..00ebf00f0 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 0e5d4144e..c511b72e6 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 f9f15f0a8..80c0efc97 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 a8bc14983..6a765693f 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 c21e411e5..cdde17fe3 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 318d67e7b..caa3c1018 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 9eaa66e1c..b93de05c9 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 a85488014..8c8be46bb 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 33f8d6194..67de934c2 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 d027d5aad..3ac0f5ebd 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 2ec6431b8..127f4bf16 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 2ab70835b..95ba7ee4c 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 02b15a978..000034581 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 101bd6779..4e39ab14f 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 8653ef908..ff8f771fe 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 e06f13421..61126c371 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 35c30fb5b..b125bac95 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 41eabefae..66a9be195 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 b64035b2b..414ca34bd 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 fbb9b74c7..72e30f6d9 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 0ce5e21f3..cc4fd2d62 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 476fe3913..d3a8075ff 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 0885a3e4a..49f05e883 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 9fc2aab50..8b293bfc7 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 31a60fecf..770b181c2 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 667d392ad..fe32b8630 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 62c162869..4ce2ab88d 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 071d8e5cc..a78d0e953 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 858d975ff..5abcc05a8 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 42d9c75c9..5bd203227 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 18d94c2e4..f16da2ed3 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 10b6f54c1..2b2cd3331 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 c65393e55..c966447a1 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 6e594ffa7..965f0f38b 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 c6f8d8568..6dd1ba4c9 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 80c0efc97..25e01b03d 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 4f15ba912..0df01f1d7 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 6a765693f..94c76e53a 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 ac311f447..0f3a6d287 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 5bd203227..085586548 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 e6f094c7d..e8c10ba0e 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 b93de05c9..1d1204083 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 607b4a3f6..cf3d2782c 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 12b1e0db9..286fd126a 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 610ebf8bb..b89122281 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 02dc71378..a41589924 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 8f1828424..933210d8d 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 3ac0f5ebd..5f814e827 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 127f4bf16..6b0ed65fd 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 000034581..b949b9a32 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 4e39ab14f..f3cbb1a49 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 38ab8969f..03cb408eb 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 414ca34bd..2be5bc4ea 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 72e30f6d9..5b6fa0350 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 a55a6524f..f75d19cfd 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 1345894d8..ba2be0750 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 e6f25f92b..19fc3d848 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 b149b6a0b..ae29c7842 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 44651380d..6366049fa 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 e334c921e..4a1c3d7a1 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 770b181c2..3680b07c9 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 000000000..49efaaf2f --- /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 000000000..74e05a094 --- /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 8d4580c24..03b306429 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 f4a860917..6c95abd3e 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 bd9a3e232..a3f7d463f 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 8180d9b58..51a9912cb 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 e8a047e3f..52715a217 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 000000000..b0db0d828 --- /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 000000000..4d6366a8c --- /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 000000000..182306eab --- /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 000000000..e2c79bae6 --- /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 000000000..f6171eb63 --- /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 000000000..eb1be413a --- /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 82c5359b7..300e59c54 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 085586548..04ec56c98 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 e8c10ba0e..dee5ab3f2 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 e38df3a9f..c8e7623ea 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 b6e6c51cd..2d9fe8a5d 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 48caa1f77..a6e270a48 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 b6bb18d5f..960b5b770 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 000000000..7eca8c578 --- /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 3efd04a8d..135835e12 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 ef8c2ad2d..6881d0676 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 dc3091149..66d1a57d3 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 0496e38ca..085e43ee6 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 9f6936a22..364181df6 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 d71343281..f33f00556 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 043ac9249..23e96aeb5 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 4ba2ca87a..4444a56ea 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 7f686cb85..833b92b89 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 c8e798096..54acdf80f 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 8ed514c83..76345ec8f 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 d70c2bfc7..37933f5f4 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 a94ceff32..5836838ef 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 32ad5b953..1a39fae07 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 000000000..bdf3e52f9 --- /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 d969210a6..3385872c3 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 035fe6e6d..5e8f0cf05 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 494583381..beebcb9db 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 3fc8393d1..beb4e86c3 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 9fe48053c..8c1132add 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 c7d7274d6..016454d04 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 d85b3618f..242028acb 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 135835e12..6a0f66a78 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 374b4b797..7314e2a00 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 d3abb4384..9303438b0 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 85bf36c1c..9c8ba75d6 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 59b8fa11a..6757a545e 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 cfe92b8dc..b835b94df 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 3db7e4f57..1c05360ff 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 2a0bbb897..000000000 --- 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 59f20630b..c8b2ad346 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 690021046..15b258481 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 a287b2bd1..0e88e685e 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 f0870da0a..115e249cd 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 de6577bfa..2a95d627e 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 a8d44b48c..62b311712 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 5f814e827..313af5d44 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 95ba7ee4c..307e4515c 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 733bfd819..fc6719051 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 000000000..0f2c9fd5f --- /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 000000000..78881a01e --- /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 9c15c6597..0e1f782d4 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 66a9be195..c959bca65 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 2be5bc4ea..51584fc37 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 cc4fd2d62..56d913b7c 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 f29c2c80e..f1c4f79b2 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 2df8f54d9..cf35092f7 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 000000000..62b896d62 --- /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 1223702f8..7d085f529 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 cd9dd61c0..a07210391 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 60c2ce0c5..dd7bac22b 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 6881d0676..007208554 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 66d1a57d3..20eb93616 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 b2f8369d0..a0c373adc 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 166ee6561..6545d480a 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 085e43ee6..4206c0584 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 364181df6..264ef0380 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 3d164ce26..b4323613a 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 5f34bc209..21ee23235 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 e85b04f4a..b168a1892 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 206e062e2..c6d494467 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 2a3c53d40..c5636b3b9 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 45c07d030..99b0e41c9 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 7e1358dfd..476a10725 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 4258f149c..934a3758a 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 1a3f1e7cd..48cc117e2 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 bebe06348..acdd3057d 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 15b258481..2191f2ac8 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 0e88e685e..1bf299075 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 5c2768ae1..3775ef8e9 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 bf5b7d417..9b0fe41e4 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 206b4bc69..531970174 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 04551feee..7ff7af932 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 00ebf00f0..c4c494f46 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 2d9fe8a5d..fd35820b5 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 a6e270a48..e91bed49e 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 960b5b770..664383cc2 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 a88580d40..b7e03e675 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 0c69ee374..61261f11d 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 3d8c90082..99edbd11d 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 fd35820b5..a854837a0 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 000000000..3cff77784 --- /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 282712098..f54822817 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 b9d90db2b..629e08acb 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 6fc7eba09..dfedb241a 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 1463d11a1..0da80ff79 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 e91bed49e..4f52f82bb 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 881d8f9bd..a3eba2c88 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 f50a17b4d..70e77f0dd 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 664383cc2..b83bc9c35 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 20eb93616..84731aa63 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 6545d480a..1c81c3017 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 971e30530..7d24fcc20 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 844a87df5..09f90012c 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 51732aaff..cf7bdde70 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 00ab9b855..1a507f77c 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 000000000..98413f21c --- /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 60e2fceea..1c20dae29 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 681ed9dee..79715a676 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 9d23bffaa..d1bdd0716 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 04a51b6b6..ce238df62 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 98413f21c..000000000 --- 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 74e05a094..000000000 --- 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 f8cee2164..d972340c6 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 bdbe1f659..809fc0b32 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 40e0e5390..abe728f76 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 cc07a17b3..a18b7eeeb 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 c62b58046..08968e90d 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 645d3a48b..855091d80 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 9fb80ae08..69e26984e 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 f5e8404be..577113c0d 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 7d3f62b3d..c7aba6272 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 0696269d8..e1da9e3b3 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 67de934c2..75a58f80b 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 b484d11bc..245e84426 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 ef58f43cd..05880d466 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 0dce2d1b6..e0ab748ce 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 260348910..e085760d0 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 8b293bfc7..00c79e108 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 3da986a51..36ea71a2a 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 809fc0b32..7a7c60fb9 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 855091d80..0a9e7b804 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 c7aba6272..96c5ef9f8 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 00c79e108..cef359be8 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 fe32b8630..03f4dc44d 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 000000000..32d0e5274 --- /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 03b306429..b9e43a7ac 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 2c9bad49a..61b03e46b 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 0968f0d99..61e34921b 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 9cd97c919..754538903 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 7ddc870e5..1a4747b7b 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 8bdd0defc..ba03d4d50 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 3fc25ade3..4d549b865 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 67bd1a0f4..f0a8032d5 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 e24ebd5b7..b10eb3fdc 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 9c9da1ca6..dc9988a03 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 32d0e5274..000000000 --- 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 d972340c6..0763cef17 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 7a7c60fb9..0d2921a58 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 abe728f76..ee27ea446 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 a18b7eeeb..c5e859ac2 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 08968e90d..be0e07baa 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 0a9e7b804..aaa8fe0e8 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 69e26984e..eb5b8185d 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 577113c0d..1ea1e9cc3 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 96c5ef9f8..22138b9bc 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 e1da9e3b3..ed68044f8 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 75a58f80b..daede08e4 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 245e84426..3810b575d 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 05880d466..0b74f6a0c 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 e0ab748ce..a796ccac6 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 e085760d0..c7f4b7ec0 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 cef359be8..577194fa9 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 000000000..caa85890f --- /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 6f4b248e5..c5f9efd1c 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 bf6b79c34..3e0b751e6 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 ef14912f9..52f3fe537 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 3c8f6ecbf..b74bd6ac6 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 5afafdd96..5b620d00a 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 625fa7e0e..88ccaa468 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 b5fdaaee3..8e4418a89 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 c5d0bf6b1..96ffcf838 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 d96685706..b610532c1 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 300e59c54..9191d3225 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 b3bdb16fd..fc81207c5 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 000000000..ac4e4366c --- /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 61e34921b..45919d2af 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 908c7939a..56fc50f57 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 8b434a29c..b6695bcb2 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 2a36c6599..6b7417786 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 a3bb1f05b..a5daf9ea0 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 ad865c4f0..2ad13c2f6 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 9f50b4537..84ab7a879 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 6c95abd3e..f2c26e784 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 000000000..b199df2cb --- /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 45919d2af..dfcd7a2c2 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 ba03d4d50..d8346be16 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 dc9988a03..f68201d73 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`, );